mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Show extension preferences in separate page (#5284)
Co-authored-by: Alex Andreev <alex.andreev.email@gmail.com> Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
parent
91ee7bd785
commit
0784085bf4
10
package.json
10
package.json
@ -208,11 +208,11 @@
|
|||||||
"@hapi/subtext": "^7.0.4",
|
"@hapi/subtext": "^7.0.4",
|
||||||
"@kubernetes/client-node": "^0.16.3",
|
"@kubernetes/client-node": "^0.16.3",
|
||||||
"@material-ui/styles": "^4.11.5",
|
"@material-ui/styles": "^4.11.5",
|
||||||
"@ogre-tools/fp": "8.0.0",
|
"@ogre-tools/fp": "9.0.0",
|
||||||
"@ogre-tools/injectable": "8.0.0",
|
"@ogre-tools/injectable": "9.0.0",
|
||||||
"@ogre-tools/injectable-extension-for-auto-registration": "8.0.0",
|
"@ogre-tools/injectable-extension-for-auto-registration": "9.0.0",
|
||||||
"@ogre-tools/injectable-extension-for-mobx": "8.0.0",
|
"@ogre-tools/injectable-extension-for-mobx": "9.0.0",
|
||||||
"@ogre-tools/injectable-react": "8.0.0",
|
"@ogre-tools/injectable-react": "9.0.0",
|
||||||
"@sentry/electron": "^3.0.7",
|
"@sentry/electron": "^3.0.7",
|
||||||
"@sentry/integrations": "^6.19.3",
|
"@sentry/integrations": "^6.19.3",
|
||||||
"@side/jest-runtime": "^1.0.1",
|
"@side/jest-runtime": "^1.0.1",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -250,6 +250,7 @@ const testNavigationItemInjectable = getInjectable({
|
|||||||
return {
|
return {
|
||||||
id: "some-test-preference-navigation-item-id",
|
id: "some-test-preference-navigation-item-id",
|
||||||
label: "Some preference navigation item",
|
label: "Some preference navigation item",
|
||||||
|
parent: "general",
|
||||||
isActive: routeIsActive,
|
isActive: routeIsActive,
|
||||||
isVisible: testRoute.isEnabled,
|
isVisible: testRoute.isEnabled,
|
||||||
navigate: navigateToPreferenceTab(testRoute),
|
navigate: navigateToPreferenceTab(testRoute),
|
||||||
|
|||||||
@ -6,8 +6,13 @@ import type { RenderResult } from "@testing-library/react";
|
|||||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import React from "react";
|
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 { 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", () => {
|
describe("preferences - navigation to extension specific preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -43,10 +48,65 @@ describe("preferences - navigation to extension specific preferences", () => {
|
|||||||
expect(actual).toBeNull();
|
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", () => {
|
describe("when extension with specific preferences is enabled", () => {
|
||||||
|
let testExtension: TestExtension;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
|
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
|
||||||
const testExtension = getRendererExtensionFake(extensionStubWithExtensionSpecificPreferenceItems);
|
|
||||||
|
testExtension = getRendererExtensionFake(
|
||||||
|
extensionStubWithExtensionSpecificPreferenceItems,
|
||||||
|
);
|
||||||
|
|
||||||
applicationBuilder.extensions.renderer.enable(testExtension);
|
applicationBuilder.extensions.renderer.enable(testExtension);
|
||||||
});
|
});
|
||||||
@ -56,20 +116,32 @@ describe("preferences - navigation to extension specific preferences", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("shows link for extension 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();
|
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", () => {
|
describe("when navigating to extension preferences using navigation", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
applicationBuilder.preferences.navigation.click("extensions");
|
applicationBuilder.preferences.navigation.click("extension-some-test-extension-id");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders", () => {
|
it("renders", () => {
|
||||||
expect(rendered.container).toMatchSnapshot();
|
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", () => {
|
it("shows extension specific preferences", () => {
|
||||||
const page = rendered.getByTestId("extension-preferences-page");
|
const page = rendered.getByTestId("extension-preferences-page");
|
||||||
|
|
||||||
@ -87,14 +159,234 @@ describe("preferences - navigation to extension specific preferences", () => {
|
|||||||
|
|
||||||
expect(actual).toBeNull();
|
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 = {
|
const extensionStubWithExtensionSpecificPreferenceItems: FakeExtensionData = {
|
||||||
id: "some-extension-id",
|
id: "some-test-extension-id",
|
||||||
name: "some-extension-name",
|
name: "some-test-extension-id",
|
||||||
appPreferences: [
|
appPreferences: [
|
||||||
{
|
{
|
||||||
title: "Some preference item",
|
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: () => <div data-testid="some-other-preference-item-hint" />,
|
||||||
|
Input: () => <div data-testid="some-other-preference-item-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: () => <div data-testid="very-other-preference-item-hint" />,
|
||||||
|
Input: () => <div data-testid="very-other-preference-item-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: () => <div data-testid="metrics-preference-item-hint" />,
|
||||||
|
Input: () => <div data-testid="metrics-preference-item-input" />,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Menu item",
|
||||||
|
id: "menu-preference-item-id",
|
||||||
|
showInPreferencesTab: "menu-extension-tab",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Hint: () => <div data-testid="menu-preference-item-hint" />,
|
||||||
|
Input: () => <div data-testid="menu-preference-item-input" />,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Survey item",
|
||||||
|
id: "survey-preference-item-id",
|
||||||
|
showInPreferencesTab: "survey-extension-tab",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Hint: () => <div data-testid="survey-preference-item-hint" />,
|
||||||
|
Input: () => <div data-testid="survey-preference-item-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: () => <div data-testid="hello-preference-item-hint" />,
|
||||||
|
Input: () => <div data-testid="hello-preference-item-input" />,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Logs",
|
||||||
|
id: "logs-preference-item-id",
|
||||||
|
showInPreferencesTab: "logs-extension-tab",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Hint: () => <div data-testid="logs-preference-item-hint" />,
|
||||||
|
Input: () => <div data-testid="logs-preference-item-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: () => <div data-testid="another-metrics-preference-item-hint" />,
|
||||||
|
Input: () => <div data-testid="another-metrics-preference-item-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: () => <div data-testid="my-preferences-item-hint" />,
|
||||||
|
Input: () => <div data-testid="my-preferences-item-input" />,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|||||||
@ -5,12 +5,18 @@
|
|||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { computed } from "mobx";
|
import { computed } from "mobx";
|
||||||
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
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({
|
const extensionPreferencesRouteInjectable = getInjectable({
|
||||||
id: "extension-preferences-route",
|
id: "extension-preferences-route",
|
||||||
|
|
||||||
instantiate: () => ({
|
instantiate: (): Route<ExtensionPreferenceRouteParams> => ({
|
||||||
path: "/preferences/extensions",
|
path: "/preferences/extension/:extensionId/:tabId?",
|
||||||
clusterFrame: false,
|
clusterFrame: false,
|
||||||
isEnabled: computed(() => true),
|
isEnabled: computed(() => true),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -13,7 +13,10 @@ const navigateToExtensionPreferencesInjectable = getInjectable({
|
|||||||
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
|
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
|
||||||
const route = di.inject(extensionPreferencesRouteInjectable);
|
const route = di.inject(extensionPreferencesRouteInjectable);
|
||||||
|
|
||||||
return () => navigateToRoute(route);
|
return (extensionId: string, tabId?: string) => navigateToRoute(route, { parameters: {
|
||||||
|
extensionId,
|
||||||
|
tabId,
|
||||||
|
}});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ describe("runManyFor", () => {
|
|||||||
let actualPromise: Promise<void>;
|
let actualPromise: Promise<void>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const rootDi = createContainer();
|
const rootDi = createContainer("irrelevant");
|
||||||
|
|
||||||
runMock = asyncFn();
|
runMock = asyncFn();
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ describe("runManyFor", () => {
|
|||||||
let actualPromise: Promise<void>;
|
let actualPromise: Promise<void>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const di = createContainer();
|
const di = createContainer("irrelevant");
|
||||||
|
|
||||||
runMock = asyncFn();
|
runMock = asyncFn();
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ describe("runManyFor", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("given invalid hierarchy, when running runnables, throws", () => {
|
it("given invalid hierarchy, when running runnables, throws", () => {
|
||||||
const rootDi = createContainer();
|
const rootDi = createContainer("irrelevant");
|
||||||
|
|
||||||
const runMock = asyncFn<(...args: unknown[]) => void>();
|
const runMock = asyncFn<(...args: unknown[]) => void>();
|
||||||
|
|
||||||
@ -218,7 +218,7 @@ describe("runManyFor", () => {
|
|||||||
let runMock: AsyncFnMock<(...args: unknown[]) => Promise<void>>;
|
let runMock: AsyncFnMock<(...args: unknown[]) => Promise<void>>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const rootDi = createContainer();
|
const rootDi = createContainer("irrelevant");
|
||||||
|
|
||||||
runMock = asyncFn();
|
runMock = asyncFn();
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ describe("runManySyncFor", () => {
|
|||||||
let runMock: jest.Mock;
|
let runMock: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const rootDi = createContainer();
|
const rootDi = createContainer("irrelevant");
|
||||||
|
|
||||||
runMock = jest.fn();
|
runMock = jest.fn();
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ describe("runManySyncFor", () => {
|
|||||||
let runMock: jest.Mock<(arg: string) => void>;
|
let runMock: jest.Mock<(arg: string) => void>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const di = createContainer();
|
const di = createContainer("irrelevant");
|
||||||
|
|
||||||
runMock = jest.fn();
|
runMock = jest.fn();
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ describe("runManySyncFor", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("given invalid hierarchy, when running runnables, throws", () => {
|
it("given invalid hierarchy, when running runnables, throws", () => {
|
||||||
const rootDi = createContainer();
|
const rootDi = createContainer("irrelevant");
|
||||||
|
|
||||||
const runMock = jest.fn();
|
const runMock = jest.fn();
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ describe("runManySyncFor", () => {
|
|||||||
let runMock: jest.Mock<(arg: string, arg2: string) => void>;
|
let runMock: jest.Mock<(arg: string, arg2: string) => void>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const rootDi = createContainer();
|
const rootDi = createContainer("irrelevant");
|
||||||
|
|
||||||
runMock = jest.fn();
|
runMock = jest.fn();
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ describe("asLegacyGlobalObjectForExtensionApiWithModifications", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
di = createContainer();
|
di = createContainer("irrelevant");
|
||||||
|
|
||||||
jest.spyOn(di, "inject");
|
jest.spyOn(di, "inject");
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import { pipeline } from "@ogre-tools/fp";
|
|||||||
import { getExtensionRoutePath } from "../renderer/routes/for-extension";
|
import { getExtensionRoutePath } from "../renderer/routes/for-extension";
|
||||||
import type { LensRendererExtensionDependencies } from "./lens-extension-set-dependencies";
|
import type { LensRendererExtensionDependencies } from "./lens-extension-set-dependencies";
|
||||||
import type { KubeObjectHandlerRegistration } from "../renderer/kube-object/handler";
|
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<LensRendererExtensionDependencies> {
|
export class LensRendererExtension extends LensExtension<LensRendererExtensionDependencies> {
|
||||||
globalPages: registries.PageRegistration[] = [];
|
globalPages: registries.PageRegistration[] = [];
|
||||||
@ -33,6 +34,7 @@ export class LensRendererExtension extends LensExtension<LensRendererExtensionDe
|
|||||||
clusterPageMenus: registries.ClusterPageMenuRegistration[] = [];
|
clusterPageMenus: registries.ClusterPageMenuRegistration[] = [];
|
||||||
kubeObjectStatusTexts: KubeObjectStatusRegistration[] = [];
|
kubeObjectStatusTexts: KubeObjectStatusRegistration[] = [];
|
||||||
appPreferences: AppPreferenceRegistration[] = [];
|
appPreferences: AppPreferenceRegistration[] = [];
|
||||||
|
appPreferenceTabs: AppPreferenceTabRegistration[] = [];
|
||||||
entitySettings: registries.EntitySettingRegistration[] = [];
|
entitySettings: registries.EntitySettingRegistration[] = [];
|
||||||
statusBarItems: StatusBarRegistration[] = [];
|
statusBarItems: StatusBarRegistration[] = [];
|
||||||
kubeObjectDetailItems: registries.KubeObjectDetailRegistration[] = [];
|
kubeObjectDetailItems: registries.KubeObjectDetailRegistration[] = [];
|
||||||
|
|||||||
@ -8,7 +8,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";
|
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||||
|
|
||||||
export const getDi = () => {
|
export const getDi = () => {
|
||||||
const di = createContainer();
|
const di = createContainer("main");
|
||||||
|
|
||||||
registerMobX(di);
|
registerMobX(di);
|
||||||
|
|
||||||
|
|||||||
@ -105,7 +105,7 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
|
|||||||
doGeneralOverrides = false,
|
doGeneralOverrides = false,
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
const di = createContainer();
|
const di = createContainer("main");
|
||||||
|
|
||||||
registerMobX(di);
|
registerMobX(di);
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ import { filter, map } from "lodash/fp";
|
|||||||
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
|
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
import { extensionPreferenceItemInjectionToken } from "./extension-preference-items.injectable";
|
import { extensionPreferenceItemInjectionToken } from "./extension-preference-items-injection-token";
|
||||||
|
|
||||||
const extensionPreferenceItemRegistratorInjectable = getInjectable({
|
const extensionPreferenceItemRegistratorInjectable = getInjectable({
|
||||||
id: "extension-preference-item-registrator",
|
id: "extension-preference-item-registrator",
|
||||||
@ -34,6 +34,7 @@ const extensionPreferenceItemRegistratorInjectable = getInjectable({
|
|||||||
instantiate: () => ({
|
instantiate: () => ({
|
||||||
id: registration.id || id,
|
id: registration.id || id,
|
||||||
title: registration.title,
|
title: registration.title,
|
||||||
|
extension,
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Hint: registration.components.Hint,
|
Hint: registration.components.Hint,
|
||||||
|
|||||||
@ -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<ExtensionPreferenceItem>({
|
||||||
|
id: "extension-preference-item-injection-token",
|
||||||
|
});
|
||||||
|
|
||||||
@ -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;
|
||||||
@ -8,36 +8,55 @@ import type { IComputedValue } from "mobx";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||||
|
import extensionPreferencesModelInjectable from "./extension-preference-model.injectable";
|
||||||
import { ExtensionSettings } from "./extension-settings";
|
import { ExtensionSettings } from "./extension-settings";
|
||||||
import { Preferences } from "./preferences";
|
import { Preferences } from "./preferences";
|
||||||
import extensionsPreferenceItemsInjectable from "./extension-preference-items.injectable";
|
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
preferenceItems: IComputedValue<RegisteredAppPreference[]>;
|
model: IComputedValue<{
|
||||||
|
preferenceItems: RegisteredAppPreference[];
|
||||||
|
extensionName?: string;
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NonInjectedExtensions = ({ preferenceItems }: Dependencies) => (
|
const NonInjectedExtensions = ({ model }: Dependencies) => {
|
||||||
<Preferences data-testid="extension-preferences-page">
|
const { extensionName, preferenceItems } = model.get();
|
||||||
<section id="extensions">
|
|
||||||
<h2>Extensions</h2>
|
return (
|
||||||
{preferenceItems.get().map((preferenceItem) => (
|
<Preferences data-testid="extension-preferences-page">
|
||||||
<ExtensionSettings
|
<section id="extensions">
|
||||||
key={preferenceItem.id}
|
<h2>
|
||||||
setting={preferenceItem}
|
{extensionName}
|
||||||
size="small"
|
{" "}
|
||||||
data-testid={`extension-preference-item-for-${preferenceItem.id}`}
|
preferences
|
||||||
/>
|
</h2>
|
||||||
))}
|
{!extensionName && (
|
||||||
</section>
|
<div
|
||||||
</Preferences>
|
className="flex items-center"
|
||||||
);
|
data-testid="error-for-extension-not-being-present"
|
||||||
|
>
|
||||||
|
No extension found
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{preferenceItems.map((preferenceItem, index) => (
|
||||||
|
<ExtensionSettings
|
||||||
|
key={`${preferenceItem.id}-${index}`}
|
||||||
|
setting={preferenceItem}
|
||||||
|
size="small"
|
||||||
|
data-testid={`extension-preference-item-for-${preferenceItem.id}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
</Preferences>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const Extensions = withInjectables<Dependencies>(
|
export const Extensions = withInjectables<Dependencies>(
|
||||||
observer(NonInjectedExtensions),
|
observer(NonInjectedExtensions),
|
||||||
|
|
||||||
{
|
{
|
||||||
getProps: (di) => ({
|
getProps: (di) => ({
|
||||||
preferenceItems: di.inject(extensionsPreferenceItemsInjectable),
|
model: di.inject(extensionPreferencesModelInjectable),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
@ -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",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
@ -24,6 +24,7 @@ const applicationPreferencesNavigationItemInjectable = getInjectable({
|
|||||||
return {
|
return {
|
||||||
id: "application",
|
id: "application",
|
||||||
label: "App",
|
label: "App",
|
||||||
|
parent: "general",
|
||||||
navigate: navigateToPreferenceTab(route),
|
navigate: navigateToPreferenceTab(route),
|
||||||
isActive: routeIsActive,
|
isActive: routeIsActive,
|
||||||
isVisible: computed(() => true),
|
isVisible: computed(() => true),
|
||||||
|
|||||||
@ -24,6 +24,7 @@ const editorPreferencesNavigationItemInjectable = getInjectable({
|
|||||||
return {
|
return {
|
||||||
id: "editor",
|
id: "editor",
|
||||||
label: "Editor",
|
label: "Editor",
|
||||||
|
parent: "general",
|
||||||
navigate: navigateToPreferenceTab(route),
|
navigate: navigateToPreferenceTab(route),
|
||||||
isActive: routeIsActive,
|
isActive: routeIsActive,
|
||||||
isVisible: computed(() => true),
|
isVisible: computed(() => true),
|
||||||
|
|||||||
@ -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;
|
||||||
@ -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<PreferenceNavigationItem[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedExtensionsNavGroup = observer((props: Dependencies) => {
|
||||||
|
if (!props.navigationItems.get().length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-testid="extension-settings">
|
||||||
|
<hr/>
|
||||||
|
<div className="header flex items-center">
|
||||||
|
<Icon
|
||||||
|
material="extension"
|
||||||
|
smallest
|
||||||
|
className="mr-3"
|
||||||
|
/>
|
||||||
|
{" "}
|
||||||
|
Extensions
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{props.navigationItems.get().map(item => (
|
||||||
|
<PreferencesNavigationTab
|
||||||
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
data-testid={`tab-link-for-${item.id}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ExtensionsNavGroup = withInjectables<Dependencies>(
|
||||||
|
NonInjectedExtensionsNavGroup,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di) => ({
|
||||||
|
navigationItems: di.inject(preferenceNavigationItemsForGroupInjectable, "extensions"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -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;
|
||||||
@ -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;
|
|
||||||
@ -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<PreferenceNavigationItem[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedGeneralNavGroup = observer((props: Dependencies) => {
|
||||||
|
if (!props.navigationItems.get().length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="header">Preferences</div>
|
||||||
|
|
||||||
|
{props.navigationItems.get().map(item => (
|
||||||
|
<PreferencesNavigationTab
|
||||||
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
data-testid={`tab-link-for-${item.id}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GeneralNavGroup = withInjectables<Dependencies>(
|
||||||
|
NonInjectedGeneralNavGroup,
|
||||||
|
|
||||||
|
{
|
||||||
|
getProps: (di) => ({
|
||||||
|
navigationItems: di.inject(preferenceNavigationItemsForGroupInjectable, "general"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -27,6 +27,7 @@ const kubernetesPreferencesNavigationItemInjectable = getInjectable({
|
|||||||
return {
|
return {
|
||||||
id: "kubernetes",
|
id: "kubernetes",
|
||||||
label: "Kubernetes",
|
label: "Kubernetes",
|
||||||
|
parent: "general",
|
||||||
navigate: navigateToPreferenceTab(route),
|
navigate: navigateToPreferenceTab(route),
|
||||||
isActive: routeIsActive,
|
isActive: routeIsActive,
|
||||||
isVisible: computed(() => true),
|
isVisible: computed(() => true),
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||||
|
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
|
||||||
import { filter, orderBy } from "lodash/fp";
|
import { filter, orderBy } from "lodash/fp";
|
||||||
import type { IComputedValue } from "mobx";
|
import type { IComputedValue } from "mobx";
|
||||||
import { computed } from "mobx";
|
import { computed } from "mobx";
|
||||||
@ -20,19 +21,24 @@ export interface PreferenceNavigationItem {
|
|||||||
isVisible: IComputedValue<boolean>;
|
isVisible: IComputedValue<boolean>;
|
||||||
navigate: () => void;
|
navigate: () => void;
|
||||||
orderNumber: number;
|
orderNumber: number;
|
||||||
|
parent: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const preferenceNavigationItemsInjectable = getInjectable({
|
const preferenceNavigationItemsInjectable = getInjectable({
|
||||||
id: "preference-navigation-items",
|
id: "preference-navigation-items",
|
||||||
|
|
||||||
instantiate: (di) =>
|
instantiate: (di) => {
|
||||||
computed((): PreferenceNavigationItem[] =>
|
const computedInjectMany = di.inject(computedInjectManyInjectable);
|
||||||
|
const navigationItems = computedInjectMany(preferenceNavigationItemInjectionToken);
|
||||||
|
|
||||||
|
return computed((): PreferenceNavigationItem[] =>
|
||||||
pipeline(
|
pipeline(
|
||||||
di.injectMany(preferenceNavigationItemInjectionToken),
|
navigationItems.get(),
|
||||||
filter((item) => !!item.isVisible.get()),
|
filter((item) => !!item.isVisible.get()),
|
||||||
(items) => orderBy([(item) => item.orderNumber], ["asc"], items),
|
(items) => orderBy([(item) => item.orderNumber], ["asc"], items),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default preferenceNavigationItemsInjectable;
|
export default preferenceNavigationItemsInjectable;
|
||||||
|
|||||||
@ -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<HTMLElement> {
|
||||||
|
item: PreferenceNavigationItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PreferencesNavigationTab = observer(({ item }: PreferenceNavigationTabProps) => (
|
||||||
|
<Tab
|
||||||
|
value={item}
|
||||||
|
label={item.label}
|
||||||
|
data-testid={`tab-link-for-${item.id}`}
|
||||||
|
active={item.isActive.get()}
|
||||||
|
/>
|
||||||
|
));
|
||||||
@ -2,61 +2,23 @@
|
|||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* 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 React from "react";
|
||||||
|
import { Tabs } from "../../tabs";
|
||||||
|
import { ExtensionsNavGroup } from "./extensions-nav-group";
|
||||||
|
import { GeneralNavGroup } from "./general-nav-group";
|
||||||
import type {
|
import type {
|
||||||
PreferenceNavigationItem,
|
PreferenceNavigationItem,
|
||||||
} from "./preference-navigation-items.injectable";
|
} from "./preference-navigation-items.injectable";
|
||||||
import preferenceNavigationItemsInjectable from "./preference-navigation-items.injectable";
|
|
||||||
|
|
||||||
import { observer } from "mobx-react";
|
export const PreferencesNavigation = () => {
|
||||||
|
return (
|
||||||
interface Dependencies {
|
<Tabs
|
||||||
navigationItems: IComputedValue<PreferenceNavigationItem[]>;
|
className="flex column"
|
||||||
}
|
scrollable={false}
|
||||||
|
onChange={(item: PreferenceNavigationItem) => item.navigate()}
|
||||||
const NonInjectedPreferencesNavigation = ({
|
>
|
||||||
navigationItems,
|
<GeneralNavGroup/>
|
||||||
}: Dependencies) => (
|
<ExtensionsNavGroup/>
|
||||||
<Tabs
|
</Tabs>
|
||||||
className="flex column"
|
);
|
||||||
scrollable={false}
|
};
|
||||||
onChange={(item: PreferenceNavigationItem) => item.navigate()}
|
|
||||||
>
|
|
||||||
<div className="header">Preferences</div>
|
|
||||||
|
|
||||||
{navigationItems.get().map((item) => (
|
|
||||||
<PreferencesNavigationTab
|
|
||||||
key={item.id}
|
|
||||||
item={item}
|
|
||||||
data-testid={`tab-link-for-${item.id}`}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Tabs>
|
|
||||||
);
|
|
||||||
|
|
||||||
interface PreferenceNavigationTabProps extends React.DOMAttributes<HTMLElement> {
|
|
||||||
item: PreferenceNavigationItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PreferencesNavigationTab = observer(({ item }: PreferenceNavigationTabProps) => (
|
|
||||||
<Tab
|
|
||||||
value={item}
|
|
||||||
label={item.label}
|
|
||||||
data-testid={`tab-link-for-${item.id}`}
|
|
||||||
active={item.isActive.get()}
|
|
||||||
/>
|
|
||||||
));
|
|
||||||
|
|
||||||
export const PreferencesNavigation = withInjectables<Dependencies>(
|
|
||||||
NonInjectedPreferencesNavigation,
|
|
||||||
|
|
||||||
{
|
|
||||||
getProps: (di) => ({
|
|
||||||
navigationItems: di.inject(preferenceNavigationItemsInjectable),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ const proxyPreferencesNavigationItemInjectable = getInjectable({
|
|||||||
return {
|
return {
|
||||||
id: "proxy",
|
id: "proxy",
|
||||||
label: "Proxy",
|
label: "Proxy",
|
||||||
|
parent: "general",
|
||||||
navigate: navigateToPreferenceTab(route),
|
navigate: navigateToPreferenceTab(route),
|
||||||
isActive: routeIsActive,
|
isActive: routeIsActive,
|
||||||
isVisible: computed(() => true),
|
isVisible: computed(() => true),
|
||||||
|
|||||||
@ -32,6 +32,7 @@ const terminalPreferencesNavigationItemInjectable = getInjectable({
|
|||||||
return {
|
return {
|
||||||
id: "telemetry",
|
id: "telemetry",
|
||||||
label: "Telemetry",
|
label: "Telemetry",
|
||||||
|
parent: "general",
|
||||||
navigate: navigateToPreferenceTab(route),
|
navigate: navigateToPreferenceTab(route),
|
||||||
isActive: routeIsActive,
|
isActive: routeIsActive,
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ const terminalPreferencesNavigationItemInjectable = getInjectable({
|
|||||||
return {
|
return {
|
||||||
id: "terminal",
|
id: "terminal",
|
||||||
label: "Terminal",
|
label: "Terminal",
|
||||||
|
parent: "general",
|
||||||
navigate: navigateToPreferenceTab(route),
|
navigate: navigateToPreferenceTab(route),
|
||||||
isActive: routeIsActive,
|
isActive: routeIsActive,
|
||||||
isVisible: computed(() => true),
|
isVisible: computed(() => true),
|
||||||
|
|||||||
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 218px;
|
width: 218px;
|
||||||
padding: 60px 0 60px 20px;
|
padding: 60px 10px 60px 20px;
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
@ -52,10 +52,6 @@
|
|||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
border-top: thin solid var(--hrColor);
|
border-top: thin solid var(--hrColor);
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.Tabs {
|
.Tabs {
|
||||||
@ -63,7 +59,6 @@
|
|||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
line-height: 16px;
|
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--textColorPrimary);
|
color: var(--textColorPrimary);
|
||||||
|
|
||||||
@ -76,14 +71,10 @@
|
|||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-top-right-radius: 0;
|
white-space: normal;
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: none;
|
content: none;
|
||||||
|
|||||||
@ -55,6 +55,9 @@ import { openMenu } from "react-select-event";
|
|||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { StatusBar } from "../status-bar/status-bar";
|
import { StatusBar } from "../status-bar/status-bar";
|
||||||
import lensProxyPortInjectable from "../../../main/lens-proxy/lens-proxy-port.injectable";
|
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 type { LensMainExtension } from "../../../extensions/lens-main-extension";
|
||||||
import trayMenuItemsInjectable from "../../../main/tray/tray-menu-item/tray-menu-items.injectable";
|
import trayMenuItemsInjectable from "../../../main/tray/tray-menu-item/tray-menu-items.injectable";
|
||||||
import type { LensExtension } from "../../../extensions/lens-extension";
|
import type { LensExtension } from "../../../extensions/lens-extension";
|
||||||
@ -100,6 +103,7 @@ export interface ApplicationBuilder {
|
|||||||
preferences: {
|
preferences: {
|
||||||
close: () => void;
|
close: () => void;
|
||||||
navigate: () => void;
|
navigate: () => void;
|
||||||
|
navigateTo: (route: Route<any>, params: Partial<NavigateToRouteOptions<any>>) => void;
|
||||||
navigation: {
|
navigation: {
|
||||||
click: (id: string) => void;
|
click: (id: string) => void;
|
||||||
};
|
};
|
||||||
@ -332,6 +336,12 @@ export const getApplicationBuilder = () => {
|
|||||||
navigateToPreferences();
|
navigateToPreferences();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
navigateTo: (route: Route<any>, params: Partial<NavigateToRouteOptions<any>>) => {
|
||||||
|
const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken);
|
||||||
|
|
||||||
|
navigateToRoute(route, params);
|
||||||
|
},
|
||||||
|
|
||||||
navigation: {
|
navigation: {
|
||||||
click: (id: string) => {
|
click: (id: string) => {
|
||||||
const link = rendered.queryByTestId(`tab-link-for-${id}`);
|
const link = rendered.queryByTestId(`tab-link-for-${id}`);
|
||||||
@ -546,7 +556,7 @@ const disableExtensionsFor = <T extends ObservableSet>(
|
|||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
extension.deregister();
|
extension.deregister();
|
||||||
|
|
||||||
extensionState.delete(extension);
|
extensionState.delete(instance);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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";
|
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||||
|
|
||||||
export const getDi = () => {
|
export const getDi = () => {
|
||||||
const di = createContainer();
|
const di = createContainer("renderer");
|
||||||
|
|
||||||
registerMobX(di);
|
registerMobX(di);
|
||||||
|
|
||||||
|
|||||||
@ -58,7 +58,7 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {})
|
|||||||
doGeneralOverrides = false,
|
doGeneralOverrides = false,
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
const di = createContainer();
|
const di = createContainer("renderer");
|
||||||
|
|
||||||
registerMobX(di);
|
registerMobX(di);
|
||||||
|
|
||||||
|
|||||||
54
yarn.lock
54
yarn.lock
@ -1189,46 +1189,46 @@
|
|||||||
"@nodelib/fs.scandir" "2.1.5"
|
"@nodelib/fs.scandir" "2.1.5"
|
||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
"@ogre-tools/fp@8.0.0", "@ogre-tools/fp@^8.0.0":
|
"@ogre-tools/fp@9.0.0", "@ogre-tools/fp@^9.0.0":
|
||||||
version "8.0.0"
|
version "9.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@ogre-tools/fp/-/fp-8.0.0.tgz#dd2319a96ce3d2edd3ee2bc2acca07a94a77bf3b"
|
resolved "https://registry.yarnpkg.com/@ogre-tools/fp/-/fp-9.0.0.tgz#926cd4f13b52961156161feeeafddf22a0ad39c0"
|
||||||
integrity sha512-8OpGUbG3avGtx6ASz3XNnK/KCyPW25RPp8oYzzU0zihKU5D4QKHy9qNkQ3npurzeg4d1k4BsgkeX+805nqtZOA==
|
integrity sha512-kMUgzhdjHuph0UWteOfyXNGBavZJX23NOA5su6fx9NdTzWhl9yB5Uf6Q//nOvL9COftjZDwnAgIaDU4MPdjyqA==
|
||||||
dependencies:
|
dependencies:
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
|
|
||||||
"@ogre-tools/injectable-extension-for-auto-registration@8.0.0":
|
"@ogre-tools/injectable-extension-for-auto-registration@9.0.0":
|
||||||
version "8.0.0"
|
version "9.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-auto-registration/-/injectable-extension-for-auto-registration-8.0.0.tgz#3a443f1f1c9b564baa78cca6a3c81ac4102660b0"
|
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-auto-registration/-/injectable-extension-for-auto-registration-9.0.0.tgz#69463737ef4f7777db4703964b8a72a5fb82d6b3"
|
||||||
integrity sha512-DX1bxn8mDwek+W/SaI5WmDHmkY3B3njs3X4pOvqRtiMis3GaWHzeCZeK3q3Iv5cd14FTW8AsfKtujPmLklNf/A==
|
integrity sha512-+3I9Z0GfA04zZoj7Nw5WhJLDFLJgr5xv8Kp1zPDuT9/OvE9EA6hzAqakMDLbvn1zZOJjkJCGk44x6UjSQJp/9w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ogre-tools/fp" "^8.0.0"
|
"@ogre-tools/fp" "^9.0.0"
|
||||||
"@ogre-tools/injectable" "^8.0.0"
|
"@ogre-tools/injectable" "^9.0.0"
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
|
|
||||||
"@ogre-tools/injectable-extension-for-mobx@8.0.0":
|
"@ogre-tools/injectable-extension-for-mobx@9.0.0":
|
||||||
version "8.0.0"
|
version "9.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-mobx/-/injectable-extension-for-mobx-8.0.0.tgz#80ff506011e078050dd8dcb72660d17181db6d34"
|
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-extension-for-mobx/-/injectable-extension-for-mobx-9.0.0.tgz#ed14df39b266e521272977821d3e05bcbd647577"
|
||||||
integrity sha512-m8gU3cEFHl9IMZLcvvoS7hVxS6p6nG3jdf6fY6MUZE0u9hx4bZuUdWYoylGQizy0FyRFQ2/m5xhTH2VdtXqx8w==
|
integrity sha512-9Hrtr7AdibcD+Fqn2qNsjiOUakAACO55TB1IqNsOJMMuqQHVB5SFZTHBzdVRqqqY2MwQvWYvd4xfy+beItD/xw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ogre-tools/fp" "^8.0.0"
|
"@ogre-tools/fp" "^9.0.0"
|
||||||
"@ogre-tools/injectable" "^8.0.0"
|
"@ogre-tools/injectable" "^9.0.0"
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
|
|
||||||
"@ogre-tools/injectable-react@8.0.0":
|
"@ogre-tools/injectable-react@9.0.0":
|
||||||
version "8.0.0"
|
version "9.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-8.0.0.tgz#b2d8db4bb697ba2822d71ec73e6cabee30f70f34"
|
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-9.0.0.tgz#c5c510e893a5c1d7994d8709f70606967cabdec2"
|
||||||
integrity sha512-rZa38fm3UzGa/09qC765Za8xJiSPOYHJGsob8UOt2JQIt/BecTNXPMHexfxy9W+DIdAer+YruUgedChdk9nvdQ==
|
integrity sha512-vGQrwkcWibRUWFPbu392riBYY4dXK051FxwyMsDYNRqmvaLo8HuumwjzS1DWS7db/P9Li+Kc+Ms670xIZepcpA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ogre-tools/fp" "^8.0.0"
|
"@ogre-tools/fp" "^9.0.0"
|
||||||
"@ogre-tools/injectable" "^8.0.0"
|
"@ogre-tools/injectable" "^9.0.0"
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
|
|
||||||
"@ogre-tools/injectable@8.0.0", "@ogre-tools/injectable@^8.0.0":
|
"@ogre-tools/injectable@9.0.0", "@ogre-tools/injectable@^9.0.0":
|
||||||
version "8.0.0"
|
version "9.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-8.0.0.tgz#ea4f98bd2466149add94d4f6a9beb7be03729da7"
|
resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-9.0.0.tgz#0819bc7b5fbae0a467f3250b10b4adb003268067"
|
||||||
integrity sha512-59p+8uGqwVQ5IpGpgfn3RA+wXzn1tjnPdFWO3GLEgjyp5dWBaMKufCpCFRvvb9sP6B68qo19aLfy/uSm4AXduw==
|
integrity sha512-z9X86Q9AEkkilLu9V33j/aXv/IUoG944AdfN6WX2zZgJqRNjESN9spoOMqdKqib6JmEjCRxpMvaMwHLQSh14fg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ogre-tools/fp" "^8.0.0"
|
"@ogre-tools/fp" "^9.0.0"
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
|
|
||||||
"@panva/asn1.js@^1.0.0":
|
"@panva/asn1.js@^1.0.0":
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user