mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Make ThemeStore non-singleton and fix navigation-to-terminal-preferences.test.ts
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
48d5aeb18c
commit
955aa28bc8
@ -270,16 +270,11 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
This setting is to change the registry URL for installing extensions by name.
|
||||||
|
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
|
|||||||
@ -12,8 +12,8 @@ import { routeInjectionToken } from "../../common/front-end-routing/route-inject
|
|||||||
import { computed } from "mobx";
|
import { computed } from "mobx";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
import type { ThemeStore } from "../../renderer/themes/store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
import themeStoreInjectable from "../../renderer/themes/store.injectable";
|
||||||
import { preferenceNavigationItemInjectionToken } from "../../renderer/components/+preferences/preferences-navigation/preference-navigation-items.injectable";
|
import { preferenceNavigationItemInjectionToken } from "../../renderer/components/+preferences/preferences-navigation/preference-navigation-items.injectable";
|
||||||
import routeIsActiveInjectable from "../../renderer/routes/route-is-active.injectable";
|
import routeIsActiveInjectable from "../../renderer/routes/route-is-active.injectable";
|
||||||
import { Preferences } from "../../renderer/components/+preferences";
|
import { Preferences } from "../../renderer/components/+preferences";
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
import themeStoreInjectable from "../../renderer/themes/store.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
import type { ThemeStore } from "../../renderer/themes/store";
|
||||||
import navigateToProxyPreferencesInjectable
|
import navigateToProxyPreferencesInjectable
|
||||||
from "../../common/front-end-routing/routes/preferences/proxy/navigate-to-proxy-preferences.injectable";
|
from "../../common/front-end-routing/routes/preferences/proxy/navigate-to-proxy-preferences.injectable";
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
import themeStoreInjectable from "../../renderer/themes/store.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
import type { ThemeStore } from "../../renderer/themes/store";
|
||||||
|
|
||||||
describe("preferences - navigation to editor preferences", () => {
|
describe("preferences - navigation to editor preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
|||||||
@ -7,9 +7,9 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
import themeStoreInjectable from "../../renderer/themes/store.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
import type { ThemeStore } from "../../renderer/themes/store";
|
||||||
import type { LensRendererExtension } from "../../extensions/lens-renderer-extension";
|
import { LensRendererExtension } from "../../extensions/lens-renderer-extension";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { getRendererExtensionFake } from "../../renderer/components/test-utils/get-renderer-extension-fake";
|
import { getRendererExtensionFake } from "../../renderer/components/test-utils/get-renderer-extension-fake";
|
||||||
|
|
||||||
@ -107,10 +107,8 @@ describe("preferences - navigation to extension specific preferences", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const extensionStubWithExtensionSpecificPreferenceItems: Partial<LensRendererExtension> = {
|
const extensionStubWithExtensionSpecificPreferenceItems = new (class extends LensRendererExtension{
|
||||||
id: "some-test-extension-id",
|
appPreferences = [
|
||||||
|
|
||||||
appPreferences: [
|
|
||||||
{
|
{
|
||||||
title: "Some preference item",
|
title: "Some preference item",
|
||||||
id: "some-preference-item-id",
|
id: "some-preference-item-id",
|
||||||
@ -131,6 +129,17 @@ const extensionStubWithExtensionSpecificPreferenceItems: Partial<LensRendererExt
|
|||||||
Input: () => <div />,
|
Input: () => <div />,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
];
|
||||||
};
|
})({
|
||||||
|
absolutePath: "/some/absolute/path",
|
||||||
|
id: "some-extension-id",
|
||||||
|
isBundled: true,
|
||||||
|
isCompatible: true,
|
||||||
|
isEnabled: true,
|
||||||
|
manifest: {
|
||||||
|
name: "some-extension-name",
|
||||||
|
version: "1.0.0",
|
||||||
|
},
|
||||||
|
manifestPath: "/some/manifest/path",
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
import themeStoreInjectable from "../../renderer/themes/store.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
import type { ThemeStore } from "../../renderer/themes/store";
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
|
|
||||||
describe("preferences - navigation to kubernetes preferences", () => {
|
describe("preferences - navigation to kubernetes preferences", () => {
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
import themeStoreInjectable from "../../renderer/themes/store.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
import type { ThemeStore } from "../../renderer/themes/store";
|
||||||
|
|
||||||
describe("preferences - navigation to proxy preferences", () => {
|
describe("preferences - navigation to proxy preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
|||||||
@ -9,8 +9,8 @@ import { getApplicationBuilder } from "../../renderer/components/test-utils/get-
|
|||||||
import { getRendererExtensionFake } from "../../renderer/components/test-utils/get-renderer-extension-fake";
|
import { getRendererExtensionFake } from "../../renderer/components/test-utils/get-renderer-extension-fake";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
import type { ThemeStore } from "../../renderer/themes/store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
import themeStoreInjectable from "../../renderer/themes/store.injectable";
|
||||||
import navigateToTelemetryPreferencesInjectable from "../../common/front-end-routing/routes/preferences/telemetry/navigate-to-telemetry-preferences.injectable";
|
import navigateToTelemetryPreferencesInjectable from "../../common/front-end-routing/routes/preferences/telemetry/navigate-to-telemetry-preferences.injectable";
|
||||||
import sentryDnsUrlInjectable from "../../renderer/components/+preferences/sentry-dns-url.injectable";
|
import sentryDnsUrlInjectable from "../../renderer/components/+preferences/sentry-dns-url.injectable";
|
||||||
|
|
||||||
|
|||||||
@ -7,10 +7,10 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import defaultShellInjectable from "../../renderer/components/+preferences/default-shell.injectable";
|
import defaultShellInjectable from "../../renderer/components/+preferences/default-shell.injectable";
|
||||||
|
import themeStoreInjectable from "../../renderer/themes/store.injectable";
|
||||||
|
import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||||
|
|
||||||
describe("preferences - navigation to terminal preferences", () => {
|
describe("preferences - navigation to terminal preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -26,12 +26,12 @@ describe("preferences - navigation to terminal preferences", () => {
|
|||||||
} as unknown as UserStore;
|
} as unknown as UserStore;
|
||||||
|
|
||||||
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
||||||
|
|
||||||
rendererDi.override(defaultShellInjectable, () => "some-default-shell");
|
rendererDi.override(defaultShellInjectable, () => "some-default-shell");
|
||||||
|
rendererDi.permitSideEffects(themeStoreInjectable);
|
||||||
const themeStoreStub = ({ themeOptions: [] }) as unknown as ThemeStore;
|
rendererDi.override(ipcRendererInjectable, () => ({
|
||||||
|
on: jest.fn(),
|
||||||
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
|
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
|
||||||
|
} as never));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -9,8 +9,8 @@ import { getApplicationBuilder } from "../../renderer/components/test-utils/get-
|
|||||||
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
|
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
import type { ThemeStore } from "../../renderer/themes/store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
import themeStoreInjectable from "../../renderer/themes/store.injectable";
|
||||||
|
|
||||||
describe("preferences - navigation using application menu", () => {
|
describe("preferences - navigation using application menu", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import userStoreInjectable from "../user-store/user-store.injectable";
|
|||||||
import type { DiContainer } from "@ogre-tools/injectable";
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
import type { ClusterStoreModel } from "../cluster-store/cluster-store";
|
import type { ClusterStoreModel } from "../cluster-store/cluster-store";
|
||||||
import { defaultTheme } from "../vars";
|
import { defaultThemeId } from "../vars";
|
||||||
import writeFileInjectable from "../fs/write-file.injectable";
|
import writeFileInjectable from "../fs/write-file.injectable";
|
||||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||||
import getConfigurationFileModelInjectable
|
import getConfigurationFileModelInjectable
|
||||||
@ -80,7 +80,7 @@ describe("user store tests", () => {
|
|||||||
userStore.httpsProxy = "abcd://defg";
|
userStore.httpsProxy = "abcd://defg";
|
||||||
|
|
||||||
expect(userStore.httpsProxy).toBe("abcd://defg");
|
expect(userStore.httpsProxy).toBe("abcd://defg");
|
||||||
expect(userStore.colorTheme).toBe(defaultTheme);
|
expect(userStore.colorTheme).toBe(defaultThemeId);
|
||||||
|
|
||||||
userStore.colorTheme = "light";
|
userStore.colorTheme = "light";
|
||||||
expect(userStore.colorTheme).toBe("light");
|
expect(userStore.colorTheme).toBe("light");
|
||||||
@ -89,7 +89,7 @@ describe("user store tests", () => {
|
|||||||
it("correctly resets theme to default value", async () => {
|
it("correctly resets theme to default value", async () => {
|
||||||
userStore.colorTheme = "some other theme";
|
userStore.colorTheme = "some other theme";
|
||||||
userStore.resetTheme();
|
userStore.resetTheme();
|
||||||
expect(userStore.colorTheme).toBe(defaultTheme);
|
expect(userStore.colorTheme).toBe(defaultThemeId);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("correctly calculates if the last seen version is an old release", () => {
|
it("correctly calculates if the last seen version is an old release", () => {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { getAppVersion } from "../utils";
|
|||||||
import type { editor } from "monaco-editor";
|
import type { editor } from "monaco-editor";
|
||||||
import merge from "lodash/merge";
|
import merge from "lodash/merge";
|
||||||
import { SemVer } from "semver";
|
import { SemVer } from "semver";
|
||||||
import { defaultTheme, defaultEditorFontFamily, defaultFontSize, defaultTerminalFontFamily } from "../vars";
|
import { defaultThemeId, defaultEditorFontFamily, defaultFontSize, defaultTerminalFontFamily } from "../vars";
|
||||||
import type { ObservableMap } from "mobx";
|
import type { ObservableMap } from "mobx";
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import { readonly } from "../utils/readonly";
|
import { readonly } from "../utils/readonly";
|
||||||
@ -77,10 +77,10 @@ const shell: PreferenceDescription<string | undefined> = {
|
|||||||
|
|
||||||
const colorTheme: PreferenceDescription<string> = {
|
const colorTheme: PreferenceDescription<string> = {
|
||||||
fromStore(val) {
|
fromStore(val) {
|
||||||
return val || defaultTheme;
|
return val || defaultThemeId;
|
||||||
},
|
},
|
||||||
toStore(val) {
|
toStore(val) {
|
||||||
if (!val || val === defaultTheme) {
|
if (!val || val === defaultThemeId) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { SemVer } from "semver";
|
import { SemVer } from "semver";
|
||||||
import packageInfo from "../../package.json";
|
import packageInfo from "../../package.json";
|
||||||
|
import type { ThemeId } from "../renderer/themes/store";
|
||||||
import { lazyInitialized } from "./utils/lazy-initialized";
|
import { lazyInitialized } from "./utils/lazy-initialized";
|
||||||
|
|
||||||
export const isMac = process.platform === "darwin";
|
export const isMac = process.platform === "darwin";
|
||||||
@ -25,7 +26,7 @@ export const isIntegrationTesting = process.argv.includes(integrationTestingArg)
|
|||||||
export const productName = packageInfo.productName;
|
export const productName = packageInfo.productName;
|
||||||
export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`;
|
export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`;
|
||||||
export const publicPath = "/build/" as string;
|
export const publicPath = "/build/" as string;
|
||||||
export const defaultTheme = "lens-dark" as string;
|
export const defaultThemeId: ThemeId = "lens-dark";
|
||||||
export const defaultFontSize = 12;
|
export const defaultFontSize = 12;
|
||||||
export const defaultTerminalFontFamily = "RobotoMono";
|
export const defaultTerminalFontFamily = "RobotoMono";
|
||||||
export const defaultEditorFontFamily = "RobotoMono";
|
export const defaultEditorFontFamily = "RobotoMono";
|
||||||
|
|||||||
@ -3,8 +3,11 @@
|
|||||||
* 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 { ThemeStore } from "../../renderer/theme.store";
|
import themeStoreInjectable from "../../renderer/themes/store.injectable";
|
||||||
|
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||||
|
|
||||||
|
const themeStore = asLegacyGlobalForExtensionApi(themeStoreInjectable);
|
||||||
|
|
||||||
export function getActiveTheme() {
|
export function getActiveTheme() {
|
||||||
return ThemeStore.getInstance().activeTheme;
|
return themeStore.activeTheme;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,12 +5,16 @@
|
|||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import ipcRendererInjectable from "./ipc-renderer/ipc-renderer.injectable";
|
import ipcRendererInjectable from "./ipc-renderer/ipc-renderer.injectable";
|
||||||
import { getValueFromRegisteredChannel } from "./get-value-from-registered-channel";
|
import { getValueFromRegisteredChannel } from "./get-value-from-registered-channel";
|
||||||
|
import type { Channel } from "../../../common/ipc-channel/channel";
|
||||||
|
|
||||||
|
export type GetValueFromRegisteredChannel = <TChannel extends Channel<TInstance>, TInstance>(channel: TChannel) => Promise<TChannel["_template"]>;
|
||||||
|
|
||||||
const getValueFromRegisteredChannelInjectable = getInjectable({
|
const getValueFromRegisteredChannelInjectable = getInjectable({
|
||||||
id: "get-value-from-registered-channel",
|
id: "get-value-from-registered-channel",
|
||||||
|
|
||||||
instantiate: (di) =>
|
instantiate: (di) => getValueFromRegisteredChannel({
|
||||||
getValueFromRegisteredChannel({ ipcRenderer: di.inject(ipcRendererInjectable) }),
|
ipcRenderer: di.inject(ipcRendererInjectable),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default getValueFromRegisteredChannelInjectable;
|
export default getValueFromRegisteredChannelInjectable;
|
||||||
|
|||||||
@ -9,9 +9,8 @@ interface Dependencies {
|
|||||||
ipcRenderer: IpcRenderer;
|
ipcRenderer: IpcRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getValueFromRegisteredChannel =
|
export const getValueFromRegisteredChannel = ({ ipcRenderer }: Dependencies) =>
|
||||||
({ ipcRenderer }: Dependencies) =>
|
|
||||||
<TChannel extends Channel<TInstance>, TInstance>(
|
<TChannel extends Channel<TInstance>, TInstance>(
|
||||||
channel: TChannel,
|
channel: TChannel,
|
||||||
): Promise<TChannel["_template"]> =>
|
): Promise<TChannel["_template"]> =>
|
||||||
ipcRenderer.invoke(channel.name);
|
ipcRenderer.invoke(channel.name);
|
||||||
|
|||||||
@ -37,7 +37,7 @@ import initClusterFrameInjectable from "./frames/cluster-frame/init-cluster-fram
|
|||||||
import commandOverlayInjectable from "./components/command-palette/command-overlay.injectable";
|
import commandOverlayInjectable from "./components/command-palette/command-overlay.injectable";
|
||||||
import { Router } from "react-router";
|
import { Router } from "react-router";
|
||||||
import historyInjectable from "./navigation/history.injectable";
|
import historyInjectable from "./navigation/history.injectable";
|
||||||
import themeStoreInjectable from "./theme-store.injectable";
|
import themeStoreInjectable from "./themes/store.injectable";
|
||||||
import navigateToAddClusterInjectable from "../common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable";
|
import navigateToAddClusterInjectable from "../common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable";
|
||||||
import addSyncEntriesInjectable from "./initializers/add-sync-entries.injectable";
|
import addSyncEntriesInjectable from "./initializers/add-sync-entries.injectable";
|
||||||
import hotbarStoreInjectable from "../common/hotbars/store.injectable";
|
import hotbarStoreInjectable from "../common/hotbars/store.injectable";
|
||||||
|
|||||||
@ -19,8 +19,6 @@ import catalogEntityStoreInjectable from "./catalog-entity-store/catalog-entity-
|
|||||||
import catalogEntityRegistryInjectable from "../../api/catalog-entity-registry/catalog-entity-registry.injectable";
|
import catalogEntityRegistryInjectable from "../../api/catalog-entity-registry/catalog-entity-registry.injectable";
|
||||||
import type { DiRender } from "../test-utils/renderFor";
|
import type { DiRender } from "../test-utils/renderFor";
|
||||||
import { renderFor } from "../test-utils/renderFor";
|
import { renderFor } from "../test-utils/renderFor";
|
||||||
import { ThemeStore } from "../../theme.store";
|
|
||||||
import { UserStore } from "../../../common/user-store";
|
|
||||||
import mockFs from "mock-fs";
|
import mockFs from "mock-fs";
|
||||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
||||||
@ -98,8 +96,6 @@ describe("<Catalog />", () => {
|
|||||||
await di.runSetups();
|
await di.runSetups();
|
||||||
|
|
||||||
mockFs();
|
mockFs();
|
||||||
UserStore.createInstance();
|
|
||||||
ThemeStore.createInstance();
|
|
||||||
CatalogEntityDetailRegistry.createInstance();
|
CatalogEntityDetailRegistry.createInstance();
|
||||||
|
|
||||||
render = renderFor(di);
|
render = renderFor(di);
|
||||||
@ -125,8 +121,6 @@ describe("<Catalog />", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
UserStore.resetInstance();
|
|
||||||
ThemeStore.resetInstance();
|
|
||||||
CatalogEntityDetailRegistry.resetInstance();
|
CatalogEntityDetailRegistry.resetInstance();
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
|
|||||||
@ -11,15 +11,20 @@ import { computed, makeObservable } from "mobx";
|
|||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { SubHeader } from "../layout/sub-header";
|
import { SubHeader } from "../layout/sub-header";
|
||||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||||
import { nodeStore } from "../+nodes/legacy-store";
|
|
||||||
import { eventStore } from "../+events/legacy-store";
|
|
||||||
import { cssNames, prevDefault } from "../../utils";
|
import { cssNames, prevDefault } from "../../utils";
|
||||||
import type { ItemObject } from "../../../common/item.store";
|
import type { ItemObject } from "../../../common/item.store";
|
||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
import { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
import { kubeSelectedUrlParam, toggleDetails } from "../kube-detail-params";
|
import { kubeSelectedUrlParam, toggleDetails } from "../kube-detail-params";
|
||||||
import { apiManager } from "../../../common/k8s-api/api-manager";
|
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||||
import { KubeObjectAge } from "../kube-object/age";
|
import { KubeObjectAge } from "../kube-object/age";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
|
import type { NodeStore } from "../+nodes/store";
|
||||||
|
import type { EventStore } from "../+events/store";
|
||||||
|
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
||||||
|
import eventStoreInjectable from "../+events/store.injectable";
|
||||||
|
import nodeStoreInjectable from "../+nodes/store.injectable";
|
||||||
|
|
||||||
export interface ClusterIssuesProps {
|
export interface ClusterIssuesProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -39,16 +44,23 @@ enum sortBy {
|
|||||||
age = "age",
|
age = "age",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
themeStore: ThemeStore;
|
||||||
|
nodeStore: NodeStore;
|
||||||
|
eventStore: EventStore;
|
||||||
|
apiManager: ApiManager;
|
||||||
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ClusterIssues extends React.Component<ClusterIssuesProps> {
|
class NonInjectedClusterIssues extends React.Component<ClusterIssuesProps & Dependencies> {
|
||||||
constructor(props: ClusterIssuesProps) {
|
constructor(props: ClusterIssuesProps & Dependencies) {
|
||||||
super(props);
|
super(props);
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get warnings(): Warning[] {
|
@computed get warnings(): Warning[] {
|
||||||
return [
|
return [
|
||||||
...nodeStore.items.flatMap(node => (
|
...this.props.nodeStore.items.flatMap(node => (
|
||||||
node.getWarningConditions()
|
node.getWarningConditions()
|
||||||
.map(({ message }) => ({
|
.map(({ message }) => ({
|
||||||
selfLink: node.selfLink,
|
selfLink: node.selfLink,
|
||||||
@ -60,14 +72,14 @@ export class ClusterIssues extends React.Component<ClusterIssuesProps> {
|
|||||||
ageMs: -node.getCreationTimestamp(),
|
ageMs: -node.getCreationTimestamp(),
|
||||||
}))
|
}))
|
||||||
)),
|
)),
|
||||||
...eventStore.getWarnings().map(warning => ({
|
...this.props.eventStore.getWarnings().map(warning => ({
|
||||||
getId: () => warning.involvedObject.uid,
|
getId: () => warning.involvedObject.uid,
|
||||||
getName: () => warning.involvedObject.name,
|
getName: () => warning.involvedObject.name,
|
||||||
renderAge: () => <KubeObjectAge key="age" object={warning} />,
|
renderAge: () => <KubeObjectAge key="age" object={warning} />,
|
||||||
ageMs: -warning.getCreationTimestamp(),
|
ageMs: -warning.getCreationTimestamp(),
|
||||||
message: warning.message,
|
message: warning.message,
|
||||||
kind: warning.kind,
|
kind: warning.kind,
|
||||||
selfLink: apiManager.lookupApiLink(warning.involvedObject, warning),
|
selfLink: this.props.apiManager.lookupApiLink(warning.involvedObject, warning),
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -108,7 +120,7 @@ export class ClusterIssues extends React.Component<ClusterIssuesProps> {
|
|||||||
renderContent() {
|
renderContent() {
|
||||||
const { warnings } = this;
|
const { warnings } = this;
|
||||||
|
|
||||||
if (!eventStore.isLoaded) {
|
if (!this.props.eventStore.isLoaded) {
|
||||||
return (
|
return (
|
||||||
<Spinner center/>
|
<Spinner center/>
|
||||||
);
|
);
|
||||||
@ -148,7 +160,7 @@ export class ClusterIssues extends React.Component<ClusterIssuesProps> {
|
|||||||
sortByDefault={{ sortBy: sortBy.object, orderBy: "asc" }}
|
sortByDefault={{ sortBy: sortBy.object, orderBy: "asc" }}
|
||||||
sortSyncWithUrl={false}
|
sortSyncWithUrl={false}
|
||||||
getTableRow={this.getTableRow}
|
getTableRow={this.getTableRow}
|
||||||
className={cssNames("box grow", ThemeStore.getInstance().activeTheme.type)}
|
className={cssNames("box grow", this.props.themeStore.activeTheme.type)}
|
||||||
>
|
>
|
||||||
<TableHead nowrap>
|
<TableHead nowrap>
|
||||||
<TableCell className="message">Message</TableCell>
|
<TableCell className="message">Message</TableCell>
|
||||||
@ -169,3 +181,13 @@ export class ClusterIssues extends React.Component<ClusterIssuesProps> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ClusterIssues = withInjectables<Dependencies, ClusterIssuesProps>(NonInjectedClusterIssues, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
...props,
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
|
apiManager: di.inject(apiManagerInjectable),
|
||||||
|
eventStore: di.inject(eventStoreInjectable),
|
||||||
|
nodeStore: di.inject(nodeStoreInjectable),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|||||||
@ -16,11 +16,12 @@ import type { PieChartData } from "../chart";
|
|||||||
import { PieChart } from "../chart";
|
import { PieChart } from "../chart";
|
||||||
import { ClusterNoMetrics } from "./cluster-no-metrics";
|
import { ClusterNoMetrics } from "./cluster-no-metrics";
|
||||||
import { bytesToUnits, cssNames } from "../../utils";
|
import { bytesToUnits, cssNames } from "../../utils";
|
||||||
import { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api";
|
import { getMetricLastPoints } from "../../../common/k8s-api/endpoints/metrics.api";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable";
|
import clusterOverviewStoreInjectable from "./cluster-overview-store/cluster-overview-store.injectable";
|
||||||
import nodeStoreInjectable from "../+nodes/store.injectable";
|
import nodeStoreInjectable from "../+nodes/store.injectable";
|
||||||
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
|
|
||||||
function createLabels(rawLabelData: [string, number | undefined][]): string[] {
|
function createLabels(rawLabelData: [string, number | undefined][]): string[] {
|
||||||
return rawLabelData.map(([key, value]) => `${key}: ${value?.toFixed(2) || "N/A"}`);
|
return rawLabelData.map(([key, value]) => `${key}: ${value?.toFixed(2) || "N/A"}`);
|
||||||
@ -29,11 +30,13 @@ function createLabels(rawLabelData: [string, number | undefined][]): string[] {
|
|||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
clusterOverviewStore: ClusterOverviewStore;
|
clusterOverviewStore: ClusterOverviewStore;
|
||||||
nodeStore: NodeStore;
|
nodeStore: NodeStore;
|
||||||
|
themeStore: ThemeStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NonInjectedClusterPieCharts = observer(({
|
const NonInjectedClusterPieCharts = observer(({
|
||||||
clusterOverviewStore,
|
clusterOverviewStore,
|
||||||
nodeStore,
|
nodeStore,
|
||||||
|
themeStore,
|
||||||
}: Dependencies) => {
|
}: Dependencies) => {
|
||||||
const renderLimitWarning = () => {
|
const renderLimitWarning = () => {
|
||||||
return (
|
return (
|
||||||
@ -51,7 +54,7 @@ const NonInjectedClusterPieCharts = observer(({
|
|||||||
const { podUsage, podAllocatableCapacity, podCapacity } = data;
|
const { podUsage, podAllocatableCapacity, podCapacity } = data;
|
||||||
const cpuLimitsOverload = cpuLimits > cpuAllocatableCapacity;
|
const cpuLimitsOverload = cpuLimits > cpuAllocatableCapacity;
|
||||||
const memoryLimitsOverload = memoryLimits > memoryAllocatableCapacity;
|
const memoryLimitsOverload = memoryLimits > memoryAllocatableCapacity;
|
||||||
const defaultColor = ThemeStore.getInstance().activeTheme.colors.pieChartDefaultColor;
|
const defaultColor = themeStore.activeTheme.colors.pieChartDefaultColor;
|
||||||
|
|
||||||
if (!memoryCapacity || !cpuCapacity || !podCapacity || !memoryAllocatableCapacity || !cpuAllocatableCapacity || !podAllocatableCapacity) return null;
|
if (!memoryCapacity || !cpuCapacity || !podCapacity || !memoryAllocatableCapacity || !cpuAllocatableCapacity || !podAllocatableCapacity) return null;
|
||||||
const cpuData: PieChartData = {
|
const cpuData: PieChartData = {
|
||||||
@ -258,5 +261,6 @@ export const ClusterPieCharts = withInjectables<Dependencies>(NonInjectedCluster
|
|||||||
getProps: (di) => ({
|
getProps: (di) => ({
|
||||||
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
clusterOverviewStore: di.inject(clusterOverviewStoreInjectable),
|
||||||
nodeStore: di.inject(nodeStoreInjectable),
|
nodeStore: di.inject(nodeStoreInjectable),
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import { Spinner } from "../../spinner";
|
|||||||
import { Table, TableCell, TableHead, TableRow } from "../../table";
|
import { Table, TableCell, TableHead, TableRow } from "../../table";
|
||||||
import { Button } from "../../button";
|
import { Button } from "../../button";
|
||||||
import { Notifications } from "../../notifications";
|
import { Notifications } from "../../notifications";
|
||||||
import { ThemeStore } from "../../../theme.store";
|
import type { ThemeStore } from "../../../themes/store";
|
||||||
import { apiManager } from "../../../../common/k8s-api/api-manager";
|
import { apiManager } from "../../../../common/k8s-api/api-manager";
|
||||||
import { SubTitle } from "../../layout/sub-title";
|
import { SubTitle } from "../../layout/sub-title";
|
||||||
import { getDetailsUrl } from "../../kube-detail-params";
|
import { getDetailsUrl } from "../../kube-detail-params";
|
||||||
@ -38,6 +38,7 @@ import userSuppliedValuesAreShownInjectable from "./user-supplied-values-are-sho
|
|||||||
import { KubeObjectAge } from "../../kube-object/age";
|
import { KubeObjectAge } from "../../kube-object/age";
|
||||||
import type { KubeJsonApiData } from "../../../../common/k8s-api/kube-json-api";
|
import type { KubeJsonApiData } from "../../../../common/k8s-api/kube-json-api";
|
||||||
import { entries } from "../../../../common/utils/objects";
|
import { entries } from "../../../../common/utils/objects";
|
||||||
|
import themeStoreInjectable from "../../../themes/store.injectable";
|
||||||
|
|
||||||
export interface ReleaseDetailsProps {
|
export interface ReleaseDetailsProps {
|
||||||
hideDetails(): void;
|
hideDetails(): void;
|
||||||
@ -50,6 +51,7 @@ interface Dependencies {
|
|||||||
updateRelease: (name: string, namespace: string, payload: HelmReleaseUpdatePayload) => Promise<HelmReleaseUpdateDetails>;
|
updateRelease: (name: string, namespace: string, payload: HelmReleaseUpdatePayload) => Promise<HelmReleaseUpdateDetails>;
|
||||||
createUpgradeChartTab: (release: HelmRelease) => void;
|
createUpgradeChartTab: (release: HelmRelease) => void;
|
||||||
userSuppliedValuesAreShown: { toggle: () => void; value: boolean };
|
userSuppliedValuesAreShown: { toggle: () => void; value: boolean };
|
||||||
|
themeStore: ThemeStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
@ -252,12 +254,12 @@ class NonInjectedReleaseDetails extends Component<ReleaseDetailsProps & Dependen
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { hideDetails } = this.props;
|
const { hideDetails, themeStore } = this.props;
|
||||||
const release = this.props.release.get();
|
const release = this.props.release.get();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
className={cssNames("ReleaseDetails", ThemeStore.getInstance().activeTheme.type)}
|
className={cssNames("ReleaseDetails", themeStore.activeTheme.type)}
|
||||||
usePortal={true}
|
usePortal={true}
|
||||||
open={Boolean(release)}
|
open={Boolean(release)}
|
||||||
title={release ? `Release: ${release.getName()}` : ""}
|
title={release ? `Release: ${release.getName()}` : ""}
|
||||||
@ -285,5 +287,6 @@ export const ReleaseDetails = withInjectables<Dependencies, ReleaseDetailsProps>
|
|||||||
userSuppliedValuesAreShown: di.inject(userSuppliedValuesAreShownInjectable),
|
userSuppliedValuesAreShown: di.inject(userSuppliedValuesAreShownInjectable),
|
||||||
updateRelease: di.inject(updateReleaseInjectable),
|
updateRelease: di.inject(updateReleaseInjectable),
|
||||||
createUpgradeChartTab: di.inject(createUpgradeChartTabInjectable),
|
createUpgradeChartTab: di.inject(createUpgradeChartTabInjectable),
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -12,16 +12,26 @@ import { ResourceMetricsContext } from "../resource-metrics";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { mapValues } from "lodash";
|
import { mapValues } from "lodash";
|
||||||
import { type MetricsTab, metricTabOptions } from "../chart/options";
|
import { type MetricsTab, metricTabOptions } from "../chart/options";
|
||||||
import { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
|
|
||||||
export const NodeCharts = observer(() => {
|
export interface NodeChartsProps {}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
themeStore: ThemeStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedNodeCharts = observer(({
|
||||||
|
themeStore,
|
||||||
|
}: Dependencies & NodeChartsProps) => {
|
||||||
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
||||||
|
|
||||||
if (!metrics || !object || !tab) return null;
|
if (!metrics || !object || !tab) return null;
|
||||||
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
||||||
|
|
||||||
const id = object.getId();
|
const id = object.getId();
|
||||||
const { chartCapacityColor } = ThemeStore.getInstance().activeTheme.colors;
|
const { chartCapacityColor } = themeStore.activeTheme.colors;
|
||||||
const {
|
const {
|
||||||
memoryUsage,
|
memoryUsage,
|
||||||
workloadMemoryUsage,
|
workloadMemoryUsage,
|
||||||
@ -148,3 +158,10 @@ export const NodeCharts = observer(() => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const NodeCharts = withInjectables<Dependencies, NodeChartsProps>(NonInjectedNodeCharts, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
...props,
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import React from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { SubTitle } from "../layout/sub-title";
|
import { SubTitle } from "../layout/sub-title";
|
||||||
import { Select } from "../select";
|
import { Select } from "../select";
|
||||||
import type { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
import type { UserStore } from "../../../common/user-store";
|
import type { UserStore } from "../../../common/user-store";
|
||||||
import { Input } from "../input";
|
import { Input } from "../input";
|
||||||
import { Switch } from "../switch";
|
import { Switch } from "../switch";
|
||||||
@ -22,8 +22,8 @@ import { withInjectables } from "@ogre-tools/injectable-react";
|
|||||||
import appPreferencesInjectable from "./app-preferences/app-preferences.injectable";
|
import appPreferencesInjectable from "./app-preferences/app-preferences.injectable";
|
||||||
import { Preferences } from "./preferences";
|
import { Preferences } from "./preferences";
|
||||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||||
import themeStoreInjectable from "../../theme-store.injectable";
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
import { defaultTheme } from "../../../common/vars";
|
import { defaultThemeId } from "../../../common/vars";
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
appPreferenceItems: IComputedValue<RegisteredAppPreference[]>;
|
appPreferenceItems: IComputedValue<RegisteredAppPreference[]>;
|
||||||
@ -57,8 +57,6 @@ const extensionInstallRegistryOptions = [
|
|||||||
|
|
||||||
const NonInjectedApplication: React.FC<Dependencies> = ({ appPreferenceItems, userStore, themeStore }) => {
|
const NonInjectedApplication: React.FC<Dependencies> = ({ appPreferenceItems, userStore, themeStore }) => {
|
||||||
const [customUrl, setCustomUrl] = React.useState(userStore.extensionRegistryUrl.customUrl || "");
|
const [customUrl, setCustomUrl] = React.useState(userStore.extensionRegistryUrl.customUrl || "");
|
||||||
const extensionSettings = appPreferenceItems.get()
|
|
||||||
.filter((preference) => preference.showInPreferencesTab === "application");
|
|
||||||
const themeOptions = [
|
const themeOptions = [
|
||||||
{
|
{
|
||||||
value: "system", // TODO: replace with a sentinal value that isn't string (and serialize it differently)
|
value: "system", // TODO: replace with a sentinal value that isn't string (and serialize it differently)
|
||||||
@ -69,6 +67,8 @@ const NonInjectedApplication: React.FC<Dependencies> = ({ appPreferenceItems, us
|
|||||||
label: name,
|
label: name,
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
|
const extensionSettings = appPreferenceItems.get()
|
||||||
|
.filter((preference) => preference.showInPreferencesTab === "application");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Preferences data-testid="application-preferences-page">
|
<Preferences data-testid="application-preferences-page">
|
||||||
@ -79,7 +79,7 @@ const NonInjectedApplication: React.FC<Dependencies> = ({ appPreferenceItems, us
|
|||||||
<Select
|
<Select
|
||||||
options={themeOptions}
|
options={themeOptions}
|
||||||
value={userStore.colorTheme}
|
value={userStore.colorTheme}
|
||||||
onChange={value => userStore.colorTheme = value?.value ?? defaultTheme}
|
onChange={value => userStore.colorTheme = value?.value ?? defaultThemeId}
|
||||||
themeName="lens"
|
themeName="lens"
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -10,11 +10,11 @@ import { SubTitle } from "../layout/sub-title";
|
|||||||
import { Input, InputValidators } from "../input";
|
import { Input, InputValidators } from "../input";
|
||||||
import { Switch } from "../switch";
|
import { Switch } from "../switch";
|
||||||
import { Select } from "../select";
|
import { Select } from "../select";
|
||||||
import type { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
import { Preferences } from "./preferences";
|
import { Preferences } from "./preferences";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||||
import themeStoreInjectable from "../../theme-store.injectable";
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
import defaultShellInjectable from "./default-shell.injectable";
|
import defaultShellInjectable from "./default-shell.injectable";
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
|
|||||||
@ -10,16 +10,26 @@ import { BarChart, memoryOptions } from "../chart";
|
|||||||
import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
|
import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
|
||||||
import { NoMetrics } from "../resource-metrics/no-metrics";
|
import { NoMetrics } from "../resource-metrics/no-metrics";
|
||||||
import { ResourceMetricsContext } from "../resource-metrics";
|
import { ResourceMetricsContext } from "../resource-metrics";
|
||||||
import { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
|
|
||||||
export const VolumeClaimDiskChart = observer(() => {
|
export interface VolumeClaimDiskChartProps {}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
themeStore: ThemeStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedVolumeClaimDiskChart = observer(({
|
||||||
|
themeStore,
|
||||||
|
}: Dependencies & VolumeClaimDiskChartProps) => {
|
||||||
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
||||||
|
|
||||||
if (!metrics || !object || !tab) return null;
|
if (!metrics || !object || !tab) return null;
|
||||||
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
||||||
|
|
||||||
const id = object.getId();
|
const id = object.getId();
|
||||||
const { chartCapacityColor } = ThemeStore.getInstance().activeTheme.colors;
|
const { chartCapacityColor } = themeStore.activeTheme.colors;
|
||||||
const { diskUsage, diskCapacity } = metrics;
|
const { diskUsage, diskCapacity } = metrics;
|
||||||
const usage = normalizeMetrics(diskUsage).data.result[0].values;
|
const usage = normalizeMetrics(diskUsage).data.result[0].values;
|
||||||
const capacity = normalizeMetrics(diskCapacity).data.result[0].values;
|
const capacity = normalizeMetrics(diskCapacity).data.result[0].values;
|
||||||
@ -51,3 +61,10 @@ export const VolumeClaimDiskChart = observer(() => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const VolumeClaimDiskChart = withInjectables<Dependencies, VolumeClaimDiskChartProps>(NonInjectedVolumeClaimDiskChart, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
...props,
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|||||||
@ -11,14 +11,20 @@ import { observer } from "mobx-react";
|
|||||||
import type { DatasetTooltipLabel, PieChartData } from "../chart";
|
import type { DatasetTooltipLabel, PieChartData } from "../chart";
|
||||||
import { PieChart } from "../chart";
|
import { PieChart } from "../chart";
|
||||||
import { cssVar, object } from "../../utils";
|
import { cssVar, object } from "../../utils";
|
||||||
import { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
|
|
||||||
export interface OverviewWorkloadStatusProps {
|
export interface OverviewWorkloadStatusProps {
|
||||||
status: Partial<Record<string, number>>;
|
status: Partial<Record<string, number>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
themeStore: ThemeStore;
|
||||||
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class OverviewWorkloadStatus extends React.Component<OverviewWorkloadStatusProps> {
|
class NonInjectedOverviewWorkloadStatus extends React.Component<OverviewWorkloadStatusProps & Dependencies> {
|
||||||
private elem: HTMLElement | null = null;
|
private elem: HTMLElement | null = null;
|
||||||
|
|
||||||
renderChart() {
|
renderChart() {
|
||||||
@ -37,7 +43,7 @@ export class OverviewWorkloadStatus extends React.Component<OverviewWorkloadStat
|
|||||||
if (statuses.length === 0) {
|
if (statuses.length === 0) {
|
||||||
chartData.datasets.push({
|
chartData.datasets.push({
|
||||||
data: [1],
|
data: [1],
|
||||||
backgroundColor: [ThemeStore.getInstance().activeTheme.colors.pieChartDefaultColor],
|
backgroundColor: [this.props.themeStore.activeTheme.colors.pieChartDefaultColor],
|
||||||
label: "Empty",
|
label: "Empty",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -84,3 +90,10 @@ export class OverviewWorkloadStatus extends React.Component<OverviewWorkloadStat
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const OverviewWorkloadStatus = withInjectables<Dependencies, OverviewWorkloadStatusProps>(NonInjectedOverviewWorkloadStatus, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
...props,
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|||||||
@ -10,17 +10,27 @@ import { BarChart } from "../chart";
|
|||||||
import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
|
import { isMetricsEmpty, normalizeMetrics } from "../../../common/k8s-api/endpoints/metrics.api";
|
||||||
import { NoMetrics } from "../resource-metrics/no-metrics";
|
import { NoMetrics } from "../resource-metrics/no-metrics";
|
||||||
import { ResourceMetricsContext } from "../resource-metrics";
|
import { ResourceMetricsContext } from "../resource-metrics";
|
||||||
import { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
import { mapValues } from "lodash";
|
import { mapValues } from "lodash";
|
||||||
import { type MetricsTab, metricTabOptions } from "../chart/options";
|
import { type MetricsTab, metricTabOptions } from "../chart/options";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
|
|
||||||
export const ContainerCharts = observer(() => {
|
export interface ContainerChartsProps {}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
themeStore: ThemeStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedContainerCharts = observer(({
|
||||||
|
themeStore,
|
||||||
|
}: Dependencies & ContainerChartsProps) => {
|
||||||
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
const { metrics, tab, object } = useContext(ResourceMetricsContext) ?? {};
|
||||||
|
|
||||||
if (!metrics || !object || !tab) return null;
|
if (!metrics || !object || !tab) return null;
|
||||||
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
||||||
|
|
||||||
const { chartCapacityColor } = ThemeStore.getInstance().activeTheme.colors;
|
const { chartCapacityColor } = themeStore.activeTheme.colors;
|
||||||
const {
|
const {
|
||||||
cpuUsage,
|
cpuUsage,
|
||||||
cpuRequests,
|
cpuRequests,
|
||||||
@ -113,3 +123,10 @@ export const ContainerCharts = observer(() => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const ContainerCharts = withInjectables<Dependencies, ContainerChartsProps>(NonInjectedContainerCharts, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
...props,
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|||||||
@ -13,9 +13,11 @@ import type { ChartProps } from "./chart";
|
|||||||
import { Chart, ChartKind } from "./chart";
|
import { Chart, ChartKind } from "./chart";
|
||||||
import { bytesToUnits, cssNames, isObject } from "../../utils";
|
import { bytesToUnits, cssNames, isObject } from "../../utils";
|
||||||
import { ZebraStripesPlugin } from "./zebra-stripes.plugin";
|
import { ZebraStripesPlugin } from "./zebra-stripes.plugin";
|
||||||
import { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
import { NoMetrics } from "../resource-metrics/no-metrics";
|
import { NoMetrics } from "../resource-metrics/no-metrics";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
|
|
||||||
export interface BarChartProps extends ChartProps {
|
export interface BarChartProps extends ChartProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
@ -24,7 +26,12 @@ export interface BarChartProps extends ChartProps {
|
|||||||
|
|
||||||
const getBarColor: Scriptable<string> = ({ dataset }) => Color(dataset?.borderColor).alpha(0.2).string();
|
const getBarColor: Scriptable<string> = ({ dataset }) => Color(dataset?.borderColor).alpha(0.2).string();
|
||||||
|
|
||||||
export const BarChart = observer(({
|
interface Dependencies {
|
||||||
|
themeStore: ThemeStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonInjectedBarChart = observer(({
|
||||||
|
themeStore,
|
||||||
name,
|
name,
|
||||||
data,
|
data,
|
||||||
className,
|
className,
|
||||||
@ -32,8 +39,8 @@ export const BarChart = observer(({
|
|||||||
plugins,
|
plugins,
|
||||||
options: customOptions,
|
options: customOptions,
|
||||||
...settings
|
...settings
|
||||||
}: BarChartProps) => {
|
}: Dependencies & BarChartProps) => {
|
||||||
const { textColorPrimary, borderFaintColor, chartStripesColor } = ThemeStore.getInstance().activeTheme.colors;
|
const { textColorPrimary, borderFaintColor, chartStripesColor } = themeStore.activeTheme.colors;
|
||||||
const { datasets: rawDatasets = [], ...rest } = data;
|
const { datasets: rawDatasets = [], ...rest } = data;
|
||||||
const datasets = rawDatasets
|
const datasets = rawDatasets
|
||||||
.filter(set => set.data?.length)
|
.filter(set => set.data?.length)
|
||||||
@ -158,6 +165,13 @@ export const BarChart = observer(({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const BarChart = withInjectables<Dependencies, BarChartProps>(NonInjectedBarChart, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
...props,
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
const tooltipCallbackWith = (precision: number): ChartTooltipCallback["label"] => (
|
const tooltipCallbackWith = (precision: number): ChartTooltipCallback["label"] => (
|
||||||
({ datasetIndex, index }, { datasets = [] }) => {
|
({ datasetIndex, index }, { datasets = [] }) => {
|
||||||
if (typeof datasetIndex !== "number" || typeof index !== "number") {
|
if (typeof datasetIndex !== "number" || typeof index !== "number") {
|
||||||
|
|||||||
@ -11,7 +11,9 @@ import ChartJS from "chart.js";
|
|||||||
import type { ChartProps } from "./chart";
|
import type { ChartProps } from "./chart";
|
||||||
import { Chart } from "./chart";
|
import { Chart } from "./chart";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
import { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
|
|
||||||
export interface PieChartProps extends ChartProps {
|
export interface PieChartProps extends ChartProps {
|
||||||
}
|
}
|
||||||
@ -41,9 +43,19 @@ function getCutout(length: number | undefined): number {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PieChart = observer((props: PieChartProps) => {
|
interface Dependencies {
|
||||||
const { data, className, options, ...chartProps } = props;
|
themeStore: ThemeStore;
|
||||||
const { contentColor } = ThemeStore.getInstance().activeTheme.colors;
|
}
|
||||||
|
|
||||||
|
const NonInjectedPieChart = observer(({
|
||||||
|
themeStore,
|
||||||
|
data,
|
||||||
|
className,
|
||||||
|
options,
|
||||||
|
showChart,
|
||||||
|
...chartProps
|
||||||
|
}: Dependencies & PieChartProps) => {
|
||||||
|
const { contentColor } = themeStore.activeTheme.colors;
|
||||||
const opts: ChartOptions = {
|
const opts: ChartOptions = {
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
tooltips: {
|
tooltips: {
|
||||||
@ -98,12 +110,20 @@ export const PieChart = observer((props: PieChartProps) => {
|
|||||||
<Chart
|
<Chart
|
||||||
className={cssNames("PieChart flex column align-center", className)}
|
className={cssNames("PieChart flex column align-center", className)}
|
||||||
data={data}
|
data={data}
|
||||||
options={props.showChart ? {} : opts}
|
options={showChart ? {} : opts}
|
||||||
|
showChart={showChart}
|
||||||
{...chartProps}
|
{...chartProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const PieChart = withInjectables<Dependencies, PieChartProps>(NonInjectedPieChart, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
...props,
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
ChartJS.Tooltip.positioners.cursor = function (elements: any, position: { x: number; y: number }) {
|
ChartJS.Tooltip.positioners.cursor = function (elements: any, position: { x: number; y: number }) {
|
||||||
return position;
|
return position;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,8 +11,6 @@ import { DockTabs } from "../dock-tabs";
|
|||||||
import type { DockStore, DockTab } from "../dock/store";
|
import type { DockStore, DockTab } from "../dock/store";
|
||||||
import { TabKind } from "../dock/store";
|
import { TabKind } from "../dock/store";
|
||||||
import { noop } from "../../../utils";
|
import { noop } from "../../../utils";
|
||||||
import { ThemeStore } from "../../../theme.store";
|
|
||||||
import { UserStore } from "../../../../common/user-store";
|
|
||||||
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
|
||||||
import dockStoreInjectable from "../dock/store.injectable";
|
import dockStoreInjectable from "../dock/store.injectable";
|
||||||
import type { DiRender } from "../../test-utils/renderFor";
|
import type { DiRender } from "../../test-utils/renderFor";
|
||||||
@ -92,16 +90,11 @@ describe("<DockTabs />", () => {
|
|||||||
|
|
||||||
dockStore = di.inject(dockStoreInjectable);
|
dockStore = di.inject(dockStoreInjectable);
|
||||||
|
|
||||||
UserStore.createInstance();
|
|
||||||
ThemeStore.createInstance();
|
|
||||||
await dockStore.whenReady;
|
await dockStore.whenReady;
|
||||||
dockStore.tabs = initialTabs;
|
dockStore.tabs = initialTabs;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
ThemeStore.resetInstance();
|
|
||||||
UserStore.resetInstance();
|
|
||||||
|
|
||||||
// TODO: A unit test may not cause side effects. Here accessing file system is a side effect.
|
// TODO: A unit test may not cause side effects. Here accessing file system is a side effect.
|
||||||
fse.remove("some-test-suite-specific-directory-for-user-data");
|
fse.remove("some-test-suite-specific-directory-for-user-data");
|
||||||
});
|
});
|
||||||
|
|||||||
@ -8,8 +8,6 @@ import "@testing-library/jest-dom/extend-expect";
|
|||||||
import * as selectEvent from "react-select-event";
|
import * as selectEvent from "react-select-event";
|
||||||
import { LogResourceSelector } from "../resource-selector";
|
import { LogResourceSelector } from "../resource-selector";
|
||||||
import { dockerPod, deploymentPod1, deploymentPod2 } from "./pod.mock";
|
import { dockerPod, deploymentPod1, deploymentPod2 } from "./pod.mock";
|
||||||
import { ThemeStore } from "../../../../theme.store";
|
|
||||||
import { UserStore } from "../../../../../common/user-store";
|
|
||||||
import mockFs from "mock-fs";
|
import mockFs from "mock-fs";
|
||||||
import { getDiForUnitTesting } from "../../../../getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../../../getDiForUnitTesting";
|
||||||
import type { DiRender } from "../../../test-utils/renderFor";
|
import type { DiRender } from "../../../test-utils/renderFor";
|
||||||
@ -144,14 +142,9 @@ describe("<LogResourceSelector />", () => {
|
|||||||
mockFs({
|
mockFs({
|
||||||
"tmp": {},
|
"tmp": {},
|
||||||
});
|
});
|
||||||
|
|
||||||
UserStore.createInstance();
|
|
||||||
ThemeStore.createInstance();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
UserStore.resetInstance();
|
|
||||||
ThemeStore.resetInstance();
|
|
||||||
mockFs.restore();
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import type { TerminalApi } from "../../../api/terminal-api";
|
|||||||
import terminalSpawningPoolInjectable from "./terminal-spawning-pool.injectable";
|
import terminalSpawningPoolInjectable from "./terminal-spawning-pool.injectable";
|
||||||
import terminalConfigInjectable from "../../../../common/user-store/terminal-config.injectable";
|
import terminalConfigInjectable from "../../../../common/user-store/terminal-config.injectable";
|
||||||
import terminalCopyOnSelectInjectable from "../../../../common/user-store/terminal-copy-on-select.injectable";
|
import terminalCopyOnSelectInjectable from "../../../../common/user-store/terminal-copy-on-select.injectable";
|
||||||
|
import themeStoreInjectable from "../../../themes/store.injectable";
|
||||||
|
|
||||||
export type CreateTerminal = (tabId: TabId, api: TerminalApi) => Terminal;
|
export type CreateTerminal = (tabId: TabId, api: TerminalApi) => Terminal;
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ const createTerminalInjectable = getInjectable({
|
|||||||
spawningPool: di.inject(terminalSpawningPoolInjectable),
|
spawningPool: di.inject(terminalSpawningPoolInjectable),
|
||||||
terminalConfig: di.inject(terminalConfigInjectable),
|
terminalConfig: di.inject(terminalConfigInjectable),
|
||||||
terminalCopyOnSelect: di.inject(terminalCopyOnSelectInjectable),
|
terminalCopyOnSelect: di.inject(terminalCopyOnSelectInjectable),
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (tabId, api) => new Terminal(dependencies, { tabId, api });
|
return (tabId, api) => new Terminal(dependencies, { tabId, api });
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { FitAddon } from "xterm-addon-fit";
|
|||||||
import type { TabId } from "../dock/store";
|
import type { TabId } from "../dock/store";
|
||||||
import type { TerminalApi } from "../../../api/terminal-api";
|
import type { TerminalApi } from "../../../api/terminal-api";
|
||||||
import { TerminalChannels } from "../../../api/terminal-api";
|
import { TerminalChannels } from "../../../api/terminal-api";
|
||||||
import { ThemeStore } from "../../../theme.store";
|
import type { ThemeStore } from "../../../themes/store";
|
||||||
import { disposer } from "../../../utils";
|
import { disposer } from "../../../utils";
|
||||||
import { isMac } from "../../../../common/vars";
|
import { isMac } from "../../../../common/vars";
|
||||||
import { once } from "lodash";
|
import { once } from "lodash";
|
||||||
@ -24,6 +24,7 @@ export interface TerminalDependencies {
|
|||||||
readonly spawningPool: HTMLElement;
|
readonly spawningPool: HTMLElement;
|
||||||
readonly terminalConfig: IComputedValue<TerminalConfig>;
|
readonly terminalConfig: IComputedValue<TerminalConfig>;
|
||||||
readonly terminalCopyOnSelect: IComputedValue<boolean>;
|
readonly terminalCopyOnSelect: IComputedValue<boolean>;
|
||||||
|
readonly themeStore: ThemeStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TerminalArguments {
|
export interface TerminalArguments {
|
||||||
@ -94,7 +95,7 @@ export class Terminal {
|
|||||||
window.addEventListener("resize", this.onResize);
|
window.addEventListener("resize", this.onResize);
|
||||||
|
|
||||||
this.disposer.push(
|
this.disposer.push(
|
||||||
reaction(() => ThemeStore.getInstance().xtermColors, colors => {
|
reaction(() => this.dependencies.themeStore.xtermColors, colors => {
|
||||||
this.xterm?.setOption("theme", colors);
|
this.xterm?.setOption("theme", colors);
|
||||||
}, {
|
}, {
|
||||||
fireImmediately: true,
|
fireImmediately: true,
|
||||||
|
|||||||
@ -10,12 +10,13 @@ import { disposeOnUnmount, observer } from "mobx-react";
|
|||||||
import { cssNames } from "../../../utils";
|
import { cssNames } from "../../../utils";
|
||||||
import type { Terminal } from "./terminal";
|
import type { Terminal } from "./terminal";
|
||||||
import type { TerminalStore } from "./store";
|
import type { TerminalStore } from "./store";
|
||||||
import { ThemeStore } from "../../../theme.store";
|
import type { ThemeStore } from "../../../themes/store";
|
||||||
import type { DockTab, DockStore } from "../dock/store";
|
import type { DockTab, DockStore } from "../dock/store";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import dockStoreInjectable from "../dock/store.injectable";
|
import dockStoreInjectable from "../dock/store.injectable";
|
||||||
import terminalStoreInjectable from "./store.injectable";
|
import terminalStoreInjectable from "./store.injectable";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
|
import themeStoreInjectable from "../../../themes/store.injectable";
|
||||||
|
|
||||||
export interface TerminalWindowProps {
|
export interface TerminalWindowProps {
|
||||||
tab: DockTab;
|
tab: DockTab;
|
||||||
@ -24,6 +25,7 @@ export interface TerminalWindowProps {
|
|||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
dockStore: DockStore;
|
dockStore: DockStore;
|
||||||
terminalStore: TerminalStore;
|
terminalStore: TerminalStore;
|
||||||
|
themeStore: ThemeStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
@ -66,7 +68,7 @@ class NonInjectedTerminalWindow extends React.Component<TerminalWindowProps & De
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cssNames("TerminalWindow", ThemeStore.getInstance().activeTheme.type)}
|
className={cssNames("TerminalWindow", this.props.themeStore.activeTheme.type)}
|
||||||
ref={elem => this.elem = elem}
|
ref={elem => this.elem = elem}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -75,9 +77,10 @@ class NonInjectedTerminalWindow extends React.Component<TerminalWindowProps & De
|
|||||||
|
|
||||||
export const TerminalWindow = withInjectables<Dependencies, TerminalWindowProps>(NonInjectedTerminalWindow, {
|
export const TerminalWindow = withInjectables<Dependencies, TerminalWindowProps>(NonInjectedTerminalWindow, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
|
...props,
|
||||||
dockStore: di.inject(dockStoreInjectable),
|
dockStore: di.inject(dockStoreInjectable),
|
||||||
terminalStore: di.inject(terminalStoreInjectable),
|
terminalStore: di.inject(terminalStoreInjectable),
|
||||||
...props,
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -12,9 +12,7 @@ import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
|
|||||||
import type { DiRender } from "../../test-utils/renderFor";
|
import type { DiRender } from "../../test-utils/renderFor";
|
||||||
import { renderFor } from "../../test-utils/renderFor";
|
import { renderFor } from "../../test-utils/renderFor";
|
||||||
import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable";
|
import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable";
|
||||||
import { ThemeStore } from "../../../theme.store";
|
|
||||||
import { ConfirmDialog } from "../../confirm-dialog";
|
import { ConfirmDialog } from "../../confirm-dialog";
|
||||||
import { UserStore } from "../../../../common/user-store";
|
|
||||||
import mockFs from "mock-fs";
|
import mockFs from "mock-fs";
|
||||||
import directoryForUserDataInjectable from "../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
import directoryForUserDataInjectable from "../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
import getConfigurationFileModelInjectable from "../../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
import getConfigurationFileModelInjectable from "../../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
||||||
@ -54,15 +52,10 @@ describe("<HotbarRemoveCommand />", () => {
|
|||||||
di.permitSideEffects(appVersionInjectable);
|
di.permitSideEffects(appVersionInjectable);
|
||||||
|
|
||||||
render = renderFor(di);
|
render = renderFor(di);
|
||||||
|
|
||||||
UserStore.createInstance();
|
|
||||||
ThemeStore.createInstance();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
mockFs.restore();
|
mockFs.restore();
|
||||||
ThemeStore.resetInstance();
|
|
||||||
UserStore.resetInstance();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders w/o errors", async () => {
|
it("renders w/o errors", async () => {
|
||||||
|
|||||||
@ -22,39 +22,41 @@ import { Spinner } from "../spinner";
|
|||||||
import type { ItemObject } from "../../../common/item.store";
|
import type { ItemObject } from "../../../common/item.store";
|
||||||
import type { Filter } from "./page-filters.store";
|
import type { Filter } from "./page-filters.store";
|
||||||
import { pageFilters } from "./page-filters.store";
|
import { pageFilters } from "./page-filters.store";
|
||||||
import { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
import { MenuActions } from "../menu/menu-actions";
|
import { MenuActions } from "../menu/menu-actions";
|
||||||
import { MenuItem } from "../menu";
|
import { MenuItem } from "../menu";
|
||||||
import { Checkbox } from "../checkbox";
|
import { Checkbox } from "../checkbox";
|
||||||
import { UserStore } from "../../../common/user-store";
|
import { UserStore } from "../../../common/user-store";
|
||||||
import type { ItemListStore } from "./list-layout";
|
import type { ItemListStore } from "./list-layout";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
|
|
||||||
export interface ItemListLayoutContentProps<I extends ItemObject, PreLoadStores extends boolean> {
|
export interface ItemListLayoutContentProps<Item extends ItemObject, PreLoadStores extends boolean> {
|
||||||
getFilters: () => Filter[];
|
getFilters: () => Filter[];
|
||||||
tableId?: string;
|
tableId?: string;
|
||||||
className: IClassName;
|
className: IClassName;
|
||||||
getItems: () => I[];
|
getItems: () => Item[];
|
||||||
store: ItemListStore<I, PreLoadStores>;
|
store: ItemListStore<Item, PreLoadStores>;
|
||||||
getIsReady: () => boolean; // show loading indicator while not ready
|
getIsReady: () => boolean; // show loading indicator while not ready
|
||||||
isSelectable?: boolean; // show checkbox in rows for selecting items
|
isSelectable?: boolean; // show checkbox in rows for selecting items
|
||||||
isConfigurable?: boolean;
|
isConfigurable?: boolean;
|
||||||
copyClassNameFromHeadCells?: boolean;
|
copyClassNameFromHeadCells?: boolean;
|
||||||
sortingCallbacks?: TableSortCallbacks<I>;
|
sortingCallbacks?: TableSortCallbacks<Item>;
|
||||||
tableProps?: Partial<TableProps<I>>; // low-level table configuration
|
tableProps?: Partial<TableProps<Item>>; // low-level table configuration
|
||||||
renderTableHeader?: (TableCellProps | undefined | null)[];
|
renderTableHeader?: (TableCellProps | undefined | null)[];
|
||||||
renderTableContents: (item: I) => (ReactNode | TableCellProps)[];
|
renderTableContents: (item: Item) => (ReactNode | TableCellProps)[];
|
||||||
renderItemMenu?: (item: I, store: ItemListStore<I, PreLoadStores>) => ReactNode;
|
renderItemMenu?: (item: Item, store: ItemListStore<Item, PreLoadStores>) => ReactNode;
|
||||||
customizeTableRowProps?: (item: I) => Partial<TableRowProps<I>>;
|
customizeTableRowProps?: (item: Item) => Partial<TableRowProps<Item>>;
|
||||||
addRemoveButtons?: Partial<AddRemoveButtonsProps>;
|
addRemoveButtons?: Partial<AddRemoveButtonsProps>;
|
||||||
virtual?: boolean;
|
virtual?: boolean;
|
||||||
|
|
||||||
// item details view
|
// item details view
|
||||||
hasDetailsView?: boolean;
|
hasDetailsView?: boolean;
|
||||||
detailsItem?: I;
|
detailsItem?: Item;
|
||||||
onDetails?: (item: I) => void;
|
onDetails?: (item: Item) => void;
|
||||||
|
|
||||||
// other
|
// other
|
||||||
customizeRemoveDialog?: (selectedItems: I[]) => Partial<ConfirmDialogParams>;
|
customizeRemoveDialog?: (selectedItems: Item[]) => Partial<ConfirmDialogParams>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to display when a store failed to load
|
* Message to display when a store failed to load
|
||||||
@ -64,9 +66,13 @@ export interface ItemListLayoutContentProps<I extends ItemObject, PreLoadStores
|
|||||||
failedToLoadMessage?: React.ReactNode;
|
failedToLoadMessage?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
themeStore: ThemeStore;
|
||||||
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ItemListLayoutContent<I extends ItemObject, PreLoadStores extends boolean> extends React.Component<ItemListLayoutContentProps<I, PreLoadStores>> {
|
class NonInjectedItemListLayoutContent<Item extends ItemObject, PreLoadStores extends boolean> extends React.Component<ItemListLayoutContentProps<Item, PreLoadStores> & Dependencies> {
|
||||||
constructor(props: ItemListLayoutContentProps<I, PreLoadStores>) {
|
constructor(props: ItemListLayoutContentProps<Item, PreLoadStores> & Dependencies) {
|
||||||
super(props);
|
super(props);
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
autoBind(this);
|
autoBind(this);
|
||||||
@ -76,11 +82,11 @@ export class ItemListLayoutContent<I extends ItemObject, PreLoadStores extends b
|
|||||||
return this.props.store.failedLoading;
|
return this.props.store.failedLoading;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderRow(item: I) {
|
renderRow(item: Item) {
|
||||||
return this.getTableRow(item);
|
return this.getTableRow(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTableRow(item: I) {
|
getTableRow(item: Item) {
|
||||||
const {
|
const {
|
||||||
isSelectable, renderTableHeader, renderTableContents, renderItemMenu,
|
isSelectable, renderTableHeader, renderTableContents, renderItemMenu,
|
||||||
store, hasDetailsView, onDetails,
|
store, hasDetailsView, onDetails,
|
||||||
@ -150,7 +156,7 @@ export class ItemListLayoutContent<I extends ItemObject, PreLoadStores extends b
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeItemsDialog(selectedItems: I[]) {
|
removeItemsDialog(selectedItems: Item[]) {
|
||||||
const { customizeRemoveDialog, store } = this.props;
|
const { customizeRemoveDialog, store } = this.props;
|
||||||
const visibleMaxNamesCount = 5;
|
const visibleMaxNamesCount = 5;
|
||||||
const selectedNames = selectedItems.map(ns => ns.getName()).slice(0, visibleMaxNamesCount).join(", ");
|
const selectedNames = selectedItems.map(ns => ns.getName()).slice(0, visibleMaxNamesCount).join(", ");
|
||||||
@ -275,10 +281,10 @@ export class ItemListLayoutContent<I extends ItemObject, PreLoadStores extends b
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks,
|
store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks,
|
||||||
detailsItem, className, tableProps = {}, tableId, getItems,
|
detailsItem, className, tableProps = {}, tableId, getItems, themeStore,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const selectedItemId = detailsItem && detailsItem.getId();
|
const selectedItemId = detailsItem && detailsItem.getId();
|
||||||
const classNames = cssNames(className, "box", "grow", ThemeStore.getInstance().activeTheme.type);
|
const classNames = cssNames(className, "box", "grow", themeStore.activeTheme.type);
|
||||||
const items = getItems();
|
const items = getItems();
|
||||||
const selectedItems = store.pickOnlySelected(items);
|
const selectedItems = store.pickOnlySelected(items);
|
||||||
|
|
||||||
@ -356,3 +362,10 @@ export class ItemListLayoutContent<I extends ItemObject, PreLoadStores extends b
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ItemListLayoutContent = withInjectables<Dependencies, ItemListLayoutContentProps<ItemObject, boolean>>(NonInjectedItemListLayoutContent, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
...props,
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
|
}),
|
||||||
|
}) as <Item extends ItemObject, PreLoadStores extends boolean>(props: ItemListLayoutContentProps<Item, PreLoadStores>) => React.ReactElement;
|
||||||
|
|||||||
@ -13,7 +13,9 @@ import { type MonacoValidator, monacoValidators } from "./monaco-validators";
|
|||||||
import { debounce, merge } from "lodash";
|
import { debounce, merge } from "lodash";
|
||||||
import { autoBind, cssNames, disposer } from "../../utils";
|
import { autoBind, cssNames, disposer } from "../../utils";
|
||||||
import { UserStore } from "../../../common/user-store";
|
import { UserStore } from "../../../common/user-store";
|
||||||
import { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
import logger from "../../../main/logger";
|
import logger from "../../../main/logger";
|
||||||
|
|
||||||
export type MonacoEditorId = string;
|
export type MonacoEditorId = string;
|
||||||
@ -35,22 +37,21 @@ export interface MonacoEditorProps {
|
|||||||
onModelChange?(model: editor.ITextModel, prev?: editor.ITextModel): void;
|
onModelChange?(model: editor.ITextModel, prev?: editor.ITextModel): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultEditorProps: Partial<MonacoEditorProps> = {
|
interface Dependencies {
|
||||||
language: "yaml",
|
themeStore: ThemeStore;
|
||||||
get theme(): MonacoTheme {
|
}
|
||||||
// theme for monaco-editor defined in `src/renderer/themes/lens-*.json`
|
|
||||||
return ThemeStore.getInstance().activeTheme.monacoTheme;
|
export function createMonacoUri(id: MonacoEditorId): Uri {
|
||||||
},
|
return Uri.file(`/monaco-editor/${id}`);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
const monacoViewStates = new WeakMap<Uri, editor.ICodeEditorViewState>();
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class MonacoEditor extends React.Component<MonacoEditorProps> {
|
class NonInjectedMonacoEditor extends React.Component<MonacoEditorProps & Dependencies> {
|
||||||
static readonly defaultProps = defaultEditorProps as object;
|
static defaultProps = {
|
||||||
static readonly viewStates = new WeakMap<Uri, editor.ICodeEditorViewState>();
|
language: "yaml" as const,
|
||||||
|
};
|
||||||
static createUri(id: MonacoEditorId): Uri {
|
|
||||||
return Uri.file(`/monaco-editor/${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
private staticId = `editor-id#${Math.round(1e7 * Math.random())}`;
|
private staticId = `editor-id#${Math.round(1e7 * Math.random())}`;
|
||||||
private dispose = disposer();
|
private dispose = disposer();
|
||||||
@ -60,7 +61,7 @@ export class MonacoEditor extends React.Component<MonacoEditorProps> {
|
|||||||
@observable readonly dimensions: { width?: number; height?: number } = {};
|
@observable readonly dimensions: { width?: number; height?: number } = {};
|
||||||
@observable unmounting = false;
|
@observable unmounting = false;
|
||||||
|
|
||||||
constructor(props: MonacoEditorProps) {
|
constructor(props: MonacoEditorProps & Dependencies) {
|
||||||
super(props);
|
super(props);
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
autoBind(this);
|
autoBind(this);
|
||||||
@ -70,8 +71,12 @@ export class MonacoEditor extends React.Component<MonacoEditorProps> {
|
|||||||
return this.props.id ?? this.staticId;
|
return this.props.id ?? this.staticId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@computed get theme() {
|
||||||
|
return this.props.theme ?? this.props.themeStore.activeTheme.monacoTheme;
|
||||||
|
}
|
||||||
|
|
||||||
@computed get model(): editor.ITextModel {
|
@computed get model(): editor.ITextModel {
|
||||||
const uri = MonacoEditor.createUri(this.id);
|
const uri = createMonacoUri(this.id);
|
||||||
const model = editor.getModel(uri);
|
const model = editor.getModel(uri);
|
||||||
|
|
||||||
if (model) {
|
if (model) {
|
||||||
@ -141,12 +146,12 @@ export class MonacoEditor extends React.Component<MonacoEditorProps> {
|
|||||||
const viewState = this.editor?.saveViewState();
|
const viewState = this.editor?.saveViewState();
|
||||||
|
|
||||||
if (viewState) {
|
if (viewState) {
|
||||||
MonacoEditor.viewStates.set(model.uri, viewState);
|
monacoViewStates.set(model.uri, viewState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected restoreViewState(model: editor.ITextModel) {
|
protected restoreViewState(model: editor.ITextModel) {
|
||||||
const viewState = MonacoEditor.viewStates.get(model.uri);
|
const viewState = monacoViewStates.get(model.uri);
|
||||||
|
|
||||||
if (viewState) {
|
if (viewState) {
|
||||||
this.editor?.restoreViewState(viewState);
|
this.editor?.restoreViewState(viewState);
|
||||||
@ -177,7 +182,8 @@ export class MonacoEditor extends React.Component<MonacoEditorProps> {
|
|||||||
if (!this.containerElem || this.editor || this.unmounting) {
|
if (!this.containerElem || this.editor || this.unmounting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { language, theme, readOnly, value: defaultValue } = this.props;
|
const { language, readOnly, value: defaultValue } = this.props;
|
||||||
|
const { theme } = this;
|
||||||
|
|
||||||
this.editor = editor.create(this.containerElem, {
|
this.editor = editor.create(this.containerElem, {
|
||||||
model: this.model,
|
model: this.model,
|
||||||
@ -214,7 +220,7 @@ export class MonacoEditor extends React.Component<MonacoEditorProps> {
|
|||||||
|
|
||||||
this.dispose.push(
|
this.dispose.push(
|
||||||
reaction(() => this.model, this.onModelChange),
|
reaction(() => this.model, this.onModelChange),
|
||||||
reaction(() => this.props.theme, theme => {
|
reaction(() => this.theme, theme => {
|
||||||
if (theme) {
|
if (theme) {
|
||||||
editor.setTheme(theme);
|
editor.setTheme(theme);
|
||||||
}
|
}
|
||||||
@ -283,3 +289,10 @@ export class MonacoEditor extends React.Component<MonacoEditorProps> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const MonacoEditor = withInjectables<Dependencies, MonacoEditorProps>(NonInjectedMonacoEditor, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
...props,
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|||||||
@ -6,8 +6,6 @@ import React from "react";
|
|||||||
import "@testing-library/jest-dom/extend-expect";
|
import "@testing-library/jest-dom/extend-expect";
|
||||||
import type { SelectOption } from "./select";
|
import type { SelectOption } from "./select";
|
||||||
import { Select } from "./select";
|
import { Select } from "./select";
|
||||||
import { UserStore } from "../../../common/user-store";
|
|
||||||
import { ThemeStore } from "../../theme.store";
|
|
||||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||||
import type { DiContainer } from "@ogre-tools/injectable";
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
import type { DiRender } from "../test-utils/renderFor";
|
import type { DiRender } from "../test-utils/renderFor";
|
||||||
@ -42,15 +40,9 @@ describe("<Select />", () => {
|
|||||||
|
|
||||||
di.permitSideEffects(getConfigurationFileModelInjectable);
|
di.permitSideEffects(getConfigurationFileModelInjectable);
|
||||||
di.permitSideEffects(appVersionInjectable);
|
di.permitSideEffects(appVersionInjectable);
|
||||||
|
|
||||||
UserStore.createInstance();
|
|
||||||
ThemeStore.createInstance();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
ThemeStore.resetInstance();
|
|
||||||
UserStore.resetInstance();
|
|
||||||
|
|
||||||
mockFs.restore();
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -12,9 +12,11 @@ import { action, computed, makeObservable } from "mobx";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import ReactSelect, { components, createFilter } from "react-select";
|
import ReactSelect, { components, createFilter } from "react-select";
|
||||||
import type { Props as ReactSelectProps, GroupBase, MultiValue, OptionsOrGroups, PropsValue, SingleValue } from "react-select";
|
import type { Props as ReactSelectProps, GroupBase, MultiValue, OptionsOrGroups, PropsValue, SingleValue } from "react-select";
|
||||||
import { ThemeStore } from "../../theme.store";
|
import type { ThemeStore } from "../../themes/store";
|
||||||
import { autoBind, cssNames } from "../../utils";
|
import { autoBind, cssNames } from "../../utils";
|
||||||
import type { SetRequired } from "type-fest";
|
import type { SetRequired } from "type-fest";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import themeStoreInjectable from "../../themes/store.injectable";
|
||||||
|
|
||||||
const { Menu } = components;
|
const { Menu } = components;
|
||||||
|
|
||||||
@ -60,26 +62,30 @@ const defaultFilter = createFilter({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
themeStore: ThemeStore;
|
||||||
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class Select<
|
class NonInjectedSelect<
|
||||||
Value,
|
Value,
|
||||||
Option extends SelectOption<Value>,
|
Option extends SelectOption<Value>,
|
||||||
IsMulti extends boolean = false,
|
IsMulti extends boolean = false,
|
||||||
Group extends GroupBase<Option> = GroupBase<Option>,
|
Group extends GroupBase<Option> = GroupBase<Option>,
|
||||||
> extends React.Component<SelectProps<Value, Option, IsMulti, Group>> {
|
> extends React.Component<SelectProps<Value, Option, IsMulti, Group> & Dependencies> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
menuPortalTarget: document.body,
|
menuPortalTarget: document.body,
|
||||||
menuPlacement: "auto",
|
menuPlacement: "auto" as const,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: SelectProps<Value, Option, IsMulti, Group>) {
|
constructor(props: SelectProps<Value, Option, IsMulti, Group> & Dependencies) {
|
||||||
super(props);
|
super(props);
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
autoBind(this);
|
autoBind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get themeClass() {
|
@computed get themeClass() {
|
||||||
const themeName = this.props.themeName || ThemeStore.getInstance().activeTheme.type;
|
const themeName = this.props.themeName || this.props.themeStore.activeTheme.type;
|
||||||
|
|
||||||
return `theme-${themeName}`;
|
return `theme-${themeName}`;
|
||||||
}
|
}
|
||||||
@ -183,3 +189,15 @@ export class Select<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const Select = withInjectables<Dependencies, SelectProps<unknown, SelectOption<unknown>, boolean>>(NonInjectedSelect, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
...props,
|
||||||
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
|
}),
|
||||||
|
}) as <
|
||||||
|
Value,
|
||||||
|
Option extends SelectOption<Value>,
|
||||||
|
IsMulti extends boolean = false,
|
||||||
|
Group extends GroupBase<Option> = GroupBase<Option>,
|
||||||
|
>(props: SelectProps<Value, Option, IsMulti, Group>) => React.ReactElement;
|
||||||
|
|||||||
@ -1,20 +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 { ThemeStore } from "./theme.store";
|
|
||||||
|
|
||||||
const themeStoreInjectable = getInjectable({
|
|
||||||
id: "theme-store",
|
|
||||||
|
|
||||||
instantiate: () => {
|
|
||||||
ThemeStore.resetInstance();
|
|
||||||
|
|
||||||
return ThemeStore.createInstance();
|
|
||||||
},
|
|
||||||
|
|
||||||
causesSideEffects: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default themeStoreInjectable;
|
|
||||||
@ -3,7 +3,7 @@
|
|||||||
* 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 type { Theme } from "../theme.store";
|
import type { Theme } from "./store";
|
||||||
|
|
||||||
const lensDarkTheme: Theme = {
|
const lensDarkTheme: Theme = {
|
||||||
"name": "Dark",
|
"name": "Dark",
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
* 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 type { Theme } from "../theme.store";
|
import type { Theme } from "./store";
|
||||||
|
|
||||||
const lensLightTheme: Theme = {
|
const lensLightTheme: Theme = {
|
||||||
"name": "Light",
|
"name": "Light",
|
||||||
|
|||||||
21
src/renderer/themes/store.injectable.ts
Normal file
21
src/renderer/themes/store.injectable.ts
Normal file
@ -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 userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
|
import ipcRendererInjectable from "../app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||||
|
import { ThemeStore } from "./store";
|
||||||
|
|
||||||
|
const themeStoreInjectable = getInjectable({
|
||||||
|
id: "theme-store",
|
||||||
|
|
||||||
|
instantiate: (di) => new ThemeStore({
|
||||||
|
ipcRenderer: di.inject(ipcRendererInjectable),
|
||||||
|
userStore: di.inject(userStoreInjectable),
|
||||||
|
}),
|
||||||
|
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default themeStoreInjectable;
|
||||||
@ -4,17 +4,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { comparer, computed, makeObservable, observable, reaction } from "mobx";
|
import { comparer, computed, makeObservable, observable, reaction } from "mobx";
|
||||||
import { autoBind, Singleton } from "./utils";
|
import { autoBind } from "../utils";
|
||||||
import { UserStore } from "../common/user-store";
|
import logger from "../../main/logger";
|
||||||
import logger from "../main/logger";
|
import lensDarkTheme from "./lens-dark";
|
||||||
import lensDarkTheme from "./themes/lens-dark";
|
import lensLightTheme from "./lens-light";
|
||||||
import lensLightTheme from "./themes/lens-light";
|
import type { MonacoTheme } from "../components/monaco-editor";
|
||||||
import type { MonacoTheme } from "./components/monaco-editor";
|
import { defaultThemeId } from "../../common/vars";
|
||||||
import { defaultTheme } from "../common/vars";
|
|
||||||
import { camelCase } from "lodash";
|
import { camelCase } from "lodash";
|
||||||
import { ipcRenderer } from "electron";
|
import type { IpcRenderer } from "electron";
|
||||||
import { getNativeThemeChannel, setNativeThemeChannel } from "../common/ipc/native-theme";
|
import { getNativeThemeChannel, setNativeThemeChannel } from "../../common/ipc/native-theme";
|
||||||
import type { ReadonlyDeep } from "type-fest/source/readonly-deep";
|
import type { ReadonlyDeep } from "type-fest/source/readonly-deep";
|
||||||
|
import assert from "assert";
|
||||||
|
|
||||||
export type ThemeId = string;
|
export type ThemeId = string;
|
||||||
|
|
||||||
@ -27,7 +27,16 @@ export interface Theme {
|
|||||||
monacoTheme: MonacoTheme;
|
monacoTheme: MonacoTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ThemeStore extends Singleton {
|
interface Dependencies {
|
||||||
|
readonly userStore: {
|
||||||
|
colorTheme: string;
|
||||||
|
terminalTheme: ThemeId;
|
||||||
|
resetTheme(): void;
|
||||||
|
};
|
||||||
|
readonly ipcRenderer: IpcRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ThemeStore {
|
||||||
private terminalColorPrefix = "terminal";
|
private terminalColorPrefix = "terminal";
|
||||||
|
|
||||||
#themes = observable.map<ThemeId, Theme>({
|
#themes = observable.map<ThemeId, Theme>({
|
||||||
@ -38,18 +47,19 @@ export class ThemeStore extends Singleton {
|
|||||||
@observable osNativeTheme: "dark" | "light" | undefined;
|
@observable osNativeTheme: "dark" | "light" | undefined;
|
||||||
|
|
||||||
@computed get activeThemeId(): ThemeId {
|
@computed get activeThemeId(): ThemeId {
|
||||||
return UserStore.getInstance().colorTheme;
|
return this.dependencies.userStore.colorTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get terminalThemeId(): ThemeId | undefined {
|
@computed get terminalThemeId(): ThemeId {
|
||||||
return UserStore.getInstance().terminalTheme;
|
return this.dependencies.userStore.terminalTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly defaultTheme: Theme;
|
||||||
|
|
||||||
@computed get activeTheme(): Theme {
|
@computed get activeTheme(): Theme {
|
||||||
return this.systemTheme
|
return this.systemTheme
|
||||||
?? this.#themes.get(this.activeThemeId)
|
?? this.#themes.get(this.activeThemeId)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
?? this.defaultTheme;
|
||||||
?? this.#themes.get(defaultTheme)!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get terminalColors(): [string, string][] {
|
@computed get terminalColors(): [string, string][] {
|
||||||
@ -85,12 +95,16 @@ export class ThemeStore extends Singleton {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor(protected readonly dependencies: Dependencies) {
|
||||||
super();
|
|
||||||
|
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
autoBind(this);
|
autoBind(this);
|
||||||
this.init();
|
this.init();
|
||||||
|
|
||||||
|
const defaultTheme = this.#themes.get(defaultThemeId);
|
||||||
|
|
||||||
|
assert(defaultTheme, `${defaultThemeId} is invalid as there is no corresponding theme`);
|
||||||
|
|
||||||
|
this.defaultTheme = defaultTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
@ -106,7 +120,7 @@ export class ThemeStore extends Singleton {
|
|||||||
this.applyActiveTheme();
|
this.applyActiveTheme();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
UserStore.getInstance().resetTheme();
|
this.dependencies.userStore.resetTheme();
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
fireImmediately: true,
|
fireImmediately: true,
|
||||||
@ -115,14 +129,14 @@ export class ThemeStore extends Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bindNativeThemeUpdateEvent() {
|
bindNativeThemeUpdateEvent() {
|
||||||
ipcRenderer.on(setNativeThemeChannel, (event, theme: "dark" | "light") => {
|
this.dependencies.ipcRenderer.on(setNativeThemeChannel, (event, theme: "dark" | "light") => {
|
||||||
this.osNativeTheme = theme;
|
this.osNativeTheme = theme;
|
||||||
this.applyActiveTheme();
|
this.applyActiveTheme();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setNativeTheme() {
|
async setNativeTheme() {
|
||||||
const theme: "dark" | "light" = await ipcRenderer.invoke(getNativeThemeChannel);
|
const theme: "dark" | "light" = await this.dependencies.ipcRenderer.invoke(getNativeThemeChannel);
|
||||||
|
|
||||||
this.osNativeTheme = theme;
|
this.osNativeTheme = theme;
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user