diff --git a/src/behaviours/cluster/sidebar-and-tab-navigation-for-core.test.tsx b/src/behaviours/cluster/sidebar-and-tab-navigation-for-core.test.tsx index 96b4a6b2d7..2aec78a47a 100644 --- a/src/behaviours/cluster/sidebar-and-tab-navigation-for-core.test.tsx +++ b/src/behaviours/cluster/sidebar-and-tab-navigation-for-core.test.tsx @@ -22,6 +22,7 @@ import pathExistsInjectable from "../../common/fs/path-exists.injectable"; import readJsonFileInjectable from "../../common/fs/read-json-file.injectable"; import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token"; import sidebarStorageInjectable from "../../renderer/components/layout/sidebar-storage/sidebar-storage.injectable"; +import hostedClusterIdInjectable from "../../renderer/cluster-frame-context/hosted-cluster-id.injectable"; import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time"; describe("cluster - sidebar and tab navigation for core", () => { @@ -38,6 +39,8 @@ describe("cluster - sidebar and tab navigation for core", () => { applicationBuilder.setEnvironmentToClusterFrame(); applicationBuilder.beforeApplicationStart(({ rendererDi }) => { + rendererDi.override(hostedClusterIdInjectable, () => "some-hosted-cluster-id"); + rendererDi.override( directoryForLensLocalStorageInjectable, () => "/some-directory-for-lens-local-storage", @@ -94,7 +97,7 @@ describe("cluster - sidebar and tab navigation for core", () => { const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); await writeJsonFileFake( - "/some-directory-for-lens-local-storage/app.json", + "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", { sidebar: { expanded: { "some-parent-id": true }, @@ -136,7 +139,7 @@ describe("cluster - sidebar and tab navigation for core", () => { const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); await writeJsonFileFake( - "/some-directory-for-lens-local-storage/app.json", + "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", { sidebar: { expanded: { "some-unknown-parent-id": true }, @@ -166,7 +169,7 @@ describe("cluster - sidebar and tab navigation for core", () => { const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); await writeJsonFileFake( - "/some-directory-for-lens-local-storage/app.json", + "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", { someThingButSidebar: {}, }, @@ -268,7 +271,7 @@ describe("cluster - sidebar and tab navigation for core", () => { const pathExistsFake = rendererDi.inject(pathExistsInjectable); const actual = await pathExistsFake( - "/some-directory-for-lens-local-storage/app.json", + "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", ); expect(actual).toBe(false); @@ -280,7 +283,7 @@ describe("cluster - sidebar and tab navigation for core", () => { const readJsonFileFake = rendererDi.inject(readJsonFileInjectable); const actual = await readJsonFileFake( - "/some-directory-for-lens-local-storage/app.json", + "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", ); expect(actual).toEqual({ diff --git a/src/behaviours/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx b/src/behaviours/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx index 75389c6631..fbfb425fe2 100644 --- a/src/behaviours/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx +++ b/src/behaviours/cluster/sidebar-and-tab-navigation-for-extensions.test.tsx @@ -18,8 +18,14 @@ import { navigateToRouteInjectionToken } from "../../common/front-end-routing/na import assert from "assert"; import type { FakeExtensionData } from "../../renderer/components/test-utils/get-renderer-extension-fake"; import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake"; +import hostedClusterIdInjectable from "../../renderer/cluster-frame-context/hosted-cluster-id.injectable"; import { advanceFakeTime, useFakeTime } from "../../common/test-utils/use-fake-time"; +// TODO: Make tooltips free of side effects by making it deterministic +jest.mock("../../renderer/components/tooltip/withTooltip", () => ({ + withTooltip: (target: any) => target, +})); + describe("cluster - sidebar and tab navigation for extensions", () => { let applicationBuilder: ApplicationBuilder; let rendererDi: DiContainer; @@ -34,6 +40,8 @@ describe("cluster - sidebar and tab navigation for extensions", () => { applicationBuilder.setEnvironmentToClusterFrame(); applicationBuilder.beforeApplicationStart(({ rendererDi }) => { + rendererDi.override(hostedClusterIdInjectable, () => "some-hosted-cluster-id"); + rendererDi.override( directoryForLensLocalStorageInjectable, () => "/some-directory-for-lens-local-storage", @@ -96,7 +104,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => { const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); await writeJsonFileFake( - "/some-directory-for-lens-local-storage/app.json", + "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", { sidebar: { expanded: { "some-extension-name-some-parent-id": true }, @@ -132,7 +140,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => { const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); await writeJsonFileFake( - "/some-directory-for-lens-local-storage/app.json", + "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", { sidebar: { expanded: { "some-extension-name-some-unknown-parent-id": true }, @@ -162,7 +170,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => { const writeJsonFileFake = rendererDi.inject(writeJsonFileInjectable); await writeJsonFileFake( - "/some-directory-for-lens-local-storage/app.json", + "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", { someThingButSidebar: {}, }, @@ -284,7 +292,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => { const pathExistsFake = rendererDi.inject(pathExistsInjectable); const actual = await pathExistsFake( - "/some-directory-for-lens-local-storage/app.json", + "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", ); expect(actual).toBe(false); @@ -296,7 +304,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => { const readJsonFileFake = rendererDi.inject(readJsonFileInjectable); const actual = await readJsonFileFake( - "/some-directory-for-lens-local-storage/app.json", + "/some-directory-for-lens-local-storage/some-hosted-cluster-id.json", ); expect(actual).toEqual({ diff --git a/src/renderer/components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable.ts new file mode 100644 index 0000000000..0ab5c66241 --- /dev/null +++ b/src/renderer/components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { CronJobTriggerDialog } from "./cronjob-trigger-dialog"; +import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token"; + +const cronJobTriggerDialogClusterFrameChildComponentInjectable = getInjectable({ + id: "cron-job-trigger-dialog-cluster-frame-child-component", + + instantiate: () => ({ + id: "cron-job-trigger-dialog", + shouldRender: computed(() => true), + Component: CronJobTriggerDialog, + }), + + injectionToken: clusterFrameChildComponentInjectionToken, + + causesSideEffects: true, +}); + +export default cronJobTriggerDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable.ts new file mode 100644 index 0000000000..712b5d4321 --- /dev/null +++ b/src/renderer/components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { DeploymentScaleDialog } from "./dialog"; +import { clusterFrameChildComponentInjectionToken } from "../../../frames/cluster-frame/cluster-frame-child-component-injection-token"; + +const deploymentScaleDialogClusterFrameChildComponentInjectable = getInjectable({ + id: "deployment-scale-dialog-cluster-frame-child-component", + + instantiate: () => ({ + id: "deployment-scale-dialog", + shouldRender: computed(() => true), + Component: DeploymentScaleDialog, + }), + + injectionToken: clusterFrameChildComponentInjectionToken, + + causesSideEffects: true, +}); + +export default deploymentScaleDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/+workloads-replicasets/scale-dialog/replicaset-scale-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/+workloads-replicasets/scale-dialog/replicaset-scale-dialog-cluster-frame-child-component.injectable.ts new file mode 100644 index 0000000000..0dd5790944 --- /dev/null +++ b/src/renderer/components/+workloads-replicasets/scale-dialog/replicaset-scale-dialog-cluster-frame-child-component.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { clusterFrameChildComponentInjectionToken } from "../../../frames/cluster-frame/cluster-frame-child-component-injection-token"; +import { ReplicaSetScaleDialog } from "./dialog"; + +const replicasetScaleDialogClusterFrameChildComponentInjectable = getInjectable({ + id: "replicaset-scale-dialog-cluster-frame-child-component", + + instantiate: () => ({ + id: "replicaset-scale-dialog", + shouldRender: computed(() => true), + Component: ReplicaSetScaleDialog, + }), + + injectionToken: clusterFrameChildComponentInjectionToken, + + causesSideEffects: true, +}); + +export default replicasetScaleDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/+workloads-statefulsets/scale/statefulset-scale-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/+workloads-statefulsets/scale/statefulset-scale-dialog-cluster-frame-child-component.injectable.ts new file mode 100644 index 0000000000..ee23dde011 --- /dev/null +++ b/src/renderer/components/+workloads-statefulsets/scale/statefulset-scale-dialog-cluster-frame-child-component.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { StatefulSetScaleDialog } from "./dialog"; +import { clusterFrameChildComponentInjectionToken } from "../../../frames/cluster-frame/cluster-frame-child-component-injection-token"; + +const statefulsetScaleDialogClusterFrameChildComponentInjectable = getInjectable({ + id: "statefulset-scale-dialog-cluster-frame-child-component", + + instantiate: () => ({ + id: "statefulset-scale-dialog", + shouldRender: computed(() => true), + Component: StatefulSetScaleDialog, + }), + + injectionToken: clusterFrameChildComponentInjectionToken, + + causesSideEffects: true, +}); + +export default statefulsetScaleDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/command-palette/command-container-cluster-frame-child-component.injectable.ts b/src/renderer/components/command-palette/command-container-cluster-frame-child-component.injectable.ts new file mode 100644 index 0000000000..09b1ce6c53 --- /dev/null +++ b/src/renderer/components/command-palette/command-container-cluster-frame-child-component.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { CommandContainer } from "./command-container"; +import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token"; + +const commandContainerClusterFrameChildComponentInjectable = getInjectable({ + id: "command-container-cluster-frame-child-component", + + instantiate: () => ({ + id: "command-container", + shouldRender: computed(() => true), + Component: CommandContainer, + }), + + causesSideEffects: true, + + injectionToken: clusterFrameChildComponentInjectionToken, +}); + +export default commandContainerClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/confirm-dialog/confirm-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/confirm-dialog/confirm-dialog-cluster-frame-child-component.injectable.ts new file mode 100644 index 0000000000..196d18b27f --- /dev/null +++ b/src/renderer/components/confirm-dialog/confirm-dialog-cluster-frame-child-component.injectable.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { ConfirmDialog } from "./confirm-dialog"; +import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token"; + +const confirmDialogClusterFrameChildComponentInjectable = getInjectable({ + id: "confirm-dialog-cluster-frame-child-component", + + instantiate: () => ({ + id: "confirm-dialog", + shouldRender: computed(() => true), + Component: ConfirmDialog, + }), + + injectionToken: clusterFrameChildComponentInjectionToken, +}); + +export default confirmDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog-cluster-frame-child-component.injectable.ts new file mode 100644 index 0000000000..ed91967d39 --- /dev/null +++ b/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog-cluster-frame-child-component.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { DeleteClusterDialog } from "./view"; +import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token"; + +const deleteClusterDialogClusterFrameChildComponentInjectable = getInjectable({ + id: "delete-cluster-dialog-cluster-frame-child-component", + + instantiate: () => ({ + id: "delete-cluster-dialog", + shouldRender: computed(() => true), + Component: DeleteClusterDialog, + }), + + injectionToken: clusterFrameChildComponentInjectionToken, + + causesSideEffects: true, +}); + +export default deleteClusterDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/kube-object-details/kube-object-details-cluster-frame-child-component.injectable.ts b/src/renderer/components/kube-object-details/kube-object-details-cluster-frame-child-component.injectable.ts new file mode 100644 index 0000000000..4799ff3099 --- /dev/null +++ b/src/renderer/components/kube-object-details/kube-object-details-cluster-frame-child-component.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { KubeObjectDetails } from "./kube-object-details"; +import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token"; + +const kubeObjectDetailsClusterFrameChildComponentInjectable = getInjectable({ + id: "kube-object-details-cluster-frame-child-component", + + instantiate: () => ({ + id: "kube-object-details", + shouldRender: computed(() => true), + Component: KubeObjectDetails, + }), + + injectionToken: clusterFrameChildComponentInjectionToken, + + causesSideEffects: true, +}); + +export default kubeObjectDetailsClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog-cluster-frame-child-component.injectable.ts new file mode 100644 index 0000000000..796e13a063 --- /dev/null +++ b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog-cluster-frame-child-component.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token"; +import { KubeConfigDialog } from "./kubeconfig-dialog"; + +const kubeconfigDialogClusterFrameChildComponentInjectable = getInjectable({ + id: "kubeconfig-dialog-cluster-frame-child-component", + + instantiate: () => ({ + id: "kubeconfig-dialog", + shouldRender: computed(() => true), + Component: KubeConfigDialog, + }), + + injectionToken: clusterFrameChildComponentInjectionToken, + + causesSideEffects: true, +}); + +export default kubeconfigDialogClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/notifications/notifications-cluster-frame-child-component.injectable.ts b/src/renderer/components/notifications/notifications-cluster-frame-child-component.injectable.ts new file mode 100644 index 0000000000..acd2e72b9e --- /dev/null +++ b/src/renderer/components/notifications/notifications-cluster-frame-child-component.injectable.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { Notifications } from "./notifications"; +import { clusterFrameChildComponentInjectionToken } from "../../frames/cluster-frame/cluster-frame-child-component-injection-token"; + +const notificationsClusterFrameChildComponentInjectable = getInjectable({ + id: "notifications-cluster-frame-child-component", + + instantiate: () => ({ + id: "notifications", + shouldRender: computed(() => true), + Component: Notifications, + }), + + injectionToken: clusterFrameChildComponentInjectionToken, +}); + +export default notificationsClusterFrameChildComponentInjectable; diff --git a/src/renderer/components/test-utils/get-application-builder.tsx b/src/renderer/components/test-utils/get-application-builder.tsx index ca288c01b6..7a60243b76 100644 --- a/src/renderer/components/test-utils/get-application-builder.tsx +++ b/src/renderer/components/test-utils/get-application-builder.tsx @@ -9,18 +9,15 @@ import type { IObservableArray, ObservableSet } from "mobx"; import { computed, observable, runInAction } from "mobx"; import React from "react"; import { Router } from "react-router"; -import { Observer } from "mobx-react"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import allowedResourcesInjectable from "../../cluster-frame-context/allowed-resources.injectable"; import type { RenderResult } from "@testing-library/react"; import { getByText, fireEvent } from "@testing-library/react"; import type { KubeResource } from "../../../common/rbac"; -import { Sidebar } from "../layout/sidebar"; import type { DiContainer } from "@ogre-tools/injectable"; import clusterStoreInjectable from "../../../common/cluster-store/cluster-store.injectable"; import type { ClusterStore } from "../../../common/cluster-store/cluster-store"; import mainExtensionsInjectable from "../../../extensions/main-extensions.injectable"; -import currentRouteComponentInjectable from "../../routes/current-route-component.injectable"; import { pipeline } from "@ogre-tools/fp"; import { flatMap, compact, join, get, filter, map, matches, last } from "lodash/fp"; import preferenceNavigationItemsInjectable from "../+preferences/preferences-navigation/preference-navigation-items.injectable"; @@ -43,7 +40,6 @@ import historyInjectable from "../../navigation/history.injectable"; import type { MinimalTrayMenuItem } from "../../../main/tray/electron-tray/electron-tray.injectable"; import electronTrayInjectable from "../../../main/tray/electron-tray/electron-tray.injectable"; import applicationWindowInjectable from "../../../main/start-main-application/lens-window/application-window/application-window.injectable"; -import { Notifications } from "../notifications/notifications"; import { getDiForUnitTesting as getRendererDi } from "../../getDiForUnitTesting"; import { getDiForUnitTesting as getMainDi } from "../../../main/getDiForUnitTesting"; import { overrideChannels } from "../../../test-utils/channel-fakes/override-channels"; @@ -61,6 +57,8 @@ import type { LensExtension } from "../../../extensions/lens-extension"; import extensionInjectable from "../../../extensions/extension-loader/extension/extension.injectable"; import { renderFor } from "./renderFor"; import { RootFrame } from "../../frames/root-frame/root-frame"; +import { ClusterFrame } from "../../frames/cluster-frame/cluster-frame"; +import hostedClusterIdInjectable from "../../cluster-frame-context/hosted-cluster-id.injectable"; type Callback = (dis: DiContainers) => void | Promise; @@ -182,28 +180,13 @@ export const getApplicationBuilder = () => { clusterFrame: { render: () => { - const currentRouteComponent = rendererDi.inject(currentRouteComponentInjectable); const history = rendererDi.inject(historyInjectable); const render = renderFor(rendererDi); return render( - - - - {() => { - const Component = currentRouteComponent.get(); - - if (!Component) { - return null; - } - - return ; - }} - - - + , ); }, @@ -419,6 +402,7 @@ export const getApplicationBuilder = () => { const namespaceStoreStub = { contextNamespaces: [], + items: [], } as unknown as NamespaceStore; const clusterFrameContextFake = new ClusterFrameContext( @@ -431,6 +415,7 @@ export const getApplicationBuilder = () => { rendererDi.override(namespaceStoreInjectable, () => namespaceStoreStub); rendererDi.override(hostedClusterInjectable, () => clusterStub); + rendererDi.override(hostedClusterIdInjectable, () => "irrelevant-hosted-cluster-id"); rendererDi.override(clusterFrameContextInjectable, () => clusterFrameContextFake); // Todo: get rid of global state. diff --git a/src/renderer/frames/cluster-frame/cluster-frame-child-component-injection-token.ts b/src/renderer/frames/cluster-frame/cluster-frame-child-component-injection-token.ts new file mode 100644 index 0000000000..c4cbdfab23 --- /dev/null +++ b/src/renderer/frames/cluster-frame/cluster-frame-child-component-injection-token.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { IComputedValue } from "mobx"; + +export interface ClusterFrameChildComponent { + id: string; + Component: React.ElementType; + shouldRender: IComputedValue; +} + +export const clusterFrameChildComponentInjectionToken = getInjectionToken({ + id: "cluster-frame-child-component", +}); diff --git a/src/renderer/frames/cluster-frame/cluster-frame-layout-child-component.injectable.tsx b/src/renderer/frames/cluster-frame/cluster-frame-layout-child-component.injectable.tsx new file mode 100644 index 0000000000..3f1f5c4a03 --- /dev/null +++ b/src/renderer/frames/cluster-frame/cluster-frame-layout-child-component.injectable.tsx @@ -0,0 +1,61 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import React from "react"; +import { getInjectable } from "@ogre-tools/injectable"; +import { clusterFrameChildComponentInjectionToken } from "./cluster-frame-child-component-injection-token"; +import { MainLayout } from "../../components/layout/main-layout"; +import { Sidebar } from "../../components/layout/sidebar"; +import { Dock } from "../../components/dock"; +import styles from "./cluster-frame.module.css"; +import { computed } from "mobx"; +import currentRouteComponentInjectable from "../../routes/current-route-component.injectable"; +import { Redirect } from "react-router"; +import startUrlInjectable from "./start-url.injectable"; +import currentPathInjectable from "../../routes/current-path.injectable"; +import { observer } from "mobx-react"; + +const clusterFrameLayoutChildComponentInjectable = getInjectable({ + id: "cluster-frame-layout-child-component", + + instantiate: (di) => { + const currentRouteComponent = di.inject(currentRouteComponentInjectable); + const startUrl = di.inject(startUrlInjectable); + const currentPath = di.inject(currentPathInjectable); + + return { + id: "cluster-frame-layout", + + shouldRender: computed(() => true), + + Component: observer(() => { + const Component = currentRouteComponent.get(); + const starting = startUrl.get(); + const current = currentPath.get(); + + return ( + } footer={}> + {Component ? ( + + ) : // NOTE: this check is to prevent an infinite loop + starting !== current ? ( + + ) : ( +
+
+ An error has occured. No route can be found matching the + current route, which is also the starting route. +
+
+ )} +
+ ); + }), + }; + }, + + injectionToken: clusterFrameChildComponentInjectionToken, +}); + +export default clusterFrameLayoutChildComponentInjectable; diff --git a/src/renderer/frames/cluster-frame/cluster-frame.tsx b/src/renderer/frames/cluster-frame/cluster-frame.tsx index 6dc014d3be..d1ce64683f 100755 --- a/src/renderer/frames/cluster-frame/cluster-frame.tsx +++ b/src/renderer/frames/cluster-frame/cluster-frame.tsx @@ -2,52 +2,30 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import styles from "./cluster-frame.module.css"; import React, { useEffect } from "react"; -import type { IComputedValue } from "mobx"; -import { observer } from "mobx-react"; -import { Redirect } from "react-router"; -import { ConfirmDialog } from "../../components/confirm-dialog"; -import { DeploymentScaleDialog } from "../../components/+workloads-deployments/scale/dialog"; -import { CronJobTriggerDialog } from "../../components/+workloads-cronjobs/cronjob-trigger-dialog"; -import { StatefulSetScaleDialog } from "../../components/+workloads-statefulsets/scale/dialog"; -import { ReplicaSetScaleDialog } from "../../components/+workloads-replicasets/scale-dialog/dialog"; -import { CommandContainer } from "../../components/command-palette/command-container"; +import { Observer, observer } from "mobx-react"; import { ErrorBoundary } from "../../components/error-boundary"; -import { MainLayout } from "../../components/layout/main-layout"; -import { Notifications } from "../../components/notifications"; -import { KubeObjectDetails } from "../../components/kube-object-details"; -import { KubeConfigDialog } from "../../components/kubeconfig-dialog"; -import { Sidebar } from "../../components/layout/sidebar"; -import { Dock } from "../../components/dock"; -import { PortForwardDialog } from "../../port-forward"; -import { DeleteClusterDialog } from "../../components/delete-cluster-dialog"; import type { NamespaceStore } from "../../components/+namespaces/store"; import { withInjectables } from "@ogre-tools/injectable-react"; import namespaceStoreInjectable from "../../components/+namespaces/store.injectable"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import { disposer } from "../../utils"; -import currentRouteComponentInjectable from "../../routes/current-route-component.injectable"; -import startUrlInjectable from "./start-url.injectable"; import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; -import currentPathInjectable from "../../routes/current-path.injectable"; +import type { ClusterFrameChildComponent } from "./cluster-frame-child-component-injection-token"; +import { clusterFrameChildComponentInjectionToken } from "./cluster-frame-child-component-injection-token"; import watchHistoryStateInjectable from "../../remote-helpers/watch-history-state.injectable"; interface Dependencies { namespaceStore: NamespaceStore; - currentRouteComponent: IComputedValue | undefined>; - startUrl: IComputedValue; subscribeStores: SubscribeStores; - currentPath: IComputedValue; + childComponents: ClusterFrameChildComponent[]; watchHistoryState: () => () => void; } export const NonInjectedClusterFrame = observer(({ namespaceStore, - currentRouteComponent, - startUrl, subscribeStores, - currentPath, + childComponents, watchHistoryState, }: Dependencies) => { useEffect(() => disposer( @@ -57,43 +35,14 @@ export const NonInjectedClusterFrame = observer(({ watchHistoryState(), ), []); - const Component = currentRouteComponent.get(); - const starting = startUrl.get(); - const current = currentPath.get(); - return ( - } - footer={} - > - { - Component - ? - // NOTE: this check is to prevent an infinite loop - : starting !== current - ? - : ( -
-
- An error has occured. No route can be found matching the current route, which is also the starting route. -
-
- ) - } -
- - - - - - - - - - - - + {childComponents + .map((child) => ( + + {() => (child.shouldRender.get() ? : null) } + + ))}
); }); @@ -102,9 +51,7 @@ export const ClusterFrame = withInjectables(NonInjectedClusterFram getProps: di => ({ namespaceStore: di.inject(namespaceStoreInjectable), subscribeStores: di.inject(subscribeStoresInjectable), - startUrl: di.inject(startUrlInjectable), - currentRouteComponent: di.inject(currentRouteComponentInjectable), - currentPath: di.inject(currentPathInjectable), + childComponents: di.injectMany(clusterFrameChildComponentInjectionToken), watchHistoryState: di.inject(watchHistoryStateInjectable), }), }); diff --git a/src/renderer/getDiForUnitTesting.tsx b/src/renderer/getDiForUnitTesting.tsx index f0bac6ca91..edc11319d7 100644 --- a/src/renderer/getDiForUnitTesting.tsx +++ b/src/renderer/getDiForUnitTesting.tsx @@ -61,6 +61,15 @@ import maximizeWindowInjectable from "./components/layout/top-bar/maximize-windo import toggleMaximizeWindowInjectable from "./components/layout/top-bar/toggle-maximize-window.injectable"; import commandContainerRootFrameChildComponentInjectable from "./components/command-palette/command-container-root-frame-child-component.injectable"; import type { HotbarStore } from "../common/hotbars/store"; +import commandContainerClusterFrameChildComponentInjectable from "./components/command-palette/command-container-cluster-frame-child-component.injectable"; +import cronJobTriggerDialogClusterFrameChildComponentInjectable from "./components/+workloads-cronjobs/cron-job-trigger-dialog-cluster-frame-child-component.injectable"; +import deploymentScaleDialogClusterFrameChildComponentInjectable from "./components/+workloads-deployments/scale/deployment-scale-dialog-cluster-frame-child-component.injectable"; +import replicasetScaleDialogClusterFrameChildComponentInjectable from "./components/+workloads-replicasets/scale-dialog/replicaset-scale-dialog-cluster-frame-child-component.injectable"; +import statefulsetScaleDialogClusterFrameChildComponentInjectable from "./components/+workloads-statefulsets/scale/statefulset-scale-dialog-cluster-frame-child-component.injectable"; +import deleteClusterDialogClusterFrameChildComponentInjectable from "./components/delete-cluster-dialog/delete-cluster-dialog-cluster-frame-child-component.injectable"; +import kubeObjectDetailsClusterFrameChildComponentInjectable from "./components/kube-object-details/kube-object-details-cluster-frame-child-component.injectable"; +import kubeconfigDialogClusterFrameChildComponentInjectable from "./components/kubeconfig-dialog/kubeconfig-dialog-cluster-frame-child-component.injectable"; +import portForwardDialogClusterFrameChildComponentInjectable from "./port-forward/port-forward-dialog-cluster-frame-child-component.injectable"; export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) => { const { @@ -105,14 +114,35 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) di.override(lensResourcesDirInjectable, () => "/irrelevant"); + // TODO: Remove side-effects and shared global state di.override(commandContainerRootFrameChildComponentInjectable, () => ({ Component: () => null, id: "command-container", shouldRender: computed(() => false), })); - di.override(watchHistoryStateInjectable, () => () => () => {}); + // TODO: Remove side-effects and shared global state + const clusterFrameChildComponentInjectables: Injectable[] = [ + commandContainerClusterFrameChildComponentInjectable, + cronJobTriggerDialogClusterFrameChildComponentInjectable, + deploymentScaleDialogClusterFrameChildComponentInjectable, + replicasetScaleDialogClusterFrameChildComponentInjectable, + statefulsetScaleDialogClusterFrameChildComponentInjectable, + deleteClusterDialogClusterFrameChildComponentInjectable, + kubeObjectDetailsClusterFrameChildComponentInjectable, + kubeconfigDialogClusterFrameChildComponentInjectable, + portForwardDialogClusterFrameChildComponentInjectable, + ]; + clusterFrameChildComponentInjectables.forEach((injectable) => { + di.override(injectable, () => ({ + Component: () => null, + id: injectable.id, + shouldRender: computed(() => false), + })); + }); + + di.override(watchHistoryStateInjectable, () => () => () => {}); di.override(openAppContextMenuInjectable, () => () => {}); di.override(goBackInjectable, () => () => {}); di.override(goForwardInjectable, () => () => {}); @@ -139,7 +169,6 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) getDisplayIndex: () => "0", }) as unknown as HotbarStore); - di.override(fileSystemProvisionerStoreInjectable, () => ({}) as FileSystemProvisionerStore); // eslint-disable-next-line unused-imports/no-unused-vars-ts diff --git a/src/renderer/port-forward/port-forward-dialog-cluster-frame-child-component.injectable.ts b/src/renderer/port-forward/port-forward-dialog-cluster-frame-child-component.injectable.ts new file mode 100644 index 0000000000..01b1ae0f7e --- /dev/null +++ b/src/renderer/port-forward/port-forward-dialog-cluster-frame-child-component.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import { PortForwardDialog } from "./port-forward-dialog"; +import { clusterFrameChildComponentInjectionToken } from "../frames/cluster-frame/cluster-frame-child-component-injection-token"; + +const portForwardDialogClusterFrameChildComponentInjectable = getInjectable({ + id: "port-forward-dialog-cluster-frame-child-component", + + instantiate: () => ({ + id: "port-forward-dialog", + shouldRender: computed(() => true), + Component: PortForwardDialog, + }), + + injectionToken: clusterFrameChildComponentInjectionToken, + + causesSideEffects: true, +}); + +export default portForwardDialogClusterFrameChildComponentInjectable;