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", () => {
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", () => {
expect(currentPath.get()).toBe("/extension/some-extension-id");
expect(currentPath.get()).toBe("/extension/some-extension-name");
});
it("query parameters is empty", () => {
@ -71,7 +71,7 @@ describe("navigate to extension page", () => {
});
it("URL is correct", () => {
expect(currentPath.get()).toBe("/extension/some-extension-id");
expect(currentPath.get()).toBe("/extension/some-extension-name");
});
it("knows query parameters", () => {
@ -99,7 +99,7 @@ describe("navigate to extension page", () => {
});
it("URL is correct", () => {
expect(currentPath.get()).toBe("/extension/some-extension-id");
expect(currentPath.get()).toBe("/extension/some-extension-name");
});
it("knows query parameters", () => {
@ -121,7 +121,7 @@ describe("navigate to extension page", () => {
});
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: {
someStringParameter: "some-string-value",
someNumberParameter: {
defaultValue: 42,
stringify: (value) => value.toString(),
parse: (value) => (value ? Number(value) : undefined),
},
someArrayParameter: {
defaultValue: ["some-array-value", "some-other-array-value"],
stringify: (value) => value.join(","),
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 { Select } from "../select";
import { Icon } from "../icon";
import { base64, iter, object } from "../../utils";
import { base64, iter } from "../../utils";
import { Notifications } from "../notifications";
import upperFirst from "lodash/upperFirst";
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 name = "";
@observable namespace = "default";
@ -235,9 +228,10 @@ export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
<Select
id="secret-input"
themeName="light"
options={this.secretTypeOptions}
options={Object.keys(this.secretTemplate) as SecretType[]}
value={type}
onChange={option => this.type = option?.value ?? SecretType.Opaque}
getOptionLabel={option => reverseSecretTypeMap[option.value]}
/>
</div>
</div>

View File

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

View File

@ -6,12 +6,12 @@
import styles from "./editor-panel.module.scss";
import throttle from "lodash/throttle";
import React from "react";
import { makeObservable, observable, reaction } from "mobx";
import { makeObservable, reaction } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import type { DockStore, TabId } from "./dock/store";
import { cssNames } from "../../utils";
import type { MonacoEditorProps } from "../monaco-editor";
import { MonacoEditor } from "../monaco-editor";
import type { MonacoEditorProps, MonacoEditorRef } from "../monaco-editor";
import { withInjectables } from "@ogre-tools/injectable-react";
import dockStoreInjectable from "./dock/store.injectable";
@ -36,7 +36,7 @@ const defaultProps: Partial<EditorPanelProps> = {
class NonInjectedEditorPanel extends React.Component<EditorPanelProps & Dependencies> {
static defaultProps = defaultProps as object;
@observable.ref editor: MonacoEditor | null = null;
private readonly editor = React.createRef<MonacoEditorRef>();
constructor(props: EditorPanelProps & Dependencies) {
super(props);
@ -46,12 +46,12 @@ class NonInjectedEditorPanel extends React.Component<EditorPanelProps & Dependen
componentDidMount() {
disposeOnUnmount(this, [
// 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,
}),
// 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)}
onChange={onChange}
onError={onError}
ref={monaco => this.editor = monaco}
ref={this.editor}
/>
);
}
}
export const EditorPanel = withInjectables<Dependencies, EditorPanelProps>(
NonInjectedEditorPanel,
{
getProps: (di, props) => ({
dockStore: di.inject(dockStoreInjectable),
...props,
}),
},
);
export const EditorPanel = withInjectables<Dependencies, EditorPanelProps>(NonInjectedEditorPanel, {
getProps: (di, props) => ({
...props,
dockStore: di.inject(dockStoreInjectable),
}),
});

View File

@ -35,6 +35,7 @@ export interface MonacoEditorProps {
onDidLayoutChange?(info: editor.EditorLayoutInfo): void;
onDidContentSizeChange?(evt: editor.IContentSizeChangedEvent): void;
onModelChange?(model: editor.ITextModel, prev?: editor.ITextModel): void;
innerRef?: React.ForwardedRef<MonacoEditorRef>;
}
interface Dependencies {
@ -47,6 +48,10 @@ export function createMonacoUri(id: MonacoEditorId): Uri {
const monacoViewStates = new WeakMap<Uri, editor.ICodeEditorViewState>();
export interface MonacoEditorRef {
focus(): void;
}
@observer
class NonInjectedMonacoEditor extends React.Component<MonacoEditorProps & Dependencies> {
static defaultProps = {
@ -290,9 +295,12 @@ class NonInjectedMonacoEditor extends React.Component<MonacoEditorProps & Depend
}
}
export const MonacoEditor = withInjectables<Dependencies, MonacoEditorProps>(NonInjectedMonacoEditor, {
getProps: (di, props) => ({
...props,
themeStore: di.inject(themeStoreInjectable),
}),
});
export const MonacoEditor = withInjectables<Dependencies, MonacoEditorProps, MonacoEditorRef>(
React.forwardRef<MonacoEditorRef, MonacoEditorProps & Dependencies>((props, ref) => <NonInjectedMonacoEditor innerRef={ref} {...props} />),
{
getProps: (di, props) => ({
...props,
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 { ThemeStore } from "../../themes/store";
import { autoBind, cssNames } from "../../utils";
import type { SetRequired } from "type-fest";
import { withInjectables } from "@ogre-tools/injectable-react";
import themeStoreInjectable from "../../themes/store.injectable";
@ -27,6 +26,11 @@ export interface SelectOption<Value> {
isSelected?: boolean;
}
/**
* @deprecated This should not be used anymore, convert the options yourself.
*/
export type LegacyAutoConvertedOptions = string[];
export interface SelectProps<
Value,
/**
@ -41,11 +45,22 @@ export interface SelectProps<
Option extends SelectOption<Value>,
IsMulti extends boolean,
Group extends GroupBase<Option> = GroupBase<Option>,
> extends SetRequired<Omit<ReactSelectProps<Option, IsMulti, Group>, "value">, "options"> {
id: string; // Optional only because of Extension API. Required to make Select deterministic in unit tests
> extends Omit<ReactSelectProps<Option, IsMulti, Group>, "value" | "options"> {
id?: string; // Optional only because of Extension API. Required to make Select deterministic in unit tests
themeName?: "dark" | "light" | "outlined" | "lens";
menuClass?: string;
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 {
@ -154,6 +169,15 @@ class NonInjectedSelect<
} = this.props;
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) {
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
isMulti={isMulti}
options={options}
value={this.findSelectedPropsValue(value, options, isMulti)}
options={convertedOptions}
value={this.findSelectedPropsValue(value, convertedOptions, isMulti)}
onKeyDown={this.onKeyDown}
className={cssNames("Select", this.themeClass, className)}
classNamePrefix="Select"