diff --git a/src/features/preferences/__snapshots__/urls-of-legacy-extensions.test.tsx.snap b/src/features/preferences/__snapshots__/urls-of-legacy-extensions.test.tsx.snap
new file mode 100644
index 0000000000..05e451bf27
--- /dev/null
+++ b/src/features/preferences/__snapshots__/urls-of-legacy-extensions.test.tsx.snap
@@ -0,0 +1,1695 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`preferences: URLs of legacy extensions given extension with both custom preference tabs and content for the default tab when navigating to specific custom preference tab using magic string URL renders 1`] = `
+
+
+
+
+
+
+
+
+ home
+
+
+
+
+
+
+
+ arrow_back
+
+
+
+
+
+
+
+ arrow_forward
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ arrow_left
+
+
+
+
+
+ arrow_right
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`preferences: URLs of legacy extensions given extension with both custom preference tabs and content for the default tab when navigating to unspecified custom preferences tab using magic string URL renders 1`] = `
+
+
+
+
+
+
+
+
+ home
+
+
+
+
+
+
+
+ arrow_back
+
+
+
+
+
+
+
+ arrow_forward
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ some-extension preferences
+
+
+
+
+ some-title-in-default-tab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ arrow_left
+
+
+
+
+
+ arrow_right
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`preferences: URLs of legacy extensions given extension with custom preferences and a custom preference tab when navigating to specific custom preference tab using magic string URL renders 1`] = `
+
+
+
+
+
+
+
+
+ home
+
+
+
+
+
+
+
+ arrow_back
+
+
+
+
+
+
+
+ arrow_forward
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ arrow_left
+
+
+
+
+
+ arrow_right
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`preferences: URLs of legacy extensions given extension with custom preferences and a custom preference tab when navigating to unspecified custom preferences tab using magic string URL renders 1`] = `
+
+
+
+
+
+
+
+
+ home
+
+
+
+
+
+
+
+ arrow_back
+
+
+
+
+
+
+
+ arrow_forward
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ some-extension preferences
+
+
+
+
+
+
+
+
+
+
+
+
+ arrow_left
+
+
+
+
+
+ arrow_right
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`preferences: URLs of legacy extensions given extension with custom preferences but without a custom preference tab when navigating to the default preference tab using magic string URL renders 1`] = `
+
+
+
+
+
+
+
+
+ home
+
+
+
+
+
+
+
+ arrow_back
+
+
+
+
+
+
+
+ arrow_forward
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ some-extension preferences
+
+
+
+
+
+
+
+
+
+
+
+
+
+ arrow_left
+
+
+
+
+
+ arrow_right
+
+
+
+
+
+
+
+
+
+`;
diff --git a/src/features/preferences/common/preferences-route-for-legacy-extensions.injectable.ts b/src/features/preferences/common/preferences-route-for-legacy-extensions.injectable.ts
new file mode 100644
index 0000000000..c1f81953a0
--- /dev/null
+++ b/src/features/preferences/common/preferences-route-for-legacy-extensions.injectable.ts
@@ -0,0 +1,21 @@
+/**
+ * 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 { frontEndRouteInjectionToken } from "../../../common/front-end-routing/front-end-route-injection-token";
+
+const preferencesRouteForLegacyExtensionsInjectable = getInjectable({
+ id: "preferences-route-for-legacy-extensions",
+
+ instantiate: () => ({
+ path: "/preferences/extension/:extensionId/:preferenceTabId?",
+ clusterFrame: false,
+ isEnabled: computed(() => true),
+ }),
+
+ injectionToken: frontEndRouteInjectionToken,
+});
+
+export default preferencesRouteForLegacyExtensionsInjectable;
diff --git a/src/features/preferences/renderer/preference-items/current-preference-tab-id.injectable.ts b/src/features/preferences/renderer/preference-items/current-preference-tab-id.injectable.ts
index e5c3be9da6..cdac5905a4 100644
--- a/src/features/preferences/renderer/preference-items/current-preference-tab-id.injectable.ts
+++ b/src/features/preferences/renderer/preference-items/current-preference-tab-id.injectable.ts
@@ -6,21 +6,44 @@ import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import preferencesRouteInjectable from "../../common/preferences-route.injectable";
import routePathParametersInjectable from "../../../../renderer/routes/route-path-parameters.injectable";
+import preferencesRouteForLegacyExtensionsInjectable from "../../common/preferences-route-for-legacy-extensions.injectable";
const currentPreferenceTabIdInjectable = getInjectable({
id: "current-preference-tab-id",
instantiate: (di) => {
const preferencesRoute = di.inject(preferencesRouteInjectable);
+ const preferencesRouteForLegacyExtensions = di.inject(preferencesRouteForLegacyExtensionsInjectable);
- const routePathParameters = di.inject(
+ const nonLegacyRoutePathParameters = di.inject(
routePathParametersInjectable,
preferencesRoute,
);
- return computed(
- () => routePathParameters.get().preferenceTabId || "app",
+ const legacyRoutePathParameters = di.inject(
+ routePathParametersInjectable,
+ preferencesRouteForLegacyExtensions,
);
+
+ return computed(() => {
+ const nonLegacyPreferenceTabId = nonLegacyRoutePathParameters.get().preferenceTabId;
+
+ if (nonLegacyPreferenceTabId) {
+ return nonLegacyPreferenceTabId;
+ }
+
+ const legacyParameters = legacyRoutePathParameters.get();
+
+ if (legacyParameters.extensionId) {
+ if (legacyParameters.preferenceTabId) {
+ return `extension-${legacyParameters.extensionId}-${legacyParameters.preferenceTabId}`;
+ }
+
+ return legacyParameters.extensionId;
+ }
+
+ return "app";
+ });
},
});
diff --git a/src/features/preferences/renderer/preference-items/preferences-composite.injectable.ts b/src/features/preferences/renderer/preference-items/preferences-composite.injectable.ts
index 45f64d6233..dd1ffd1ed8 100644
--- a/src/features/preferences/renderer/preference-items/preferences-composite.injectable.ts
+++ b/src/features/preferences/renderer/preference-items/preferences-composite.injectable.ts
@@ -31,7 +31,7 @@ const preferencesCompositeInjectable = getInjectable({
logError(
`Tried to create preferences, but encountered references to unknown ids: "${ids.missingParentIds.join(
'", "',
- )}". Available ids are: "${ids.availableParentIds.join('", "')}"`,
+ )}".\n\nAvailable ids are:\n\n${ids.availableParentIds.join("\n")}`,
);
},
diff --git a/src/features/preferences/renderer/preferences-route-component-for-legacy-extensions.injectable.ts b/src/features/preferences/renderer/preferences-route-component-for-legacy-extensions.injectable.ts
new file mode 100644
index 0000000000..981c4b3c84
--- /dev/null
+++ b/src/features/preferences/renderer/preferences-route-component-for-legacy-extensions.injectable.ts
@@ -0,0 +1,21 @@
+/**
+ * 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 { routeSpecificComponentInjectionToken } from "../../../renderer/routes/route-specific-component-injection-token";
+import preferencesRouteForLegacyExtensions from "../common/preferences-route-for-legacy-extensions.injectable";
+import { Preferences } from "./preferences";
+
+const preferencesRouteComponentInjectable = getInjectable({
+ id: "preferences-route-component-for-legacy-extensions",
+
+ instantiate: (di) => ({
+ route: di.inject(preferencesRouteForLegacyExtensions),
+ Component: Preferences,
+ }),
+
+ injectionToken: routeSpecificComponentInjectionToken,
+});
+
+export default preferencesRouteComponentInjectable;
diff --git a/src/features/preferences/urls-of-legacy-extensions.test.tsx b/src/features/preferences/urls-of-legacy-extensions.test.tsx
new file mode 100644
index 0000000000..1eba04c683
--- /dev/null
+++ b/src/features/preferences/urls-of-legacy-extensions.test.tsx
@@ -0,0 +1,330 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import type { RenderResult } from "@testing-library/react";
+import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
+import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
+import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements";
+import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements";
+import React from "react";
+import type { Navigate } from "../../renderer/navigation/navigate.injectable";
+import navigateInjectable from "../../renderer/navigation/navigate.injectable";
+
+describe("preferences: URLs of legacy extensions", () => {
+ let builder: ApplicationBuilder;
+
+ beforeEach(() => {
+ builder = getApplicationBuilder();
+ });
+
+ describe("given extension with custom preferences and a custom preference tab", () => {
+ let rendered: RenderResult;
+ let discover: Discover;
+ let navigate: Navigate;
+
+ beforeEach(async () => {
+ rendered = await builder.render();
+
+ discover = discoverFor(() => rendered);
+
+ const testExtension = {
+ id: "some-extension-id",
+ name: "some-extension",
+
+ rendererOptions: {
+ appPreferenceTabs: [
+ {
+ title: "Some title",
+ id: "some-preference-tab-id",
+ orderNumber: 1,
+ },
+
+ {
+ title: "Some other title",
+ id: "some-other-preference-tab-id",
+ orderNumber: 2,
+ },
+ ],
+
+ appPreferences: [
+ {
+ title: "some-title",
+ id: "some-preference-item-id",
+ showInPreferencesTab: "some-preference-tab-id",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+
+ {
+ title: "some-other-title",
+ id: "some-other-preference-item-id",
+ showInPreferencesTab: "some-other-preference-tab-id",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ ],
+ },
+ };
+
+ builder.extensions.enable(testExtension);
+
+ navigate = builder.applicationWindow.only.di.inject(navigateInjectable);
+ });
+
+ describe("when navigating to specific custom preference tab using magic string URL", () => {
+ beforeEach(() => {
+ navigate("/preferences/extension/some-extension/some-preference-tab-id");
+ });
+
+ it("renders", () => {
+ expect(rendered.baseElement).toMatchSnapshot();
+ });
+
+ it("shows the custom preference page for the custom preference tab", () => {
+ const { discovered } = discover.getSingleElement(
+ "preference-page",
+ "preference-item-for-extension-some-extension-additional-page-some-preference-tab-id",
+ );
+
+ expect(discovered).not.toBeNull();
+ });
+
+ it("shows the custom preferences", () => {
+ const { discovered } = discover.getSingleElement(
+ "some-preference",
+ );
+
+ expect(discovered).not.toBeNull();
+ });
+ });
+
+ describe("when navigating to unspecified custom preferences tab using magic string URL", () => {
+ beforeEach(() => {
+ navigate("/preferences/extension/some-extension");
+ });
+
+ it("renders", () => {
+ expect(rendered.baseElement).toMatchSnapshot();
+ });
+
+ it("cannot find contents of the arbitrary custom preference tabs", () => {
+ const { discovered } = discover.querySingleElement(
+ "some-preference",
+ );
+
+ expect(discovered).toBeNull();
+ });
+
+ it("shows the empty default preference page for the extension", () => {
+ const { discovered } = discover.getSingleElement(
+ "preference-page",
+ "preference-item-for-extension-some-extension-page",
+ );
+
+ expect(discovered).not.toBeNull();
+ });
+ });
+ });
+
+ describe("given extension with custom preferences but without a custom preference tab", () => {
+ let rendered: RenderResult;
+ let discover: Discover;
+ let navigate: Navigate;
+
+ beforeEach(async () => {
+ rendered = await builder.render();
+
+ discover = discoverFor(() => rendered);
+
+ const testExtension = {
+ id: "some-extension-id",
+ name: "some-extension",
+
+ rendererOptions: {
+ appPreferences: [
+ {
+ title: "some-title",
+ id: "some-preference-item-id",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ ],
+ },
+ };
+
+ builder.extensions.enable(testExtension);
+
+ navigate = builder.applicationWindow.only.di.inject(navigateInjectable);
+ });
+
+ describe("when navigating to the default preference tab using magic string URL", () => {
+ beforeEach(() => {
+ navigate("/preferences/extension/some-extension");
+ });
+
+ it("renders", () => {
+ expect(rendered.baseElement).toMatchSnapshot();
+ });
+
+ it("shows the preference page for the custom preferences", () => {
+ const { discovered } = discover.getSingleElement(
+ "preference-page",
+ "preference-item-for-extension-some-extension-page",
+ );
+
+ expect(discovered).not.toBeNull();
+ });
+
+ it("shows the custom preferences", () => {
+ const { discovered } = discover.getSingleElement("some-preference");
+
+ expect(discovered).not.toBeNull();
+ });
+ });
+ });
+
+ describe("given extension with both custom preference tabs and content for the default tab", () => {
+ let rendered: RenderResult;
+ let discover: Discover;
+ let navigate: Navigate;
+
+ beforeEach(async () => {
+ rendered = await builder.render();
+
+ discover = discoverFor(() => rendered);
+
+ const testExtension = {
+ id: "some-extension-id",
+ name: "some-extension",
+
+ rendererOptions: {
+ appPreferenceTabs: [
+ {
+ title: "Some title",
+ id: "some-preference-tab-id",
+ orderNumber: 1,
+ },
+
+ {
+ title: "Some other title",
+ id: "some-other-preference-tab-id",
+ orderNumber: 2,
+ },
+ ],
+
+ appPreferences: [
+ {
+ title: "some-title",
+ id: "some-preference-item-id",
+ showInPreferencesTab: "some-preference-tab-id",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+
+ {
+ title: "some-other-title",
+ id: "some-other-preference-item-id",
+ showInPreferencesTab: "some-other-preference-tab-id",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+
+ {
+ title: "some-title-in-default-tab",
+ id: "some-preference-item-id-in-default-tab",
+
+ components: {
+ Hint: () => ,
+ Input: () => ,
+ },
+ },
+ ],
+ },
+ };
+
+ builder.extensions.enable(testExtension);
+
+ navigate = builder.applicationWindow.only.di.inject(navigateInjectable);
+ });
+
+ describe("when navigating to specific custom preference tab using magic string URL", () => {
+ beforeEach(() => {
+ navigate("/preferences/extension/some-extension/some-preference-tab-id");
+ });
+
+ it("renders", () => {
+ expect(rendered.baseElement).toMatchSnapshot();
+ });
+
+ it("shows the custom preference page for the custom preference tab", () => {
+ const { discovered } = discover.getSingleElement(
+ "preference-page",
+ "preference-item-for-extension-some-extension-additional-page-some-preference-tab-id",
+ );
+
+ expect(discovered).not.toBeNull();
+ });
+
+ it("shows the custom preferences", () => {
+ const { discovered } = discover.getSingleElement(
+ "some-preference",
+ );
+
+ expect(discovered).not.toBeNull();
+ });
+ });
+
+ describe("when navigating to unspecified custom preferences tab using magic string URL", () => {
+ beforeEach(() => {
+ navigate("/preferences/extension/some-extension");
+ });
+
+ it("renders", () => {
+ expect(rendered.baseElement).toMatchSnapshot();
+ });
+
+ it("cannot find contents of the arbitrary custom preference tabs", () => {
+ const { discovered } = discover.querySingleElement(
+ "some-preference",
+ "some-preference-in-custom-tab",
+ );
+
+ expect(discovered).toBeNull();
+ });
+
+ it("shows the default preference page for the extension", () => {
+ const { discovered } = discover.getSingleElement(
+ "preference-page",
+ "preference-item-for-extension-some-extension-page",
+ );
+
+ expect(discovered).not.toBeNull();
+ });
+
+ it("shows preferences of default preference page of the extension", () => {
+ const { discovered } = discover.getSingleElement(
+ "some-preference",
+ "some-preference-in-default-tab",
+ );
+
+ expect(discovered).not.toBeNull();
+ });
+ });
+ });
+});