1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

final tweaks

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-04-21 15:40:10 -04:00
parent 06cfc3e96b
commit 7dde2326bf
7 changed files with 68 additions and 56 deletions

View File

@ -45,7 +45,7 @@ describe("extension special characters in page registrations", () => {
it("knows URL", () => { it("knows URL", () => {
const currentPath = applicationBuilder.dis.rendererDi.inject(currentPathInjectable); const currentPath = applicationBuilder.dis.rendererDi.inject(currentPathInjectable);
expect(currentPath.get()).toBe("/extension/some-extension-id--/some-page-id"); expect(currentPath.get()).toBe("/extension/some-extension-name--/some-page-id");
}); });
}); });
}); });

View File

@ -52,7 +52,7 @@ describe("navigate to extension page", () => {
}); });
it("URL is correct", () => { it("URL is correct", () => {
expect(currentPath.get()).toBe("/extension/some-extension-id"); expect(currentPath.get()).toBe("/extension/some-extension-name");
}); });
it("query parameters is empty", () => { it("query parameters is empty", () => {
@ -71,7 +71,7 @@ describe("navigate to extension page", () => {
}); });
it("URL is correct", () => { it("URL is correct", () => {
expect(currentPath.get()).toBe("/extension/some-extension-id"); expect(currentPath.get()).toBe("/extension/some-extension-name");
}); });
it("knows query parameters", () => { it("knows query parameters", () => {
@ -99,7 +99,7 @@ describe("navigate to extension page", () => {
}); });
it("URL is correct", () => { it("URL is correct", () => {
expect(currentPath.get()).toBe("/extension/some-extension-id"); expect(currentPath.get()).toBe("/extension/some-extension-name");
}); });
it("knows query parameters", () => { it("knows query parameters", () => {
@ -121,7 +121,7 @@ describe("navigate to extension page", () => {
}); });
it("URL is correct", () => { it("URL is correct", () => {
expect(currentPath.get()).toBe("/extension/some-extension-id/some-child-page-id"); expect(currentPath.get()).toBe("/extension/some-extension-name/some-child-page-id");
}); });
}); });
}); });
@ -160,20 +160,14 @@ const extensionWithPagesHavingParameters: SetRequired<Partial<LensRendererExtens
params: { params: {
someStringParameter: "some-string-value", someStringParameter: "some-string-value",
someNumberParameter: { someNumberParameter: {
defaultValue: 42, defaultValue: 42,
stringify: (value) => value.toString(), stringify: (value) => value.toString(),
parse: (value) => (value ? Number(value) : undefined), parse: (value) => (value ? Number(value) : undefined),
}, },
someArrayParameter: { someArrayParameter: {
defaultValue: ["some-array-value", "some-other-array-value"], defaultValue: ["some-array-value", "some-other-array-value"],
stringify: (value) => value.join(","), stringify: (value) => value.join(","),
parse: (value: string[]) => (!isEmpty(value) ? value : undefined), parse: (value: string[]) => (!isEmpty(value) ? value : undefined),
}, },
}, },

View File

@ -18,7 +18,7 @@ import { SubTitle } from "../layout/sub-title";
import { NamespaceSelect } from "../+namespaces/namespace-select"; import { NamespaceSelect } from "../+namespaces/namespace-select";
import { Select } from "../select"; import { Select } from "../select";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { base64, iter, object } from "../../utils"; import { base64, iter } from "../../utils";
import { Notifications } from "../notifications"; import { Notifications } from "../notifications";
import upperFirst from "lodash/upperFirst"; import upperFirst from "lodash/upperFirst";
import { showDetails } from "../kube-detail-params"; import { showDetails } from "../kube-detail-params";
@ -71,13 +71,6 @@ export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
}, },
}; };
private get secretTypeOptions() {
return object.keys(this.secretTemplate).map(type => ({
value: type,
label: reverseSecretTypeMap[type],
}));
}
@observable secret = this.secretTemplate; @observable secret = this.secretTemplate;
@observable name = ""; @observable name = "";
@observable namespace = "default"; @observable namespace = "default";
@ -235,9 +228,10 @@ export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
<Select <Select
id="secret-input" id="secret-input"
themeName="light" themeName="light"
options={this.secretTypeOptions} options={Object.keys(this.secretTemplate) as SecretType[]}
value={type} value={type}
onChange={option => this.type = option?.value ?? SecretType.Opaque} onChange={option => this.type = option?.value ?? SecretType.Opaque}
getOptionLabel={option => reverseSecretTypeMap[option.value]}
/> />
</div> </div>
</div> </div>

View File

@ -60,13 +60,9 @@ class NonInjectedClusterManager extends React.Component<Dependencies> {
} }
export const ClusterManager = withInjectables<Dependencies>(NonInjectedClusterManager, { export const ClusterManager = withInjectables<Dependencies>(NonInjectedClusterManager, {
getProps: (di) => { getProps: (di) => ({
const welcomeRoute = di.inject(welcomeRouteInjectable);
return {
catalogPreviousActiveTabStorage: di.inject(catalogPreviousActiveTabStorageInjectable), catalogPreviousActiveTabStorage: di.inject(catalogPreviousActiveTabStorageInjectable),
currentRouteComponent: di.inject(currentRouteComponentInjectable), currentRouteComponent: di.inject(currentRouteComponentInjectable),
welcomeUrl: buildURL(welcomeRoute.path), welcomeUrl: buildURL(di.inject(welcomeRouteInjectable).path),
}; }),
},
}); });

View File

@ -6,12 +6,12 @@
import styles from "./editor-panel.module.scss"; import styles from "./editor-panel.module.scss";
import throttle from "lodash/throttle"; import throttle from "lodash/throttle";
import React from "react"; import React from "react";
import { makeObservable, observable, reaction } from "mobx"; import { makeObservable, reaction } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import type { DockStore, TabId } from "./dock/store"; import type { DockStore, TabId } from "./dock/store";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import type { MonacoEditorProps } from "../monaco-editor";
import { MonacoEditor } from "../monaco-editor"; import { MonacoEditor } from "../monaco-editor";
import type { MonacoEditorProps, MonacoEditorRef } from "../monaco-editor";
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";
@ -36,7 +36,7 @@ const defaultProps: Partial<EditorPanelProps> = {
class NonInjectedEditorPanel extends React.Component<EditorPanelProps & Dependencies> { class NonInjectedEditorPanel extends React.Component<EditorPanelProps & Dependencies> {
static defaultProps = defaultProps as object; static defaultProps = defaultProps as object;
@observable.ref editor: MonacoEditor | null = null; private readonly editor = React.createRef<MonacoEditorRef>();
constructor(props: EditorPanelProps & Dependencies) { constructor(props: EditorPanelProps & Dependencies) {
super(props); super(props);
@ -46,12 +46,12 @@ class NonInjectedEditorPanel extends React.Component<EditorPanelProps & Dependen
componentDidMount() { componentDidMount() {
disposeOnUnmount(this, [ disposeOnUnmount(this, [
// keep focus on editor's area when <Dock/> just opened // keep focus on editor's area when <Dock/> just opened
reaction(() => this.props.dockStore.isOpen, isOpen => isOpen && this.editor?.focus(), { reaction(() => this.props.dockStore.isOpen, isOpen => isOpen && this.editor.current?.focus(), {
fireImmediately: true, fireImmediately: true,
}), }),
// focus to editor on dock's resize or turning into fullscreen mode // focus to editor on dock's resize or turning into fullscreen mode
this.props.dockStore.onResize(throttle(() => this.editor?.focus(), 250)), this.props.dockStore.onResize(throttle(() => this.editor.current?.focus(), 250)),
]); ]);
} }
@ -68,19 +68,15 @@ class NonInjectedEditorPanel extends React.Component<EditorPanelProps & Dependen
className={cssNames(styles.EditorPanel, className)} className={cssNames(styles.EditorPanel, className)}
onChange={onChange} onChange={onChange}
onError={onError} onError={onError}
ref={monaco => this.editor = monaco} ref={this.editor}
/> />
); );
} }
} }
export const EditorPanel = withInjectables<Dependencies, EditorPanelProps>( export const EditorPanel = withInjectables<Dependencies, EditorPanelProps>(NonInjectedEditorPanel, {
NonInjectedEditorPanel,
{
getProps: (di, props) => ({ getProps: (di, props) => ({
dockStore: di.inject(dockStoreInjectable),
...props, ...props,
dockStore: di.inject(dockStoreInjectable),
}), }),
}, });
);

View File

@ -35,6 +35,7 @@ export interface MonacoEditorProps {
onDidLayoutChange?(info: editor.EditorLayoutInfo): void; onDidLayoutChange?(info: editor.EditorLayoutInfo): void;
onDidContentSizeChange?(evt: editor.IContentSizeChangedEvent): void; onDidContentSizeChange?(evt: editor.IContentSizeChangedEvent): void;
onModelChange?(model: editor.ITextModel, prev?: editor.ITextModel): void; onModelChange?(model: editor.ITextModel, prev?: editor.ITextModel): void;
innerRef?: React.ForwardedRef<MonacoEditorRef>;
} }
interface Dependencies { interface Dependencies {
@ -47,6 +48,10 @@ export function createMonacoUri(id: MonacoEditorId): Uri {
const monacoViewStates = new WeakMap<Uri, editor.ICodeEditorViewState>(); const monacoViewStates = new WeakMap<Uri, editor.ICodeEditorViewState>();
export interface MonacoEditorRef {
focus(): void;
}
@observer @observer
class NonInjectedMonacoEditor extends React.Component<MonacoEditorProps & Dependencies> { class NonInjectedMonacoEditor extends React.Component<MonacoEditorProps & Dependencies> {
static defaultProps = { static defaultProps = {
@ -290,9 +295,12 @@ class NonInjectedMonacoEditor extends React.Component<MonacoEditorProps & Depend
} }
} }
export const MonacoEditor = withInjectables<Dependencies, MonacoEditorProps>(NonInjectedMonacoEditor, { export const MonacoEditor = withInjectables<Dependencies, MonacoEditorProps, MonacoEditorRef>(
React.forwardRef<MonacoEditorRef, MonacoEditorProps & Dependencies>((props, ref) => <NonInjectedMonacoEditor innerRef={ref} {...props} />),
{
getProps: (di, props) => ({ getProps: (di, props) => ({
...props, ...props,
themeStore: di.inject(themeStoreInjectable), themeStore: di.inject(themeStoreInjectable),
}), }),
}); },
);

View File

@ -14,7 +14,6 @@ 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 type { ThemeStore } from "../../themes/store"; import type { ThemeStore } from "../../themes/store";
import { autoBind, cssNames } from "../../utils"; import { autoBind, cssNames } from "../../utils";
import type { SetRequired } from "type-fest";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import themeStoreInjectable from "../../themes/store.injectable"; import themeStoreInjectable from "../../themes/store.injectable";
@ -27,6 +26,11 @@ export interface SelectOption<Value> {
isSelected?: boolean; isSelected?: boolean;
} }
/**
* @deprecated This should not be used anymore, convert the options yourself.
*/
export type LegacyAutoConvertedOptions = string[];
export interface SelectProps< export interface SelectProps<
Value, Value,
/** /**
@ -41,11 +45,22 @@ export interface SelectProps<
Option extends SelectOption<Value>, Option extends SelectOption<Value>,
IsMulti extends boolean, IsMulti extends boolean,
Group extends GroupBase<Option> = GroupBase<Option>, Group extends GroupBase<Option> = GroupBase<Option>,
> extends SetRequired<Omit<ReactSelectProps<Option, IsMulti, Group>, "value">, "options"> { > extends Omit<ReactSelectProps<Option, IsMulti, Group>, "value" | "options"> {
id: string; // Optional only because of Extension API. Required to make Select deterministic in unit tests id?: string; // Optional only because of Extension API. Required to make Select deterministic in unit tests
themeName?: "dark" | "light" | "outlined" | "lens"; themeName?: "dark" | "light" | "outlined" | "lens";
menuClass?: string; menuClass?: string;
value?: PropsValue<Value>; value?: PropsValue<Value>;
options: NonNullable<ReactSelectProps<Option, IsMulti, Group>["options"]> | LegacyAutoConvertedOptions;
/**
* @deprecated This option does nothing
*/
isCreatable?: boolean;
/**
* @deprecated We will always auto convert options if they are of type `string`
*/
autoConvertOptions?: boolean;
} }
function isGroup<Option, Group extends GroupBase<Option>>(optionOrGroup: Option | Group): optionOrGroup is Group { function isGroup<Option, Group extends GroupBase<Option>>(optionOrGroup: Option | Group): optionOrGroup is Group {
@ -154,6 +169,15 @@ class NonInjectedSelect<
} = this.props; } = this.props;
const WrappedMenu = components.Menu ?? Menu; const WrappedMenu = components.Menu ?? Menu;
const convertedOptions = options.map(option => (
typeof option === "string"
? {
value: option,
label: option,
} as unknown as Option
: option
));
if (options.length > 0 && !(options?.[0] as { label?: string }).label) { if (options.length > 0 && !(options?.[0] as { label?: string }).label) {
console.warn("[SELECT]: will not display any label in dropdown"); console.warn("[SELECT]: will not display any label in dropdown");
} }
@ -170,8 +194,8 @@ class NonInjectedSelect<
}} }}
filterOption={defaultFilter} // This is done because the default filter crashes on symbols filterOption={defaultFilter} // This is done because the default filter crashes on symbols
isMulti={isMulti} isMulti={isMulti}
options={options} options={convertedOptions}
value={this.findSelectedPropsValue(value, options, isMulti)} value={this.findSelectedPropsValue(value, convertedOptions, isMulti)}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
className={cssNames("Select", this.themeClass, className)} className={cssNames("Select", this.themeClass, className)}
classNamePrefix="Select" classNamePrefix="Select"