From d3ce43e73d21ae808f93b19e86573d3862478a88 Mon Sep 17 00:00:00 2001 From: chh <1474479+chenhunghan@users.noreply.github.com> Date: Mon, 23 Aug 2021 14:31:24 +0300 Subject: [PATCH] Add 'welcome banner' extension API (#3656) * Add react-material-ui-carousel Signed-off-by: Hung-Han (Henry) Chen * Add welcomeBanners extension api Signed-off-by: Hung-Han (Henry) Chen --- package.json | 1 + src/extensions/extension-loader.ts | 1 + src/extensions/lens-renderer-extension.ts | 1 + src/extensions/registries/index.ts | 1 + .../registries/welcome-banner-registry.ts | 35 +++++++++++++++++++ src/main/window-manager.ts | 30 ++++++++++++++++ .../+welcome/__test__/welcome.test.tsx | 25 ++++++++++--- src/renderer/components/+welcome/welcome.tsx | 18 ++++++++-- src/renderer/initializers/registries.ts | 1 + yarn.lock | 34 ++++++++++++++++++ 10 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 src/extensions/registries/welcome-banner-registry.ts diff --git a/package.json b/package.json index 1575672ae3..7089d08a72 100644 --- a/package.json +++ b/package.json @@ -230,6 +230,7 @@ "proper-lockfile": "^4.1.2", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-material-ui-carousel": "^2.3.1", "react-monaco-editor": "^0.44.0", "react-router": "^5.2.0", "react-virtualized-auto-sizer": "^1.0.6", diff --git a/src/extensions/extension-loader.ts b/src/extensions/extension-loader.ts index 321ab80259..0f860a964d 100644 --- a/src/extensions/extension-loader.ts +++ b/src/extensions/extension-loader.ts @@ -278,6 +278,7 @@ export class ExtensionLoader extends Singleton { 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 dd5670fd67..0ae8a44323 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -38,6 +38,7 @@ export class LensRendererExtension extends LensExtension { kubeWorkloadsOverviewItems: registries.WorkloadsOverviewDetailRegistration[] = []; commands: registries.CommandRegistration[] = []; welcomeMenus: registries.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 febc0ba7ca..99809265a2 100644 --- a/src/extensions/registries/index.ts +++ b/src/extensions/registries/index.ts @@ -32,6 +32,7 @@ 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"; export * from "./topbar-registry"; diff --git a/src/extensions/registries/welcome-banner-registry.ts b/src/extensions/registries/welcome-banner-registry.ts new file mode 100644 index 0000000000..ef36da8444 --- /dev/null +++ b/src/extensions/registries/welcome-banner-registry.ts @@ -0,0 +1,35 @@ +/** + * 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 { BaseRegistry } from "./base-registry"; + +/** + * WelcomeBannerRegistration is for an extension to register + * Provide a Banner component to be renderered in the welcome screen. + */ +export interface WelcomeBannerRegistration { + /** + * The banner component to be shown on the welcome screen. + */ + Banner?: React.ComponentType +} + +export class WelcomeBannerRegistry extends BaseRegistry { } diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index 88d7dff0af..f1715a4284 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -89,6 +89,7 @@ export class WindowManager extends Singleton { nodeIntegration: true, nodeIntegrationInSubFrames: true, enableRemoteModule: true, + webviewTag: true }, }); this.windowState.manage(this.mainWindow); @@ -121,6 +122,35 @@ export class WindowManager extends Singleton { }) .on("did-finish-load", () => { logger.info("[WINDOW-MANAGER]: Main window loaded"); + }) + .on("will-attach-webview", (event, webPreferences, params) => { + logger.info("[WINDOW-MANAGER]: Attaching webview"); + // Following is security recommendations because we allow webview tag (webviewTag: true) + // suggested by https://www.electronjs.org/docs/tutorial/security#11-verify-webview-options-before-creation + // and https://www.electronjs.org/docs/tutorial/security#10-do-not-use-allowpopups + + if (webPreferences.preload) { + logger.warn("[WINDOW-MANAGER]: Strip away preload scripts of webview"); + delete webPreferences.preload; + } + + // @ts-expect-error some electron version uses webPreferences.preloadURL/webPreferences.preload + if (webPreferences.preloadURL) { + logger.warn("[WINDOW-MANAGER]: Strip away preload scripts of webview"); + delete webPreferences.preload; + } + + if (params.allowpopups) { + logger.warn("[WINDOW-MANAGER]: We do not allow allowpopups props, stop webview from renderer"); + + // event.preventDefault() will destroy the guest page. + event.preventDefault(); + + return; + } + + // Always disable Node.js integration for all webviews + webPreferences.nodeIntegration = false; }); } diff --git a/src/renderer/components/+welcome/__test__/welcome.test.tsx b/src/renderer/components/+welcome/__test__/welcome.test.tsx index 97493e07ff..52f579643f 100644 --- a/src/renderer/components/+welcome/__test__/welcome.test.tsx +++ b/src/renderer/components/+welcome/__test__/welcome.test.tsx @@ -20,20 +20,22 @@ */ import React from "react"; -import { render } from "@testing-library/react"; +import { render, screen } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; import { Welcome } from "../welcome"; -import { TopBarRegistry, WelcomeMenuRegistry } from "../../../../extensions/registries"; +import { TopBarRegistry, WelcomeMenuRegistry, WelcomeBannerRegistry } from "../../../../extensions/registries"; describe("", () => { beforeEach(() => { TopBarRegistry.createInstance(); WelcomeMenuRegistry.createInstance(); + WelcomeBannerRegistry.createInstance(); }); afterEach(() => { TopBarRegistry.resetInstance(); WelcomeMenuRegistry.resetInstance(); + WelcomeBannerRegistry.resetInstance(); }); it("renders items in the top bar", async () => { @@ -48,8 +50,23 @@ describe("", () => { } ]); - const { getByTestId } = render(); + render(); - expect(await getByTestId(testId)).toHaveTextContent(text); + expect(screen.getByTestId(testId)).toHaveTextContent(text); + }); + + it("renders registered in WelcomeBannerRegistry and hide logo", async () => { + const testId = "testId"; + + WelcomeBannerRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => [ + { + Banner: () =>
+ } + ]); + + const { container } = render(); + + expect(screen.queryByTestId(testId)).toBeInTheDocument(); + expect(container.getElementsByClassName("logo").length).toBe(0); }); }); diff --git a/src/renderer/components/+welcome/welcome.tsx b/src/renderer/components/+welcome/welcome.tsx index feb676d8fd..ffb290fb09 100644 --- a/src/renderer/components/+welcome/welcome.tsx +++ b/src/renderer/components/+welcome/welcome.tsx @@ -22,20 +22,34 @@ import "./welcome.scss"; import React from "react"; import { observer } from "mobx-react"; +import Carousel from "react-material-ui-carousel"; import { Icon } from "../icon"; import { productName, slackUrl } from "../../../common/vars"; import { WelcomeMenuRegistry } from "../../../extensions/registries"; import { WelcomeTopbar } from "../cluster-manager/welcome-topbar"; +import { WelcomeBannerRegistry } from "../../../extensions/registries"; @observer export class Welcome extends React.Component { render() { + const welcomeBanner = WelcomeBannerRegistry.getInstance().getItems(); + return ( <> -
+
- + {welcomeBanner.length > 0 ? ( + 1} + autoPlay={true} + > + {welcomeBanner.map((item, index) => + + )} + + ) : }

Welcome to {productName} 5!

diff --git a/src/renderer/initializers/registries.ts b/src/renderer/initializers/registries.ts index 1d455a0bcc..3063213bc8 100644 --- a/src/renderer/initializers/registries.ts +++ b/src/renderer/initializers/registries.ts @@ -34,6 +34,7 @@ export function initRegistries() { registries.KubeObjectStatusRegistry.createInstance(); registries.StatusBarRegistry.createInstance(); registries.WelcomeMenuRegistry.createInstance(); + registries.WelcomeBannerRegistry.createInstance(); registries.WorkloadsOverviewDetailRegistry.createInstance(); registries.TopBarRegistry.createInstance(); } diff --git a/yarn.lock b/yarn.lock index 9739cc1c97..606325daee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1875,6 +1875,15 @@ "@types/prop-types" "*" csstype "^3.0.2" +"@types/react@^16.8.12": + version "16.14.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.14.tgz#853de95a32a6a0e719192e222eacad024add2b8e" + integrity sha512-uwIWDYW8LznHzEMJl7ag9St1RsK0gw/xaFZ5+uI1ZM1HndwUgmPH3/wQkSb87GkOVg7shUxnpNW8DcN0AzvG5Q== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/readable-stream@^2.3.9": version "2.3.9" resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.9.tgz#40a8349e6ace3afd2dd1b6d8e9b02945de4566a9" @@ -1917,6 +1926,11 @@ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + "@types/semver@^7.2.0", "@types/semver@^7.3.5": version "7.3.8" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.8.tgz#508a27995498d7586dcecd77c25e289bfaf90c59" @@ -2940,6 +2954,13 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +auto-bind@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-2.1.1.tgz#8ae509671ecdfbd5009fc99b0f19ae9c3a2abf50" + integrity sha512-NUwV1i9D3vxxY1KnfZgSZ716d6ovY7o8LfOwLhGIPFBowIb6Ln6DBW64+jCqPzUznel2hRSkQnYQqvh7/ldw8A== + dependencies: + "@types/react" "^16.8.12" + auto-bind@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" @@ -11957,6 +11978,14 @@ react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== +react-material-ui-carousel@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/react-material-ui-carousel/-/react-material-ui-carousel-2.3.1.tgz#73b516f831d45df70bb9219f56fee14a2788e332" + integrity sha512-QV3z+x10x19rSAPSHMmkHtdx/PSiwqo6FeAaY8Y04LtuKM0ZxHsgeMslNdcI8M1G2B48391YqN+ouWFPYqAbTA== + dependencies: + auto-bind "^2.1.1" + react-swipeable "^6.1.0" + react-monaco-editor@^0.44.0: version "0.44.0" resolved "https://registry.yarnpkg.com/react-monaco-editor/-/react-monaco-editor-0.44.0.tgz#9f966fd00b6c30e8be8873a3fbc86f14a0da2ba4" @@ -12032,6 +12061,11 @@ react-select@3.2.0: react-input-autosize "^3.0.0" react-transition-group "^4.3.0" +react-swipeable@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-6.2.0.tgz#057271cb7a6fb4af9d2a3f6d80ccdf33e2f64d47" + integrity sha512-nWQ8dEM8e/uswZLSIkXUsAnQmnX4MTcryOHBQIQYRMJFDpgDBSiVbKsz/BZVCIScF4NtJh16oyxwaNOepR6xSw== + react-table@^7.7.0: version "7.7.0" resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.7.0.tgz#e2ce14d7fe3a559f7444e9ecfe8231ea8373f912"