1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
lens/src/renderer/components/kube-object-status-icon/kube-object-status-icon.tsx
Janne Savolainen a12b94405a
Resolve kube object status texts again on re-render to make it look like it's reactive (#5875)
* Make tests for kube object status icon more realistic

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Start resolving kube object status texts on each re-render to mimic reactivity

Note: This is done due the current implementation exposed in Extension API expects it to work so. However, this is bad.

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Switch to using existing implementation for isDefined

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
2022-07-27 09:21:38 +03:00

152 lines
4.6 KiB
TypeScript

/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import "./kube-object-status-icon.scss";
import React from "react";
import { Icon } from "../icon";
import { cssNames, formatDuration, getOrInsert, isDefined } from "../../utils";
import { withInjectables } from "@ogre-tools/injectable-react";
import kubeObjectStatusTextsForObjectInjectable from "./kube-object-status-texts-for-object.injectable";
import type { KubeObject } from "../../../common/k8s-api/kube-object";
import type { KubeObjectStatus } from "../../../common/k8s-api/kube-object-status";
import { KubeObjectStatusLevel } from "../../../common/k8s-api/kube-object-status";
import type { IComputedValue } from "mobx";
import { observer } from "mobx-react";
import type { KubeObjectStatusText } from "./kube-object-status-text-injection-token";
function statusClassName(level: KubeObjectStatusLevel): string {
switch (level) {
case KubeObjectStatusLevel.INFO:
return "info";
case KubeObjectStatusLevel.WARNING:
return "warning";
case KubeObjectStatusLevel.CRITICAL:
return "error";
}
}
function statusTitle(level: KubeObjectStatusLevel): string {
switch (level) {
case KubeObjectStatusLevel.INFO:
return "Info";
case KubeObjectStatusLevel.WARNING:
return "Warning";
case KubeObjectStatusLevel.CRITICAL:
return "Critical";
}
}
function getAge(timestamp: string | undefined) {
return timestamp
? formatDuration(Date.now() - new Date(timestamp).getTime(), true)
: "";
}
interface SplitStatusesByLevel {
maxLevel: string;
criticals: KubeObjectStatus[];
warnings: KubeObjectStatus[];
infos: KubeObjectStatus[];
}
/**
* This function returns the class level for corresponding to the highest status level
* and the statuses split by their levels.
* @param statuses a list of status items
*/
function splitByLevel(statuses: KubeObjectStatus[]): SplitStatusesByLevel {
const parts = new Map<KubeObjectStatusLevel, KubeObjectStatus[]>();
for (const status of statuses) {
const part = getOrInsert(parts, status.level, []);
part.push(status);
}
const criticals = parts.get(KubeObjectStatusLevel.CRITICAL) ?? [];
const warnings = parts.get(KubeObjectStatusLevel.WARNING) ?? [];
const infos = parts.get(KubeObjectStatusLevel.INFO) ?? [];
const maxLevel = statusClassName(criticals[0]?.level ?? warnings[0]?.level ?? infos[0].level);
return { maxLevel, criticals, warnings, infos };
}
export interface KubeObjectStatusIconProps {
object: KubeObject;
}
interface Dependencies {
statuses: IComputedValue<KubeObjectStatusText[]>;
}
@observer
class NonInjectedKubeObjectStatusIcon extends React.Component<KubeObjectStatusIconProps & Dependencies> {
renderStatuses(statuses: KubeObjectStatus[], level: number) {
const filteredStatuses = statuses.filter((item) => item.level == level);
return filteredStatuses.length > 0 && (
<div className={cssNames("level", statusClassName(level))}>
<span className="title">
{statusTitle(level)}
</span>
{
filteredStatuses.map((status, index) => (
<div key={`kube-resource-status-${level}-${index}`} className={cssNames("status", "msg")}>
{`- ${status.text} `}
<span className="age">
{" . "}
{getAge(status.timestamp)}
</span>
</div>
))
}
</div>
);
}
render() {
const statusTexts = this.props.statuses.get();
const statuses = statusTexts
.map((statusText) => statusText.resolve(this.props.object))
.filter(isDefined);
if (statuses.length === 0) {
return null;
}
const { maxLevel, criticals, warnings, infos } = splitByLevel(statuses);
return (
<Icon
material={maxLevel}
className={cssNames("KubeObjectStatusIcon", maxLevel)}
data-testid={`kube-object-status-icon-for-${this.props.object.getId()}`}
tooltip={{
children: (
<div className="KubeObjectStatusTooltip">
{this.renderStatuses(criticals, KubeObjectStatusLevel.CRITICAL)}
{this.renderStatuses(warnings, KubeObjectStatusLevel.WARNING)}
{this.renderStatuses(infos, KubeObjectStatusLevel.INFO)}
</div>
),
}}
/>
);
}
}
export const KubeObjectStatusIcon = withInjectables<Dependencies, KubeObjectStatusIconProps>(
NonInjectedKubeObjectStatusIcon,
{
getProps: (di, props) => ({
statuses: di.inject(kubeObjectStatusTextsForObjectInjectable, props.object),
...props,
}),
},
);