mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Technical requirements for behavioural unit tests (#5084)
* Implement a lot of technical requirements for behavioural unit tests Note: the crux of this was to make routing env-agnostic, and not based on URLs as magic strings, but instead something type-enforced. Note: extension-based routes comply to same exact interface by "late-registering" their routes when installed. Routes are just injectables. Note: another chunk of global shared state is no more. Note: a lot of explicit side effects have been cornered to injectables. Note: a lot of stuff has become reactive as part if this. Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make a directory commonly available Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Require id for <Select /> to prevent non-deterministic renders This was caused by global state in a 3rd party lib: "react-select". Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Specify id for all <Select /> to satisfy previous commit Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Prevent explicit side effect in component by using existing dependency instead Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract instantiation of "conf" as injectables for causing side effects Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce a legacy-helper to make gradual refactoring of inheritors of Singleton easier Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make legacy unit tests for hotbar green and more simple by using the new legacy helper Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Temporarily kludge all unit tests green with a disclaimer about allowing side-effects Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove kludge in previous commit by explicitly permitting specific side effects where old unit tests require it Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Prevent old unit test with side effects from accessing file system Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Migrate to actual typing for di.permitSideEffects Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Prevent unit tests from failing because of non-standard method of HTML-element not present in js-dom Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Adapt integration tests to recent changes Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Fix code style Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Fix artifact from bad rebase Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Add a deprecation from a review comment Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove change that is not required Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant comment Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix code style Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant file Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix bad merge Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Improve variable name Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Tweak logger interface to be more descriptive Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make injecting legacy singleton always provide new instance Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove conditional typing when not needed Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Improve naming of variable Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove unnecessary code style changes Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove flag for causing side effects from too broad scope Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Override side-effects in unit test using injectable instead of monkey patching Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Flag some side-effects and add general overrides Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Fix unit tests in CI by removing explicit side-effect Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove explicit side-effect from getting default shell Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce abstraction for getting absolute paths Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using abstraction for getting absolute path to control explicit side effect Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce abstraction for joining paths Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using abstraction for joining paths to control explicit side effect Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix fake implementation for join paths Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix test after removing explicit side effect Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove explicit side effects from kubeconfig-syncs Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix arguments after removing explicit side effect Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make registrators not async for not being needed anymore Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make generalCatalogEntities non-observable, as there is no requirement Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove redundant code Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify logic for registering general catalog entity sources Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add TODO Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Replace function for getting application menu items with reactive solution Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix typo in interface name Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove global shared state usages of hot bar store Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant enum Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
1 parent
ab2cd27a27
commit
a277cfcf02
583 files changed
+13073
-6556
No files matched your search
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import appPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/app/app-preferences-route.injectable";
|
||||
import { Application } from "./application";
|
||||
|
||||
const appPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "app-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(appPreferencesRouteInjectable),
|
||||
Component: Application,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default appPreferencesRouteComponentInjectable;
|
||||
@@ -7,8 +7,8 @@ import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { Select, SelectOption } from "../select";
|
||||
import { ThemeStore } from "../../theme.store";
|
||||
import { UserStore } from "../../../common/user-store";
|
||||
import type { ThemeStore } from "../../theme.store";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { Input } from "../input";
|
||||
import { Switch } from "../switch";
|
||||
import moment from "moment-timezone";
|
||||
@@ -19,6 +19,9 @@ import { ExtensionSettings } from "./extension-settings";
|
||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import appPreferencesInjectable from "./app-preferences/app-preferences.injectable";
|
||||
import { Preferences } from "./preferences";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import themeStoreInjectable from "../../theme-store.injectable";
|
||||
|
||||
const timezoneOptions: SelectOption<string>[] = moment.tz.names().map(zone => ({
|
||||
label: zone,
|
||||
@@ -31,100 +34,106 @@ const updateChannelOptions: SelectOption<string>[] = Array.from(
|
||||
|
||||
interface Dependencies {
|
||||
appPreferenceItems: IComputedValue<RegisteredAppPreference[]>;
|
||||
userStore: UserStore;
|
||||
themeStore: ThemeStore;
|
||||
}
|
||||
|
||||
const NonInjectedApplication: React.FC<Dependencies> = ({ appPreferenceItems }) => {
|
||||
const userStore = UserStore.getInstance();
|
||||
const NonInjectedApplication: React.FC<Dependencies> = ({ appPreferenceItems, userStore, themeStore }) => {
|
||||
const [customUrl, setCustomUrl] = React.useState(userStore.extensionRegistryUrl.customUrl || "");
|
||||
const extensionSettings = appPreferenceItems.get().filter((preference) => preference.showInPreferencesTab === "application");
|
||||
const themeStore = ThemeStore.getInstance();
|
||||
|
||||
return (
|
||||
<section id="application">
|
||||
<h2 data-testid="application-header">Application</h2>
|
||||
<section id="appearance">
|
||||
<SubTitle title="Theme" />
|
||||
<Select
|
||||
options={[
|
||||
{ label: "Sync with computer", value: "system" },
|
||||
...themeStore.themeOptions,
|
||||
]}
|
||||
value={userStore.colorTheme}
|
||||
onChange={({ value }) => userStore.colorTheme = value}
|
||||
themeName="lens"
|
||||
/>
|
||||
<Preferences data-testid="application-preferences-page">
|
||||
<section id="application">
|
||||
<h2 data-testid="application-header">Application</h2>
|
||||
<section id="appearance">
|
||||
<SubTitle title="Theme" />
|
||||
<Select
|
||||
id="theme-input"
|
||||
options={[
|
||||
{ label: "Sync with computer", value: "system" },
|
||||
...themeStore.themeOptions,
|
||||
]}
|
||||
value={userStore.colorTheme}
|
||||
onChange={({ value }) => userStore.colorTheme = value}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section id="extensionRegistryUrl">
|
||||
<SubTitle title="Extension Install Registry" />
|
||||
<Select
|
||||
id="extension-install-registry-input"
|
||||
options={Object.values(ExtensionRegistryLocation)}
|
||||
value={userStore.extensionRegistryUrl.location}
|
||||
onChange={action(({ value }) => {
|
||||
userStore.extensionRegistryUrl.location = value;
|
||||
|
||||
if (userStore.extensionRegistryUrl.location === ExtensionRegistryLocation.CUSTOM) {
|
||||
userStore.extensionRegistryUrl.customUrl = "";
|
||||
}
|
||||
})}
|
||||
themeName="lens"
|
||||
/>
|
||||
<p className="mt-4 mb-5 leading-relaxed">
|
||||
This setting is to change the registry URL for installing extensions by name.{" "}
|
||||
If you are unable to access the default registry ({defaultExtensionRegistryUrl}){" "}
|
||||
you can change it in your <b>.npmrc</b> file or in the input below.
|
||||
</p>
|
||||
|
||||
<Input
|
||||
theme="round-black"
|
||||
validators={isUrl}
|
||||
value={customUrl}
|
||||
onChange={setCustomUrl}
|
||||
onBlur={() => userStore.extensionRegistryUrl.customUrl = customUrl}
|
||||
placeholder="Custom Extension Registry URL..."
|
||||
disabled={userStore.extensionRegistryUrl.location !== ExtensionRegistryLocation.CUSTOM}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section id="other">
|
||||
<SubTitle title="Start-up" />
|
||||
<Switch checked={userStore.openAtLogin} onChange={() => userStore.openAtLogin = !userStore.openAtLogin}>
|
||||
Automatically start Lens on login
|
||||
</Switch>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
{extensionSettings.map(setting => (
|
||||
<ExtensionSettings key={setting.id} setting={setting} size="normal" />
|
||||
))}
|
||||
|
||||
<section id="update-channel">
|
||||
<SubTitle title="Update Channel" />
|
||||
<Select
|
||||
id="update-channel-input"
|
||||
options={updateChannelOptions}
|
||||
value={userStore.updateChannel}
|
||||
onChange={({ value }) => userStore.updateChannel = value}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section id="locale">
|
||||
<SubTitle title="Locale Timezone" />
|
||||
<Select
|
||||
id="timezone-input"
|
||||
options={timezoneOptions}
|
||||
value={userStore.localeTimezone}
|
||||
onChange={({ value }) => userStore.setLocaleTimezone(value)}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<hr/>
|
||||
|
||||
<section id="extensionRegistryUrl">
|
||||
<SubTitle title="Extension Install Registry" />
|
||||
<Select
|
||||
options={Object.values(ExtensionRegistryLocation)}
|
||||
value={userStore.extensionRegistryUrl.location}
|
||||
onChange={action(({ value }) => {
|
||||
userStore.extensionRegistryUrl.location = value;
|
||||
|
||||
if (userStore.extensionRegistryUrl.location === ExtensionRegistryLocation.CUSTOM) {
|
||||
userStore.extensionRegistryUrl.customUrl = "";
|
||||
}
|
||||
})}
|
||||
themeName="lens"
|
||||
/>
|
||||
<p className="mt-4 mb-5 leading-relaxed">
|
||||
This setting is to change the registry URL for installing extensions by name.{" "}
|
||||
If you are unable to access the default registry ({defaultExtensionRegistryUrl}){" "}
|
||||
you can change it in your <b>.npmrc</b> file or in the input below.
|
||||
</p>
|
||||
|
||||
<Input
|
||||
theme="round-black"
|
||||
validators={isUrl}
|
||||
value={customUrl}
|
||||
onChange={setCustomUrl}
|
||||
onBlur={() => userStore.extensionRegistryUrl.customUrl = customUrl}
|
||||
placeholder="Custom Extension Registry URL..."
|
||||
disabled={userStore.extensionRegistryUrl.location !== ExtensionRegistryLocation.CUSTOM}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section id="other">
|
||||
<SubTitle title="Start-up" />
|
||||
<Switch checked={userStore.openAtLogin} onChange={() => userStore.openAtLogin = !userStore.openAtLogin}>
|
||||
Automatically start Lens on login
|
||||
</Switch>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
{extensionSettings.map(setting => (
|
||||
<ExtensionSettings key={setting.id} setting={setting} size="normal" />
|
||||
))}
|
||||
|
||||
<section id="update-channel">
|
||||
<SubTitle title="Update Channel" />
|
||||
<Select
|
||||
options={updateChannelOptions}
|
||||
value={userStore.updateChannel}
|
||||
onChange={({ value }) => userStore.updateChannel = value}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section id="locale">
|
||||
<SubTitle title="Locale Timezone" />
|
||||
<Select
|
||||
options={timezoneOptions}
|
||||
value={userStore.localeTimezone}
|
||||
onChange={({ value }) => userStore.setLocaleTimezone(value)}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
</Preferences>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -134,6 +143,8 @@ export const Application = withInjectables<Dependencies>(
|
||||
{
|
||||
getProps: (di) => ({
|
||||
appPreferenceItems: di.inject(appPreferencesInjectable),
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
themeStore: di.inject(themeStoreInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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 observableHistoryInjectable from "../../navigation/observable-history.injectable";
|
||||
import navigateToFrontPageInjectable from "../../../common/front-end-routing/navigate-to-front-page.injectable";
|
||||
|
||||
const closePreferencesInjectable = getInjectable({
|
||||
id: "close-preferences",
|
||||
|
||||
instantiate: (di) => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
const navigateToFrontPage = di.inject(navigateToFrontPageInjectable);
|
||||
|
||||
return () => {
|
||||
if (observableHistory.length === 1) {
|
||||
navigateToFrontPage();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
observableHistory.goBack();
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default closePreferencesInjectable;
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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 isWindowsInjectable from "../../../common/vars/is-windows.injectable";
|
||||
|
||||
const defaultShellInjectable = getInjectable({
|
||||
id: "default-shell",
|
||||
|
||||
instantiate: (di) => {
|
||||
const isWindows = di.inject(isWindowsInjectable);
|
||||
|
||||
return (
|
||||
process.env.SHELL ||
|
||||
process.env.PTYSHELL ||
|
||||
(isWindows ? "powershell.exe" : "System default shell")
|
||||
);
|
||||
},
|
||||
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default defaultShellInjectable;
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import { Editor } from "./editor";
|
||||
import editorPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/editor/editor-preferences-route.injectable";
|
||||
|
||||
const editorPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "editor-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(editorPreferencesRouteInjectable),
|
||||
Component: Editor,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default editorPreferencesRouteComponentInjectable;
|
||||
@@ -4,12 +4,15 @@
|
||||
*/
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { UserStore } from "../../../common/user-store";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { Switch } from "../switch";
|
||||
import { Select } from "../select";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { SubHeader } from "../layout/sub-header";
|
||||
import { Input, InputValidators } from "../input";
|
||||
import { Preferences } from "./preferences";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
|
||||
enum EditorLineNumbersStyles {
|
||||
on = "On",
|
||||
@@ -18,78 +21,100 @@ enum EditorLineNumbersStyles {
|
||||
interval = "Interval",
|
||||
}
|
||||
|
||||
export const Editor = observer(() => {
|
||||
const editorConfiguration = UserStore.getInstance().editorConfiguration;
|
||||
interface Dependencies {
|
||||
userStore: UserStore;
|
||||
}
|
||||
|
||||
const NonInjectedEditor = observer(({ userStore }: Dependencies) => {
|
||||
const editorConfiguration = userStore.editorConfiguration;
|
||||
|
||||
return (
|
||||
<section id="editor">
|
||||
<h2 data-testid="editor-configuration-header">Editor configuration</h2>
|
||||
<Preferences data-testid="editor-preferences-page">
|
||||
<section id="editor">
|
||||
<h2 data-testid="editor-configuration-header">Editor configuration</h2>
|
||||
|
||||
<SubTitle title="Minimap"/>
|
||||
<section>
|
||||
<div className="flex gaps justify-space-between">
|
||||
<div className="flex gaps align-center">
|
||||
<Switch
|
||||
checked={editorConfiguration.minimap.enabled}
|
||||
onChange={() => editorConfiguration.minimap.enabled = !editorConfiguration.minimap.enabled}
|
||||
>
|
||||
Show minimap
|
||||
</Switch>
|
||||
<SubTitle title="Minimap" />
|
||||
<section>
|
||||
<div className="flex gaps justify-space-between">
|
||||
<div className="flex gaps align-center">
|
||||
<Switch
|
||||
checked={editorConfiguration.minimap.enabled}
|
||||
onChange={() => editorConfiguration.minimap.enabled = !editorConfiguration.minimap.enabled}
|
||||
>
|
||||
Show minimap
|
||||
</Switch>
|
||||
</div>
|
||||
<div className="flex gaps align-center">
|
||||
<SubHeader compact>Position</SubHeader>
|
||||
<Select
|
||||
id="minimap-input"
|
||||
themeName="lens"
|
||||
options={["left", "right"]}
|
||||
value={editorConfiguration.minimap.side}
|
||||
onChange={({ value }) => editorConfiguration.minimap.side = value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gaps align-center">
|
||||
<SubHeader compact>Position</SubHeader>
|
||||
<Select
|
||||
themeName="lens"
|
||||
options={["left", "right"]}
|
||||
value={editorConfiguration.minimap.side}
|
||||
onChange={({ value }) => editorConfiguration.minimap.side = value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Line numbers"/>
|
||||
<Select
|
||||
options={Object.entries(EditorLineNumbersStyles).map(([value, label]) => ({ label, value }))}
|
||||
value={editorConfiguration.lineNumbers}
|
||||
onChange={({ value }) => editorConfiguration.lineNumbers = value}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<SubTitle title="Line numbers" />
|
||||
<Select
|
||||
id="editor-line-numbers-input"
|
||||
options={Object.entries(EditorLineNumbersStyles).map(([value, label]) => ({
|
||||
label,
|
||||
value,
|
||||
}))}
|
||||
value={editorConfiguration.lineNumbers}
|
||||
onChange={({ value }) => editorConfiguration.lineNumbers = value}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Tab size"/>
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="number"
|
||||
min={1}
|
||||
validators={InputValidators.isNumber}
|
||||
value={editorConfiguration.tabSize.toString()}
|
||||
onChange={value => editorConfiguration.tabSize = Number(value)}
|
||||
/>
|
||||
<section>
|
||||
<SubTitle title="Tab size" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="number"
|
||||
min={1}
|
||||
validators={InputValidators.isNumber}
|
||||
value={editorConfiguration.tabSize.toString()}
|
||||
onChange={value => editorConfiguration.tabSize = Number(value)}
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<SubTitle title="Font size" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="number"
|
||||
min={10}
|
||||
validators={InputValidators.isNumber}
|
||||
value={editorConfiguration.fontSize.toString()}
|
||||
onChange={value => editorConfiguration.fontSize = Number(value)}
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<SubTitle title="Font family" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="text"
|
||||
value={editorConfiguration.fontFamily}
|
||||
onChange={value => editorConfiguration.fontFamily = value}
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<SubTitle title="Font size"/>
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="number"
|
||||
min={10}
|
||||
validators={InputValidators.isNumber}
|
||||
value={editorConfiguration.fontSize.toString()}
|
||||
onChange={value => editorConfiguration.fontSize = Number(value)}
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<SubTitle title="Font family"/>
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="text"
|
||||
value={editorConfiguration.fontFamily}
|
||||
onChange={value => editorConfiguration.fontFamily = value}
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
</Preferences>
|
||||
);
|
||||
});
|
||||
|
||||
export const Editor = withInjectables<Dependencies>(
|
||||
NonInjectedEditor,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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 { filter, map } from "lodash/fp";
|
||||
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { extensionPreferenceItemInjectionToken } from "./extension-preference-items.injectable";
|
||||
|
||||
const extensionPreferenceItemRegistratorInjectable = getInjectable({
|
||||
id: "extension-preference-item-registrator",
|
||||
|
||||
instantiate:
|
||||
(di) =>
|
||||
(extension: LensRendererExtension, extensionInstallationCount) => {
|
||||
const injectables = pipeline(
|
||||
extension.appPreferences,
|
||||
|
||||
filter(
|
||||
(registration) => !registration.showInPreferencesTab,
|
||||
),
|
||||
|
||||
map((registration) => {
|
||||
const id = `extension-preferences-item-${registration.id}-for-extension-${extension.sanitizedExtensionId}`;
|
||||
|
||||
return getInjectable({
|
||||
id: `${id}-for-instance-${extensionInstallationCount}`,
|
||||
injectionToken: extensionPreferenceItemInjectionToken,
|
||||
|
||||
instantiate: () => ({
|
||||
id: registration.id || id,
|
||||
title: registration.title,
|
||||
extension,
|
||||
|
||||
components: {
|
||||
Hint: registration.components.Hint,
|
||||
Input: registration.components.Input,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
injectables.forEach(di.register);
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
injectionToken: extensionRegistratorInjectionToken,
|
||||
});
|
||||
|
||||
export default extensionPreferenceItemRegistratorInjectable;
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import { filter, overSome } from "lodash/fp";
|
||||
import { computed } from "mobx";
|
||||
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||
|
||||
interface ExtensionPreferenceItem extends RegisteredAppPreference {
|
||||
extension: LensRendererExtension;
|
||||
}
|
||||
|
||||
export const extensionPreferenceItemInjectionToken = getInjectionToken<ExtensionPreferenceItem>({
|
||||
id: "extension-preference-item-injection-token",
|
||||
});
|
||||
|
||||
const extensionsPreferenceItemsInjectable = getInjectable({
|
||||
id: "extension-preference-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const extensions = di.inject(rendererExtensionsInjectable);
|
||||
|
||||
return computed(() => {
|
||||
const enabledExtensions = extensions.get();
|
||||
|
||||
return pipeline(
|
||||
di.injectMany(extensionPreferenceItemInjectionToken),
|
||||
|
||||
filter((item) =>
|
||||
overSome([
|
||||
isNonExtensionItem,
|
||||
isEnabledExtensionItemFor(enabledExtensions),
|
||||
])(item),
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const isNonExtensionItem = (item: ExtensionPreferenceItem) => !item.extension;
|
||||
|
||||
const isEnabledExtensionItemFor =
|
||||
(enabledExtensions: LensRendererExtension[]) => (item: ExtensionPreferenceItem) =>
|
||||
!!enabledExtensions.find((extension) => extension === item.extension);
|
||||
|
||||
export default extensionsPreferenceItemsInjectable;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import extensionPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
|
||||
import { Extensions } from "./extensions";
|
||||
|
||||
const extensionPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "extension-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(extensionPreferencesRouteInjectable),
|
||||
Component: Extensions,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default extensionPreferencesRouteComponentInjectable;
|
||||
@@ -4,15 +4,15 @@
|
||||
*/
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import type { AppPreferenceRegistration } from "./app-preferences/app-preference-registration";
|
||||
import React from "react";
|
||||
import React, { DOMAttributes } from "react";
|
||||
import { cssNames } from "../../../renderer/utils";
|
||||
|
||||
export interface ExtensionSettingsProps {
|
||||
export interface ExtensionSettingsProps extends DOMAttributes<HTMLDivElement> {
|
||||
setting: AppPreferenceRegistration;
|
||||
size: "small" | "normal";
|
||||
}
|
||||
|
||||
export function ExtensionSettings({ setting, size }: ExtensionSettingsProps) {
|
||||
export function ExtensionSettings({ setting, size, ...props }: ExtensionSettingsProps) {
|
||||
const {
|
||||
title,
|
||||
id,
|
||||
@@ -21,7 +21,7 @@ export function ExtensionSettings({ setting, size }: ExtensionSettingsProps) {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<section id={id} className={cssNames(size)}>
|
||||
<section id={id} className={cssNames(size)} {...props}>
|
||||
<SubTitle title={title} />
|
||||
<Input />
|
||||
<div className="hint">
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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 { filter, map } from "lodash/fp";
|
||||
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { telemetryPreferenceItemInjectionToken } from "./telemetry-preference-items.injectable";
|
||||
|
||||
const extensionTelemetryPreferenceItemRegistratorInjectable = getInjectable({
|
||||
id: "extension-telemetry-preference-item-registrator",
|
||||
|
||||
instantiate:
|
||||
(di) =>
|
||||
(extension: LensRendererExtension, extensionInstallationCount) => {
|
||||
const injectables = pipeline(
|
||||
extension.appPreferences,
|
||||
|
||||
filter(
|
||||
(registration) => registration.showInPreferencesTab === "telemetry",
|
||||
),
|
||||
|
||||
map((registration) => {
|
||||
const id = `telemetry-preferences-item-${registration.id}-for-extension-${extension.sanitizedExtensionId}`;
|
||||
|
||||
return getInjectable({
|
||||
id: `${id}-for-instance-${extensionInstallationCount}`,
|
||||
injectionToken: telemetryPreferenceItemInjectionToken,
|
||||
|
||||
instantiate: () => ({
|
||||
id: registration.id || id,
|
||||
title: registration.title,
|
||||
extension,
|
||||
|
||||
components: {
|
||||
Hint: registration.components.Hint,
|
||||
Input: registration.components.Input,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
injectables.forEach(di.register);
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
injectionToken: extensionRegistratorInjectionToken,
|
||||
});
|
||||
|
||||
export default extensionTelemetryPreferenceItemRegistratorInjectable;
|
||||
@@ -8,33 +8,36 @@ import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||
import appPreferencesInjectable from "./app-preferences/app-preferences.injectable";
|
||||
import { ExtensionSettings } from "./extension-settings";
|
||||
import { Preferences } from "./preferences";
|
||||
import extensionsPreferenceItemsInjectable from "./extension-preference-items.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
appPreferenceItems: IComputedValue<RegisteredAppPreference[]>;
|
||||
preferenceItems: IComputedValue<RegisteredAppPreference[]>;
|
||||
}
|
||||
|
||||
const NonInjectedExtensions: React.FC<Dependencies> = ({ appPreferenceItems }) => {
|
||||
|
||||
const settings = appPreferenceItems.get();
|
||||
|
||||
return (
|
||||
const NonInjectedExtensions = ({ preferenceItems }: Dependencies) => (
|
||||
<Preferences data-testid="extension-preferences-page">
|
||||
<section id="extensions">
|
||||
<h2>Extensions</h2>
|
||||
{settings.filter(e => !e.showInPreferencesTab).map((setting) =>
|
||||
<ExtensionSettings key={setting.id} setting={setting} size="small" />,
|
||||
)}
|
||||
{preferenceItems.get().map((preferenceItem) => (
|
||||
<ExtensionSettings
|
||||
key={preferenceItem.id}
|
||||
setting={preferenceItem}
|
||||
size="small"
|
||||
data-testid={`extension-preference-item-for-${preferenceItem.id}`}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
</Preferences>
|
||||
);
|
||||
|
||||
export const Extensions = withInjectables<Dependencies>(
|
||||
observer(NonInjectedExtensions),
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
appPreferenceItems: di.inject(appPreferencesInjectable),
|
||||
preferenceItems: di.inject(extensionsPreferenceItemsInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -144,7 +144,8 @@ export class HelmCharts extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<div className="flex gaps">
|
||||
<Select id="HelmRepoSelect"
|
||||
<Select
|
||||
id="HelmRepoSelect"
|
||||
placeholder="Repositories"
|
||||
isLoading={this.loadingAvailableRepos}
|
||||
isDisabled={this.loadingAvailableRepos}
|
||||
|
||||
@@ -2,20 +2,22 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import fse from "fs-extra";
|
||||
import { computed, makeObservable, observable, reaction } from "mobx";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { Notice } from "../+extensions/notice";
|
||||
|
||||
import { KubeconfigSyncEntry, KubeconfigSyncValue, UserStore } from "../../../common/user-store";
|
||||
import { isWindows } from "../../../common/vars";
|
||||
import logger from "../../../main/logger";
|
||||
import type { KubeconfigSyncEntry, KubeconfigSyncValue, UserStore } from "../../../common/user-store";
|
||||
import { iter, tuple } from "../../utils";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { PathPicker } from "../path-picker/path-picker";
|
||||
import { Spinner } from "../spinner";
|
||||
import { RemovableItem } from "./removable-item";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import type { Logger } from "../../../common/logger";
|
||||
|
||||
interface SyncInfo {
|
||||
type: "file" | "folder" | "unknown";
|
||||
@@ -30,7 +32,7 @@ interface Value {
|
||||
info: SyncInfo;
|
||||
}
|
||||
|
||||
async function getMapEntry({ filePath, ...data }: KubeconfigSyncEntry): Promise<[string, Value]> {
|
||||
async function getMapEntry({ filePath, ...data }: KubeconfigSyncEntry, logger: Logger): Promise<[string, Value]> {
|
||||
try {
|
||||
// stat follows the stat(2) linux syscall spec, namely it follows symlinks
|
||||
const stats = await fse.stat(filePath);
|
||||
@@ -53,16 +55,22 @@ async function getMapEntry({ filePath, ...data }: KubeconfigSyncEntry): Promise<
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllEntries(filePaths: string[]): Promise<[string, Value][]> {
|
||||
return Promise.all(filePaths.map(filePath => getMapEntry({ filePath })));
|
||||
export async function getAllEntries(filePaths: string[], logger: Logger): Promise<[string, Value][]> {
|
||||
return Promise.all(filePaths.map(filePath => getMapEntry({ filePath }, logger)));
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
userStore: UserStore;
|
||||
isWindows: boolean;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class KubeconfigSyncs extends React.Component {
|
||||
class NonInjectedKubeconfigSyncs extends React.Component<Dependencies> {
|
||||
syncs = observable.map<string, Value>();
|
||||
@observable loaded = false;
|
||||
|
||||
constructor(props: {}) {
|
||||
constructor(props: Dependencies) {
|
||||
super(props);
|
||||
makeObservable(this);
|
||||
}
|
||||
@@ -70,8 +78,8 @@ export class KubeconfigSyncs extends React.Component {
|
||||
async componentDidMount() {
|
||||
const mapEntries = await Promise.all(
|
||||
iter.map(
|
||||
UserStore.getInstance().syncKubeconfigEntries,
|
||||
([filePath, ...value]) => getMapEntry({ filePath, ...value }),
|
||||
this.props.userStore.syncKubeconfigEntries,
|
||||
([filePath, ...value]) => getMapEntry({ filePath, ...value }, this.props.logger),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -82,7 +90,7 @@ export class KubeconfigSyncs extends React.Component {
|
||||
reaction(
|
||||
() => Array.from(this.syncs.entries(), ([filePath, { data }]) => tuple.from(filePath, data)),
|
||||
syncs => {
|
||||
UserStore.getInstance().syncKubeconfigEntries.replace(syncs);
|
||||
this.props.userStore.syncKubeconfigEntries.replace(syncs);
|
||||
},
|
||||
),
|
||||
]);
|
||||
@@ -97,7 +105,7 @@ export class KubeconfigSyncs extends React.Component {
|
||||
}
|
||||
|
||||
onPick = async (filePaths: string[]) => {
|
||||
this.syncs.merge(await getAllEntries(filePaths));
|
||||
this.syncs.merge(await getAllEntries(filePaths, this.props.logger));
|
||||
};
|
||||
|
||||
getIconName(entry: Entry) {
|
||||
@@ -153,7 +161,7 @@ export class KubeconfigSyncs extends React.Component {
|
||||
}
|
||||
|
||||
renderSyncButtons() {
|
||||
if (isWindows) {
|
||||
if (this.props.isWindows) {
|
||||
return (
|
||||
<div className="flex gaps align-center mb-5">
|
||||
<PathPicker
|
||||
@@ -195,3 +203,15 @@ export class KubeconfigSyncs extends React.Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const KubeconfigSyncs = withInjectables<Dependencies>(
|
||||
NonInjectedKubeconfigSyncs,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
isWindows: di.inject(isWindowsInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -7,92 +7,108 @@ import React, { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Input, InputValidators } from "../input";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { UserStore } from "../../../common/user-store";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { SelectOption, Select } from "../select";
|
||||
import { Switch } from "../switch";
|
||||
import { packageMirrors } from "../../../common/user-store/preferences-helpers";
|
||||
import directoryForBinariesInjectable from "../../../common/app-paths/directory-for-binaries/directory-for-binaries.injectable";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { kubectlBinaryPath } from "../../../common/vars";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import directoryForKubectlBinariesInjectable from "../../../common/app-paths/directory-for-kubectl-binaries/directory-for-kubectl-binaries.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
defaultPathForGeneralBinaries: string;
|
||||
defaultPathForKubectlBinaries: string;
|
||||
userStore: UserStore;
|
||||
}
|
||||
|
||||
const NonInjectedKubectlBinaries: React.FC<Dependencies> = observer(({ defaultPathForKubectlBinaries }) => {
|
||||
const userStore = UserStore.getInstance();
|
||||
const [downloadPath, setDownloadPath] = useState(userStore.downloadBinariesPath || "");
|
||||
const [binariesPath, setBinariesPath] = useState(userStore.kubectlBinariesPath || "");
|
||||
const pathValidator = downloadPath ? InputValidators.isPath : undefined;
|
||||
const downloadMirrorOptions: SelectOption<string>[] = Array.from(
|
||||
packageMirrors.entries(),
|
||||
([value, { label, platforms }]) => ({ value, label, platforms }),
|
||||
);
|
||||
const NonInjectedKubectlBinaries: React.FC<Dependencies> = observer(
|
||||
({ defaultPathForGeneralBinaries, defaultPathForKubectlBinaries, userStore }) => {
|
||||
const [downloadPath, setDownloadPath] = useState(
|
||||
userStore.downloadBinariesPath || "",
|
||||
);
|
||||
|
||||
const save = () => {
|
||||
userStore.downloadBinariesPath = downloadPath;
|
||||
userStore.kubectlBinariesPath = binariesPath;
|
||||
};
|
||||
const [binariesPath, setBinariesPath] = useState(
|
||||
userStore.kubectlBinariesPath || "",
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<SubTitle title="Kubectl binary download"/>
|
||||
<Switch
|
||||
checked={userStore.downloadKubectlBinaries}
|
||||
onChange={() => userStore.downloadKubectlBinaries = !userStore.downloadKubectlBinaries}
|
||||
>
|
||||
Download kubectl binaries matching the Kubernetes cluster version
|
||||
</Switch>
|
||||
</section>
|
||||
const pathValidator = downloadPath ? InputValidators.isPath : undefined;
|
||||
const downloadMirrorOptions: SelectOption<string>[] = Array.from(
|
||||
packageMirrors.entries(),
|
||||
([value, { label, platforms }]) => ({ value, label, platforms }),
|
||||
);
|
||||
|
||||
<section>
|
||||
<SubTitle title="Download mirror" />
|
||||
<Select
|
||||
placeholder="Download mirror for kubectl"
|
||||
options={downloadMirrorOptions}
|
||||
value={userStore.downloadMirror}
|
||||
onChange={({ value }: SelectOption) => userStore.downloadMirror = value}
|
||||
disabled={!userStore.downloadKubectlBinaries}
|
||||
isOptionDisabled={({ platforms }) => !platforms.has(process.platform)}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
const save = () => {
|
||||
userStore.downloadBinariesPath = downloadPath;
|
||||
userStore.kubectlBinariesPath = binariesPath;
|
||||
};
|
||||
|
||||
<section>
|
||||
<SubTitle title="Directory for binaries" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
value={downloadPath}
|
||||
placeholder={defaultPathForKubectlBinaries}
|
||||
validators={pathValidator}
|
||||
onChange={setDownloadPath}
|
||||
onBlur={save}
|
||||
disabled={!userStore.downloadKubectlBinaries}
|
||||
/>
|
||||
<div className="hint">
|
||||
The directory to download binaries into.
|
||||
</div>
|
||||
</section>
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<SubTitle title="Kubectl binary download" />
|
||||
<Switch
|
||||
checked={userStore.downloadKubectlBinaries}
|
||||
onChange={() => userStore.downloadKubectlBinaries = !userStore.downloadKubectlBinaries}
|
||||
>
|
||||
Download kubectl binaries matching the Kubernetes cluster version
|
||||
</Switch>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Path to kubectl binary" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
placeholder={kubectlBinaryPath.get()}
|
||||
value={binariesPath}
|
||||
validators={pathValidator}
|
||||
onChange={setBinariesPath}
|
||||
onBlur={save}
|
||||
disabled={userStore.downloadKubectlBinaries}
|
||||
/>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
});
|
||||
<section>
|
||||
<SubTitle title="Download mirror" />
|
||||
<Select
|
||||
id="download-mirror-input"
|
||||
placeholder="Download mirror for kubectl"
|
||||
options={downloadMirrorOptions}
|
||||
value={userStore.downloadMirror}
|
||||
onChange={({ value }: SelectOption) => userStore.downloadMirror = value}
|
||||
disabled={!userStore.downloadKubectlBinaries}
|
||||
isOptionDisabled={({ platforms }) =>
|
||||
!platforms.has(process.platform)
|
||||
}
|
||||
themeName="lens"
|
||||
/>
|
||||
</section>
|
||||
|
||||
export const KubectlBinaries = withInjectables<Dependencies>(NonInjectedKubectlBinaries, {
|
||||
getProps: (di) => ({
|
||||
defaultPathForKubectlBinaries: di.inject(directoryForBinariesInjectable),
|
||||
}),
|
||||
});
|
||||
<section>
|
||||
<SubTitle title="Directory for binaries" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
value={downloadPath}
|
||||
placeholder={defaultPathForGeneralBinaries}
|
||||
validators={pathValidator}
|
||||
onChange={setDownloadPath}
|
||||
onBlur={save}
|
||||
disabled={!userStore.downloadKubectlBinaries}
|
||||
/>
|
||||
<div className="hint">The directory to download binaries into.</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Path to kubectl binary" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
placeholder={defaultPathForKubectlBinaries}
|
||||
value={binariesPath}
|
||||
validators={pathValidator}
|
||||
onChange={setBinariesPath}
|
||||
onBlur={save}
|
||||
disabled={userStore.downloadKubectlBinaries}
|
||||
/>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export const KubectlBinaries = withInjectables<Dependencies>(
|
||||
NonInjectedKubectlBinaries,
|
||||
{
|
||||
getProps: (di) => ({
|
||||
defaultPathForGeneralBinaries: di.inject(directoryForBinariesInjectable),
|
||||
defaultPathForKubectlBinaries: di.inject(directoryForKubectlBinariesInjectable),
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import kubernetesPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/kubernetes/kubernetes-preferences-route.injectable";
|
||||
import { Kubernetes } from "./kubernetes";
|
||||
|
||||
const kubernetesPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "kubernetes-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(kubernetesPreferencesRouteInjectable),
|
||||
Component: Kubernetes,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default kubernetesPreferencesRouteComponentInjectable;
|
||||
@@ -8,24 +8,25 @@ import React from "react";
|
||||
import { HelmCharts } from "./helm-charts";
|
||||
import { KubeconfigSyncs } from "./kubeconfig-syncs";
|
||||
import { KubectlBinaries } from "./kubectl-binaries";
|
||||
import { Preferences } from "./preferences";
|
||||
|
||||
export const Kubernetes = observer(() => {
|
||||
return (
|
||||
export const Kubernetes = observer(() => (
|
||||
<Preferences data-testid="kubernetes-preferences-page">
|
||||
<section id="kubernetes">
|
||||
<section id="kubectl">
|
||||
<h2 data-testid="kubernetes-header">Kubernetes</h2>
|
||||
<KubectlBinaries />
|
||||
</section>
|
||||
<hr/>
|
||||
<hr />
|
||||
<section id="kube-sync">
|
||||
<h2 data-testid="kubernetes-sync-header">Kubeconfig Syncs</h2>
|
||||
<KubeconfigSyncs />
|
||||
</section>
|
||||
<hr/>
|
||||
<hr />
|
||||
<section id="helm">
|
||||
<h2>Helm Charts</h2>
|
||||
<HelmCharts/>
|
||||
<HelmCharts />
|
||||
</section>
|
||||
</section>
|
||||
);
|
||||
});
|
||||
</Preferences>
|
||||
));
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import appPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/app/app-preferences-route.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const applicationPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "application-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(appPreferencesRouteInjectable);
|
||||
const navigateToPreferenceTab = di.inject(navigateToPreferenceTabInjectable);
|
||||
|
||||
const routeIsActive = di.inject(
|
||||
routeIsActiveInjectable,
|
||||
route,
|
||||
);
|
||||
|
||||
return {
|
||||
id: "application",
|
||||
label: "App",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
isVisible: computed(() => true),
|
||||
orderNumber: 10,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default applicationPreferencesNavigationItemInjectable;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import editorPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/editor/editor-preferences-route.injectable";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const editorPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "editor-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(editorPreferencesRouteInjectable);
|
||||
const navigateToPreferenceTab = di.inject(navigateToPreferenceTabInjectable);
|
||||
|
||||
const routeIsActive = di.inject(
|
||||
routeIsActiveInjectable,
|
||||
route,
|
||||
);
|
||||
|
||||
return {
|
||||
id: "editor",
|
||||
label: "Editor",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
isVisible: computed(() => true),
|
||||
orderNumber: 40,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default editorPreferencesNavigationItemInjectable;
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import extensionsPreferenceItemsInjectable from "../extension-preference-items.injectable";
|
||||
import extensionPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/extension/extension-preferences-route.injectable";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const extensionsPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "extension-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const preferenceItems = di.inject(
|
||||
extensionsPreferenceItemsInjectable,
|
||||
);
|
||||
|
||||
const navigateToPreferenceTab = di.inject(
|
||||
navigateToPreferenceTabInjectable,
|
||||
);
|
||||
|
||||
const route = di.inject(
|
||||
extensionPreferencesRouteInjectable,
|
||||
);
|
||||
|
||||
const routeIsActive = di.inject(
|
||||
routeIsActiveInjectable,
|
||||
route,
|
||||
);
|
||||
|
||||
return {
|
||||
id: "extensions",
|
||||
label: "Extensions",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
|
||||
isVisible: computed(
|
||||
() => preferenceItems.get().length > 0,
|
||||
),
|
||||
|
||||
orderNumber: 70,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default extensionsPreferencesNavigationItemInjectable;
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import kubernetesPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/kubernetes/kubernetes-preferences-route.injectable";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const kubernetesPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "kubernetes-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const navigateToPreferenceTab = di.inject(navigateToPreferenceTabInjectable);
|
||||
|
||||
const route = di.inject(
|
||||
kubernetesPreferencesRouteInjectable,
|
||||
);
|
||||
|
||||
const routeIsActive = di.inject(
|
||||
routeIsActiveInjectable,
|
||||
route,
|
||||
);
|
||||
|
||||
return {
|
||||
id: "kubernetes",
|
||||
label: "Kubernetes",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
isVisible: computed(() => true),
|
||||
orderNumber: 30,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default kubernetesPreferencesNavigationItemInjectable;
|
||||
+21
@@ -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 type { Route } from "../../../../common/front-end-routing/route-injection-token";
|
||||
import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token";
|
||||
|
||||
const navigateToPreferenceTabInjectable = getInjectable({
|
||||
id: "navigate-to-preference-tab",
|
||||
|
||||
instantiate: (di) => {
|
||||
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
|
||||
|
||||
return (route: Route) => () => {
|
||||
navigateToRoute(route, { withoutAffectingBackButton: true });
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default navigateToPreferenceTabInjectable;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import { filter, orderBy } from "lodash/fp";
|
||||
import { computed, IComputedValue } from "mobx";
|
||||
|
||||
export const preferenceNavigationItemInjectionToken =
|
||||
getInjectionToken<PreferenceNavigationItem>({
|
||||
id: "preference-navigation-item-injection-token",
|
||||
});
|
||||
|
||||
export interface PreferenceNavigationItem {
|
||||
id: string;
|
||||
label: string;
|
||||
isActive: IComputedValue<boolean>;
|
||||
isVisible: IComputedValue<boolean>;
|
||||
navigate: () => void;
|
||||
orderNumber: number;
|
||||
}
|
||||
|
||||
const preferenceNavigationItemsInjectable = getInjectable({
|
||||
id: "preference-navigation-items",
|
||||
|
||||
instantiate: (di) =>
|
||||
computed((): PreferenceNavigationItem[] =>
|
||||
pipeline(
|
||||
di.injectMany(preferenceNavigationItemInjectionToken),
|
||||
filter((item) => !!item.isVisible.get()),
|
||||
(items) => orderBy([(item) => item.orderNumber], ["asc"], items),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
export default preferenceNavigationItemsInjectable;
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { Tab, Tabs } from "../../tabs";
|
||||
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import React from "react";
|
||||
import preferenceNavigationItemsInjectable, {
|
||||
PreferenceNavigationItem,
|
||||
} from "./preference-navigation-items.injectable";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
interface Dependencies {
|
||||
navigationItems: IComputedValue<PreferenceNavigationItem[]>;
|
||||
}
|
||||
|
||||
const NonInjectedPreferencesNavigation = ({
|
||||
navigationItems,
|
||||
}: Dependencies) => (
|
||||
<Tabs
|
||||
className="flex column"
|
||||
scrollable={false}
|
||||
onChange={(item: PreferenceNavigationItem) => item.navigate()}
|
||||
>
|
||||
<div className="header">Preferences</div>
|
||||
|
||||
{navigationItems.get().map((item) => (
|
||||
<PreferencesNavigationTab
|
||||
key={item.id}
|
||||
item={item}
|
||||
data-testid={`tab-link-for-${item.id}`}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
);
|
||||
|
||||
interface PreferenceNavigationTabProps extends React.DOMAttributes<HTMLElement> {
|
||||
item: PreferenceNavigationItem;
|
||||
}
|
||||
|
||||
const PreferencesNavigationTab = observer(({ item }: PreferenceNavigationTabProps) => (
|
||||
<Tab
|
||||
value={item}
|
||||
label={item.label}
|
||||
data-testid={`tab-link-for-${item.id}`}
|
||||
active={item.isActive.get()}
|
||||
/>
|
||||
));
|
||||
|
||||
export const PreferencesNavigation = withInjectables<Dependencies>(
|
||||
NonInjectedPreferencesNavigation,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
navigationItems: di.inject(preferenceNavigationItemsInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import proxyPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/proxy/proxy-preferences-route.injectable";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const proxyPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "proxy-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(proxyPreferencesRouteInjectable);
|
||||
|
||||
const navigateToPreferenceTab = di.inject(
|
||||
navigateToPreferenceTabInjectable,
|
||||
);
|
||||
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return {
|
||||
id: "proxy",
|
||||
label: "Proxy",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
isVisible: computed(() => true),
|
||||
orderNumber: 20,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default proxyPreferencesNavigationItemInjectable;
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import telemetryPreferenceItemsInjectable from "../telemetry-preference-items.injectable";
|
||||
import telemetryPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/telemetry/telemetry-preferences-route.injectable";
|
||||
import sentryDnsUrlInjectable from "../sentry-dns-url.injectable";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const terminalPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "telemetry-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const sentryDnsUrl = di.inject(sentryDnsUrlInjectable);
|
||||
|
||||
const telemetryPreferenceItems = di.inject(
|
||||
telemetryPreferenceItemsInjectable,
|
||||
);
|
||||
|
||||
const navigateToPreferenceTab = di.inject(
|
||||
navigateToPreferenceTabInjectable,
|
||||
);
|
||||
|
||||
const route = di.inject(telemetryPreferencesRouteInjectable);
|
||||
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return {
|
||||
id: "telemetry",
|
||||
label: "Telemetry",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
|
||||
isVisible: computed(
|
||||
() => !!sentryDnsUrl || telemetryPreferenceItems.get().length > 0,
|
||||
),
|
||||
|
||||
orderNumber: 60,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default terminalPreferencesNavigationItemInjectable;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { preferenceNavigationItemInjectionToken } from "./preference-navigation-items.injectable";
|
||||
import routeIsActiveInjectable from "../../../routes/route-is-active.injectable";
|
||||
import { computed } from "mobx";
|
||||
import terminalPreferencesRouteInjectable from "../../../../common/front-end-routing/routes/preferences/terminal/terminal-preferences-route.injectable";
|
||||
import navigateToPreferenceTabInjectable from "./navigate-to-preference-tab.injectable";
|
||||
|
||||
const terminalPreferencesNavigationItemInjectable = getInjectable({
|
||||
id: "terminal-preferences-navigation-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const navigateToPreferenceTab = di.inject(
|
||||
navigateToPreferenceTabInjectable,
|
||||
);
|
||||
|
||||
const route = di.inject(terminalPreferencesRouteInjectable);
|
||||
|
||||
const routeIsActive = di.inject(routeIsActiveInjectable, route);
|
||||
|
||||
return {
|
||||
id: "terminal",
|
||||
label: "Terminal",
|
||||
navigate: navigateToPreferenceTab(route),
|
||||
isActive: routeIsActive,
|
||||
isVisible: computed(() => true),
|
||||
orderNumber: 50,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: preferenceNavigationItemInjectionToken,
|
||||
});
|
||||
|
||||
export default terminalPreferencesNavigationItemInjectable;
|
||||
@@ -3,99 +3,46 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import "./preferences.scss";
|
||||
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { matchPath, Redirect, Route, RouteProps, Switch } from "react-router";
|
||||
import {
|
||||
appRoute,
|
||||
appURL,
|
||||
editorURL,
|
||||
extensionRoute,
|
||||
extensionURL,
|
||||
kubernetesRoute,
|
||||
kubernetesURL,
|
||||
preferencesURL,
|
||||
proxyRoute,
|
||||
proxyURL,
|
||||
editorRoute,
|
||||
telemetryRoute,
|
||||
telemetryURL,
|
||||
terminalRoute,
|
||||
terminalURL,
|
||||
} from "../../../common/routes";
|
||||
import { navigateWithoutHistoryChange, navigation } from "../../navigation";
|
||||
import { SettingLayout } from "../layout/setting-layout";
|
||||
import { Tab, Tabs } from "../tabs";
|
||||
import { Application } from "./application";
|
||||
import { Kubernetes } from "./kubernetes";
|
||||
import { Editor } from "./editor";
|
||||
import { Terminal } from "./terminal";
|
||||
import { LensProxy } from "./proxy";
|
||||
import { Telemetry } from "./telemetry";
|
||||
import { Extensions } from "./extensions";
|
||||
import { sentryDsn } from "../../../common/vars";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||
import appPreferencesInjectable from "./app-preferences/app-preferences.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
appPreferenceItems: IComputedValue<RegisteredAppPreference[]>;
|
||||
import { SettingLayout } from "../layout/setting-layout";
|
||||
import { PreferencesNavigation } from "./preferences-navigation/preferences-navigation";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import closePreferencesInjectable from "./close-preferences.injectable";
|
||||
|
||||
interface PreferencesProps extends React.DOMAttributes<any> {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const NonInjectedPreferences: React.FC<Dependencies> = ({ appPreferenceItems }) => {
|
||||
interface Dependencies {
|
||||
closePreferences: () => void;
|
||||
}
|
||||
|
||||
function renderNavigation() {
|
||||
const extensions = appPreferenceItems.get();
|
||||
const telemetryExtensions = extensions.filter(e => e.showInPreferencesTab == "telemetry");
|
||||
const currentLocation = navigation.location.pathname;
|
||||
const isActive = (route: RouteProps) => !!matchPath(currentLocation, { path: route.path, exact: route.exact });
|
||||
const NonInjectedPreferences = ({
|
||||
children,
|
||||
closePreferences,
|
||||
...props
|
||||
}: PreferencesProps & Dependencies) => (
|
||||
<SettingLayout
|
||||
navigation={<PreferencesNavigation />}
|
||||
className="Preferences"
|
||||
contentGaps={false}
|
||||
closeButtonProps={{ "data-testid": "close-preferences" }}
|
||||
back={closePreferences}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</SettingLayout>
|
||||
);
|
||||
|
||||
return (
|
||||
<Tabs className="flex column" scrollable={false} onChange={(url) => navigateWithoutHistoryChange({ pathname: url })}>
|
||||
<div className="header">Preferences</div>
|
||||
<Tab value={appURL()} label="Application" data-testid="application-tab" active={isActive(appRoute)}/>
|
||||
<Tab value={proxyURL()} label="Proxy" data-testid="proxy-tab" active={isActive(proxyRoute)}/>
|
||||
<Tab value={kubernetesURL()} label="Kubernetes" data-testid="kubernetes-tab" active={isActive(kubernetesRoute)}/>
|
||||
<Tab value={editorURL()} label="Editor" data-testid="editor-tab" active={isActive(editorRoute)}/>
|
||||
<Tab value={terminalURL()} label="Terminal" data-testid="terminal-tab" active={isActive(terminalRoute)}/>
|
||||
{(telemetryExtensions.length > 0 || !!sentryDsn) &&
|
||||
<Tab value={telemetryURL()} label="Telemetry" data-testid="telemetry-tab" active={isActive(telemetryRoute)}/>
|
||||
}
|
||||
{extensions.filter(e => !e.showInPreferencesTab).length > 0 &&
|
||||
<Tab value={extensionURL()} label="Extensions" data-testid="extensions-tab" active={isActive(extensionRoute)}/>
|
||||
}
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingLayout
|
||||
navigation={renderNavigation()}
|
||||
className="Preferences"
|
||||
contentGaps={false}
|
||||
>
|
||||
<Switch>
|
||||
<Route path={appURL()} component={Application}/>
|
||||
<Route path={proxyURL()} component={LensProxy}/>
|
||||
<Route path={kubernetesURL()} component={Kubernetes}/>
|
||||
<Route path={editorURL()} component={Editor}/>
|
||||
<Route path={terminalURL()} component={Terminal}/>
|
||||
<Route path={telemetryURL()} component={Telemetry}/>
|
||||
<Route path={extensionURL()} component={Extensions}/>
|
||||
<Redirect exact from={`${preferencesURL()}/`} to={appURL()}/>
|
||||
</Switch>
|
||||
</SettingLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export const Preferences = withInjectables<Dependencies>(
|
||||
observer(NonInjectedPreferences),
|
||||
export const Preferences = withInjectables<Dependencies, PreferencesProps>(
|
||||
NonInjectedPreferences,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
appPreferenceItems: di.inject(appPreferencesInjectable),
|
||||
getProps: (di, props) => ({
|
||||
closePreferences: di.inject(closePreferencesInjectable),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import proxyPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/proxy/proxy-preferences-route.injectable";
|
||||
import { LensProxy } from "./proxy";
|
||||
|
||||
const proxyPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "proxy-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(proxyPreferencesRouteInjectable),
|
||||
Component: LensProxy,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default proxyPreferencesRouteComponentInjectable;
|
||||
@@ -2,48 +2,66 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
|
||||
import { UserStore } from "../../../common/user-store";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { Input } from "../input";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { Switch } from "../switch";
|
||||
import { Preferences } from "./preferences";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
|
||||
export const LensProxy = observer(() => {
|
||||
const [proxy, setProxy] = React.useState(UserStore.getInstance().httpsProxy || "");
|
||||
const store = UserStore.getInstance();
|
||||
interface Dependencies {
|
||||
userStore: UserStore;
|
||||
}
|
||||
|
||||
const NonInjectedLensProxy = observer(({ userStore }: Dependencies) => {
|
||||
const [proxy, setProxy] = React.useState(userStore.httpsProxy || "");
|
||||
|
||||
return (
|
||||
<section id="proxy">
|
||||
<section>
|
||||
<h2 data-testid="proxy-header">Proxy</h2>
|
||||
<SubTitle title="HTTP Proxy"/>
|
||||
<Input
|
||||
theme="round-black"
|
||||
placeholder="Type HTTP proxy url (example: http://proxy.acme.org:8080)"
|
||||
value={proxy}
|
||||
onChange={v => setProxy(v)}
|
||||
onBlur={() => UserStore.getInstance().httpsProxy = proxy}
|
||||
/>
|
||||
<small className="hint">
|
||||
Proxy is used only for non-cluster communication.
|
||||
</small>
|
||||
</section>
|
||||
<Preferences data-testid="proxy-preferences-page">
|
||||
<section id="proxy">
|
||||
<section>
|
||||
<h2 data-testid="proxy-header">Proxy</h2>
|
||||
<SubTitle title="HTTP Proxy" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
placeholder="Type HTTP proxy url (example: http://proxy.acme.org:8080)"
|
||||
value={proxy}
|
||||
onChange={v => setProxy(v)}
|
||||
onBlur={() => userStore.httpsProxy = proxy}
|
||||
/>
|
||||
<small className="hint">
|
||||
Proxy is used only for non-cluster communication.
|
||||
</small>
|
||||
</section>
|
||||
|
||||
<hr className="small"/>
|
||||
<hr className="small" />
|
||||
|
||||
<section className="small">
|
||||
<SubTitle title="Certificate Trust"/>
|
||||
<Switch checked={store.allowUntrustedCAs} onChange={() => store.allowUntrustedCAs = !store.allowUntrustedCAs}>
|
||||
Allow untrusted Certificate Authorities
|
||||
</Switch>
|
||||
<small className="hint">
|
||||
This will make Lens to trust ANY certificate authority without any validations.{" "}
|
||||
Needed with some corporate proxies that do certificate re-writing.{" "}
|
||||
Does not affect cluster communications!
|
||||
</small>
|
||||
<section className="small">
|
||||
<SubTitle title="Certificate Trust" />
|
||||
<Switch checked={userStore.allowUntrustedCAs}
|
||||
onChange={() => userStore.allowUntrustedCAs = !userStore.allowUntrustedCAs}>
|
||||
Allow untrusted Certificate Authorities
|
||||
</Switch>
|
||||
<small className="hint">
|
||||
This will make Lens to trust ANY certificate authority without any validations.{" "}
|
||||
Needed with some corporate proxies that do certificate re-writing.{" "}
|
||||
Does not affect cluster communications!
|
||||
</small>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
</Preferences>
|
||||
);
|
||||
});
|
||||
|
||||
export const LensProxy = withInjectables<Dependencies>(
|
||||
NonInjectedLensProxy,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 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 { sentryDsn } from "../../../common/vars";
|
||||
|
||||
const sentryDnsUrlInjectable = getInjectable({
|
||||
id: "sentry-dns-url",
|
||||
instantiate: () => sentryDsn,
|
||||
});
|
||||
|
||||
export default sentryDnsUrlInjectable;
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { filter, overSome } from "lodash/fp";
|
||||
import { computed } from "mobx";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import type { AppPreferenceRegistration } from "./app-preferences/app-preference-registration";
|
||||
|
||||
export interface ExtensionTelemetryPreferenceRegistration extends AppPreferenceRegistration {
|
||||
extension?: LensRendererExtension;
|
||||
}
|
||||
|
||||
export const telemetryPreferenceItemInjectionToken = getInjectionToken<ExtensionTelemetryPreferenceRegistration>({
|
||||
id: "telemetry-preference-item-injection-token",
|
||||
});
|
||||
|
||||
const telemetryPreferenceItemsInjectable = getInjectable({
|
||||
id: "telemetry-preference-items",
|
||||
instantiate: (di) => {
|
||||
const extensions = di.inject(rendererExtensionsInjectable);
|
||||
|
||||
return computed(() => {
|
||||
const enabledExtensions = extensions.get();
|
||||
|
||||
return pipeline(
|
||||
di.injectMany(telemetryPreferenceItemInjectionToken),
|
||||
filter((item) =>
|
||||
overSome([
|
||||
isNonExtensionItem,
|
||||
isEnabledExtensionItemFor(enabledExtensions),
|
||||
])(item),
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const isNonExtensionItem = (item: ExtensionTelemetryPreferenceRegistration) => !item.extension;
|
||||
|
||||
const isEnabledExtensionItemFor =
|
||||
(enabledExtensions: LensRendererExtension[]) => (item: ExtensionTelemetryPreferenceRegistration) =>
|
||||
!!enabledExtensions.find((extension) => extension === item.extension);
|
||||
|
||||
|
||||
export default telemetryPreferenceItemsInjectable;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import telemetryPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/telemetry/telemetry-preferences-route.injectable";
|
||||
import { Telemetry } from "./telemetry";
|
||||
|
||||
const telemetryPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "telemetry-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(telemetryPreferencesRouteInjectable),
|
||||
Component: Telemetry,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default telemetryPreferencesRouteComponentInjectable;
|
||||
@@ -4,52 +4,67 @@
|
||||
*/
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { UserStore } from "../../../common/user-store";
|
||||
import { sentryDsn } from "../../../common/vars";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { Checkbox } from "../checkbox";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { ExtensionSettings } from "./extension-settings";
|
||||
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
|
||||
import appPreferencesInjectable from "./app-preferences/app-preferences.injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import { Preferences } from "./preferences";
|
||||
import type { ExtensionTelemetryPreferenceRegistration } from "./telemetry-preference-items.injectable";
|
||||
import telemetryPreferenceItemsInjectable from "./telemetry-preference-items.injectable";
|
||||
import sentryDnsUrlInjectable from "./sentry-dns-url.injectable";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
appPreferenceItems: IComputedValue<RegisteredAppPreference[]>;
|
||||
telemetryPreferenceItems: IComputedValue<ExtensionTelemetryPreferenceRegistration[]>;
|
||||
sentryDnsUrl: string;
|
||||
userStore: UserStore;
|
||||
}
|
||||
|
||||
const NonInjectedTelemetry: React.FC<Dependencies> = ({ appPreferenceItems }) => {
|
||||
const extensions = appPreferenceItems.get();
|
||||
const telemetryExtensions = extensions.filter(e => e.showInPreferencesTab == "telemetry");
|
||||
|
||||
const NonInjectedTelemetry: React.FC<Dependencies> = ({
|
||||
telemetryPreferenceItems,
|
||||
sentryDnsUrl,
|
||||
userStore,
|
||||
}) => {
|
||||
return (
|
||||
<section id="telemetry">
|
||||
<h2 data-testid="telemetry-header">Telemetry</h2>
|
||||
{telemetryExtensions.map((extension) => <ExtensionSettings key={extension.id} setting={extension} size="small" />)}
|
||||
{sentryDsn ? (
|
||||
<React.Fragment key='sentry'>
|
||||
<section id='sentry' className="small">
|
||||
<SubTitle title='Automatic Error Reporting' />
|
||||
<Checkbox
|
||||
label="Allow automatic error reporting"
|
||||
value={UserStore.getInstance().allowErrorReporting}
|
||||
onChange={value => {
|
||||
UserStore.getInstance().allowErrorReporting = value;
|
||||
}}
|
||||
/>
|
||||
<div className="hint">
|
||||
<span>
|
||||
Automatic error reports provide vital information about issues and application crashes.
|
||||
It is highly recommended to keep this feature enabled to ensure fast turnaround for issues you might encounter.
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
<hr className="small" />
|
||||
</React.Fragment>) :
|
||||
// we don't need to shows the checkbox at all if Sentry dsn is not a valid url
|
||||
null
|
||||
}
|
||||
</section>
|
||||
<Preferences data-testid="telemetry-preferences-page">
|
||||
<section id="telemetry">
|
||||
<h2 data-testid="telemetry-header">Telemetry</h2>
|
||||
{telemetryPreferenceItems.get().map((item) => (
|
||||
<ExtensionSettings
|
||||
key={item.id}
|
||||
setting={item}
|
||||
size="small"
|
||||
data-testid={`telemetry-preference-item-for-${item.id}`}
|
||||
/>
|
||||
))}
|
||||
{sentryDnsUrl ? (
|
||||
<React.Fragment key="sentry">
|
||||
<section id="sentry" className="small" data-testid="telemetry-preferences-for-automatic-error-reporting">
|
||||
<SubTitle title="Automatic Error Reporting" />
|
||||
<Checkbox
|
||||
label="Allow automatic error reporting"
|
||||
value={userStore.allowErrorReporting}
|
||||
onChange={(value) => {
|
||||
userStore.allowErrorReporting = value;
|
||||
}}
|
||||
/>
|
||||
<div className="hint">
|
||||
<span>
|
||||
Automatic error reports provide vital information about issues
|
||||
and application crashes. It is highly recommended to keep this
|
||||
feature enabled to ensure fast turnaround for issues you might
|
||||
encounter.
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
<hr className="small" />
|
||||
</React.Fragment>
|
||||
) : // we don't need to shows the checkbox at all if Sentry dsn is not a valid url
|
||||
null}
|
||||
</section>
|
||||
</Preferences>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -58,7 +73,9 @@ export const Telemetry = withInjectables<Dependencies>(
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
appPreferenceItems: di.inject(appPreferencesInjectable),
|
||||
telemetryPreferenceItems: di.inject(telemetryPreferenceItemsInjectable),
|
||||
sentryDnsUrl: di.inject(sentryDnsUrlInjectable),
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token";
|
||||
import terminalPreferencesRouteInjectable from "../../../common/front-end-routing/routes/preferences/terminal/terminal-preferences-route.injectable";
|
||||
import { Terminal } from "./terminal";
|
||||
|
||||
const terminalPreferencesRouteComponentInjectable = getInjectable({
|
||||
id: "terminal-preferences-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(terminalPreferencesRouteInjectable),
|
||||
Component: Terminal,
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
export default terminalPreferencesRouteComponentInjectable;
|
||||
@@ -5,80 +5,98 @@
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { UserStore } from "../../../common/user-store";
|
||||
import type { UserStore } from "../../../common/user-store";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { Input, InputValidators } from "../input";
|
||||
import { isWindows } from "../../../common/vars";
|
||||
import { Switch } from "../switch";
|
||||
import { Select } from "../select";
|
||||
import { ThemeStore } from "../../theme.store";
|
||||
import type { ThemeStore } from "../../theme.store";
|
||||
import { Preferences } from "./preferences";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
|
||||
import themeStoreInjectable from "../../theme-store.injectable";
|
||||
import defaultShellInjectable from "./default-shell.injectable";
|
||||
|
||||
export const Terminal = observer(() => {
|
||||
const userStore = UserStore.getInstance();
|
||||
const themeStore = ThemeStore.getInstance();
|
||||
const defaultShell = process.env.SHELL
|
||||
|| process.env.PTYSHELL
|
||||
|| (
|
||||
isWindows
|
||||
? "powershell.exe"
|
||||
: "System default shell"
|
||||
interface Dependencies {
|
||||
userStore: UserStore;
|
||||
themeStore: ThemeStore;
|
||||
defaultShell: string;
|
||||
}
|
||||
|
||||
const NonInjectedTerminal = observer(({ userStore, themeStore, defaultShell }: Dependencies) => {
|
||||
return (
|
||||
<Preferences data-testid="terminal-preferences-page">
|
||||
<section>
|
||||
<h2>Terminal</h2>
|
||||
|
||||
<section id="shell">
|
||||
<SubTitle title="Terminal Shell Path" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
placeholder={defaultShell}
|
||||
value={userStore.shell}
|
||||
onChange={(value) => userStore.shell = value}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section id="terminalSelection">
|
||||
<SubTitle title="Terminal copy & paste" />
|
||||
<Switch
|
||||
checked={userStore.terminalCopyOnSelect}
|
||||
onChange={() => userStore.terminalCopyOnSelect = !userStore.terminalCopyOnSelect}
|
||||
>
|
||||
Copy on select and paste on right-click
|
||||
</Switch>
|
||||
</section>
|
||||
|
||||
<section id="terminalTheme">
|
||||
<SubTitle title="Terminal theme" />
|
||||
<Select
|
||||
id="terminal-theme-input"
|
||||
themeName="lens"
|
||||
options={[
|
||||
{ label: "Match theme", value: "" },
|
||||
...themeStore.themeOptions,
|
||||
]}
|
||||
value={userStore.terminalTheme}
|
||||
onChange={({ value }) => userStore.terminalTheme = value}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Font size" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="number"
|
||||
min={10}
|
||||
validators={InputValidators.isNumber}
|
||||
value={userStore.terminalConfig.fontSize.toString()}
|
||||
onChange={(value) => userStore.terminalConfig.fontSize = Number(value)}
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<SubTitle title="Font family" />
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="text"
|
||||
value={userStore.terminalConfig.fontFamily}
|
||||
onChange={(value) => userStore.terminalConfig.fontFamily = value}
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
</Preferences>
|
||||
);
|
||||
|
||||
return (<section>
|
||||
<h2>Terminal</h2>
|
||||
|
||||
<section id="shell">
|
||||
<SubTitle title="Terminal Shell Path"/>
|
||||
<Input
|
||||
theme="round-black"
|
||||
placeholder={defaultShell}
|
||||
value={userStore.shell}
|
||||
onChange={(value) => userStore.shell = value}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section id="terminalSelection">
|
||||
<SubTitle title="Terminal copy & paste" />
|
||||
<Switch
|
||||
checked={userStore.terminalCopyOnSelect}
|
||||
onChange={() => userStore.terminalCopyOnSelect = !userStore.terminalCopyOnSelect}
|
||||
>
|
||||
Copy on select and paste on right-click
|
||||
</Switch>
|
||||
</section>
|
||||
|
||||
<section id="terminalTheme">
|
||||
<SubTitle title="Terminal theme" />
|
||||
<Select
|
||||
themeName="lens"
|
||||
options={[
|
||||
{ label: "Match theme", value: "" },
|
||||
...themeStore.themeOptions,
|
||||
]}
|
||||
value={userStore.terminalTheme}
|
||||
onChange={({ value }) => userStore.terminalTheme = value}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<SubTitle title="Font size"/>
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="number"
|
||||
min={10}
|
||||
validators={InputValidators.isNumber}
|
||||
value={userStore.terminalConfig.fontSize.toString()}
|
||||
onChange={(value) => userStore.terminalConfig.fontSize=Number(value)}
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<SubTitle title="Font family"/>
|
||||
<Input
|
||||
theme="round-black"
|
||||
type="text"
|
||||
value={userStore.terminalConfig.fontFamily}
|
||||
onChange={(value) => userStore.terminalConfig.fontFamily=value}
|
||||
/>
|
||||
</section>
|
||||
</section>);
|
||||
});
|
||||
|
||||
export const Terminal = withInjectables<Dependencies>(
|
||||
NonInjectedTerminal,
|
||||
|
||||
{
|
||||
getProps: (di) => ({
|
||||
userStore: di.inject(userStoreInjectable),
|
||||
themeStore: di.inject(themeStoreInjectable),
|
||||
defaultShell: di.inject(defaultShellInjectable),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user