From ab4a8c8a1df9b82ad1a901e113a8db83d9ccdae9 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Tue, 28 Dec 2021 12:35:47 +0200 Subject: [PATCH 1/3] Convert a class component to functional component Co-authored-by: Mikko Aspiala Signed-off-by: Janne Savolainen --- src/renderer/components/+welcome/welcome.tsx | 147 ++++++++++++------- 1 file changed, 90 insertions(+), 57 deletions(-) diff --git a/src/renderer/components/+welcome/welcome.tsx b/src/renderer/components/+welcome/welcome.tsx index 669e768782..52d71bffb0 100644 --- a/src/renderer/components/+welcome/welcome.tsx +++ b/src/renderer/components/+welcome/welcome.tsx @@ -30,70 +30,103 @@ import { WelcomeBannerRegistry } from "../../../extensions/registries"; export const defaultWidth = 320; -@observer -export class Welcome extends React.Component { - render() { - const welcomeBanner = WelcomeBannerRegistry.getInstance().getItems(); +export const Welcome: React.FC = observer(() => { + const welcomeBanner = WelcomeBannerRegistry.getInstance().getItems(); - // if there is banner with specified width, use it to calculate the width of the container - const maxWidth = welcomeBanner.reduce((acc, curr) => { - const currWidth = curr.width ?? 0; + // if there is banner with specified width, use it to calculate the width of the container + const maxWidth = welcomeBanner.reduce((acc, curr) => { + const currWidth = curr.width ?? 0; - if (acc > currWidth) { - return acc; - } + if (acc > currWidth) { + return acc; + } - return currWidth; - }, defaultWidth); + return currWidth; + }, defaultWidth); - return ( -
-
- {welcomeBanner.length > 0 ? ( - 1} - autoPlay={true} - navButtonsAlwaysInvisible={true} - indicatorIconButtonProps={{ - style: { - color: "var(--iconActiveBackground)", - }, - }} - activeIndicatorIconButtonProps={{ - style: { - color: "var(--iconActiveColor)", - }, - }} - interval={8000} + return ( +
+
+ {welcomeBanner.length > 0 ? ( + 1} + autoPlay={true} + navButtonsAlwaysInvisible={true} + indicatorIconButtonProps={{ + style: { + color: "var(--iconActiveBackground)", + }, + }} + activeIndicatorIconButtonProps={{ + style: { + color: "var(--iconActiveColor)", + }, + }} + interval={8000} + > + {welcomeBanner.map((item, index) => ( + + ))} + + ) : ( + + )} + +
+
+

Welcome to {productName} 5!

+ +

+ 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. +
+
+ If you have any questions or feedback, please join our{" "} + + Lens Community slack channel + + . +

+ +
- ); - } -} +
+ ); +}); From 04ac2ff3ddc66f9cc5b1fd5478377086b93262e1 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Tue, 28 Dec 2021 12:52:39 +0200 Subject: [PATCH 2/3] Remove WelcomeMenuRegistry in favor of reactive solution Co-authored-by: Mikko Aspiala Signed-off-by: Janne Savolainen --- .../extension-loader/extension-loader.ts | 1 - src/extensions/lens-renderer-extension.ts | 3 +- src/extensions/registries/index.ts | 1 - .../renderer-extensions.injectable.ts | 33 +++++++++++ src/renderer/bootstrap.tsx | 3 - .../+welcome/__test__/welcome.test.tsx | 15 +++-- .../get-welcome-menu-items.ts | 46 ++++++++++++++++ .../welcome-menu-items.injectable.ts} | 25 ++++----- .../welcome-menu-registration.d.ts} | 4 -- src/renderer/components/+welcome/welcome.tsx | 55 ++++++++++++------- src/renderer/initializers/index.ts | 1 - src/renderer/initializers/registries.ts | 1 - 12 files changed, 139 insertions(+), 49 deletions(-) create mode 100644 src/extensions/renderer-extensions.injectable.ts create mode 100644 src/renderer/components/+welcome/welcome-menu-items/get-welcome-menu-items.ts rename src/renderer/{initializers/welcome-menu-registry.ts => components/+welcome/welcome-menu-items/welcome-menu-items.injectable.ts} (69%) rename src/{extensions/registries/welcome-menu-registry.ts => renderer/components/+welcome/welcome-menu-items/welcome-menu-registration.d.ts} (90%) diff --git a/src/extensions/extension-loader/extension-loader.ts b/src/extensions/extension-loader/extension-loader.ts index af5f05c100..fb0c42e1ac 100644 --- a/src/extensions/extension-loader/extension-loader.ts +++ b/src/extensions/extension-loader/extension-loader.ts @@ -263,7 +263,6 @@ export class ExtensionLoader { registries.EntitySettingRegistry.getInstance().add(extension.entitySettings), registries.StatusBarRegistry.getInstance().add(extension.statusBarItems), registries.CommandRegistry.getInstance().add(extension.commands), - registries.WelcomeMenuRegistry.getInstance().add(extension.welcomeMenus), registries.WelcomeBannerRegistry.getInstance().add(extension.welcomeBanners), registries.CatalogEntityDetailRegistry.getInstance().add(extension.catalogEntityDetailItems), registries.TopBarRegistry.getInstance().add(extension.topBarItems), diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 67567969ee..388f6ba1f2 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -27,6 +27,7 @@ import type { Disposer } from "../common/utils"; import { catalogEntityRegistry, EntityFilter } from "../renderer/api/catalog-entity-registry"; import { catalogCategoryRegistry, CategoryFilter } from "../renderer/api/catalog-category-registry"; import type { KubernetesCluster } from "../common/catalog-entities"; +import type { WelcomeMenuRegistration } from "../renderer/components/+welcome/welcome-menu-items/welcome-menu-registration"; export class LensRendererExtension extends LensExtension { globalPages: registries.PageRegistration[] = []; @@ -40,7 +41,7 @@ export class LensRendererExtension extends LensExtension { kubeObjectMenuItems: registries.KubeObjectMenuRegistration[] = []; kubeWorkloadsOverviewItems: registries.WorkloadsOverviewDetailRegistration[] = []; commands: registries.CommandRegistration[] = []; - welcomeMenus: registries.WelcomeMenuRegistration[] = []; + welcomeMenus: WelcomeMenuRegistration[] = []; welcomeBanners: registries.WelcomeBannerRegistration[] = []; catalogEntityDetailItems: registries.CatalogEntityDetailRegistration[] = []; topBarItems: registries.TopBarRegistration[] = []; diff --git a/src/extensions/registries/index.ts b/src/extensions/registries/index.ts index 4dd64a9c82..b9c249ce4c 100644 --- a/src/extensions/registries/index.ts +++ b/src/extensions/registries/index.ts @@ -30,7 +30,6 @@ export * from "./kube-object-menu-registry"; export * from "./kube-object-status-registry"; export * from "./command-registry"; export * from "./entity-setting-registry"; -export * from "./welcome-menu-registry"; export * from "./welcome-banner-registry"; export * from "./catalog-entity-detail-registry"; export * from "./workloads-overview-detail-registry"; diff --git a/src/extensions/renderer-extensions.injectable.ts b/src/extensions/renderer-extensions.injectable.ts new file mode 100644 index 0000000000..dddcf11e80 --- /dev/null +++ b/src/extensions/renderer-extensions.injectable.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import type { IComputedValue } from "mobx"; +import extensionsInjectable from "./extensions.injectable"; +import type { LensRendererExtension } from "./lens-renderer-extension"; + +const rendererExtensionsInjectable = getInjectable({ + lifecycle: lifecycleEnum.singleton, + + instantiate: (di) => + di.inject(extensionsInjectable) as IComputedValue, +}); + +export default rendererExtensionsInjectable; diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 1de360d18a..6e9e9fb471 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -114,9 +114,6 @@ export async function bootstrap(comp: () => Promise, di: Dependenc logger.info(`${logPrefix} initializing KubeObjectDetailRegistry`); initializers.initKubeObjectDetailRegistry(); - logger.info(`${logPrefix} initializing WelcomeMenuRegistry`); - initializers.initWelcomeMenuRegistry(); - logger.info(`${logPrefix} initializing WorkloadsOverviewDetailRegistry`); initializers.initWorkloadsOverviewDetailRegistry(); diff --git a/src/renderer/components/+welcome/__test__/welcome.test.tsx b/src/renderer/components/+welcome/__test__/welcome.test.tsx index 2024d69c7e..fa94149caa 100644 --- a/src/renderer/components/+welcome/__test__/welcome.test.tsx +++ b/src/renderer/components/+welcome/__test__/welcome.test.tsx @@ -20,11 +20,14 @@ */ import React from "react"; -import { render, screen } from "@testing-library/react"; +import { screen } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; import { Welcome } from "../welcome"; -import { TopBarRegistry, WelcomeMenuRegistry, WelcomeBannerRegistry } from "../../../../extensions/registries"; +import { TopBarRegistry, WelcomeBannerRegistry } from "../../../../extensions/registries"; import { defaultWidth } from "../welcome"; +import { getDiForUnitTesting } from "../../getDiForUnitTesting"; +import type { DiRender } from "../../test-utils/renderFor"; +import { renderFor } from "../../test-utils/renderFor"; jest.mock( "electron", @@ -39,15 +42,19 @@ jest.mock( ); describe("", () => { + let render: DiRender; + beforeEach(() => { + const di = getDiForUnitTesting(); + + render = renderFor(di); + TopBarRegistry.createInstance(); - WelcomeMenuRegistry.createInstance(); WelcomeBannerRegistry.createInstance(); }); afterEach(() => { TopBarRegistry.resetInstance(); - WelcomeMenuRegistry.resetInstance(); WelcomeBannerRegistry.resetInstance(); }); diff --git a/src/renderer/components/+welcome/welcome-menu-items/get-welcome-menu-items.ts b/src/renderer/components/+welcome/welcome-menu-items/get-welcome-menu-items.ts new file mode 100644 index 0000000000..c766704cef --- /dev/null +++ b/src/renderer/components/+welcome/welcome-menu-items/get-welcome-menu-items.ts @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import { computed, IComputedValue } from "mobx"; +import type { LensRendererExtension } from "../../../../extensions/lens-renderer-extension"; +import { navigate } from "../../../navigation"; +import { catalogURL } from "../../../../common/routes"; + +interface Dependencies { + extensions: IComputedValue; +} + +export const getWelcomeMenuItems = ({ extensions }: Dependencies) => { + const browseClusters = { + title: "Browse Clusters in Catalog", + icon: "view_list", + click: () => + navigate( + catalogURL({ + params: { group: "entity.k8slens.dev", kind: "KubernetesCluster" }, + }), + ), + }; + + return computed(() => [ + browseClusters, + ...extensions.get().flatMap((extension) => extension.welcomeMenus), + ]); +}; diff --git a/src/renderer/initializers/welcome-menu-registry.ts b/src/renderer/components/+welcome/welcome-menu-items/welcome-menu-items.injectable.ts similarity index 69% rename from src/renderer/initializers/welcome-menu-registry.ts rename to src/renderer/components/+welcome/welcome-menu-items/welcome-menu-items.injectable.ts index f658fe5715..384b0b07bc 100644 --- a/src/renderer/initializers/welcome-menu-registry.ts +++ b/src/renderer/components/+welcome/welcome-menu-items/welcome-menu-items.injectable.ts @@ -18,18 +18,17 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import rendererExtensionsInjectable from "../../../../extensions/renderer-extensions.injectable"; +import { getWelcomeMenuItems } from "./get-welcome-menu-items"; -import { catalogURL } from "../../common/routes"; -import { WelcomeMenuRegistry } from "../../extensions/registries"; -import { navigate } from "../navigation"; +const welcomeMenuItemsInjectable = getInjectable({ + instantiate: (di) => + getWelcomeMenuItems({ + extensions: di.inject(rendererExtensionsInjectable), + }), -export function initWelcomeMenuRegistry() { - WelcomeMenuRegistry.getInstance() - .add([ - { - title: "Browse Clusters in Catalog", - icon: "view_list", - click: () => navigate(catalogURL({ params: { group: "entity.k8slens.dev", kind: "KubernetesCluster" }} )), - }, - ]); -} + lifecycle: lifecycleEnum.singleton, +}); + +export default welcomeMenuItemsInjectable; diff --git a/src/extensions/registries/welcome-menu-registry.ts b/src/renderer/components/+welcome/welcome-menu-items/welcome-menu-registration.d.ts similarity index 90% rename from src/extensions/registries/welcome-menu-registry.ts rename to src/renderer/components/+welcome/welcome-menu-items/welcome-menu-registration.d.ts index 7092028459..8f4d9833b3 100644 --- a/src/extensions/registries/welcome-menu-registry.ts +++ b/src/renderer/components/+welcome/welcome-menu-items/welcome-menu-registration.d.ts @@ -19,12 +19,8 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { BaseRegistry } from "./base-registry"; - export interface WelcomeMenuRegistration { title: string | (() => string); icon: string; click: () => void | Promise; } - -export class WelcomeMenuRegistry extends BaseRegistry {} diff --git a/src/renderer/components/+welcome/welcome.tsx b/src/renderer/components/+welcome/welcome.tsx index 52d71bffb0..f90ede88b8 100644 --- a/src/renderer/components/+welcome/welcome.tsx +++ b/src/renderer/components/+welcome/welcome.tsx @@ -22,15 +22,22 @@ import "./welcome.scss"; import React from "react"; import { observer } from "mobx-react"; +import type { IComputedValue } from "mobx"; import Carousel from "react-material-ui-carousel"; import { Icon } from "../icon"; import { productName, slackUrl } from "../../../common/vars"; -import { WelcomeMenuRegistry } from "../../../extensions/registries"; import { WelcomeBannerRegistry } from "../../../extensions/registries"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import welcomeMenuItemsInjectable from "./welcome-menu-items/welcome-menu-items.injectable"; +import type { WelcomeMenuRegistration } from "./welcome-menu-items/welcome-menu-registration"; export const defaultWidth = 320; -export const Welcome: React.FC = observer(() => { +interface Dependencies { + welcomeMenuItems: IComputedValue +} + +const NonInjectedWelcome: React.FC = ({ welcomeMenuItems }) => { const welcomeBanner = WelcomeBannerRegistry.getInstance().getItems(); // if there is banner with specified width, use it to calculate the width of the container @@ -106,27 +113,35 @@ export const Welcome: React.FC = observer(() => { style={{ width: `${defaultWidth}px` }} data-testid="welcome-menu-container" > - {WelcomeMenuRegistry.getInstance() - .getItems() - .map((item, index) => ( -
  • item.click()} - > - - - {typeof item.title === "string" - ? item.title - : item.title()} - - -
  • - ))} + {welcomeMenuItems.get().map((item, index) => ( +
  • item.click()} + > + + + {typeof item.title === "string" + ? item.title + : item.title()} + + +
  • + ))}
    ); -}); +}; + +export const Welcome = withInjectables( + observer(NonInjectedWelcome), + + { + getProps: (di) => ({ + welcomeMenuItems: di.inject(welcomeMenuItemsInjectable), + }), + }, +); diff --git a/src/renderer/initializers/index.ts b/src/renderer/initializers/index.ts index 55d1bbbb1b..b865368988 100644 --- a/src/renderer/initializers/index.ts +++ b/src/renderer/initializers/index.ts @@ -27,7 +27,6 @@ export * from "./ipc"; export * from "./kube-object-detail-registry"; export * from "./kube-object-menu-registry"; export * from "./registries"; -export * from "./welcome-menu-registry"; export * from "./workloads-overview-detail-registry"; export * from "./catalog-category-registry"; export * from "./status-bar-registry"; diff --git a/src/renderer/initializers/registries.ts b/src/renderer/initializers/registries.ts index 3063213bc8..4fddb35b3d 100644 --- a/src/renderer/initializers/registries.ts +++ b/src/renderer/initializers/registries.ts @@ -33,7 +33,6 @@ export function initRegistries() { registries.KubeObjectMenuRegistry.createInstance(); registries.KubeObjectStatusRegistry.createInstance(); registries.StatusBarRegistry.createInstance(); - registries.WelcomeMenuRegistry.createInstance(); registries.WelcomeBannerRegistry.createInstance(); registries.WorkloadsOverviewDetailRegistry.createInstance(); registries.TopBarRegistry.createInstance(); From c5613a2e6a38a3acf82452c46442c9a2a884ed45 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Tue, 28 Dec 2021 13:13:19 +0200 Subject: [PATCH 3/3] Remove WelcomeBannerRegistry in favor of reactive solution Co-authored-by: Mikko Aspiala Signed-off-by: Janne Savolainen --- .../extension-loader/extension-loader.ts | 1 - src/extensions/lens-renderer-extension.ts | 3 +- src/extensions/registries/index.ts | 1 - .../+welcome/__test__/welcome.test.tsx | 95 ++++++++++++------- .../welcome-banner-items.injectable.ts | 37 ++++++++ .../welcome-banner-registration.d.ts} | 4 - src/renderer/components/+welcome/welcome.tsx | 17 ++-- src/renderer/initializers/registries.ts | 1 - 8 files changed, 112 insertions(+), 47 deletions(-) create mode 100644 src/renderer/components/+welcome/welcome-banner-items/welcome-banner-items.injectable.ts rename src/{extensions/registries/welcome-banner-registry.ts => renderer/components/+welcome/welcome-banner-items/welcome-banner-registration.d.ts} (91%) diff --git a/src/extensions/extension-loader/extension-loader.ts b/src/extensions/extension-loader/extension-loader.ts index fb0c42e1ac..db3e924209 100644 --- a/src/extensions/extension-loader/extension-loader.ts +++ b/src/extensions/extension-loader/extension-loader.ts @@ -263,7 +263,6 @@ export class ExtensionLoader { registries.EntitySettingRegistry.getInstance().add(extension.entitySettings), registries.StatusBarRegistry.getInstance().add(extension.statusBarItems), registries.CommandRegistry.getInstance().add(extension.commands), - registries.WelcomeBannerRegistry.getInstance().add(extension.welcomeBanners), registries.CatalogEntityDetailRegistry.getInstance().add(extension.catalogEntityDetailItems), registries.TopBarRegistry.getInstance().add(extension.topBarItems), ]; diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 388f6ba1f2..2ccb2cfe7a 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -28,6 +28,7 @@ import { catalogEntityRegistry, EntityFilter } from "../renderer/api/catalog-ent import { catalogCategoryRegistry, CategoryFilter } from "../renderer/api/catalog-category-registry"; import type { KubernetesCluster } from "../common/catalog-entities"; import type { WelcomeMenuRegistration } from "../renderer/components/+welcome/welcome-menu-items/welcome-menu-registration"; +import type { WelcomeBannerRegistration } from "../renderer/components/+welcome/welcome-banner-items/welcome-banner-registration"; export class LensRendererExtension extends LensExtension { globalPages: registries.PageRegistration[] = []; @@ -42,7 +43,7 @@ export class LensRendererExtension extends LensExtension { kubeWorkloadsOverviewItems: registries.WorkloadsOverviewDetailRegistration[] = []; commands: registries.CommandRegistration[] = []; welcomeMenus: WelcomeMenuRegistration[] = []; - welcomeBanners: registries.WelcomeBannerRegistration[] = []; + welcomeBanners: WelcomeBannerRegistration[] = []; catalogEntityDetailItems: registries.CatalogEntityDetailRegistration[] = []; topBarItems: registries.TopBarRegistration[] = []; diff --git a/src/extensions/registries/index.ts b/src/extensions/registries/index.ts index b9c249ce4c..2cd822f77a 100644 --- a/src/extensions/registries/index.ts +++ b/src/extensions/registries/index.ts @@ -30,7 +30,6 @@ export * from "./kube-object-menu-registry"; export * from "./kube-object-status-registry"; export * from "./command-registry"; export * from "./entity-setting-registry"; -export * from "./welcome-banner-registry"; export * from "./catalog-entity-detail-registry"; export * from "./workloads-overview-detail-registry"; export * from "./topbar-registry"; diff --git a/src/renderer/components/+welcome/__test__/welcome.test.tsx b/src/renderer/components/+welcome/__test__/welcome.test.tsx index fa94149caa..f7ae85bfea 100644 --- a/src/renderer/components/+welcome/__test__/welcome.test.tsx +++ b/src/renderer/components/+welcome/__test__/welcome.test.tsx @@ -22,50 +22,60 @@ import React from "react"; import { screen } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; -import { Welcome } from "../welcome"; -import { TopBarRegistry, WelcomeBannerRegistry } from "../../../../extensions/registries"; -import { defaultWidth } from "../welcome"; +import { defaultWidth, Welcome } from "../welcome"; +import { computed } from "mobx"; +import { TopBarRegistry } from "../../../../extensions/registries"; import { getDiForUnitTesting } from "../../getDiForUnitTesting"; import type { DiRender } from "../../test-utils/renderFor"; import { renderFor } from "../../test-utils/renderFor"; +import type { ConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable"; +import rendererExtensionsInjectable from "../../../../extensions/renderer-extensions.injectable"; +import { LensRendererExtension } from "../../../../extensions/lens-renderer-extension"; +import type { WelcomeBannerRegistration } from "../welcome-banner-items/welcome-banner-registration"; -jest.mock( - "electron", - () => ({ - ipcRenderer: { - on: jest.fn(), - }, - app: { - getPath: () => "tmp", - }, - }), -); +jest.mock("electron", () => ({ + ipcRenderer: { + on: jest.fn(), + }, + app: { + getPath: () => "tmp", + }, +})); describe("", () => { let render: DiRender; + let di: ConfigurableDependencyInjectionContainer; + let welcomeBannersStub: WelcomeBannerRegistration[]; beforeEach(() => { - const di = getDiForUnitTesting(); + di = getDiForUnitTesting(); render = renderFor(di); + welcomeBannersStub = []; + + di.override(rendererExtensionsInjectable, () => + computed(() => [ + new TestExtension({ + id: "some-id", + welcomeBanners: welcomeBannersStub, + }), + ]), + ); + TopBarRegistry.createInstance(); - WelcomeBannerRegistry.createInstance(); }); afterEach(() => { TopBarRegistry.resetInstance(); - WelcomeBannerRegistry.resetInstance(); }); it("renders registered in WelcomeBannerRegistry and hide logo", async () => { const testId = "testId"; - WelcomeBannerRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [ - { - Banner: () =>
    , - }, - ]); + welcomeBannersStub.push({ + Banner: () =>
    , + }); const { container } = render(); @@ -74,16 +84,15 @@ describe("", () => { }); it("calculates max width from WelcomeBanner.width registered in WelcomeBannerRegistry", async () => { - WelcomeBannerRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [ - { - width: 100, - Banner: () =>
    , - }, - { - width: 800, - Banner: () =>
    , - }, - ]); + welcomeBannersStub.push({ + width: 100, + Banner: () =>
    , + }); + + welcomeBannersStub.push({ + width: 800, + Banner: () =>
    , + }); render(); @@ -99,3 +108,25 @@ describe("", () => { }); }); }); + +class TestExtension extends LensRendererExtension { + constructor({ + id, + welcomeBanners, + }: { + id: string; + welcomeBanners: WelcomeBannerRegistration[]; + }) { + super({ + id, + absolutePath: "irrelevant", + isBundled: false, + isCompatible: false, + isEnabled: false, + manifest: { name: id, version: "some-version" }, + manifestPath: "irrelevant", + }); + + this.welcomeBanners = welcomeBanners; + } +} diff --git a/src/renderer/components/+welcome/welcome-banner-items/welcome-banner-items.injectable.ts b/src/renderer/components/+welcome/welcome-banner-items/welcome-banner-items.injectable.ts new file mode 100644 index 0000000000..288bb540ce --- /dev/null +++ b/src/renderer/components/+welcome/welcome-banner-items/welcome-banner-items.injectable.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import rendererExtensionsInjectable from "../../../../extensions/renderer-extensions.injectable"; +import { computed } from "mobx"; + +const welcomeBannerItemsInjectable = getInjectable({ + instantiate: (di) => { + const extensions = di.inject(rendererExtensionsInjectable); + + return computed(() => [ + ...extensions.get().flatMap((extension) => extension.welcomeBanners), + ]); + }, + + lifecycle: lifecycleEnum.singleton, +}); + +export default welcomeBannerItemsInjectable; diff --git a/src/extensions/registries/welcome-banner-registry.ts b/src/renderer/components/+welcome/welcome-banner-items/welcome-banner-registration.d.ts similarity index 91% rename from src/extensions/registries/welcome-banner-registry.ts rename to src/renderer/components/+welcome/welcome-banner-items/welcome-banner-registration.d.ts index 1102dc8a3f..ec7cf3cbdb 100644 --- a/src/extensions/registries/welcome-banner-registry.ts +++ b/src/renderer/components/+welcome/welcome-banner-items/welcome-banner-registration.d.ts @@ -19,8 +19,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { BaseRegistry } from "./base-registry"; - /** * WelcomeBannerRegistration is for an extension to register * Provide a Banner component to be renderered in the welcome screen. @@ -35,5 +33,3 @@ export interface WelcomeBannerRegistration { */ width?: number } - -export class WelcomeBannerRegistry extends BaseRegistry { } diff --git a/src/renderer/components/+welcome/welcome.tsx b/src/renderer/components/+welcome/welcome.tsx index f90ede88b8..56aefbfc3c 100644 --- a/src/renderer/components/+welcome/welcome.tsx +++ b/src/renderer/components/+welcome/welcome.tsx @@ -26,22 +26,24 @@ import type { IComputedValue } from "mobx"; import Carousel from "react-material-ui-carousel"; import { Icon } from "../icon"; import { productName, slackUrl } from "../../../common/vars"; -import { WelcomeBannerRegistry } from "../../../extensions/registries"; import { withInjectables } from "@ogre-tools/injectable-react"; import welcomeMenuItemsInjectable from "./welcome-menu-items/welcome-menu-items.injectable"; import type { WelcomeMenuRegistration } from "./welcome-menu-items/welcome-menu-registration"; +import welcomeBannerItemsInjectable from "./welcome-banner-items/welcome-banner-items.injectable"; +import type { WelcomeBannerRegistration } from "./welcome-banner-items/welcome-banner-registration"; export const defaultWidth = 320; interface Dependencies { welcomeMenuItems: IComputedValue + welcomeBannerItems: IComputedValue } -const NonInjectedWelcome: React.FC = ({ welcomeMenuItems }) => { - const welcomeBanner = WelcomeBannerRegistry.getInstance().getItems(); +const NonInjectedWelcome: React.FC = ({ welcomeMenuItems, welcomeBannerItems }) => { + const welcomeBanners = welcomeBannerItems.get(); // if there is banner with specified width, use it to calculate the width of the container - const maxWidth = welcomeBanner.reduce((acc, curr) => { + const maxWidth = welcomeBanners.reduce((acc, curr) => { const currWidth = curr.width ?? 0; if (acc > currWidth) { @@ -57,10 +59,10 @@ const NonInjectedWelcome: React.FC = ({ welcomeMenuItems }) => { style={{ width: `${maxWidth}px` }} data-testid="welcome-banner-container" > - {welcomeBanner.length > 0 ? ( + {welcomeBanners.length > 0 ? ( 1} + indicators={welcomeBanners.length > 1} autoPlay={true} navButtonsAlwaysInvisible={true} indicatorIconButtonProps={{ @@ -75,7 +77,7 @@ const NonInjectedWelcome: React.FC = ({ welcomeMenuItems }) => { }} interval={8000} > - {welcomeBanner.map((item, index) => ( + {welcomeBanners.map((item, index) => ( ))} @@ -142,6 +144,7 @@ export const Welcome = withInjectables( { getProps: (di) => ({ welcomeMenuItems: di.inject(welcomeMenuItemsInjectable), + welcomeBannerItems: di.inject(welcomeBannerItemsInjectable), }), }, ); diff --git a/src/renderer/initializers/registries.ts b/src/renderer/initializers/registries.ts index 4fddb35b3d..3130fe6984 100644 --- a/src/renderer/initializers/registries.ts +++ b/src/renderer/initializers/registries.ts @@ -33,7 +33,6 @@ export function initRegistries() { registries.KubeObjectMenuRegistry.createInstance(); registries.KubeObjectStatusRegistry.createInstance(); registries.StatusBarRegistry.createInstance(); - registries.WelcomeBannerRegistry.createInstance(); registries.WorkloadsOverviewDetailRegistry.createInstance(); registries.TopBarRegistry.createInstance(); }