1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Merge branch 'cluster-icon-settings-injectables' of https://github.com/lensapp/lens into cluster-icon-settings-injectables

This commit is contained in:
Alex Andreev 2023-03-13 15:25:07 +03:00
commit 77197c889d
10 changed files with 1018 additions and 568 deletions

777
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,6 @@
"adr": "^1.4.3",
"cross-env": "^7.0.3",
"lerna": "^6.5.1",
"rimraf": "^4.3.1"
"rimraf": "^4.4.0"
}
}

View File

@ -80,6 +80,8 @@ export interface ContainerState {
terminated?: ContainerStateTerminated;
}
export type ContainerStateValues = Partial<ContainerState[keyof ContainerState]>;
export interface PodContainerStatus {
name: string;
state?: ContainerState;
@ -649,7 +651,7 @@ export class Pod extends KubeObject<
.filter(({ name }) => runningContainerNames.has(name));
}
getContainerStatuses(includeInitContainers = true) {
getContainerStatuses(includeInitContainers = true): PodContainerStatus[] {
const { containerStatuses = [], initContainerStatuses = [] } = this.status ?? {};
if (includeInitContainers) {

View File

@ -856,6 +856,601 @@ exports[`installing update when started when user checks for updates when new up
`;
exports[`installing update when started when user checks for updates when new update is discovered when download fails renders 1`] = `
<body>
<div>
<div
class="ClusterManager"
>
<div
class="topBar"
>
<div
class="items"
>
<div
class="preventedDragging"
>
<i
class="Icon material interactive disabled focusable"
data-testid="home-button"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
</div>
<div
class="size-sm"
/>
<div
class="preventedDragging"
>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
</div>
<div
class="size-sm"
/>
<div
class="preventedDragging"
>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="separator"
/>
</div>
</div>
<main>
<div
id="lens-views"
/>
<div
class="flex justify-center Welcome align-center"
data-testid="welcome-page"
>
<div
data-testid="welcome-banner-container"
style="width: 320px;"
>
<i
class="Icon logo svg focusable"
>
<span
class="icon"
/>
</i>
<div
class="flex justify-center"
>
<div
data-testid="welcome-text-container"
style="width: 320px;"
>
<h2>
Welcome to some-product-name!
</h2>
<p>
To get you started we have auto-detected your clusters in your
kubeconfig file and added them to the catalog, your centralized
view for managing all your cloud-native resources.
<br />
<br />
If you have any questions or feedback, please join our
<a
class="link"
href="https://forums.k8slens.dev"
rel="noreferrer"
target="_blank"
>
Lens Forums
</a>
.
</p>
<ul
class="block"
data-testid="welcome-menu-container"
style="width: 320px;"
>
<li
class="flex grid-12"
>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="view_list"
>
view_list
</span>
</i>
<a
class="box col-10"
>
Browse Clusters in Catalog
</a>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="navigate_next"
>
navigate_next
</span>
</i>
</li>
</ul>
</div>
</div>
</div>
</div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
>
<div
class="HotbarCell isDraggingOwner animateDown"
index="0"
>
<div
style="z-index: 12; position: absolute;"
>
<div
class="HotbarIcon contextMenuAvailable"
>
<div
class="Avatar rounded disabled avatar"
id="hotbarIcon-hotbar-icon-catalog-entity"
style="width: 40px; height: 40px; background: rgb(5, 1, 130);"
>
Ca
</div>
</div>
</div>
</div>
<div
class="HotbarCell isDraggingOwner animateDown"
index="1"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="2"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="3"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="4"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="5"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="6"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="7"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="8"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="9"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="10"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="11"
/>
</div>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
1
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div>
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
data-testid="status-bar-left"
/>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;
exports[`installing update when started when user checks for updates when new update is discovered when download succeeds given checking for updates again when check resolves with different update that was previously downloaded when download resolves successfully renders 1`] = `
<body>
<div>
<div
class="ClusterManager"
>
<div
class="topBar"
>
<div
class="items"
>
<div
class="preventedDragging"
>
<i
class="Icon material interactive disabled focusable"
data-testid="home-button"
>
<span
class="icon"
data-icon-name="home"
>
home
</span>
</i>
</div>
<div
class="size-sm"
/>
<div
class="preventedDragging"
>
<i
class="Icon material interactive disabled focusable"
data-testid="history-back"
>
<span
class="icon"
data-icon-name="arrow_back"
>
arrow_back
</span>
</i>
</div>
<div
class="size-sm"
/>
<div
class="preventedDragging"
>
<i
class="Icon material interactive disabled focusable"
data-testid="history-forward"
>
<span
class="icon"
data-icon-name="arrow_forward"
>
arrow_forward
</span>
</i>
</div>
<div
class="size-sm"
/>
<div
class="preventedDragging"
>
<button
class="updateButton"
data-testid="update-button"
data-warning-level="light"
id="update-lens-button"
>
Update
<i
class="Icon icon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</button>
</div>
<div
class="separator"
/>
</div>
</div>
<main>
<div
id="lens-views"
/>
<div
class="flex justify-center Welcome align-center"
data-testid="welcome-page"
>
<div
data-testid="welcome-banner-container"
style="width: 320px;"
>
<i
class="Icon logo svg focusable"
>
<span
class="icon"
/>
</i>
<div
class="flex justify-center"
>
<div
data-testid="welcome-text-container"
style="width: 320px;"
>
<h2>
Welcome to some-product-name!
</h2>
<p>
To get you started we have auto-detected your clusters in your
kubeconfig file and added them to the catalog, your centralized
view for managing all your cloud-native resources.
<br />
<br />
If you have any questions or feedback, please join our
<a
class="link"
href="https://forums.k8slens.dev"
rel="noreferrer"
target="_blank"
>
Lens Forums
</a>
.
</p>
<ul
class="block"
data-testid="welcome-menu-container"
style="width: 320px;"
>
<li
class="flex grid-12"
>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="view_list"
>
view_list
</span>
</i>
<a
class="box col-10"
>
Browse Clusters in Catalog
</a>
<i
class="Icon box col-1 material focusable"
>
<span
class="icon"
data-icon-name="navigate_next"
>
navigate_next
</span>
</i>
</li>
</ul>
</div>
</div>
</div>
</div>
</main>
<div
class="HotbarMenu flex column"
>
<div
class="HotbarItems flex column gaps"
>
<div
class="HotbarCell isDraggingOwner animateDown"
index="0"
>
<div
style="z-index: 12; position: absolute;"
>
<div
class="HotbarIcon contextMenuAvailable"
>
<div
class="Avatar rounded disabled avatar"
id="hotbarIcon-hotbar-icon-catalog-entity"
style="width: 40px; height: 40px; background: rgb(5, 1, 130);"
>
Ca
</div>
</div>
</div>
</div>
<div
class="HotbarCell isDraggingOwner animateDown"
index="1"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="2"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="3"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="4"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="5"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="6"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="7"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="8"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="9"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="10"
/>
<div
class="HotbarCell isDraggingOwner animateDown"
index="11"
/>
</div>
<div
class="HotbarSelector"
>
<i
class="Icon Icon previous material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_left"
>
arrow_left
</span>
</i>
<div
class="HotbarIndex"
>
<div
class="badge Badge small clickable"
id="hotbarIndex"
>
1
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="arrow_right"
>
arrow_right
</span>
</i>
</div>
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
data-testid="status-bar-left"
/>
<div
class="rightSide"
data-testid="status-bar-right"
/>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;
exports[`installing update when started when user checks for updates when new update is discovered when download succeeds given checking for updates again when check resolves with same update that is already downloaded renders 1`] = `
<body>
<div>
<div

View File

@ -918,31 +918,6 @@ exports[`installing update using tray when started when user checks for updates
</span>
</i>
</div>
<div
class="size-sm"
/>
<div
class="preventedDragging"
>
<button
class="updateButton"
data-testid="update-button"
data-warning-level="light"
id="update-lens-button"
>
Update
<i
class="Icon icon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</button>
</div>
<div
class="separator"
/>

View File

@ -18,10 +18,11 @@ import setUpdateOnQuitInjectable from "../../main/electron-app/features/set-upda
import processCheckingForUpdatesInjectable from "./main/process-checking-for-updates.injectable";
import { testUsingFakeTime } from "../../test-utils/use-fake-time";
import staticFilesDirectoryInjectable from "../../common/vars/static-files-directory.injectable";
import electronQuitAndInstallUpdateInjectable from "../../main/electron-app/features/electron-quit-and-install-update.injectable";
describe("installing update", () => {
let builder: ApplicationBuilder;
let quitAndInstallUpdateMock: jest.Mock;
let electronQuitAndInstallUpdateMock: jest.Mock;
let checkForPlatformUpdatesMock: AsyncFnMock<CheckForPlatformUpdates>;
let downloadPlatformUpdateMock: AsyncFnMock<DownloadPlatformUpdate>;
let setUpdateOnQuitMock: jest.Mock;
@ -32,7 +33,7 @@ describe("installing update", () => {
builder = getApplicationBuilder();
builder.beforeApplicationStart((mainDi) => {
quitAndInstallUpdateMock = jest.fn();
electronQuitAndInstallUpdateMock = jest.fn();
checkForPlatformUpdatesMock = asyncFn();
downloadPlatformUpdateMock = asyncFn();
setUpdateOnQuitMock = jest.fn();
@ -52,8 +53,8 @@ describe("installing update", () => {
);
mainDi.override(
quitAndInstallUpdateInjectable,
() => quitAndInstallUpdateMock,
electronQuitAndInstallUpdateInjectable,
() => electronQuitAndInstallUpdateMock,
);
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
@ -64,6 +65,7 @@ describe("installing update", () => {
describe("when started", () => {
let rendered: RenderResult;
let processCheckingForUpdates: (source: string) => Promise<{ updateIsReadyToBeInstalled: boolean }>;
let quitAndInstallUpdate: () => void;
beforeEach(async () => {
rendered = await builder.render();
@ -71,6 +73,10 @@ describe("installing update", () => {
processCheckingForUpdates = builder.mainDi.inject(
processCheckingForUpdatesInjectable,
);
quitAndInstallUpdate = builder.mainDi.inject(
quitAndInstallUpdateInjectable,
);
});
it("renders", () => {
@ -155,7 +161,7 @@ describe("installing update", () => {
});
it("does not quit and install update yet", () => {
expect(quitAndInstallUpdateMock).not.toHaveBeenCalled();
expect(electronQuitAndInstallUpdateMock).not.toHaveBeenCalled();
});
it("still shows normal tray icon", () => {
@ -167,6 +173,12 @@ describe("installing update", () => {
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("does not show the update button", () => {
const button = rendered.queryByTestId("update-button");
expect(button).not.toBeInTheDocument();
});
});
describe("when download succeeds", () => {
@ -175,7 +187,7 @@ describe("installing update", () => {
});
it("does not quit and install update yet", () => {
expect(quitAndInstallUpdateMock).not.toHaveBeenCalled();
expect(electronQuitAndInstallUpdateMock).not.toHaveBeenCalled();
});
it("shows tray icon for update being available", () => {
@ -218,6 +230,26 @@ describe("installing update", () => {
"/some-static-files-directory/build/tray/trayIconUpdateAvailableTemplate.png",
);
});
it("does not quit and install update yet", () => {
expect(electronQuitAndInstallUpdateMock).not.toHaveBeenCalled();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("shows the update button", () => {
const button = rendered.getByTestId("update-button");
expect(button).toBeInTheDocument();
});
it("when triggering the update, quits and installs the update", () => {
quitAndInstallUpdate();
expect(electronQuitAndInstallUpdateMock).toHaveBeenCalled();
});
});
describe("when check resolves with different update that was previously downloaded", () => {
@ -237,6 +269,45 @@ describe("installing update", () => {
"/some-static-files-directory/build/tray/trayIconCheckingForUpdatesTemplate.png",
);
});
it("still shows the update button", () => {
const button = rendered.getByTestId("update-button");
expect(button).toBeInTheDocument();
});
describe("when download resolves successfully", () => {
beforeEach(async () => {
await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: true });
});
it("still shows the update button", () => {
const button =
rendered.getByTestId("update-button");
expect(button).toBeInTheDocument();
});
it("does not quit and install update yet", () => {
expect(electronQuitAndInstallUpdateMock).not.toHaveBeenCalled();
});
it("shows tray icon for update being available", () => {
expect(builder.tray.getIconPath()).toBe(
"/some-static-files-directory/build/tray/trayIconUpdateAvailableTemplate.png",
);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("when triggering the update, quits and installs the update", () => {
quitAndInstallUpdate();
expect(electronQuitAndInstallUpdateMock).toHaveBeenCalled();
});
});
});
});
});

View File

@ -40,12 +40,12 @@ const downloadUpdateInjectable = getInjectable({
if (!downloadWasSuccessful) {
progressOfUpdateDownload.set({ percentage: 0, failed: "Download of update failed" });
discoveredVersionState.set(null);
} else {
const currentDateTime = getCurrentDateTime();
updateDownloadedDate.set(currentDateTime);
}
const currentDateTime = getCurrentDateTime();
updateDownloadedDate.set(currentDateTime);
downloadingUpdateState.set(false);
});

View File

@ -5,11 +5,11 @@
import "./pods.scss";
import React, { Fragment } from "react";
import React from "react";
import { observer } from "mobx-react";
import { Link } from "react-router-dom";
import { KubeObjectListLayout } from "../kube-object-list-layout";
import type { NodeApi, Pod } from "../../../common/k8s-api/endpoints";
import type { ContainerStateValues, NodeApi, Pod } from "../../../common/k8s-api/endpoints";
import { StatusBrick } from "../status-brick";
import { cssNames, getConvertedParts, object, stopPropagation } from "@k8slens/utilities";
import startCase from "lodash/startCase";
@ -21,8 +21,8 @@ import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../kube-object/age";
import { withInjectables } from "@ogre-tools/injectable-react";
import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import type { EventStore } from "../+events/store";
import type { PodStore } from "./store";
import nodeApiInjectable from "../../../common/k8s-api/endpoints/node.api.injectable";
@ -52,8 +52,12 @@ interface Dependencies {
@observer
class NonInjectedPods extends React.Component<Dependencies> {
renderState<T extends string>(name: string, ready: boolean, key: string, data: Partial<Record<T, string | number>> | undefined) {
return data && (
renderState(name: string, ready: boolean, key: string, data?: ContainerStateValues) {
if (!data) {
return;
}
return (
<>
<div className="title">
{name}
@ -64,40 +68,37 @@ class NonInjectedPods extends React.Component<Dependencies> {
</span>
</div>
{object.entries(data).map(([name, value]) => (
<div key={name} className="flex gaps align-center">
<div className="name">
{startCase(name)}
</div>
<div className="value">
{value}
</div>
</div>
<React.Fragment key={name}>
<div className="name">{startCase(name)}</div>
<div className="value">{value}</div>
</React.Fragment>
))}
</>
);
}
renderContainersStatus(pod: Pod) {
return pod.getContainerStatuses()
.map(({ name, state = {}, ready }) => (
<Fragment key={name}>
<StatusBrick
className={cssNames(state, { ready })}
tooltip={{
formatters: {
tableView: true,
},
children: (
<>
{this.renderState(name, ready, "running", state.running)}
{this.renderState(name, ready, "waiting", state.waiting)}
{this.renderState(name, ready, "terminated", state.terminated)}
</>
),
}}
/>
</Fragment>
));
return pod.getContainerStatuses().map(({ name, state, ready }) => {
return (
<StatusBrick
key={name}
className={cssNames(state, { ready })}
tooltip={{
formatters: {
tableView: true,
nowrap: true,
},
children: (
<>
{this.renderState(name, ready, "running", state?.running)}
{this.renderState(name, ready, "waiting", state?.waiting)}
{this.renderState(name, ready, "terminated", state?.terminated)}
</>
),
}}
/>
);
});
}
render() {

View File

@ -63,27 +63,36 @@
}
&.tableView {
min-width: 200px;
display: grid;
gap: var(--padding);
grid-template-columns: max-content 1fr;
grid-template-rows: repeat(2, 1fr);
> :not(:last-child) {
margin-bottom: var(--flex-gap);
// backward compatibility: skips element in DOM to consider only children in grid-flow
> .flex {
display: contents;
}
> * {
white-space: pre-wrap;
word-break: break-word;
}
.title {
grid-column: 1 / 3; // merge
color: var(--textColorAccent);
text-align: center;
font-weight: bold;
}
.name {
color: var(--textColorAccent);
text-align: right;
flex: 0 0 35%;
color: var(--textColorAccent);
}
.value {
text-align: left;
max-width: 300px;
word-break: break-word;
color: var(--textColorSecondary);
}
}
}

View File

@ -30,7 +30,7 @@
"sass-loader": "^13.2.0",
"style-loader": "^3.3.1",
"ts-loader": "^9.4.1",
"webpack": "^5.75.0",
"webpack": "^5.76.0",
"webpack-cli": "^4.10.0",
"webpack-node-externals": "^3.0.0"
}