diff --git a/src/behaviours/cluster/extension-api/reactively-disable-cluster-pages.test.tsx b/src/behaviours/cluster/extension-api/reactively-disable-cluster-pages.test.tsx new file mode 100644 index 0000000000..4cc5e45576 --- /dev/null +++ b/src/behaviours/cluster/extension-api/reactively-disable-cluster-pages.test.tsx @@ -0,0 +1,70 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { RenderResult } from "@testing-library/react"; +import type { IObservableValue } from "mobx"; +import { observable, runInAction, computed } from "mobx"; +import React from "react"; +import type { TestExtensionRenderer } from "../../../renderer/components/test-utils/get-extension-fake"; +import type { ApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; +import { getExtensionFakeFor } from "../../../renderer/components/test-utils/get-extension-fake"; + +describe("reactively disable cluster pages", () => { + let builder: ApplicationBuilder; + let rendered: RenderResult; + let someObservable: IObservableValue; + let rendererTestExtension: TestExtensionRenderer; + + beforeEach(async () => { + builder = getApplicationBuilder(); + + builder.setEnvironmentToClusterFrame(); + + const getExtensionFake = getExtensionFakeFor(builder); + + someObservable = observable.box(false); + + const testExtension = getExtensionFake({ + id: "test-extension-id", + name: "test-extension", + + rendererOptions: { + clusterPages: [{ + components: { + Page: () =>
Some page
, + }, + + enabled: computed(() => someObservable.get()), + }], + }, + }); + + rendered = await builder.render(); + + builder.extensions.enable(testExtension); + + rendererTestExtension = testExtension.renderer; + }); + + it("when navigating to the page, does not show the page", () => { + rendererTestExtension.navigate(); + + const actual = rendered.queryByTestId("some-test-page"); + + expect(actual).not.toBeInTheDocument(); + }); + + it("given page becomes enabled, when navigating to the page, shows the page", () => { + runInAction(() => { + someObservable.set(true); + }); + + rendererTestExtension.navigate(); + + const actual = rendered.queryByTestId("some-test-page"); + + expect(actual).toBeInTheDocument(); + }); +}); diff --git a/src/behaviours/cluster/kube-object-details/extension-api/reactively-hide-kube-object-detail-item.test.tsx b/src/behaviours/cluster/kube-object-details/extension-api/reactively-hide-kube-object-detail-item.test.tsx new file mode 100644 index 0000000000..77c9c24552 --- /dev/null +++ b/src/behaviours/cluster/kube-object-details/extension-api/reactively-hide-kube-object-detail-item.test.tsx @@ -0,0 +1,138 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { RenderResult } from "@testing-library/react"; +import type { ApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder"; +import { getExtensionFakeFor } from "../../../../renderer/components/test-utils/get-extension-fake"; +import { getInjectable } from "@ogre-tools/injectable"; +import { frontEndRouteInjectionToken } from "../../../../common/front-end-routing/front-end-route-injection-token"; +import type { IObservableValue } from "mobx"; +import { runInAction, computed, observable } from "mobx"; +import React from "react"; +import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token"; +import { routeSpecificComponentInjectionToken } from "../../../../renderer/routes/route-specific-component-injection-token"; +import { KubeObject } from "../../../../common/k8s-api/kube-object"; +import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable"; +import { KubeObjectDetails } from "../../../../renderer/components/kube-object-details"; +import type { ApiManager } from "../../../../common/k8s-api/api-manager"; + +describe("reactively hide kube object detail item", () => { + let builder: ApplicationBuilder; + let rendered: RenderResult; + let someObservable: IObservableValue; + + beforeEach(async () => { + builder = getApplicationBuilder(); + + builder.beforeApplicationStart(({ rendererDi }) => { + rendererDi.override( + apiManagerInjectable, + () => + ({ + getStore: () => ({ + getByPath: () => + getKubeObjectStub("some-kind", "some-api-version"), + }), + } as unknown as ApiManager), + ); + }); + + const rendererDi = builder.dis.rendererDi; + + rendererDi.register(testRouteInjectable, testRouteComponentInjectable); + + builder.setEnvironmentToClusterFrame(); + + const getExtensionFake = getExtensionFakeFor(builder); + + someObservable = observable.box(false); + + const testExtension = getExtensionFake({ + id: "test-extension-id", + name: "test-extension", + + rendererOptions: { + kubeObjectDetailItems: [ + { + kind: "some-kind", + apiVersions: ["some-api-version"], + + components: { + Details: () => ( +
+ Some detail +
+ ), + }, + + visible: computed(() => someObservable.get()), + }, + ], + }, + }); + + rendered = await builder.render(); + + const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken); + const testRoute = rendererDi.inject(testRouteInjectable); + + navigateToRoute(testRoute); + + builder.extensions.enable(testExtension); + }); + + it("does not show the kube object detail item", () => { + const actual = rendered.queryByTestId("some-kube-object-detail-item"); + + expect(actual).not.toBeInTheDocument(); + }); + + it("given item should be shown, shows the kube object detail item", () => { + runInAction(() => { + someObservable.set(true); + }); + + const actual = rendered.queryByTestId("some-kube-object-detail-item"); + + expect(actual).toBeInTheDocument(); + }); +}); + +const testRouteInjectable = getInjectable({ + id: "test-route", + + instantiate: () => ({ + path: "/test-route", + clusterFrame: true, + isEnabled: computed(() => true), + }), + + injectionToken: frontEndRouteInjectionToken, +}); + +const testRouteComponentInjectable = getInjectable({ + id: "test-route-component", + + instantiate: (di) => ({ + route: di.inject(testRouteInjectable), + + Component: () => , + }), + + injectionToken: routeSpecificComponentInjectionToken, +}); + +const getKubeObjectStub = (kind: string, apiVersion: string) => + KubeObject.create({ + apiVersion, + kind, + metadata: { + uid: "some-uid", + name: "some-name", + resourceVersion: "some-resource-version", + namespace: "some-namespace", + selfLink: "", + }, + }); diff --git a/src/behaviours/cluster/kube-object-menu/extension-api/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx b/src/behaviours/cluster/kube-object-menu/extension-api/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx index 98ec08aa8d..8b48d76160 100644 --- a/src/behaviours/cluster/kube-object-menu/extension-api/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx +++ b/src/behaviours/cluster/kube-object-menu/extension-api/disable-kube-object-menu-items-when-cluster-is-not-relevant.test.tsx @@ -18,7 +18,6 @@ import { routeSpecificComponentInjectionToken } from "../../../../renderer/route import { KubeObject } from "../../../../common/k8s-api/kube-object"; import extensionShouldBeEnabledForClusterFrameInjectable from "../../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable"; import { KubeObjectMenu } from "../../../../renderer/components/kube-object-menu"; -import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable"; describe("disable kube object menu items when cluster is not relevant", () => { let builder: ApplicationBuilder; @@ -30,10 +29,6 @@ describe("disable kube object menu items when cluster is not relevant", () => { beforeEach(async () => { builder = getApplicationBuilder(); - builder.beforeApplicationStart(({ mainDi }) => { - mainDi.override(apiManagerInjectable, () => ({})); - }); - const rendererDi = builder.dis.rendererDi; rendererDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable); diff --git a/src/behaviours/cluster/kube-object-menu/extension-api/reactively-hide-kube-object-menu-item.test.tsx b/src/behaviours/cluster/kube-object-menu/extension-api/reactively-hide-kube-object-menu-item.test.tsx new file mode 100644 index 0000000000..74f33a657a --- /dev/null +++ b/src/behaviours/cluster/kube-object-menu/extension-api/reactively-hide-kube-object-menu-item.test.tsx @@ -0,0 +1,126 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { RenderResult } from "@testing-library/react"; +import type { ApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder"; +import { getExtensionFakeFor } from "../../../../renderer/components/test-utils/get-extension-fake"; +import { getInjectable } from "@ogre-tools/injectable"; +import { frontEndRouteInjectionToken } from "../../../../common/front-end-routing/front-end-route-injection-token"; +import type { IObservableValue } from "mobx"; +import { observable, runInAction, computed } from "mobx"; +import React from "react"; +import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token"; +import { routeSpecificComponentInjectionToken } from "../../../../renderer/routes/route-specific-component-injection-token"; +import { KubeObject } from "../../../../common/k8s-api/kube-object"; +import { KubeObjectMenu } from "../../../../renderer/components/kube-object-menu"; + +describe("reactively hide kube object menu item", () => { + let builder: ApplicationBuilder; + let rendered: RenderResult; + let someObservable: IObservableValue; + + beforeEach(async () => { + builder = getApplicationBuilder(); + + const rendererDi = builder.dis.rendererDi; + + rendererDi.register(testRouteInjectable, testRouteComponentInjectable); + + builder.setEnvironmentToClusterFrame(); + + const getExtensionFake = getExtensionFakeFor(builder); + + someObservable = observable.box(false); + + const testExtension = getExtensionFake({ + id: "test-extension-id", + name: "test-extension", + + rendererOptions: { + kubeObjectMenuItems: [ + { + kind: "some-kind", + apiVersions: ["some-api-version"], + components: { + MenuItem: () => ( +
Some menu item
+ ), + }, + + visible: computed(() => someObservable.get()), + }, + ], + }, + }); + + rendered = await builder.render(); + + const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken); + const testRoute = rendererDi.inject(testRouteInjectable); + + navigateToRoute(testRoute); + + builder.extensions.enable(testExtension); + }); + + it("does not show the kube object menu item", () => { + const actual = rendered.queryByTestId("some-kube-object-menu-item"); + + expect(actual).not.toBeInTheDocument(); + }); + + it("given item should be shown, shows the kube object menu item", () => { + runInAction(() => { + someObservable.set(true); + }); + + const actual = rendered.queryByTestId("some-kube-object-menu-item"); + + expect(actual).toBeInTheDocument(); + }); +}); + +const testRouteInjectable = getInjectable({ + id: "test-route", + + instantiate: () => ({ + path: "/test-route", + clusterFrame: true, + isEnabled: computed(() => true), + }), + + injectionToken: frontEndRouteInjectionToken, +}); + +const testRouteComponentInjectable = getInjectable({ + id: "test-route-component", + + instantiate: (di) => ({ + route: di.inject(testRouteInjectable), + + Component: () => ( + + ), + }), + + injectionToken: routeSpecificComponentInjectionToken, +}); + +const getKubeObjectStub = (kind: string, apiVersion: string) => + KubeObject.create({ + apiVersion, + kind, + metadata: { + uid: "some-uid", + name: "some-name", + resourceVersion: "some-resource-version", + namespace: "some-namespace", + selfLink: "", + }, + }); + diff --git a/src/behaviours/cluster/kube-object-status-icon/extension-api/reactively-hide-kube-object-status.test.tsx b/src/behaviours/cluster/kube-object-status-icon/extension-api/reactively-hide-kube-object-status.test.tsx new file mode 100644 index 0000000000..3c9695d8fa --- /dev/null +++ b/src/behaviours/cluster/kube-object-status-icon/extension-api/reactively-hide-kube-object-status.test.tsx @@ -0,0 +1,132 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { RenderResult } from "@testing-library/react"; +import type { ApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder"; +import { getExtensionFakeFor } from "../../../../renderer/components/test-utils/get-extension-fake"; +import { getInjectable } from "@ogre-tools/injectable"; +import { frontEndRouteInjectionToken } from "../../../../common/front-end-routing/front-end-route-injection-token"; +import type { IObservableValue } from "mobx"; +import { observable, runInAction, computed } from "mobx"; +import React from "react"; +import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token"; +import { routeSpecificComponentInjectionToken } from "../../../../renderer/routes/route-specific-component-injection-token"; +import extensionShouldBeEnabledForClusterFrameInjectable from "../../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable"; +import { KubeObject } from "../../../../common/k8s-api/kube-object"; +import { KubeObjectStatusLevel } from "../../../../common/k8s-api/kube-object-status"; +import { KubeObjectStatusIcon } from "../../../../renderer/components/kube-object-status-icon"; + +describe("reactively hide kube object status", () => { + let builder: ApplicationBuilder; + let rendered: RenderResult; + let someObservable: IObservableValue; + + beforeEach(async () => { + builder = getApplicationBuilder(); + + const rendererDi = builder.dis.rendererDi; + + rendererDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable); + + rendererDi.register(testRouteInjectable, testRouteComponentInjectable); + + builder.setEnvironmentToClusterFrame(); + + const getExtensionFake = getExtensionFakeFor(builder); + + someObservable = observable.box(false); + + const testExtension = getExtensionFake({ + id: "test-extension-id", + name: "test-extension", + + rendererOptions: { + kubeObjectStatusTexts: [ + { + kind: "some-kind", + apiVersions: ["some-api-version"], + + resolve: () => ({ + level: KubeObjectStatusLevel.CRITICAL, + text: "some-kube-object-status-text", + }), + + visible: computed(() => someObservable.get()), + }, + ], + }, + }); + + rendered = await builder.render(); + + const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken); + const testRoute = rendererDi.inject(testRouteInjectable); + + navigateToRoute(testRoute); + + builder.extensions.enable(testExtension); + }); + + it("does not show the kube object status", () => { + const actual = rendered.baseElement.querySelectorAll( + ".KubeObjectStatusIcon", + ); + + expect(actual).toHaveLength(0); + }); + + it("given item should be shown, shows the kube object status", () => { + runInAction(() => { + someObservable.set(true); + }); + + const actual = rendered.baseElement.querySelectorAll( + ".KubeObjectStatusIcon", + ); + + expect(actual).toHaveLength(1); + }); +}); + +const testRouteInjectable = getInjectable({ + id: "test-route", + + instantiate: () => ({ + path: "/test-route", + clusterFrame: true, + isEnabled: computed(() => true), + }), + + injectionToken: frontEndRouteInjectionToken, +}); + +const testRouteComponentInjectable = getInjectable({ + id: "test-route-component", + + instantiate: (di) => ({ + route: di.inject(testRouteInjectable), + + Component: () => ( + + ), + }), + + injectionToken: routeSpecificComponentInjectionToken, +}); + +const getKubeObjectStub = (kind: string, apiVersion: string) => + KubeObject.create({ + apiVersion, + kind, + metadata: { + uid: "some-uid", + name: "some-name", + resourceVersion: "some-resource-version", + namespace: "some-namespace", + selfLink: "", + }, + }); diff --git a/src/behaviours/cluster/workloads/overview/extension-api/reactively-hide-workloads-overview-details-item.test.tsx b/src/behaviours/cluster/workloads/overview/extension-api/reactively-hide-workloads-overview-details-item.test.tsx new file mode 100644 index 0000000000..3b02d3dad9 --- /dev/null +++ b/src/behaviours/cluster/workloads/overview/extension-api/reactively-hide-workloads-overview-details-item.test.tsx @@ -0,0 +1,75 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { RenderResult } from "@testing-library/react"; +import type { IObservableValue } from "mobx"; +import { computed, observable, runInAction } from "mobx"; +import React from "react"; +import navigateToWorkloadsOverviewInjectable from "../../../../../common/front-end-routing/routes/cluster/workloads/overview/navigate-to-workloads-overview.injectable"; +import type { ApplicationBuilder } from "../../../../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../../../../renderer/components/test-utils/get-application-builder"; +import { getExtensionFakeFor } from "../../../../../renderer/components/test-utils/get-extension-fake"; + +describe("reactively hide workloads overview details item", () => { + let builder: ApplicationBuilder; + let rendered: RenderResult; + let someObservable: IObservableValue; + + beforeEach(async () => { + builder = getApplicationBuilder(); + + const rendererDi = builder.dis.rendererDi; + + builder.setEnvironmentToClusterFrame(); + + const getExtensionFake = getExtensionFakeFor(builder); + + someObservable = observable.box(false); + + const testExtension = getExtensionFake({ + id: "test-extension-id", + name: "test-extension", + + rendererOptions: { + kubeWorkloadsOverviewItems: [ + { + components: { + Details: () => ( +
Some detail component
+ ), + }, + + visible: computed(() => someObservable.get()), + }, + ], + }, + }); + + rendered = await builder.render(); + + const navigateToWorkloadsOverview = rendererDi.inject( + navigateToWorkloadsOverviewInjectable, + ); + + navigateToWorkloadsOverview(); + + builder.extensions.enable(testExtension); + }); + + it("does not show the workload overview detail item", () => { + const actual = rendered.queryByTestId("some-workload-overview-detail-item"); + + expect(actual).not.toBeInTheDocument(); + }); + + it("given item should be shown, shows the workload overview detail item", () => { + runInAction(() => { + someObservable.set(true); + }); + + const actual = rendered.queryByTestId("some-workload-overview-detail-item"); + + expect(actual).toBeInTheDocument(); + }); +}); diff --git a/src/behaviours/routes/extension-api/reactively-disable-global-pages.test.tsx b/src/behaviours/routes/extension-api/reactively-disable-global-pages.test.tsx new file mode 100644 index 0000000000..523a56cf79 --- /dev/null +++ b/src/behaviours/routes/extension-api/reactively-disable-global-pages.test.tsx @@ -0,0 +1,68 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { RenderResult } from "@testing-library/react"; +import type { IObservableValue } from "mobx"; +import { observable, runInAction, computed } from "mobx"; +import React from "react"; +import type { TestExtensionRenderer } from "../../../renderer/components/test-utils/get-extension-fake"; +import type { ApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder"; +import { getExtensionFakeFor } from "../../../renderer/components/test-utils/get-extension-fake"; + +describe("reactively disable global pages", () => { + let builder: ApplicationBuilder; + let rendered: RenderResult; + let someObservable: IObservableValue; + let rendererTestExtension: TestExtensionRenderer; + + beforeEach(async () => { + builder = getApplicationBuilder(); + + const getExtensionFake = getExtensionFakeFor(builder); + + someObservable = observable.box(false); + + const testExtension = getExtensionFake({ + id: "test-extension-id", + name: "test-extension", + + rendererOptions: { + globalPages: [{ + components: { + Page: () =>
Some page
, + }, + + enabled: computed(() => someObservable.get()), + }], + }, + }); + + rendered = await builder.render(); + + builder.extensions.enable(testExtension); + + rendererTestExtension = testExtension.renderer; + }); + + it("when navigating to the page, does not show the page", () => { + rendererTestExtension.navigate(); + + const actual = rendered.queryByTestId("some-test-page"); + + expect(actual).not.toBeInTheDocument(); + }); + + it("given page becomes enabled, when navigating to the page, shows the page", () => { + runInAction(() => { + someObservable.set(true); + }); + + rendererTestExtension.navigate(); + + const actual = rendered.queryByTestId("some-test-page"); + + expect(actual).toBeInTheDocument(); + }); +}); diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 0a65a12bae..d744d261d4 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -90,6 +90,8 @@ export class LensRendererExtension extends LensExtension { return (void cluster) || true; diff --git a/src/extensions/registries/page-registry.ts b/src/extensions/registries/page-registry.ts index b698460a54..6584b3aa9b 100644 --- a/src/extensions/registries/page-registry.ts +++ b/src/extensions/registries/page-registry.ts @@ -2,6 +2,7 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import type { IComputedValue } from "mobx"; import type { PageParamInit, PageParam } from "../../renderer/navigation"; // Extensions-api -> Custom page registration @@ -14,6 +15,7 @@ export interface PageRegistration { id?: string; params?: PageParams, "name" | "prefix">>; components: PageComponents; + enabled?: IComputedValue; } export interface PageComponents { diff --git a/src/extensions/renderer-api/catalog.ts b/src/extensions/renderer-api/catalog.ts index 87506926e2..43b840a39a 100644 --- a/src/extensions/renderer-api/catalog.ts +++ b/src/extensions/renderer-api/catalog.ts @@ -10,6 +10,7 @@ import type { Disposer } from "../../common/utils"; import catalogCategoryRegistryInjectable from "../../common/catalog/category-registry.injectable"; import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; import catalogEntityRegistryInjectable from "../../renderer/api/catalog/entity/registry.injectable"; +import activeKubernetesClusterInjectable from "../../renderer/cluster-frame-context/active-kubernetes-cluster.injectable"; export const catalogCategories = asLegacyGlobalForExtensionApi(catalogCategoryRegistryInjectable); @@ -53,3 +54,7 @@ export class CatalogEntityRegistry { } export const catalogEntities = new CatalogEntityRegistry(); + +export const activeCluster = asLegacyGlobalForExtensionApi( + activeKubernetesClusterInjectable, +); diff --git a/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-detail-registrator.injectable.ts b/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-detail-registrator.injectable.ts index f591c5a200..e82b1f43e5 100644 --- a/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-detail-registrator.injectable.ts +++ b/src/renderer/components/+workloads-overview/workload-overview-details/workload-overview-detail-registrator.injectable.ts @@ -38,9 +38,13 @@ const workloadOverviewDetailRegistratorInjectable = getInjectable({ instantiate: () => ({ Component: registration.components.Details, - enabled: computed(() => - extensionShouldBeEnabledForClusterFrame.value.get(), - ), + enabled: computed(() => { + if (!extensionShouldBeEnabledForClusterFrame.value.get()) { + return false; + } + + return registration.visible ? registration.visible.get() : true; + }), orderNumber: 0.5 + (registration.priority ? 100 - registration.priority : 50), diff --git a/src/renderer/components/+workloads-overview/workloads-overview-detail-registration.ts b/src/renderer/components/+workloads-overview/workloads-overview-detail-registration.ts index 19e59494da..ab23619b95 100644 --- a/src/renderer/components/+workloads-overview/workloads-overview-detail-registration.ts +++ b/src/renderer/components/+workloads-overview/workloads-overview-detail-registration.ts @@ -1,3 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { IComputedValue } from "mobx"; + /** * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. @@ -9,4 +15,5 @@ interface WorkloadsOverviewDetailComponents { export interface WorkloadsOverviewDetailRegistration { components: WorkloadsOverviewDetailComponents; priority?: number; + visible?: IComputedValue; } diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-item-registrator.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-item-registrator.injectable.ts index 74d7adf894..bf7e296921 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-item-registrator.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/kube-object-detail-item-registrator.injectable.ts @@ -10,9 +10,7 @@ import extensionShouldBeEnabledForClusterFrameInjectable from "../../../extensio import { kubeObjectDetailItemInjectionToken } from "./kube-object-detail-item-injection-token"; import { extensionRegistratorInjectionToken } from "../../../../extensions/extension-loader/extension-registrator-injection-token"; import currentKubeObjectInDetailsInjectable from "../current-kube-object-in-details.injectable"; -import { - kubeObjectMatchesToKindAndApiVersion, -} from "./kube-object-matches-to-kind-and-api-version"; +import { kubeObjectMatchesToKindAndApiVersion } from "./kube-object-matches-to-kind-and-api-version"; const kubeObjectDetailItemRegistratorInjectable = getInjectable({ id: "kube-object-detail-item-registrator", @@ -45,20 +43,24 @@ const kubeObjectDetailItemRegistratorInjectable = getInjectable({ id, instantiate: (di) => { - const kubeObject = di.inject( - currentKubeObjectInDetailsInjectable, - ); + const kubeObject = di.inject(currentKubeObjectInDetailsInjectable); return { kind: registration.kind, apiVersions: registration.apiVersions, Component: registration.components.Details, - enabled: computed( - () => - extensionShouldBeEnabledForClusterFrame.value.get() && - isRelevantKubeObject(kubeObject.get()), - ), + enabled: computed(() => { + if (!extensionShouldBeEnabledForClusterFrame.value.get()) { + return false; + } + + if (!isRelevantKubeObject(kubeObject.get())) { + return false; + } + + return registration.visible ? registration.visible.get() : true; + }), orderNumber: 300 - (registration.priority || 50), }; diff --git a/src/renderer/components/kube-object-details/kube-object-detail-registration.ts b/src/renderer/components/kube-object-details/kube-object-detail-registration.ts index ba5ec84f2e..c755630a80 100644 --- a/src/renderer/components/kube-object-details/kube-object-detail-registration.ts +++ b/src/renderer/components/kube-object-details/kube-object-detail-registration.ts @@ -5,6 +5,7 @@ import type { KubeObject } from "../../../common/k8s-api/kube-object"; import type { KubeObjectDetailsProps } from "./kube-object-details"; import type React from "react"; +import type { IComputedValue } from "mobx"; export interface KubeObjectDetailComponents { Details: React.ComponentType>; @@ -15,4 +16,5 @@ export interface KubeObjectDetailRegistration apiVersions: string[]; components: KubeObjectDetailComponents; priority?: number; + visible?: IComputedValue; } diff --git a/src/renderer/components/kube-object-menu/kube-object-menu-item-registrator.injectable.ts b/src/renderer/components/kube-object-menu/kube-object-menu-item-registrator.injectable.ts index 4ee77ff4d0..e5df278afc 100644 --- a/src/renderer/components/kube-object-menu/kube-object-menu-item-registrator.injectable.ts +++ b/src/renderer/components/kube-object-menu/kube-object-menu-item-registrator.injectable.ts @@ -40,9 +40,13 @@ const kubeObjectMenuItemRegistratorInjectable = getInjectable({ apiVersions: registration.apiVersions, Component: registration.components.MenuItem, - enabled: computed(() => - extensionShouldBeEnabledForClusterFrame.value.get(), - ), + enabled: computed(() => { + if (!extensionShouldBeEnabledForClusterFrame.value.get()) { + return false; + } + + return registration.visible ? registration.visible.get() : true; + }), orderNumber: 100, }), diff --git a/src/renderer/components/kube-object-menu/kube-object-menu-registration.ts b/src/renderer/components/kube-object-menu/kube-object-menu-registration.ts index a506846fc5..939fea89f0 100644 --- a/src/renderer/components/kube-object-menu/kube-object-menu-registration.ts +++ b/src/renderer/components/kube-object-menu/kube-object-menu-registration.ts @@ -3,6 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import type { IComputedValue } from "mobx"; import type React from "react"; import type { KubeObject } from "../../../common/k8s-api/kube-object"; @@ -19,4 +20,5 @@ export interface KubeObjectMenuRegistration { kind: string; apiVersions: string[]; components: KubeObjectMenuComponents; + visible?: IComputedValue; } diff --git a/src/renderer/components/kube-object-status-icon/kube-object-status-registration.ts b/src/renderer/components/kube-object-status-icon/kube-object-status-registration.ts index 01fd22b203..301b1b29eb 100644 --- a/src/renderer/components/kube-object-status-icon/kube-object-status-registration.ts +++ b/src/renderer/components/kube-object-status-icon/kube-object-status-registration.ts @@ -2,6 +2,7 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import type { IComputedValue } from "mobx"; import type { KubeObject } from "../../../common/k8s-api/kube-object"; import type { KubeObjectStatus } from "../../../common/k8s-api/kube-object-status"; @@ -9,4 +10,5 @@ export interface KubeObjectStatusRegistration { kind: string; apiVersions: string[]; resolve: (object: KubeObject) => KubeObjectStatus; + visible?: IComputedValue; } diff --git a/src/renderer/components/kube-object-status-icon/kube-object-status-text-registrator.injectable.ts b/src/renderer/components/kube-object-status-icon/kube-object-status-text-registrator.injectable.ts index 0592330e8c..b5ba2bb922 100644 --- a/src/renderer/components/kube-object-status-icon/kube-object-status-text-registrator.injectable.ts +++ b/src/renderer/components/kube-object-status-icon/kube-object-status-text-registrator.injectable.ts @@ -38,9 +38,13 @@ const kubeObjectStatusTextRegistratorInjectable = getInjectable({ instantiate: () => ({ ...registration, - enabled: computed(() => - extensionShouldBeEnabledForClusterFrame.value.get(), - ), + enabled: computed(() => { + if (!extensionShouldBeEnabledForClusterFrame.value.get()) { + return false; + } + + return registration.visible ? registration.visible.get() : true; + }), }), injectionToken: kubeObjectStatusTextInjectionToken, diff --git a/src/renderer/routes/extension-route-registrator.injectable.tsx b/src/renderer/routes/extension-route-registrator.injectable.tsx index 241e6950a5..d36c17cdad 100644 --- a/src/renderer/routes/extension-route-registrator.injectable.tsx +++ b/src/renderer/routes/extension-route-registrator.injectable.tsx @@ -35,16 +35,22 @@ const extensionRouteRegistratorInjectable = getInjectable({ return [ ...extension.globalPages.map( - toRouteInjectable( - false, - computed(() => true), + toRouteInjectable(false, (registration) => + computed(() => + registration.enabled ? registration.enabled.get() : true, + ), ), ), ...extension.clusterPages.map( - toRouteInjectable( - true, - computed(() => extensionShouldBeEnabledForClusterFrame.value.get()), + toRouteInjectable(true, (registration) => + computed(() => { + if (!extensionShouldBeEnabledForClusterFrame.value.get()) { + return false; + } + + return registration.enabled ? registration.enabled.get() : true; + }), ), ), ].flat(); @@ -61,7 +67,7 @@ const toRouteInjectableFor = di: DiContainerForInjection, extension: LensRendererExtension, ) => - (clusterFrame: boolean, isEnabled: IComputedValue) => + (clusterFrame: boolean, getIsEnabled: (registration: PageRegistration) => IComputedValue) => (registration: PageRegistration) => { const routeInjectable = getInjectable({ id: `route-${registration.id}-for-extension-${extension.sanitizedExtensionId}`, @@ -69,7 +75,7 @@ const toRouteInjectableFor = instantiate: () => ({ path: getExtensionRoutePath(extension, registration.id), clusterFrame, - isEnabled, + isEnabled: getIsEnabled(registration), extension, }),