diff --git a/package.json b/package.json
index ca4cce21df..75074300bb 100644
--- a/package.json
+++ b/package.json
@@ -208,11 +208,11 @@
"@hapi/subtext": "^7.0.4",
"@kubernetes/client-node": "^0.16.3",
"@material-ui/styles": "^4.11.5",
- "@ogre-tools/fp": "8.0.0",
- "@ogre-tools/injectable": "8.0.0",
- "@ogre-tools/injectable-extension-for-auto-registration": "8.0.0",
- "@ogre-tools/injectable-extension-for-mobx": "8.0.0",
- "@ogre-tools/injectable-react": "8.0.0",
+ "@ogre-tools/fp": "9.0.0",
+ "@ogre-tools/injectable": "9.0.0",
+ "@ogre-tools/injectable-extension-for-auto-registration": "9.0.0",
+ "@ogre-tools/injectable-extension-for-mobx": "9.0.0",
+ "@ogre-tools/injectable-react": "9.0.0",
"@sentry/electron": "^3.0.7",
"@sentry/integrations": "^6.19.3",
"@side/jest-runtime": "^1.0.1",
diff --git a/src/behaviours/preferences/__snapshots__/navigation-to-extension-specific-preferences.test.tsx.snap b/src/behaviours/preferences/__snapshots__/navigation-to-extension-specific-preferences.test.tsx.snap
index 2adb0f198f..79787b5d8d 100644
--- a/src/behaviours/preferences/__snapshots__/navigation-to-extension-specific-preferences.test.tsx.snap
+++ b/src/behaviours/preferences/__snapshots__/navigation-to-extension-specific-preferences.test.tsx.snap
@@ -1,5 +1,835 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`preferences - navigation to extension specific preferences given in preferences, when rendered given extension with registered tab shows extension tab in general area 1`] = `
+
+`;
+
+exports[`preferences - navigation to extension specific preferences given in preferences, when rendered given extension with registered tab when navigating to specific extension tab renders 1`] = `
+
+
+
+
+
+
+
+
+ registered-tab-page-id
+
+ preferences
+
+
+
+ License item
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`preferences - navigation to extension specific preferences given in preferences, when rendered given extensions with tabs having same id when navigating to first extension tab renders 1`] = `
+
+
+
+
+
+
+
+
+ registered-tab-page-id
+
+ preferences
+
+
+
+ License item
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`preferences - navigation to extension specific preferences given in preferences, when rendered given extensions with tabs having same id when navigating to second extension tab renders 1`] = `
+
+
+
+
+
+
+
+
+ duplicated-tab-page-id
+
+ preferences
+
+
+
+ Another metrics
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`preferences - navigation to extension specific preferences given in preferences, when rendered given multiple extensions with specific preferences, when navigating to extension specific preferences page renders 1`] = `
+
+
+
+
+
+
+
+
+ some-test-extension-id
+
+ preferences
+
+
+
+ Some preference item
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
exports[`preferences - navigation to extension specific preferences given in preferences, when rendered renders 1`] = `
+
+
+
+ extension
+
+
+
Extensions
+
+
+
+ some-test-extension-id
+
+
+
@@ -1181,16 +2034,39 @@ exports[`preferences - navigation to extension specific preferences given in pre
+
+
+
+ extension
+
+
+
Extensions
+
+
+
+ some-test-extension-id
+
+
+
@@ -1206,7 +2082,9 @@ exports[`preferences - navigation to extension specific preferences given in pre
id="extensions"
>
- Extensions
+ some-test-extension-id
+
+ preferences
`;
+
+exports[`preferences - navigation to extension specific preferences given in preferences, when rendered when extension with specific preferences is enabled when navigating to extension preferences using navigation when extension is disabled renders 1`] = `
+
+
+
+
+
+
+
+
+
+
+ preferences
+
+
+ No extension found
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`preferences - navigation to extension specific preferences when navigating to extension specific tab renders 1`] = `
+
+
+
+
+
+
+
+
+ duplicated-tab-page-id
+
+ preferences
+
+
+
+ Another metrics
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`preferences - navigation to extension specific preferences when navigating to someone else extension specific tab renders 1`] = `
+
+
+
+
+
+
+
+
+ extension-using-someone-else-tab-id
+
+ preferences
+
+
+
+ My preferences
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/src/behaviours/preferences/closing-preferences.test.tsx b/src/behaviours/preferences/closing-preferences.test.tsx
index 0ca2c91da7..dddd7e18dc 100644
--- a/src/behaviours/preferences/closing-preferences.test.tsx
+++ b/src/behaviours/preferences/closing-preferences.test.tsx
@@ -250,6 +250,7 @@ const testNavigationItemInjectable = getInjectable({
return {
id: "some-test-preference-navigation-item-id",
label: "Some preference navigation item",
+ parent: "general",
isActive: routeIsActive,
isVisible: testRoute.isEnabled,
navigate: navigateToPreferenceTab(testRoute),
diff --git a/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx b/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx
index 5eb9b2f384..bded3769f2 100644
--- a/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx
+++ b/src/behaviours/preferences/navigation-to-extension-specific-preferences.test.tsx
@@ -6,8 +6,13 @@ 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 React from "react";
-import type { FakeExtensionData } from "../../renderer/components/test-utils/get-renderer-extension-fake";
+import "@testing-library/jest-dom/extend-expect";
+import type { FakeExtensionData, TestExtension } from "../../renderer/components/test-utils/get-renderer-extension-fake";
import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake";
+import type { DiContainer } from "@ogre-tools/injectable";
+import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting";
+import extensionPreferencesRouteInjectable from "../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
+
describe("preferences - navigation to extension specific preferences", () => {
let applicationBuilder: ApplicationBuilder;
@@ -43,10 +48,65 @@ describe("preferences - navigation to extension specific preferences", () => {
expect(actual).toBeNull();
});
+ describe("given multiple extensions with specific preferences, when navigating to extension specific preferences page", () => {
+ beforeEach(async () => {
+ const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
+ const someTestExtension = getRendererExtensionFake(extensionStubWithExtensionSpecificPreferenceItems);
+ const someOtherTestExtension = getRendererExtensionFake(someOtherExtensionStubWithExtensionSpecificPreferenceItems);
+
+ await applicationBuilder.extensions.renderer.enable(someTestExtension, someOtherTestExtension);
+ applicationBuilder.preferences.navigation.click("extension-some-test-extension-id");
+ });
+
+ it("renders", () => {
+ expect(rendered.container).toMatchSnapshot();
+ });
+
+ it("doesn't show preferences from unrelated extension", () => {
+ const actual = rendered.queryByTestId("extension-preference-item-for-some-other-preference-item-id");
+
+ expect(actual).toBeNull();
+ });
+
+ it("shows preferences from related extension", () => {
+ const actual = rendered.getByTestId("extension-preference-item-for-some-preference-item-id");
+
+ expect(actual).not.toBeNull();
+ });
+ });
+
+ describe("given multiple extensions with and without specific preferences", () => {
+ beforeEach(async () => {
+ const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
+ const someTestExtension = getRendererExtensionFake(extensionStubWithExtensionSpecificPreferenceItems);
+ const extensionWithoutPreferences = getRendererExtensionFake(extensionStubWithoutPreferences);
+ const extensionWithSpecificTab = getRendererExtensionFake(extensionStubWithShowInPreferencesTab);
+
+ await applicationBuilder.extensions.renderer.enable(someTestExtension, extensionWithoutPreferences, extensionWithSpecificTab);
+ });
+
+ it("doesn't show link for extension without preferences", () => {
+ const actual = rendered.queryByTestId("tab-link-for-extension-without-preferences-id");
+
+ expect(actual).toBeNull();
+ });
+
+ it("doesn't show link for preferences intended for a specific tab", () => {
+ const actual = rendered.queryByTestId("tab-link-for-extension-specified-preferences-page-id");
+
+ expect(actual).toBeNull();
+ });
+ });
+
describe("when extension with specific preferences is enabled", () => {
+ let testExtension: TestExtension;
+
beforeEach(() => {
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
- const testExtension = getRendererExtensionFake(extensionStubWithExtensionSpecificPreferenceItems);
+
+ testExtension = getRendererExtensionFake(
+ extensionStubWithExtensionSpecificPreferenceItems,
+ );
applicationBuilder.extensions.renderer.enable(testExtension);
});
@@ -56,20 +116,32 @@ describe("preferences - navigation to extension specific preferences", () => {
});
it("shows link for extension preferences", () => {
- const actual = rendered.getByTestId("tab-link-for-extensions");
+ const actual = rendered.getByTestId("tab-link-for-extension-some-test-extension-id");
expect(actual).not.toBeNull();
});
+ it("link should not be active", () => {
+ const actual = rendered.getByTestId("tab-link-for-extension-some-test-extension-id");
+
+ expect(actual).not.toHaveClass("active");
+ });
+
describe("when navigating to extension preferences using navigation", () => {
beforeEach(() => {
- applicationBuilder.preferences.navigation.click("extensions");
+ applicationBuilder.preferences.navigation.click("extension-some-test-extension-id");
});
it("renders", () => {
expect(rendered.container).toMatchSnapshot();
});
+ it("shows proper page title", () => {
+ const title = rendered.getByText("some-test-extension-id preferences");
+
+ expect(title).toBeInTheDocument();
+ });
+
it("shows extension specific preferences", () => {
const page = rendered.getByTestId("extension-preferences-page");
@@ -87,14 +159,234 @@ describe("preferences - navigation to extension specific preferences", () => {
expect(actual).toBeNull();
});
+
+ it("link is active", () => {
+ const actual = rendered.getByTestId("tab-link-for-extension-some-test-extension-id");
+
+ expect(actual).toHaveClass("active");
+ });
+
+ describe("when extension is disabled", () => {
+ beforeEach(() => {
+ applicationBuilder.extensions.renderer.disable(testExtension);
+ });
+
+ it("renders", () => {
+ expect(rendered.baseElement).toMatchSnapshot();
+ });
+
+ it("shows the error message about extension not being present", () => {
+ expect(rendered.getByTestId("error-for-extension-not-being-present")).toBeInTheDocument();
+ });
+
+ it("when extension is enabled again, does not show the error message anymore", () => {
+ applicationBuilder.extensions.renderer.enable(testExtension);
+
+ expect(rendered.queryByTestId("error-for-extension-not-being-present")).not.toBeInTheDocument();
+ });
+ });
});
});
+
+ describe("given extension with registered tab", () => {
+ beforeEach(async () => {
+ const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
+ const extension = getRendererExtensionFake(extensionStubWithWithRegisteredTab);
+
+ await applicationBuilder.extensions.renderer.enable(extension);
+ });
+
+ it("shows extension tab in general area", () => {
+ const actual = rendered.getByTestId("tab-link-for-extension-registered-tab-page-id-nav-item-metrics-extension-tab");
+
+ expect(actual).toMatchSnapshot();
+ });
+
+ it("does not show custom settings block", () => {
+ const actual = rendered.queryByTestId("extension-settings");
+
+ expect(actual).not.toBeInTheDocument();
+ });
+
+ describe("when navigating to specific extension tab", () => {
+ beforeEach(() => {
+ applicationBuilder.preferences.navigation.click("extension-registered-tab-page-id-nav-item-metrics-extension-tab");
+ });
+ it("renders", () => {
+ expect(rendered.container).toMatchSnapshot();
+ });
+ it("shows related preferences for this tab", () => {
+ const actual = rendered.getByTestId("metrics-preference-item-hint");
+
+ expect(actual).toBeInTheDocument();
+ });
+ it("does not show unrelated preferences for this tab", () => {
+ const actual = rendered.queryByTestId("survey-preference-item-hint");
+
+ expect(actual).not.toBeInTheDocument();
+ });
+ });
+ });
+
+ describe("given extension with few registered tabs", () => {
+ const tabs = [
+ "tab-link-for-extension-hello-world-tab-page-id-nav-item-hello-extension-tab",
+ "tab-link-for-extension-hello-world-tab-page-id-nav-item-logs-extension-tab",
+ ];
+
+ beforeEach(async () => {
+ const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
+ const extension = getRendererExtensionFake(extensionStubWithWithRegisteredTabs);
+
+ await applicationBuilder.extensions.renderer.enable(extension);
+ });
+
+ it.each(tabs)("shows '%s' tab in general area", (tab) => {
+ const tabElement = rendered.getByTestId(tab);
+
+ expect(tabElement).toBeInTheDocument();
+ });
+ });
+
+ describe("given extensions with tabs having same id", () => {
+ beforeEach(async () => {
+ const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
+ const extension = getRendererExtensionFake(extensionStubWithWithRegisteredTab);
+ const otherExtension = getRendererExtensionFake(extensionStubWithWithSameRegisteredTab);
+
+ await applicationBuilder.extensions.renderer.enable(extension, otherExtension);
+ });
+
+ it("shows tab from the first extension", () => {
+ const actual = rendered.getByTestId("tab-link-for-extension-registered-tab-page-id-nav-item-metrics-extension-tab");
+
+ expect(actual).toBeInTheDocument();
+ });
+
+ it("shows tab from the second extension", () => {
+ const actual = rendered.getByTestId("tab-link-for-extension-duplicated-tab-page-id-nav-item-metrics-extension-tab");
+
+ expect(actual).toBeInTheDocument();
+ });
+
+ describe("when navigating to first extension tab", () => {
+ beforeEach(() => {
+ applicationBuilder.preferences.navigation.click("extension-registered-tab-page-id-nav-item-metrics-extension-tab");
+ });
+
+ it("renders", () => {
+ expect(rendered.container).toMatchSnapshot();
+ });
+
+ it("shows related preferences for this tab", () => {
+ const actual = rendered.getByTestId("metrics-preference-item-hint");
+
+ expect(actual).toBeInTheDocument();
+ });
+
+ it("does not show unrelated preferences for this tab", () => {
+ const actual = rendered.queryByTestId("another-metrics-preference-item-hint");
+
+ expect(actual).not.toBeInTheDocument();
+ });
+ });
+
+ describe("when navigating to second extension tab", () => {
+ beforeEach(() => {
+ applicationBuilder.preferences.navigation.click("extension-duplicated-tab-page-id-nav-item-metrics-extension-tab");
+ });
+
+ it("renders", () => {
+ expect(rendered.container).toMatchSnapshot();
+ });
+
+ it("shows related preferences for this tab", () => {
+ const actual = rendered.getByTestId("another-metrics-preference-item-hint");
+
+ expect(actual).toBeInTheDocument();
+ });
+
+ it("does not show unrelated preferences for this tab", () => {
+ const actual = rendered.queryByTestId("metrics-preference-item-hint");
+
+ expect(actual).not.toBeInTheDocument();
+ });
+ });
+ });
+ });
+
+ describe("when navigating to extension specific tab", () => {
+ let rendered: RenderResult;
+ let di: DiContainer;
+
+ beforeEach(async () => {
+ di = getDiForUnitTesting({ doGeneralOverrides: true });
+
+ const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
+ const extension = getRendererExtensionFake(extensionStubWithWithSameRegisteredTab);
+ const otherExtension = getRendererExtensionFake(extensionUsingSomeoneElseTab);
+
+ applicationBuilder.beforeRender(() => {
+ const extensionRoute = di.inject(extensionPreferencesRouteInjectable);
+ const params = { parameters: {
+ extensionId: "duplicated-tab-page-id",
+ tabId: "metrics-extension-tab",
+ }};
+
+ applicationBuilder.preferences.navigateTo(extensionRoute, params);
+ });
+
+ await applicationBuilder.extensions.renderer.enable(extension, otherExtension);
+ rendered = await applicationBuilder.render();
+ });
+
+ it("renders", () => {
+ expect(rendered.container).toMatchSnapshot();
+ });
+
+ it("does render related preferences for specific tab", () => {
+ expect(rendered.getByTestId("another-metrics-preference-item-hint")).toBeInTheDocument();
+ });
+
+ it("does not render related preferences for specific tab", () => {
+ expect(rendered.queryByTestId("my-preferences-item-hint")).not.toBeInTheDocument();
+ });
+ });
+
+ describe("when navigating to someone else extension specific tab", () => {
+ let rendered: RenderResult;
+ let di: DiContainer;
+
+ beforeEach(async () => {
+ di = getDiForUnitTesting({ doGeneralOverrides: true });
+
+ const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
+ const extension = getRendererExtensionFake(extensionStubWithWithSameRegisteredTab);
+ const extensionUsingOtherTab = getRendererExtensionFake(extensionUsingSomeoneElseTab);
+
+ applicationBuilder.beforeRender(() => {
+ const extensionRoute = di.inject(extensionPreferencesRouteInjectable);
+ const params = { parameters: {
+ extensionId: "extension-using-someone-else-tab-id",
+ tabId: "metrics-extension-tab",
+ }};
+
+ applicationBuilder.preferences.navigateTo(extensionRoute, params);
+ });
+
+ await applicationBuilder.extensions.renderer.enable(extension, extensionUsingOtherTab);
+ rendered = await applicationBuilder.render();
+ });
+
+ it("renders", () => {
+ expect(rendered.container).toMatchSnapshot();
+ });
});
});
const extensionStubWithExtensionSpecificPreferenceItems: FakeExtensionData = {
- id: "some-extension-id",
- name: "some-extension-name",
+ id: "some-test-extension-id",
+ name: "some-test-extension-id",
appPreferences: [
{
title: "Some preference item",
@@ -119,3 +411,166 @@ const extensionStubWithExtensionSpecificPreferenceItems: FakeExtensionData = {
],
};
+const someOtherExtensionStubWithExtensionSpecificPreferenceItems: FakeExtensionData = {
+ id: "some-other-test-extension-id",
+ name: "some-other-test-extension-id",
+
+ appPreferences: [
+ {
+ title: "Test preference item",
+ id: "some-other-preference-item-id",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ ],
+};
+
+const extensionStubWithoutPreferences: FakeExtensionData = {
+ id: "without-preferences-id",
+ name: "without-preferences-id",
+};
+
+const extensionStubWithShowInPreferencesTab: FakeExtensionData = {
+ id: "specified-preferences-page-id",
+ name: "specified-preferences-page-name",
+
+ appPreferences: [
+ {
+ title: "Test preference item",
+ id: "very-other-preference-item-id",
+ showInPreferencesTab: "some-tab",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ ],
+};
+
+const extensionStubWithWithRegisteredTab: FakeExtensionData = {
+ id: "registered-tab-page-id",
+ name: "registered-tab-page-id",
+
+ appPreferences: [
+ {
+ title: "License item",
+ id: "metrics-preference-item-id",
+ showInPreferencesTab: "metrics-extension-tab",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ {
+ title: "Menu item",
+ id: "menu-preference-item-id",
+ showInPreferencesTab: "menu-extension-tab",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ {
+ title: "Survey item",
+ id: "survey-preference-item-id",
+ showInPreferencesTab: "survey-extension-tab",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ ],
+
+ appPreferenceTabs: [{
+ title: "Metrics tab",
+ id: "metrics-extension-tab",
+ orderNumber: 100,
+ }],
+};
+
+const extensionStubWithWithRegisteredTabs: FakeExtensionData = {
+ id: "hello-world-tab-page-id",
+ name: "hello-world-tab-page-id",
+
+ appPreferences: [
+ {
+ title: "Hello world",
+ id: "hello-preference-item-id",
+ showInPreferencesTab: "hello-extension-tab",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ {
+ title: "Logs",
+ id: "logs-preference-item-id",
+ showInPreferencesTab: "logs-extension-tab",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ ],
+
+ appPreferenceTabs: [{
+ title: "Metrics tab",
+ id: "hello-extension-tab",
+ orderNumber: 100,
+ }, {
+ title: "Logs tab",
+ id: "logs-extension-tab",
+ orderNumber: 200,
+ }],
+};
+
+const extensionStubWithWithSameRegisteredTab: FakeExtensionData = {
+ id: "duplicated-tab-page-id",
+ name: "duplicated-tab-page-id",
+
+ appPreferences: [
+ {
+ title: "Another metrics",
+ id: "another-metrics-preference-item-id",
+ showInPreferencesTab: "metrics-extension-tab",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ ],
+
+ appPreferenceTabs: [{
+ title: "Metrics tab",
+ id: "metrics-extension-tab",
+ orderNumber: 100,
+ }],
+};
+
+const extensionUsingSomeoneElseTab: FakeExtensionData = {
+ id: "extension-using-someone-else-tab-id",
+ name: "extension-using-someone-else-tab-id",
+
+ appPreferences: [
+ {
+ title: "My preferences",
+ id: "my-preferences-item-id",
+ showInPreferencesTab: "metrics-extension-tab",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ ],
+};
diff --git a/src/common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable.ts b/src/common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable.ts
index 01a87fa3c2..38d25b4114 100644
--- a/src/common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable.ts
+++ b/src/common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable.ts
@@ -5,12 +5,18 @@
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
+import type { Route } from "../../../front-end-route-injection-token";
+
+interface ExtensionPreferenceRouteParams {
+ extensionId: string;
+ tabId?: string;
+}
const extensionPreferencesRouteInjectable = getInjectable({
id: "extension-preferences-route",
- instantiate: () => ({
- path: "/preferences/extensions",
+ instantiate: (): Route => ({
+ path: "/preferences/extension/:extensionId/:tabId?",
clusterFrame: false,
isEnabled: computed(() => true),
}),
diff --git a/src/common/front-end-routing/routes/preferences/extension/navigate-to-extension-preferences.injectable.ts b/src/common/front-end-routing/routes/preferences/extension/navigate-to-extension-preferences.injectable.ts
index 6e2f80d864..c51c45ed28 100644
--- a/src/common/front-end-routing/routes/preferences/extension/navigate-to-extension-preferences.injectable.ts
+++ b/src/common/front-end-routing/routes/preferences/extension/navigate-to-extension-preferences.injectable.ts
@@ -13,7 +13,10 @@ const navigateToExtensionPreferencesInjectable = getInjectable({
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
const route = di.inject(extensionPreferencesRouteInjectable);
- return () => navigateToRoute(route);
+ return (extensionId: string, tabId?: string) => navigateToRoute(route, { parameters: {
+ extensionId,
+ tabId,
+ }});
},
});
diff --git a/src/common/runnable/run-many-for.test.ts b/src/common/runnable/run-many-for.test.ts
index 193c1fd178..35db17952a 100644
--- a/src/common/runnable/run-many-for.test.ts
+++ b/src/common/runnable/run-many-for.test.ts
@@ -15,7 +15,7 @@ describe("runManyFor", () => {
let actualPromise: Promise;
beforeEach(() => {
- const rootDi = createContainer();
+ const rootDi = createContainer("irrelevant");
runMock = asyncFn();
@@ -67,7 +67,7 @@ describe("runManyFor", () => {
let actualPromise: Promise;
beforeEach(() => {
- const di = createContainer();
+ const di = createContainer("irrelevant");
runMock = asyncFn();
@@ -170,7 +170,7 @@ describe("runManyFor", () => {
});
it("given invalid hierarchy, when running runnables, throws", () => {
- const rootDi = createContainer();
+ const rootDi = createContainer("irrelevant");
const runMock = asyncFn<(...args: unknown[]) => void>();
@@ -218,7 +218,7 @@ describe("runManyFor", () => {
let runMock: AsyncFnMock<(...args: unknown[]) => Promise>;
beforeEach(() => {
- const rootDi = createContainer();
+ const rootDi = createContainer("irrelevant");
runMock = asyncFn();
diff --git a/src/common/runnable/run-many-sync-for.test.ts b/src/common/runnable/run-many-sync-for.test.ts
index 215b1a3b15..fe47516725 100644
--- a/src/common/runnable/run-many-sync-for.test.ts
+++ b/src/common/runnable/run-many-sync-for.test.ts
@@ -11,7 +11,7 @@ describe("runManySyncFor", () => {
let runMock: jest.Mock;
beforeEach(() => {
- const rootDi = createContainer();
+ const rootDi = createContainer("irrelevant");
runMock = jest.fn();
@@ -50,7 +50,7 @@ describe("runManySyncFor", () => {
let runMock: jest.Mock<(arg: string) => void>;
beforeEach(() => {
- const di = createContainer();
+ const di = createContainer("irrelevant");
runMock = jest.fn();
@@ -99,7 +99,7 @@ describe("runManySyncFor", () => {
});
it("given invalid hierarchy, when running runnables, throws", () => {
- const rootDi = createContainer();
+ const rootDi = createContainer("irrelevant");
const runMock = jest.fn();
@@ -147,7 +147,7 @@ describe("runManySyncFor", () => {
let runMock: jest.Mock<(arg: string, arg2: string) => void>;
beforeEach(() => {
- const rootDi = createContainer();
+ const rootDi = createContainer("irrelevant");
runMock = jest.fn();
diff --git a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.test.ts b/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.test.ts
index 3a69f0d563..ebfc77f40e 100644
--- a/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.test.ts
+++ b/src/extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api-with-modifications.test.ts
@@ -21,7 +21,7 @@ describe("asLegacyGlobalObjectForExtensionApiWithModifications", () => {
};
beforeEach(() => {
- di = createContainer();
+ di = createContainer("irrelevant");
jest.spyOn(di, "inject");
diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts
index 1ed9164cb0..25b1e15061 100644
--- a/src/extensions/lens-renderer-extension.ts
+++ b/src/extensions/lens-renderer-extension.ts
@@ -26,6 +26,7 @@ import { pipeline } from "@ogre-tools/fp";
import { getExtensionRoutePath } from "../renderer/routes/for-extension";
import type { LensRendererExtensionDependencies } from "./lens-extension-set-dependencies";
import type { KubeObjectHandlerRegistration } from "../renderer/kube-object/handler";
+import type { AppPreferenceTabRegistration } from "../renderer/components/+preferences/app-preference-tab/app-preference-tab-registration";
export class LensRendererExtension extends LensExtension {
globalPages: registries.PageRegistration[] = [];
@@ -33,6 +34,7 @@ export class LensRendererExtension extends LensExtension {
- const di = createContainer();
+ const di = createContainer("main");
registerMobX(di);
diff --git a/src/main/getDiForUnitTesting.ts b/src/main/getDiForUnitTesting.ts
index 45fedce511..f17a4e6217 100644
--- a/src/main/getDiForUnitTesting.ts
+++ b/src/main/getDiForUnitTesting.ts
@@ -105,7 +105,7 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
doGeneralOverrides = false,
} = opts;
- const di = createContainer();
+ const di = createContainer("main");
registerMobX(di);
diff --git a/src/renderer/components/+preferences/app-preference-tab/app-preference-tab-registration.ts b/src/renderer/components/+preferences/app-preference-tab/app-preference-tab-registration.ts
new file mode 100644
index 0000000000..2fed65d79e
--- /dev/null
+++ b/src/renderer/components/+preferences/app-preference-tab/app-preference-tab-registration.ts
@@ -0,0 +1,11 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+export interface AppPreferenceTabRegistration {
+ title: string;
+ id: string;
+ orderNumber?: number;
+}
+
diff --git a/src/renderer/components/+preferences/extension-preference-item-registrator.injectable.ts b/src/renderer/components/+preferences/extension-preference-item-registrator.injectable.ts
index 1129699bbd..8182cfd2af 100644
--- a/src/renderer/components/+preferences/extension-preference-item-registrator.injectable.ts
+++ b/src/renderer/components/+preferences/extension-preference-item-registrator.injectable.ts
@@ -7,7 +7,7 @@ import { filter, map } from "lodash/fp";
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
import { pipeline } from "@ogre-tools/fp";
-import { extensionPreferenceItemInjectionToken } from "./extension-preference-items.injectable";
+import { extensionPreferenceItemInjectionToken } from "./extension-preference-items-injection-token";
const extensionPreferenceItemRegistratorInjectable = getInjectable({
id: "extension-preference-item-registrator",
@@ -34,6 +34,7 @@ const extensionPreferenceItemRegistratorInjectable = getInjectable({
instantiate: () => ({
id: registration.id || id,
title: registration.title,
+ extension,
components: {
Hint: registration.components.Hint,
diff --git a/src/renderer/components/+preferences/extension-preference-items-injection-token.ts b/src/renderer/components/+preferences/extension-preference-items-injection-token.ts
new file mode 100644
index 0000000000..6c634f60da
--- /dev/null
+++ b/src/renderer/components/+preferences/extension-preference-items-injection-token.ts
@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import { getInjectionToken } from "@ogre-tools/injectable";
+import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
+import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
+
+ interface ExtensionPreferenceItem extends RegisteredAppPreference {
+ extension: LensRendererExtension;
+ }
+
+export const extensionPreferenceItemInjectionToken = getInjectionToken({
+ id: "extension-preference-item-injection-token",
+});
+
diff --git a/src/renderer/components/+preferences/extension-preference-model.injectable.ts b/src/renderer/components/+preferences/extension-preference-model.injectable.ts
new file mode 100644
index 0000000000..5e4fd06de0
--- /dev/null
+++ b/src/renderer/components/+preferences/extension-preference-model.injectable.ts
@@ -0,0 +1,32 @@
+/**
+ * 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 extensionPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
+import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
+import routePathParametersInjectable from "../../routes/route-path-parameters.injectable";
+import { getExtensionPreferenceItems } from "./get-extension-preference-items";
+
+const extensionPreferencesModelInjectable = getInjectable({
+ id: "extension-preferences-model",
+
+ instantiate: (di) => {
+ const route = di.inject(extensionPreferencesRouteInjectable);
+ const pathParameters = di.inject(routePathParametersInjectable, route);
+ const extensions = di.inject(rendererExtensionsInjectable);
+
+ return computed(() => {
+ const { extensionId, tabId } = pathParameters.get();
+ const targetExtension = extensions.get().find((extension) => extension.sanitizedExtensionId === extensionId);
+
+ return {
+ extensionName: targetExtension?.manifest.name,
+ preferenceItems: getExtensionPreferenceItems(targetExtension, tabId),
+ };
+ });
+ },
+});
+
+export default extensionPreferencesModelInjectable;
diff --git a/src/renderer/components/+preferences/extensions.tsx b/src/renderer/components/+preferences/extensions.tsx
index 930b74c8ee..94b56a2716 100644
--- a/src/renderer/components/+preferences/extensions.tsx
+++ b/src/renderer/components/+preferences/extensions.tsx
@@ -8,36 +8,55 @@ import type { IComputedValue } from "mobx";
import { observer } from "mobx-react";
import React from "react";
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
+import extensionPreferencesModelInjectable from "./extension-preference-model.injectable";
import { ExtensionSettings } from "./extension-settings";
import { Preferences } from "./preferences";
-import extensionsPreferenceItemsInjectable from "./extension-preference-items.injectable";
interface Dependencies {
- preferenceItems: IComputedValue;
+ model: IComputedValue<{
+ preferenceItems: RegisteredAppPreference[];
+ extensionName?: string;
+ }>;
}
-const NonInjectedExtensions = ({ preferenceItems }: Dependencies) => (
-
-
- Extensions
- {preferenceItems.get().map((preferenceItem) => (
-
- ))}
-
-
-);
+const NonInjectedExtensions = ({ model }: Dependencies) => {
+ const { extensionName, preferenceItems } = model.get();
+
+ return (
+
+
+
+ {extensionName}
+ {" "}
+ preferences
+
+ {!extensionName && (
+
+ No extension found
+
+ )}
+ {preferenceItems.map((preferenceItem, index) => (
+
+ ))}
+
+
+ );
+};
export const Extensions = withInjectables(
observer(NonInjectedExtensions),
{
getProps: (di) => ({
- preferenceItems: di.inject(extensionsPreferenceItemsInjectable),
+ model: di.inject(extensionPreferencesModelInjectable),
}),
},
);
diff --git a/src/renderer/components/+preferences/get-extension-preference-items.ts b/src/renderer/components/+preferences/get-extension-preference-items.ts
new file mode 100644
index 0000000000..a9c2237d18
--- /dev/null
+++ b/src/renderer/components/+preferences/get-extension-preference-items.ts
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
+import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
+
+export function getExtensionPreferenceItems(extension?: LensRendererExtension, tabId?: string): RegisteredAppPreference[] {
+ if (!extension) {
+ return [];
+ }
+
+ const preferences = extension.appPreferences.map(preference => ({
+ id: preference.id || preference.title,
+ ...preference,
+ }));
+
+ if (tabId) {
+ return preferences.filter(preference => preference.showInPreferencesTab == tabId);
+ }
+
+ return preferences.filter(preference => !preference.showInPreferencesTab);
+}
diff --git a/src/renderer/components/+preferences/preferences-navigation/__tests__/preferences-navigation.test.tsx b/src/renderer/components/+preferences/preferences-navigation/__tests__/preferences-navigation.test.tsx
new file mode 100644
index 0000000000..0d4547ae6a
--- /dev/null
+++ b/src/renderer/components/+preferences/preferences-navigation/__tests__/preferences-navigation.test.tsx
@@ -0,0 +1,152 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import "@testing-library/jest-dom/extend-expect";
+import type { RenderResult } from "@testing-library/react";
+import { computed } from "mobx";
+import { noop } from "../../../../utils";
+import type { ApplicationBuilder } from "../../../test-utils/get-application-builder";
+import { getApplicationBuilder } from "../../../test-utils/get-application-builder";
+import type { PreferenceNavigationItem } from "../preference-navigation-items.injectable";
+import preferenceNavigationItemsInjectable from "../preference-navigation-items.injectable";
+
+describe.only("preferences - navigation block with links", () => {
+ let applicationBuilder: ApplicationBuilder;
+
+ beforeEach(() => {
+ applicationBuilder = getApplicationBuilder();
+ });
+
+ describe("given in preferences, when rendered", () => {
+ let renderer: RenderResult;
+
+ describe("when general navigation items passed", () => {
+ beforeEach(async () => {
+ applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
+ rendererDi.override(preferenceNavigationItemsInjectable, () =>
+ computed(() => generalNavItems),
+ );
+ });
+
+ applicationBuilder.beforeRender(() => {
+ applicationBuilder.preferences.navigate();
+ });
+
+ renderer = await applicationBuilder.render();
+ });
+
+ const links = ["General", "Proxy"];
+
+ it.each(links)("renders link with text content %s", (link) => {
+ expect(renderer.container).toHaveTextContent(link);
+ });
+
+ it("does not show custom settings block", () => {
+ expect(
+ renderer.queryByTestId("extension-settings"),
+ ).not.toBeInTheDocument();
+ });
+ });
+
+ describe("when general + extension navigation items passed", () => {
+ beforeEach(async () => {
+ applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
+ rendererDi.override(preferenceNavigationItemsInjectable, () =>
+ computed(() => [...generalNavItems, ...extensionNavItems]),
+ );
+ });
+
+ applicationBuilder.beforeRender(() => {
+ applicationBuilder.preferences.navigate();
+ });
+
+ renderer = await applicationBuilder.render();
+ });
+
+ const generalLinks = ["General", "Proxy"];
+
+ it.each(generalLinks)(
+ "renders general link with text content %s",
+ (link) => {
+ expect(renderer.container).toHaveTextContent(link);
+ },
+ );
+
+ it("shows custom settings block", () => {
+ expect(
+ renderer.queryByTestId("extension-settings"),
+ ).toBeInTheDocument();
+ });
+
+ const extensionLinks = ["lensapp-node-menu", "lensapp-pod-menu"];
+
+ it.each(extensionLinks)("shows extension navigation item %s", (link) => {
+ expect(
+ renderer.getByTestId(
+ `tab-link-for-extension-preferences-navigation-item-${link}`,
+ ),
+ ).toBeInTheDocument();
+ });
+
+ it("renders extension navigation items inside custom settings block", () => {
+ const settingsBlock = renderer.getByTestId("extension-settings");
+
+ expect(settingsBlock).toHaveTextContent("lensapp-node-menu");
+ });
+ });
+ });
+});
+
+const generalNavItems: PreferenceNavigationItem[] = [
+ {
+ id: "general",
+ label: "General",
+ isActive: computed(() => false),
+ isVisible: computed(() => true),
+ navigate: () => noop,
+ orderNumber: 0,
+ parent: "general",
+ },
+ {
+ id: "proxy",
+ label: "Proxy",
+ isActive: computed(() => false),
+ isVisible: computed(() => true),
+ navigate: () => noop,
+ orderNumber: 1,
+ parent: "general",
+ },
+];
+
+const extensionNavItems = [
+ {
+ id: "extension-preferences-navigation-item-lensapp-node-menu",
+ label: "lensapp-node-menu",
+ isActive: computed(() => false),
+ isVisible: computed(() => true),
+ navigate: () => noop,
+ orderNumber: 0,
+ parent: "extensions",
+ },
+ {
+ id: "extension-preferences-navigation-item-lensapp-pod-menu",
+ label: "lensapp-pod-menu",
+ isActive: computed(() => false),
+ isVisible: computed(() => true),
+ navigate: () => noop,
+ orderNumber: 0,
+ parent: "extensions",
+ },
+ {
+ id: "extension-preferences-navigation-item-metrics-plugin",
+ label: "metrics-plugin",
+ isActive: computed(() => false),
+ isVisible: computed(() => false),
+ navigate: () => noop,
+ orderNumber: 0,
+ parent: "extensions",
+ },
+];
+
diff --git a/src/renderer/components/+preferences/preferences-navigation/application-preferences-navigation-item.injectable.ts b/src/renderer/components/+preferences/preferences-navigation/application-preferences-navigation-item.injectable.ts
index 0b333f4fed..a3e35d35a9 100644
--- a/src/renderer/components/+preferences/preferences-navigation/application-preferences-navigation-item.injectable.ts
+++ b/src/renderer/components/+preferences/preferences-navigation/application-preferences-navigation-item.injectable.ts
@@ -24,6 +24,7 @@ const applicationPreferencesNavigationItemInjectable = getInjectable({
return {
id: "application",
label: "App",
+ parent: "general",
navigate: navigateToPreferenceTab(route),
isActive: routeIsActive,
isVisible: computed(() => true),
diff --git a/src/renderer/components/+preferences/preferences-navigation/editor-preferences-navigation-item.injectable.ts b/src/renderer/components/+preferences/preferences-navigation/editor-preferences-navigation-item.injectable.ts
index f5738cf985..686a688aa6 100644
--- a/src/renderer/components/+preferences/preferences-navigation/editor-preferences-navigation-item.injectable.ts
+++ b/src/renderer/components/+preferences/preferences-navigation/editor-preferences-navigation-item.injectable.ts
@@ -24,6 +24,7 @@ const editorPreferencesNavigationItemInjectable = getInjectable({
return {
id: "editor",
label: "Editor",
+ parent: "general",
navigate: navigateToPreferenceTab(route),
isActive: routeIsActive,
isVisible: computed(() => true),
diff --git a/src/renderer/components/+preferences/preferences-navigation/extension-tab-preferences-navigation-item.injectable.ts b/src/renderer/components/+preferences/preferences-navigation/extension-tab-preferences-navigation-item.injectable.ts
new file mode 100644
index 0000000000..8cd0c4895b
--- /dev/null
+++ b/src/renderer/components/+preferences/preferences-navigation/extension-tab-preferences-navigation-item.injectable.ts
@@ -0,0 +1,60 @@
+/**
+ * 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 { map } from "lodash/fp";
+
+import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
+import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
+
+import type { LensRendererExtension } from "../../../../extensions/lens-renderer-extension";
+import { extensionRegistratorInjectionToken } from "../../../../extensions/extension-loader/extension-registrator-injection-token";
+import { pipeline } from "@ogre-tools/fp";
+import extensionPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
+import navigateToExtensionPreferencesInjectable from "../../../../common/front-end-routing/routes/preferences/extension/navigate-to-extension-preferences.injectable";
+import type { LensExtension } from "../../../../extensions/lens-extension";
+import routePathParametersInjectable from "../../../routes/route-path-parameters.injectable";
+
+const extensionSpecificTabNavigationItemRegistratorInjectable = getInjectable({
+ id: "extension-specific-tab-preferences-navigation-items",
+
+ instantiate: (di) => {
+ return (ext: LensExtension) => {
+ const extension = ext as LensRendererExtension;
+ const navigateToExtensionPreferences = di.inject(
+ navigateToExtensionPreferencesInjectable,
+ );
+ const route = di.inject(extensionPreferencesRouteInjectable);
+ const routeIsActive = di.inject(routeIsActiveInjectable, route);
+ const pathParameters = di.inject(routePathParametersInjectable, route);
+
+ return pipeline(
+ extension.appPreferenceTabs,
+
+ map((tab) => {
+ const id = `extension-${extension.sanitizedExtensionId}-nav-item-${tab.id}`;
+ const isActive = computed(() => routeIsActive.get() && pathParameters.get().tabId === tab.id);
+
+ return getInjectable({
+ id,
+ injectionToken: preferenceNavigationItemInjectionToken,
+ instantiate: () => ({
+ id,
+ label: tab.title,
+ parent: "general",
+ orderNumber: tab.orderNumber || 100,
+ navigate: () => navigateToExtensionPreferences(extension.sanitizedExtensionId, tab.id),
+ isVisible: computed(() => true),
+ isActive,
+ }),
+ });
+ }),
+ );
+ };
+ },
+ injectionToken: extensionRegistratorInjectionToken,
+});
+
+export default extensionSpecificTabNavigationItemRegistratorInjectable;
diff --git a/src/renderer/components/+preferences/preferences-navigation/extensions-nav-group.tsx b/src/renderer/components/+preferences/preferences-navigation/extensions-nav-group.tsx
new file mode 100644
index 0000000000..fd0c4158f6
--- /dev/null
+++ b/src/renderer/components/+preferences/preferences-navigation/extensions-nav-group.tsx
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import React from "react";
+import { withInjectables } from "@ogre-tools/injectable-react";
+import type { IComputedValue } from "mobx";
+import type { PreferenceNavigationItem } from "./preference-navigation-items.injectable";
+import { Icon } from "../../icon";
+import { PreferencesNavigationTab } from "./preference-navigation-tab";
+import preferenceNavigationItemsForGroupInjectable from "./preference-navigation-items-for-group.injectable";
+import { observer } from "mobx-react";
+
+interface Dependencies {
+ navigationItems: IComputedValue;
+}
+
+const NonInjectedExtensionsNavGroup = observer((props: Dependencies) => {
+ if (!props.navigationItems.get().length) {
+ return null;
+ }
+
+ return (
+
+
+
+
+ {" "}
+ Extensions
+
+
+ {props.navigationItems.get().map(item => (
+
+ ))}
+
+
+ );
+});
+
+export const ExtensionsNavGroup = withInjectables(
+ NonInjectedExtensionsNavGroup,
+
+ {
+ getProps: (di) => ({
+ navigationItems: di.inject(preferenceNavigationItemsForGroupInjectable, "extensions"),
+ }),
+ },
+);
diff --git a/src/renderer/components/+preferences/preferences-navigation/extensions-preferences-navigation-item-registrator.injectable.ts b/src/renderer/components/+preferences/preferences-navigation/extensions-preferences-navigation-item-registrator.injectable.ts
new file mode 100644
index 0000000000..64b152e573
--- /dev/null
+++ b/src/renderer/components/+preferences/preferences-navigation/extensions-preferences-navigation-item-registrator.injectable.ts
@@ -0,0 +1,57 @@
+/**
+ * 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 extensionPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
+import navigateToExtensionPreferencesInjectable from "../../../../common/front-end-routing/routes/preferences/extension/navigate-to-extension-preferences.injectable";
+import { extensionRegistratorInjectionToken } from "../../../../extensions/extension-loader/extension-registrator-injection-token";
+import type { LensRendererExtension } from "../../../../extensions/lens-renderer-extension";
+import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
+import routePathParametersInjectable from "../../../routes/route-path-parameters.injectable";
+import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
+
+const extensionPreferencesNavigationItemRegistratorInjectable = getInjectable({
+ id: "extension-preferences-navigation-item",
+
+ instantiate: (di) => {
+ return (ext) => {
+ const extension = ext as LensRendererExtension;
+ const navigateToExtensionPreferences = di.inject(
+ navigateToExtensionPreferencesInjectable,
+ );
+
+ const extensionHasPreferences = extension.appPreferences.length > 0;
+ const extensionHasGeneralPreferences = extension.appPreferences.some(preferences =>
+ !preferences.showInPreferencesTab,
+ );
+ const isVisible = computed(() => extensionHasPreferences && extensionHasGeneralPreferences);
+ const extensionRoute = di.inject(extensionPreferencesRouteInjectable);
+ const pathParameters = di.inject(routePathParametersInjectable, extensionRoute);
+ const routeIsActive = di.inject(routeIsActiveInjectable, extensionRoute);
+ const isActive = computed(() => routeIsActive.get() && pathParameters.get().extensionId === extension.sanitizedExtensionId);
+ const id = `extension-preferences-navigation-item-${extension.sanitizedExtensionId}`;
+
+ const injectable = getInjectable({
+ id,
+ injectionToken: preferenceNavigationItemInjectionToken,
+ instantiate: () => ({
+ id: `extension-${extension.sanitizedExtensionId}`,
+ label: `${extension.name}`,
+ navigate: () => navigateToExtensionPreferences(extension.sanitizedExtensionId),
+ isActive,
+ isVisible,
+ orderNumber: 20,
+ parent: "extensions",
+ }),
+ });
+
+ return [injectable];
+ };
+ },
+
+ injectionToken: extensionRegistratorInjectionToken,
+});
+
+export default extensionPreferencesNavigationItemRegistratorInjectable;
diff --git a/src/renderer/components/+preferences/preferences-navigation/extensions-preferences-navigation-item.injectable.ts b/src/renderer/components/+preferences/preferences-navigation/extensions-preferences-navigation-item.injectable.ts
deleted file mode 100644
index 029e9ba896..0000000000
--- a/src/renderer/components/+preferences/preferences-navigation/extensions-preferences-navigation-item.injectable.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * 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 { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
-import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
-import { computed } from "mobx";
-import extensionsPreferenceItemsInjectable from "../extension-preference-items.injectable";
-import extensionPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
-import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
-
-const extensionsPreferencesNavigationItemInjectable = getInjectable({
- id: "extension-preferences-navigation-item",
-
- instantiate: (di) => {
- const preferenceItems = di.inject(
- extensionsPreferenceItemsInjectable,
- );
-
- const navigateToPreferenceTab = di.inject(
- navigateToPreferenceTabInjectable,
- );
-
- const route = di.inject(
- extensionPreferencesRouteInjectable,
- );
-
- const routeIsActive = di.inject(
- routeIsActiveInjectable,
- route,
- );
-
- return {
- id: "extensions",
- label: "Extensions",
- navigate: navigateToPreferenceTab(route),
- isActive: routeIsActive,
-
- isVisible: computed(
- () => preferenceItems.get().length > 0,
- ),
-
- orderNumber: 70,
- };
- },
-
- injectionToken: preferenceNavigationItemInjectionToken,
-});
-
-export default extensionsPreferencesNavigationItemInjectable;
diff --git a/src/renderer/components/+preferences/preferences-navigation/general-nav-group.tsx b/src/renderer/components/+preferences/preferences-navigation/general-nav-group.tsx
new file mode 100644
index 0000000000..0dc3b4693e
--- /dev/null
+++ b/src/renderer/components/+preferences/preferences-navigation/general-nav-group.tsx
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import React from "react";
+import { withInjectables } from "@ogre-tools/injectable-react";
+import type { IComputedValue } from "mobx";
+import type { PreferenceNavigationItem } from "./preference-navigation-items.injectable";
+import { PreferencesNavigationTab } from "./preference-navigation-tab";
+import preferenceNavigationItemsForGroupInjectable from "./preference-navigation-items-for-group.injectable";
+import { observer } from "mobx-react";
+
+interface Dependencies {
+ navigationItems: IComputedValue;
+}
+
+const NonInjectedGeneralNavGroup = observer((props: Dependencies) => {
+ if (!props.navigationItems.get().length) {
+ return null;
+ }
+
+ return (
+
+ Preferences
+
+ {props.navigationItems.get().map(item => (
+
+ ))}
+
+ );
+});
+
+export const GeneralNavGroup = withInjectables(
+ NonInjectedGeneralNavGroup,
+
+ {
+ getProps: (di) => ({
+ navigationItems: di.inject(preferenceNavigationItemsForGroupInjectable, "general"),
+ }),
+ },
+);
diff --git a/src/renderer/components/+preferences/preferences-navigation/kubernetes-preferences-navigation-item.injectable.ts b/src/renderer/components/+preferences/preferences-navigation/kubernetes-preferences-navigation-item.injectable.ts
index ec3429a212..6147021087 100644
--- a/src/renderer/components/+preferences/preferences-navigation/kubernetes-preferences-navigation-item.injectable.ts
+++ b/src/renderer/components/+preferences/preferences-navigation/kubernetes-preferences-navigation-item.injectable.ts
@@ -27,6 +27,7 @@ const kubernetesPreferencesNavigationItemInjectable = getInjectable({
return {
id: "kubernetes",
label: "Kubernetes",
+ parent: "general",
navigate: navigateToPreferenceTab(route),
isActive: routeIsActive,
isVisible: computed(() => true),
diff --git a/src/renderer/components/+preferences/preferences-navigation/preference-navigation-items-for-group.injectable.ts b/src/renderer/components/+preferences/preferences-navigation/preference-navigation-items-for-group.injectable.ts
new file mode 100644
index 0000000000..9c63626f2b
--- /dev/null
+++ b/src/renderer/components/+preferences/preferences-navigation/preference-navigation-items-for-group.injectable.ts
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import {
+ getInjectable,
+ lifecycleEnum,
+} from "@ogre-tools/injectable";
+import { computed } from "mobx";
+import type { PreferenceNavigationItem } from "./preference-navigation-items.injectable";
+import preferenceNavigationItemsInjectable from "./preference-navigation-items.injectable";
+
+const preferenceNavigationItemsForGroupInjectable = getInjectable({
+ id: "preference-navigation-items-for-group",
+
+ instantiate: (di, group: string) => {
+ const preferenceNavigationItems = di.inject(preferenceNavigationItemsInjectable);
+
+ return computed((): PreferenceNavigationItem[] =>
+ preferenceNavigationItems.get().filter((item) => item.parent == group),
+ );
+ },
+
+ lifecycle: lifecycleEnum.keyedSingleton({
+ getInstanceKey: (di, group: string) => group,
+ }),
+});
+
+export default preferenceNavigationItemsForGroupInjectable;
+
diff --git a/src/renderer/components/+preferences/preferences-navigation/preference-navigation-items.injectable.ts b/src/renderer/components/+preferences/preferences-navigation/preference-navigation-items.injectable.ts
index e72d3f7b4d..8340ff003e 100644
--- a/src/renderer/components/+preferences/preferences-navigation/preference-navigation-items.injectable.ts
+++ b/src/renderer/components/+preferences/preferences-navigation/preference-navigation-items.injectable.ts
@@ -4,6 +4,7 @@
*/
import { pipeline } from "@ogre-tools/fp";
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
+import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
import { filter, orderBy } from "lodash/fp";
import type { IComputedValue } from "mobx";
import { computed } from "mobx";
@@ -20,19 +21,24 @@ export interface PreferenceNavigationItem {
isVisible: IComputedValue;
navigate: () => void;
orderNumber: number;
+ parent: string;
}
const preferenceNavigationItemsInjectable = getInjectable({
id: "preference-navigation-items",
- instantiate: (di) =>
- computed((): PreferenceNavigationItem[] =>
+ instantiate: (di) => {
+ const computedInjectMany = di.inject(computedInjectManyInjectable);
+ const navigationItems = computedInjectMany(preferenceNavigationItemInjectionToken);
+
+ return computed((): PreferenceNavigationItem[] =>
pipeline(
- di.injectMany(preferenceNavigationItemInjectionToken),
+ navigationItems.get(),
filter((item) => !!item.isVisible.get()),
(items) => orderBy([(item) => item.orderNumber], ["asc"], items),
),
- ),
+ );
+ },
});
export default preferenceNavigationItemsInjectable;
diff --git a/src/renderer/components/+preferences/preferences-navigation/preference-navigation-tab.tsx b/src/renderer/components/+preferences/preferences-navigation/preference-navigation-tab.tsx
new file mode 100644
index 0000000000..a236c74ea9
--- /dev/null
+++ b/src/renderer/components/+preferences/preferences-navigation/preference-navigation-tab.tsx
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { observer } from "mobx-react";
+import React from "react";
+import { Tab } from "../../tabs";
+import type { PreferenceNavigationItem } from "./preference-navigation-items.injectable";
+
+interface PreferenceNavigationTabProps extends React.DOMAttributes {
+ item: PreferenceNavigationItem;
+}
+
+export const PreferencesNavigationTab = observer(({ item }: PreferenceNavigationTabProps) => (
+
+));
diff --git a/src/renderer/components/+preferences/preferences-navigation/preferences-navigation.tsx b/src/renderer/components/+preferences/preferences-navigation/preferences-navigation.tsx
index b728412f97..55830433cd 100644
--- a/src/renderer/components/+preferences/preferences-navigation/preferences-navigation.tsx
+++ b/src/renderer/components/+preferences/preferences-navigation/preferences-navigation.tsx
@@ -2,61 +2,23 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
-import { Tab, Tabs } from "../../tabs";
-
-import { withInjectables } from "@ogre-tools/injectable-react";
-import type { IComputedValue } from "mobx";
import React from "react";
+import { Tabs } from "../../tabs";
+import { ExtensionsNavGroup } from "./extensions-nav-group";
+import { GeneralNavGroup } from "./general-nav-group";
import type {
PreferenceNavigationItem,
} from "./preference-navigation-items.injectable";
-import preferenceNavigationItemsInjectable from "./preference-navigation-items.injectable";
-import { observer } from "mobx-react";
-
-interface Dependencies {
- navigationItems: IComputedValue;
-}
-
-const NonInjectedPreferencesNavigation = ({
- navigationItems,
-}: Dependencies) => (
- item.navigate()}
- >
- Preferences
-
- {navigationItems.get().map((item) => (
-
- ))}
-
-);
-
-interface PreferenceNavigationTabProps extends React.DOMAttributes {
- item: PreferenceNavigationItem;
-}
-
-const PreferencesNavigationTab = observer(({ item }: PreferenceNavigationTabProps) => (
-
-));
-
-export const PreferencesNavigation = withInjectables(
- NonInjectedPreferencesNavigation,
-
- {
- getProps: (di) => ({
- navigationItems: di.inject(preferenceNavigationItemsInjectable),
- }),
- },
-);
+export const PreferencesNavigation = () => {
+ return (
+ item.navigate()}
+ >
+
+
+
+ );
+};
diff --git a/src/renderer/components/+preferences/preferences-navigation/proxy-preferences-navigation-item.injectable.ts b/src/renderer/components/+preferences/preferences-navigation/proxy-preferences-navigation-item.injectable.ts
index d4a588db87..fd0b835d12 100644
--- a/src/renderer/components/+preferences/preferences-navigation/proxy-preferences-navigation-item.injectable.ts
+++ b/src/renderer/components/+preferences/preferences-navigation/proxy-preferences-navigation-item.injectable.ts
@@ -24,6 +24,7 @@ const proxyPreferencesNavigationItemInjectable = getInjectable({
return {
id: "proxy",
label: "Proxy",
+ parent: "general",
navigate: navigateToPreferenceTab(route),
isActive: routeIsActive,
isVisible: computed(() => true),
diff --git a/src/renderer/components/+preferences/preferences-navigation/telemetry-preferences-navigation-item.injectable.ts b/src/renderer/components/+preferences/preferences-navigation/telemetry-preferences-navigation-item.injectable.ts
index f6576696c6..1f95c74a0d 100644
--- a/src/renderer/components/+preferences/preferences-navigation/telemetry-preferences-navigation-item.injectable.ts
+++ b/src/renderer/components/+preferences/preferences-navigation/telemetry-preferences-navigation-item.injectable.ts
@@ -32,6 +32,7 @@ const terminalPreferencesNavigationItemInjectable = getInjectable({
return {
id: "telemetry",
label: "Telemetry",
+ parent: "general",
navigate: navigateToPreferenceTab(route),
isActive: routeIsActive,
diff --git a/src/renderer/components/+preferences/preferences-navigation/terminal-preferences-navigation-item.injectable.ts b/src/renderer/components/+preferences/preferences-navigation/terminal-preferences-navigation-item.injectable.ts
index b623148c10..f5ad555c3b 100644
--- a/src/renderer/components/+preferences/preferences-navigation/terminal-preferences-navigation-item.injectable.ts
+++ b/src/renderer/components/+preferences/preferences-navigation/terminal-preferences-navigation-item.injectable.ts
@@ -24,6 +24,7 @@ const terminalPreferencesNavigationItemInjectable = getInjectable({
return {
id: "terminal",
label: "Terminal",
+ parent: "general",
navigate: navigateToPreferenceTab(route),
isActive: routeIsActive,
isVisible: computed(() => true),
diff --git a/src/renderer/components/layout/setting-layout.scss b/src/renderer/components/layout/setting-layout.scss
index 7e7ed325cb..244b88b3dc 100644
--- a/src/renderer/components/layout/setting-layout.scss
+++ b/src/renderer/components/layout/setting-layout.scss
@@ -34,7 +34,7 @@
.sidebar {
width: 218px;
- padding: 60px 0 60px 20px;
+ padding: 60px 10px 60px 20px;
h2 {
font-size: 15px;
@@ -52,10 +52,6 @@
margin-right: 20px;
height: 1px;
border-top: thin solid var(--hrColor);
-
- &:first-child {
- display: none;
- }
}
.Tabs {
@@ -63,7 +59,6 @@
padding: 6px 10px;
font-size: 13px;
font-weight: 800;
- line-height: 16px;
text-transform: uppercase;
color: var(--textColorPrimary);
@@ -76,14 +71,10 @@
padding: 6px 10px;
margin-bottom: 2px;
border-radius: 4px;
- white-space: nowrap;
- text-overflow: ellipsis;
- overflow: hidden;
font-size: 15px;
line-height: 20px;
cursor: pointer;
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
+ white-space: normal;
&::after {
content: none;
diff --git a/src/renderer/components/test-utils/get-application-builder.tsx b/src/renderer/components/test-utils/get-application-builder.tsx
index ae54d4f71c..42401e1288 100644
--- a/src/renderer/components/test-utils/get-application-builder.tsx
+++ b/src/renderer/components/test-utils/get-application-builder.tsx
@@ -55,6 +55,9 @@ import { openMenu } from "react-select-event";
import userEvent from "@testing-library/user-event";
import { StatusBar } from "../status-bar/status-bar";
import lensProxyPortInjectable from "../../../main/lens-proxy/lens-proxy-port.injectable";
+import type { Route } from "../../../common/front-end-routing/front-end-route-injection-token";
+import type { NavigateToRouteOptions } from "../../../common/front-end-routing/navigate-to-route-injection-token";
+import { navigateToRouteInjectionToken } from "../../../common/front-end-routing/navigate-to-route-injection-token";
import type { LensMainExtension } from "../../../extensions/lens-main-extension";
import trayMenuItemsInjectable from "../../../main/tray/tray-menu-item/tray-menu-items.injectable";
import type { LensExtension } from "../../../extensions/lens-extension";
@@ -100,6 +103,7 @@ export interface ApplicationBuilder {
preferences: {
close: () => void;
navigate: () => void;
+ navigateTo: (route: Route, params: Partial>) => void;
navigation: {
click: (id: string) => void;
};
@@ -332,6 +336,12 @@ export const getApplicationBuilder = () => {
navigateToPreferences();
},
+ navigateTo: (route: Route, params: Partial>) => {
+ const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken);
+
+ navigateToRoute(route, params);
+ },
+
navigation: {
click: (id: string) => {
const link = rendered.queryByTestId(`tab-link-for-${id}`);
@@ -546,7 +556,7 @@ const disableExtensionsFor = (
runInAction(() => {
extension.deregister();
- extensionState.delete(extension);
+ extensionState.delete(instance);
});
});
};
diff --git a/src/renderer/getDi.tsx b/src/renderer/getDi.tsx
index 094af6292f..3380d25c06 100644
--- a/src/renderer/getDi.tsx
+++ b/src/renderer/getDi.tsx
@@ -9,7 +9,7 @@ import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx";
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
export const getDi = () => {
- const di = createContainer();
+ const di = createContainer("renderer");
registerMobX(di);
diff --git a/src/renderer/getDiForUnitTesting.tsx b/src/renderer/getDiForUnitTesting.tsx
index 78ecf47438..2f030c8ab9 100644
--- a/src/renderer/getDiForUnitTesting.tsx
+++ b/src/renderer/getDiForUnitTesting.tsx
@@ -58,7 +58,7 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {})
doGeneralOverrides = false,
} = opts;
- const di = createContainer();
+ const di = createContainer("renderer");
registerMobX(di);
diff --git a/yarn.lock b/yarn.lock
index 8632bb789f..d8ee6d4e89 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1189,46 +1189,46 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
-"@ogre-tools/fp@8.0.0", "@ogre-tools/fp@^8.0.0":
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/@ogre-tools/fp/-/fp-8.0.0.tgz#dd2319a96ce3d2edd3ee2bc2acca07a94a77bf3b"
- integrity sha512-8OpGUbG3avGtx6ASz3XNnK/KCyPW25RPp8oYzzU0zihKU5D4QKHy9qNkQ3npurzeg4d1k4BsgkeX+805nqtZOA==
+"@ogre-tools/fp@9.0.0", "@ogre-tools/fp@^9.0.0":
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/@ogre-tools/fp/-/fp-9.0.0.tgz#926cd4f13b52961156161feeeafddf22a0ad39c0"
+ integrity sha512-kMUgzhdjHuph0UWteOfyXNGBavZJX23NOA5su6fx9NdTzWhl9yB5Uf6Q//nOvL9COftjZDwnAgIaDU4MPdjyqA==
dependencies:
lodash "^4.17.21"
-"@ogre-tools/injectable-extension-for-auto-registration@8.0.0":
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-auto-registration/-/injectable-extension-for-auto-registration-8.0.0.tgz#3a443f1f1c9b564baa78cca6a3c81ac4102660b0"
- integrity sha512-DX1bxn8mDwek+W/SaI5WmDHmkY3B3njs3X4pOvqRtiMis3GaWHzeCZeK3q3Iv5cd14FTW8AsfKtujPmLklNf/A==
+"@ogre-tools/injectable-extension-for-auto-registration@9.0.0":
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-auto-registration/-/injectable-extension-for-auto-registration-9.0.0.tgz#69463737ef4f7777db4703964b8a72a5fb82d6b3"
+ integrity sha512-+3I9Z0GfA04zZoj7Nw5WhJLDFLJgr5xv8Kp1zPDuT9/OvE9EA6hzAqakMDLbvn1zZOJjkJCGk44x6UjSQJp/9w==
dependencies:
- "@ogre-tools/fp" "^8.0.0"
- "@ogre-tools/injectable" "^8.0.0"
+ "@ogre-tools/fp" "^9.0.0"
+ "@ogre-tools/injectable" "^9.0.0"
lodash "^4.17.21"
-"@ogre-tools/injectable-extension-for-mobx@8.0.0":
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-mobx/-/injectable-extension-for-mobx-8.0.0.tgz#80ff506011e078050dd8dcb72660d17181db6d34"
- integrity sha512-m8gU3cEFHl9IMZLcvvoS7hVxS6p6nG3jdf6fY6MUZE0u9hx4bZuUdWYoylGQizy0FyRFQ2/m5xhTH2VdtXqx8w==
+"@ogre-tools/injectable-extension-for-mobx@9.0.0":
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-mobx/-/injectable-extension-for-mobx-9.0.0.tgz#ed14df39b266e521272977821d3e05bcbd647577"
+ integrity sha512-9Hrtr7AdibcD+Fqn2qNsjiOUakAACO55TB1IqNsOJMMuqQHVB5SFZTHBzdVRqqqY2MwQvWYvd4xfy+beItD/xw==
dependencies:
- "@ogre-tools/fp" "^8.0.0"
- "@ogre-tools/injectable" "^8.0.0"
+ "@ogre-tools/fp" "^9.0.0"
+ "@ogre-tools/injectable" "^9.0.0"
lodash "^4.17.21"
-"@ogre-tools/injectable-react@8.0.0":
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-8.0.0.tgz#b2d8db4bb697ba2822d71ec73e6cabee30f70f34"
- integrity sha512-rZa38fm3UzGa/09qC765Za8xJiSPOYHJGsob8UOt2JQIt/BecTNXPMHexfxy9W+DIdAer+YruUgedChdk9nvdQ==
+"@ogre-tools/injectable-react@9.0.0":
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-9.0.0.tgz#c5c510e893a5c1d7994d8709f70606967cabdec2"
+ integrity sha512-vGQrwkcWibRUWFPbu392riBYY4dXK051FxwyMsDYNRqmvaLo8HuumwjzS1DWS7db/P9Li+Kc+Ms670xIZepcpA==
dependencies:
- "@ogre-tools/fp" "^8.0.0"
- "@ogre-tools/injectable" "^8.0.0"
+ "@ogre-tools/fp" "^9.0.0"
+ "@ogre-tools/injectable" "^9.0.0"
lodash "^4.17.21"
-"@ogre-tools/injectable@8.0.0", "@ogre-tools/injectable@^8.0.0":
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-8.0.0.tgz#ea4f98bd2466149add94d4f6a9beb7be03729da7"
- integrity sha512-59p+8uGqwVQ5IpGpgfn3RA+wXzn1tjnPdFWO3GLEgjyp5dWBaMKufCpCFRvvb9sP6B68qo19aLfy/uSm4AXduw==
+"@ogre-tools/injectable@9.0.0", "@ogre-tools/injectable@^9.0.0":
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-9.0.0.tgz#0819bc7b5fbae0a467f3250b10b4adb003268067"
+ integrity sha512-z9X86Q9AEkkilLu9V33j/aXv/IUoG944AdfN6WX2zZgJqRNjESN9spoOMqdKqib6JmEjCRxpMvaMwHLQSh14fg==
dependencies:
- "@ogre-tools/fp" "^8.0.0"
+ "@ogre-tools/fp" "^9.0.0"
lodash "^4.17.21"
"@panva/asn1.js@^1.0.0":