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:
parent
06cfc3e96b
commit
7dde2326bf
@ -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");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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);
|
catalogPreviousActiveTabStorage: di.inject(catalogPreviousActiveTabStorageInjectable),
|
||||||
|
currentRouteComponent: di.inject(currentRouteComponentInjectable),
|
||||||
return {
|
welcomeUrl: buildURL(di.inject(welcomeRouteInjectable).path),
|
||||||
catalogPreviousActiveTabStorage: di.inject(catalogPreviousActiveTabStorageInjectable),
|
}),
|
||||||
currentRouteComponent: di.inject(currentRouteComponentInjectable),
|
|
||||||
welcomeUrl: buildURL(welcomeRoute.path),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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) => ({
|
||||||
|
...props,
|
||||||
{
|
dockStore: di.inject(dockStoreInjectable),
|
||||||
getProps: (di, props) => ({
|
}),
|
||||||
dockStore: di.inject(dockStoreInjectable),
|
});
|
||||||
...props,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|||||||
@ -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>(
|
||||||
getProps: (di, props) => ({
|
React.forwardRef<MonacoEditorRef, MonacoEditorProps & Dependencies>((props, ref) => <NonInjectedMonacoEditor innerRef={ref} {...props} />),
|
||||||
...props,
|
{
|
||||||
themeStore: di.inject(themeStoreInjectable),
|
getProps: (di, props) => ({
|
||||||
}),
|
...props,
|
||||||
});
|
themeStore: di.inject(themeStoreInjectable),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user