diff --git a/package.json b/package.json index d96c2c9cdf..46c6cb6f7f 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,8 @@ "bundledKubectlVersion": "1.23.3", "bundledHelmVersion": "3.7.2", "sentryDsn": "", - "contentSecurityPolicy": "script-src 'unsafe-eval' 'self'; frame-src http://*.localhost:*/; img-src * data:" + "contentSecurityPolicy": "script-src 'unsafe-eval' 'self'; frame-src http://*.localhost:*/; img-src * data:", + "welcomeRoute": "/welcome" }, "engines": { "node": ">=16 <17" diff --git a/src/common/front-end-routing/routes/welcome/default-welcome-route.injectable.ts b/src/common/front-end-routing/routes/welcome/default-welcome-route.injectable.ts new file mode 100644 index 0000000000..d8db889033 --- /dev/null +++ b/src/common/front-end-routing/routes/welcome/default-welcome-route.injectable.ts @@ -0,0 +1,26 @@ +/** + * 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 welcomeRouteConfigInjectable from "./welcome-route-config.injectable"; +import { frontEndRouteInjectionToken } from "../../front-end-route-injection-token"; + +const defaultWelcomeRouteInjectable = getInjectable({ + id: "default-welcome-route", + + instantiate: (di) => { + const welcomeRoute = di.inject(welcomeRouteConfigInjectable); + + return { + path: "/welcome", + clusterFrame: false, + isEnabled: computed(() => welcomeRoute === "/welcome"), + }; + }, + + injectionToken: frontEndRouteInjectionToken, +}); + +export default defaultWelcomeRouteInjectable; diff --git a/src/common/front-end-routing/routes/welcome/welcome-route-config.global-override-for-injectable.ts b/src/common/front-end-routing/routes/welcome/welcome-route-config.global-override-for-injectable.ts new file mode 100644 index 0000000000..703c0798a2 --- /dev/null +++ b/src/common/front-end-routing/routes/welcome/welcome-route-config.global-override-for-injectable.ts @@ -0,0 +1,8 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getGlobalOverride } from "../../../test-utils/get-global-override"; +import welcomeRouteConfig from "./welcome-route-config.injectable"; + +export default getGlobalOverride(welcomeRouteConfig, () => "/welcome"); diff --git a/src/common/front-end-routing/routes/welcome/welcome-route-config.injectable.ts b/src/common/front-end-routing/routes/welcome/welcome-route-config.injectable.ts new file mode 100644 index 0000000000..53a61ab99f --- /dev/null +++ b/src/common/front-end-routing/routes/welcome/welcome-route-config.injectable.ts @@ -0,0 +1,18 @@ +/** + * 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 packageJsonInjectable from "../../../vars/package-json.injectable"; + +const welcomeRouteConfigInjectable = getInjectable({ + id: "welcome-route-config", + + instantiate: (di) => { + const packageJson = di.inject(packageJsonInjectable); + + return packageJson.config.welcomeRoute; + }, +}); + +export default welcomeRouteConfigInjectable; diff --git a/src/common/front-end-routing/routes/welcome/welcome-route.injectable.ts b/src/common/front-end-routing/routes/welcome/welcome-route.injectable.ts index 75d722ab46..839a7446c1 100644 --- a/src/common/front-end-routing/routes/welcome/welcome-route.injectable.ts +++ b/src/common/front-end-routing/routes/welcome/welcome-route.injectable.ts @@ -4,16 +4,21 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; +import welcomeRouteConfigInjectable from "./welcome-route-config.injectable"; import { frontEndRouteInjectionToken } from "../../front-end-route-injection-token"; const welcomeRouteInjectable = getInjectable({ id: "welcome-route", - instantiate: () => ({ - path: "/welcome", - clusterFrame: false, - isEnabled: computed(() => true), - }), + instantiate: (di) => { + const welcomeRoute = di.inject(welcomeRouteConfigInjectable); + + return { + path: welcomeRoute, + clusterFrame: false, + isEnabled: computed(() => true), + }; + }, injectionToken: frontEndRouteInjectionToken, }); diff --git a/src/common/front-end-routing/verify-that-all-routes-have-route-component.test.ts b/src/common/front-end-routing/verify-that-all-routes-have-route-component.test.ts index 4b6776bdaa..acd1d4401d 100644 --- a/src/common/front-end-routing/verify-that-all-routes-have-route-component.test.ts +++ b/src/common/front-end-routing/verify-that-all-routes-have-route-component.test.ts @@ -5,7 +5,7 @@ import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting"; import { routeSpecificComponentInjectionToken } from "../../renderer/routes/route-specific-component-injection-token"; import { frontEndRouteInjectionToken } from "./front-end-route-injection-token"; -import { filter, map, matches } from "lodash/fp"; +import { filter, map } from "lodash/fp"; import clusterStoreInjectable from "../cluster-store/cluster-store.injectable"; import type { ClusterStore } from "../cluster-store/cluster-store"; import { pipeline } from "@ogre-tools/fp"; @@ -27,9 +27,11 @@ describe("verify-that-all-routes-have-component", () => { routes, map( - (route) => ({ - path: route.path, - routeComponent: routeComponents.find(matches({ route })), + (currentRoute) => ({ + path: currentRoute.path, + routeComponent: routeComponents.find(({ route }) => ( + route.path === currentRoute.path + && route.clusterFrame === currentRoute.clusterFrame)), }), ), diff --git a/src/features/welcome/setting-welcome-page.test.tsx b/src/features/welcome/setting-welcome-page.test.tsx new file mode 100644 index 0000000000..fc5a0fe4a9 --- /dev/null +++ b/src/features/welcome/setting-welcome-page.test.tsx @@ -0,0 +1,99 @@ +/** + * 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 React from "react"; +import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import type { FakeExtensionOptions } from "../../renderer/components/test-utils/get-extension-fake"; +import welcomeRouteConfigInjectable from "../../common/front-end-routing/routes/welcome/welcome-route-config.injectable"; +import welcomeRouteInjectable from "../../common/front-end-routing/routes/welcome/welcome-route.injectable"; +import type { Route } from "../../common/front-end-routing/front-end-route-injection-token"; + + +describe("setting-welcome-page", () => { + let applicationBuilder: ApplicationBuilder; + let rendered : RenderResult; + let welcomeRoute: Route; + + beforeEach(() => { + applicationBuilder = getApplicationBuilder(); + }); + + describe("given configuration of welcome page route is the default", () => { + beforeEach(async () => { + applicationBuilder.beforeApplicationStart((mainDi) => { + mainDi.override(welcomeRouteConfigInjectable, () => "/welcome"); + }); + + applicationBuilder.beforeWindowStart((windowDi) => { + windowDi.override(welcomeRouteConfigInjectable, () => "/welcome"); + }); + + // enable the extension even though the welcomeRoute is not overriden + applicationBuilder.extensions.enable(extensionWithWelcomePage); + rendered = await applicationBuilder.render(); + + const windowDi = applicationBuilder.applicationWindow.only.di; + + welcomeRoute = windowDi.inject(welcomeRouteInjectable); + }); + + it("sets the default welcome page", () => { + expect(welcomeRoute.path).toEqual("/welcome"); + }); + + it("launches to the default welcome page", () => { + const welcomePage = rendered.getByTestId("welcome-page"); // from the Welcome component (welcome.tsx) + + expect(welcomePage).toBeInTheDocument(); + }); + }); + + describe("given configuration of welcome page route is set to a custom page", () => { + beforeEach(async () => { + applicationBuilder.beforeApplicationStart((mainDi) => { + mainDi.override(welcomeRouteConfigInjectable, () => "/extension/some-extension-name/some-welcome-page"); + }); + + applicationBuilder.beforeWindowStart((windowDi) => { + windowDi.override(welcomeRouteConfigInjectable, () => "/extension/some-extension-name/some-welcome-page"); + }); + + applicationBuilder.extensions.enable(extensionWithWelcomePage); + rendered = await applicationBuilder.render(); + + const windowDi = applicationBuilder.applicationWindow.only.di; + + welcomeRoute = windowDi.inject(welcomeRouteInjectable); + }); + + it("sets the custom welcome page", () => { + expect(welcomeRoute.path).toEqual("/extension/some-extension-name/some-welcome-page"); + }); + + it("launches to the custom welcome page ", () => { + const welcomePage = rendered.getByTestId("some-welcome-test-id"); + + expect(welcomePage).toBeInTheDocument(); + }); + }); +}); + +const extensionWithWelcomePage: FakeExtensionOptions = { + id: "some-extension-id", + name: "some-extension-name", + + rendererOptions: { + globalPages: [ + { + id: "some-welcome-page", + components: { + Page: () =>
Welcome page
, + }, + }, + ], + }, +}; diff --git a/src/renderer/components/+welcome/welcome-route-component.injectable.ts b/src/renderer/components/+welcome/default-welcome-route-component.injectable.ts similarity index 58% rename from src/renderer/components/+welcome/welcome-route-component.injectable.ts rename to src/renderer/components/+welcome/default-welcome-route-component.injectable.ts index 65fa0dfb00..5472c49787 100644 --- a/src/renderer/components/+welcome/welcome-route-component.injectable.ts +++ b/src/renderer/components/+welcome/default-welcome-route-component.injectable.ts @@ -4,18 +4,18 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { Welcome } from "./welcome"; -import welcomeRouteInjectable from "../../../common/front-end-routing/routes/welcome/welcome-route.injectable"; +import defaultWelcomeRouteInjectable from "../../../common/front-end-routing/routes/welcome/default-welcome-route.injectable"; import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; -const welcomeRouteComponentInjectable = getInjectable({ - id: "welcome-route-component", +const defaultWelcomeRouteComponentInjectable = getInjectable({ + id: "default-welcome-route-component", instantiate: (di) => ({ - route: di.inject(welcomeRouteInjectable), + route: di.inject(defaultWelcomeRouteInjectable), Component: Welcome, }), injectionToken: routeSpecificComponentInjectionToken, }); -export default welcomeRouteComponentInjectable; +export default defaultWelcomeRouteComponentInjectable; diff --git a/src/renderer/components/cluster-manager/cluster-manager.scss b/src/renderer/components/cluster-manager/cluster-manager.scss index dec5a069a2..b5bfbbebae 100644 --- a/src/renderer/components/cluster-manager/cluster-manager.scss +++ b/src/renderer/components/cluster-manager/cluster-manager.scss @@ -25,6 +25,10 @@ grid-area: menu; } + .error { + z-index: 1; + } + #lens-views { position: absolute; left: 0; diff --git a/src/renderer/components/cluster-manager/cluster-manager.tsx b/src/renderer/components/cluster-manager/cluster-manager.tsx index 85fb8a80c0..ab418ec827 100644 --- a/src/renderer/components/cluster-manager/cluster-manager.tsx +++ b/src/renderer/components/cluster-manager/cluster-manager.tsx @@ -21,12 +21,14 @@ import { buildURL } from "../../../common/utils/buildUrl"; import type { StorageLayer } from "../../utils"; import type { WatchForGeneralEntityNavigation } from "../../api/helpers/watch-for-general-entity-navigation.injectable"; import watchForGeneralEntityNavigationInjectable from "../../api/helpers/watch-for-general-entity-navigation.injectable"; +import currentPathInjectable from "../../routes/current-path.injectable"; interface Dependencies { catalogPreviousActiveTabStorage: StorageLayer; currentRouteComponent: IComputedValue; welcomeUrl: string; watchForGeneralEntityNavigation: WatchForGeneralEntityNavigation; + currentPath: IComputedValue; } @observer @@ -37,19 +39,40 @@ class NonInjectedClusterManager extends React.Component { ]); } - render() { + renderMainComponent() { const Component = this.props.currentRouteComponent.get(); - if (!Component) { + if (Component) { + return ; + } + + const currentPath = this.props.currentPath.get(); + + if (currentPath !== this.props.welcomeUrl) { return ; } + return ( +
+

ERROR!!

+

+ No matching route for the current path: + {" "} + {currentPath} + {" "} + which is the welcomeUrl. This is a bug. +

+
+ ); + } + + render() { return (
- + {this.renderMainComponent()}
@@ -65,5 +88,6 @@ export const ClusterManager = withInjectables(NonInjectedClusterMa currentRouteComponent: di.inject(currentRouteComponentInjectable), welcomeUrl: buildURL(di.inject(welcomeRouteInjectable).path), watchForGeneralEntityNavigation: di.inject(watchForGeneralEntityNavigationInjectable), + currentPath: di.inject(currentPathInjectable), }), }); diff --git a/src/renderer/routes/current-route-component.injectable.ts b/src/renderer/routes/current-route-component.injectable.ts index 56b047c37b..a4a8133c4f 100644 --- a/src/renderer/routes/current-route-component.injectable.ts +++ b/src/renderer/routes/current-route-component.injectable.ts @@ -4,7 +4,6 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; -import { matches } from "lodash/fp"; import { computed } from "mobx"; import currentRouteInjectable from "./current-route.injectable"; import { routeSpecificComponentInjectionToken } from "./route-specific-component-injection-token"; @@ -24,11 +23,13 @@ const currentRouteComponentInjectable = getInjectable({ return undefined; } - const routeSpecificComponent = routeComponents + return routeComponents .get() - .find(matches({ route: currentRoute })); - - return routeSpecificComponent?.Component; + .find(({ route }) => ( + route.path === currentRoute.path + && route.clusterFrame === currentRoute.clusterFrame + )) + ?.Component; }); }, });