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..5fc637f845 --- /dev/null +++ b/src/behaviours/cluster/kube-object-menu/extension-api/reactively-hide-kube-object-menu-item.test.tsx @@ -0,0 +1,129 @@ +/** + * 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 { 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.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: { + 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/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; }