mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
refactor & add few tests
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
87b4049f62
commit
d098b4fee4
@ -0,0 +1,133 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2021 OpenLens Authors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import "@testing-library/jest-dom/extend-expect";
|
||||||
|
import { NamespaceSelectFilter } from "../namespace-select-filter";
|
||||||
|
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||||
|
import { DiRender, renderFor } from "../../test-utils/renderFor";
|
||||||
|
import { ThemeStore } from "../../../theme.store";
|
||||||
|
import { UserStore } from "../../../../common/user-store";
|
||||||
|
import namespaceStoreInjectable from "../namespace.store.injectable";
|
||||||
|
import { NamespaceStore } from "../namespace.store";
|
||||||
|
import { AppPaths } from "../../../../common/app-paths";
|
||||||
|
import { Namespace } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import { StorageHelper } from "../../../utils";
|
||||||
|
import { observable } from "mobx";
|
||||||
|
|
||||||
|
jest.mock("electron", () => ({ // TODO: remove mocks
|
||||||
|
app: {
|
||||||
|
getVersion: () => "99.99.99",
|
||||||
|
getName: () => "lens",
|
||||||
|
setName: jest.fn(),
|
||||||
|
setPath: jest.fn(),
|
||||||
|
getPath: () => "tmp",
|
||||||
|
},
|
||||||
|
ipcMain: {
|
||||||
|
on: jest.fn(),
|
||||||
|
handle: jest.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("NamespaceSelectFilter", () => {
|
||||||
|
let render: DiRender;
|
||||||
|
let namespaceStore: NamespaceStore;
|
||||||
|
|
||||||
|
beforeAll(async () => { // TODO: remove beforeAll
|
||||||
|
await AppPaths.init();
|
||||||
|
|
||||||
|
UserStore.resetInstance();
|
||||||
|
UserStore.createInstance();
|
||||||
|
|
||||||
|
ThemeStore.resetInstance();
|
||||||
|
ThemeStore.createInstance();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const di = getDiForUnitTesting();
|
||||||
|
const storage = observable({
|
||||||
|
initialized: true,
|
||||||
|
loaded: true,
|
||||||
|
data: {} as Record<string, any>,
|
||||||
|
});
|
||||||
|
const storageHelper = new StorageHelper<string[]>("namespace_select", {
|
||||||
|
autoInit: true,
|
||||||
|
defaultValue: undefined,
|
||||||
|
storage: {
|
||||||
|
async getItem(key: string) {
|
||||||
|
return storage.data[key];
|
||||||
|
},
|
||||||
|
setItem(key: string, value: any) {
|
||||||
|
storage.data[key] = value;
|
||||||
|
},
|
||||||
|
removeItem(key: string) {
|
||||||
|
delete storage.data[key];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
namespaceStore = new NamespaceStore({
|
||||||
|
storage: storageHelper,
|
||||||
|
autoInit: false,
|
||||||
|
});
|
||||||
|
namespaceStore.resetSelection();
|
||||||
|
di.override(namespaceStoreInjectable, namespaceStore);
|
||||||
|
|
||||||
|
render = renderFor(di);
|
||||||
|
});
|
||||||
|
|
||||||
|
it ("renders without errors using defaults", async () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<><NamespaceSelectFilter /></>);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it ("renders all namespaces by default", async () => {
|
||||||
|
const { getByTestId } = render(<><NamespaceSelectFilter /></>);
|
||||||
|
const select = getByTestId("namespace-select-filter");
|
||||||
|
|
||||||
|
expect(select.getElementsByClassName("Select__placeholder")[0].innerHTML).toEqual("All namespaces");
|
||||||
|
});
|
||||||
|
|
||||||
|
it ("renders selected namespaces", async () => {
|
||||||
|
namespaceStore.items.replace([
|
||||||
|
new Namespace({ kind: "Namespace", apiVersion: "v1", metadata: { name: "one ", uid: "one", resourceVersion: "1" }}),
|
||||||
|
new Namespace({ kind: "Namespace", apiVersion: "v1", metadata: { name: "two ", uid: "two", resourceVersion: "1" }}),
|
||||||
|
new Namespace({ kind: "Namespace", apiVersion: "v1", metadata: { name: "three ", uid: "three", resourceVersion: "1" }}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
namespaceStore.selectNamespaces(["two", "three"]);
|
||||||
|
|
||||||
|
const { getByTestId } = render(<><NamespaceSelectFilter /></>);
|
||||||
|
const select = getByTestId("namespace-select-filter");
|
||||||
|
|
||||||
|
expect(select.getElementsByClassName("Select__placeholder")[0].innerHTML).toEqual("Namespaces: two, three");
|
||||||
|
});
|
||||||
|
|
||||||
|
// it ("renders items", async () => {
|
||||||
|
// namespaceStore.items.replace([
|
||||||
|
// new Namespace({ kind: "Namespace", apiVersion: "v1", metadata: { name: "one ", uid: "one", resourceVersion: "1" }}),
|
||||||
|
// new Namespace({ kind: "Namespace", apiVersion: "v1", metadata: { name: "two ", uid: "two", resourceVersion: "1" }}),
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
// const { } = render(<><NamespaceSelectFilter /></>);
|
||||||
|
// });
|
||||||
|
});
|
||||||
@ -22,20 +22,28 @@
|
|||||||
import "./namespace-select-filter.scss";
|
import "./namespace-select-filter.scss";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import { disposeOnUnmount, observer } from "mobx-react";
|
import { disposeOnUnmount, observer } from "mobx-react";
|
||||||
import { components, PlaceholderProps } from "react-select";
|
import { components, InputActionMeta, PlaceholderProps } from "react-select";
|
||||||
import { action, computed, makeObservable, observable, reaction } from "mobx";
|
import { action, computed, makeObservable, observable, reaction } from "mobx";
|
||||||
|
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { NamespaceSelect } from "./namespace-select";
|
import { NamespaceSelect } from "./namespace-select";
|
||||||
import { namespaceStore } from "./namespace.store";
|
import type { NamespaceStore } from "./namespace.store";
|
||||||
|
|
||||||
import type { SelectOption, SelectProps } from "../select";
|
import type { SelectOption, SelectProps } from "../select";
|
||||||
import { isMac } from "../../../common/vars";
|
import { isMac } from "../../../common/vars";
|
||||||
|
import namespaceStoreInjectable from "./namespace.store.injectable";
|
||||||
|
|
||||||
const Placeholder = observer((props: PlaceholderProps<any, boolean>) => {
|
interface NamespaceSelectFilterPlaceholderProps {
|
||||||
|
namespaceStore: NamespaceStore
|
||||||
|
}
|
||||||
|
|
||||||
|
const Placeholder = observer((props: PlaceholderProps<any, boolean> & NamespaceSelectFilterPlaceholderProps) => {
|
||||||
const getPlaceholder = (): React.ReactNode => {
|
const getPlaceholder = (): React.ReactNode => {
|
||||||
if (props.isFocused) {
|
const { namespaceStore, isFocused } = props;
|
||||||
|
|
||||||
|
if (isFocused || !namespaceStore) {
|
||||||
return <>Search Namespaces ...</>;
|
return <>Search Namespaces ...</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,14 +61,22 @@ const Placeholder = observer((props: PlaceholderProps<any, boolean>) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<components.Placeholder {...props}>
|
<components.Placeholder {...props} data-testid="namespace-select-filter-placeholder">
|
||||||
{getPlaceholder()}
|
{getPlaceholder()}
|
||||||
</components.Placeholder>
|
</components.Placeholder>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export interface NamespaceSelectFilterProps extends SelectProps {
|
||||||
|
maxItems?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NamespaceSelectFilterDependencies {
|
||||||
|
namespaceStore: NamespaceStore,
|
||||||
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class NamespaceSelectFilter extends React.Component<SelectProps> {
|
export class NonInjectedNamespaceSelectFilter extends React.Component<NamespaceSelectFilterProps & NamespaceSelectFilterDependencies> {
|
||||||
static isMultiSelection = observable.box(false);
|
static isMultiSelection = observable.box(false);
|
||||||
static isMenuOpen = observable.box(false);
|
static isMenuOpen = observable.box(false);
|
||||||
|
|
||||||
@ -69,36 +85,41 @@ export class NamespaceSelectFilter extends React.Component<SelectProps> {
|
|||||||
*/
|
*/
|
||||||
private selected = observable.set<string>();
|
private selected = observable.set<string>();
|
||||||
private didToggle = false;
|
private didToggle = false;
|
||||||
|
private inputValue: string;
|
||||||
|
|
||||||
constructor(props: SelectProps) {
|
constructor(props: NamespaceSelectFilterProps & NamespaceSelectFilterDependencies) {
|
||||||
super(props);
|
super(props);
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get isMultiSelection() {
|
get namespaceStore() {
|
||||||
return NamespaceSelectFilter.isMultiSelection.get();
|
return this.props.namespaceStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
set isMultiSelection(val: boolean) {
|
set isMultiSelection(val: boolean) {
|
||||||
NamespaceSelectFilter.isMultiSelection.set(val);
|
NonInjectedNamespaceSelectFilter.isMultiSelection.set(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get isMenuOpen() {
|
@computed get isMenuOpen() {
|
||||||
return NamespaceSelectFilter.isMenuOpen.get();
|
return NonInjectedNamespaceSelectFilter.isMenuOpen.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
set isMenuOpen(val: boolean) {
|
set isMenuOpen(val: boolean) {
|
||||||
NamespaceSelectFilter.isMenuOpen.set(val);
|
NonInjectedNamespaceSelectFilter.isMenuOpen.set(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
get maxItems() {
|
||||||
|
return this.props.maxItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
disposeOnUnmount(this, [
|
disposeOnUnmount(this, [
|
||||||
reaction(() => this.isMenuOpen, newVal => {
|
reaction(() => this.isMenuOpen, newVal => {
|
||||||
if (newVal) { // rising edge of selection
|
if (newVal) { // rising edge of selection
|
||||||
if (namespaceStore.areAllSelectedImplicitly) {
|
if (this.namespaceStore.areAllSelectedImplicitly) {
|
||||||
this.selected.replace([""]);
|
this.selected.replace([""]);
|
||||||
} else {
|
} else {
|
||||||
this.selected.replace(namespaceStore.selectedNames);
|
this.selected.replace(this.namespaceStore.selectedNames);
|
||||||
}
|
}
|
||||||
this.didToggle = false;
|
this.didToggle = false;
|
||||||
}
|
}
|
||||||
@ -108,8 +129,8 @@ export class NamespaceSelectFilter extends React.Component<SelectProps> {
|
|||||||
|
|
||||||
formatOptionLabel({ value: namespace, label }: SelectOption) {
|
formatOptionLabel({ value: namespace, label }: SelectOption) {
|
||||||
const isSelected = namespace
|
const isSelected = namespace
|
||||||
? !namespaceStore.areAllSelectedImplicitly && namespaceStore.hasContext(namespace)
|
? !this.namespaceStore.areAllSelectedImplicitly && this.namespaceStore.hasContext(namespace)
|
||||||
: namespaceStore.areAllSelectedImplicitly;
|
: this.namespaceStore.areAllSelectedImplicitly;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gaps align-center">
|
<div className="flex gaps align-center">
|
||||||
@ -124,16 +145,30 @@ export class NamespaceSelectFilter extends React.Component<SelectProps> {
|
|||||||
onChange = ([{ value: namespace }]: SelectOption[]) => {
|
onChange = ([{ value: namespace }]: SelectOption[]) => {
|
||||||
if (namespace) {
|
if (namespace) {
|
||||||
if (this.isMultiSelection) {
|
if (this.isMultiSelection) {
|
||||||
this.didToggle = true;
|
if (this.inputValue === "" && (this.namespaceStore.items.length < this.maxItems)) {
|
||||||
namespaceStore.toggleSingle(namespace);
|
this.didToggle = true;
|
||||||
|
this.namespaceStore.toggleSingle(namespace);
|
||||||
|
} else {
|
||||||
|
if (this.namespaceStore.areAllSelectedImplicitly && namespace === "") {
|
||||||
|
this.namespaceStore.selectSingle(namespace);
|
||||||
|
} else {
|
||||||
|
this.namespaceStore.toggleSingle(namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
namespaceStore.selectSingle(namespace);
|
this.namespaceStore.selectSingle(namespace);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
namespaceStore.selectAll();
|
this.namespaceStore.selectAll();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onInputChange = (value: string, meta: InputActionMeta) => {
|
||||||
|
if (meta.action === "input-change") this.inputValue = value;
|
||||||
|
if (meta.action === "menu-close") this.inputValue = "";
|
||||||
|
};
|
||||||
|
|
||||||
private isSelectionKey(e: React.KeyboardEvent): boolean {
|
private isSelectionKey(e: React.KeyboardEvent): boolean {
|
||||||
if (isMac) {
|
if (isMac) {
|
||||||
return e.key === "Meta";
|
return e.key === "Meta";
|
||||||
@ -169,6 +204,7 @@ export class NamespaceSelectFilter extends React.Component<SelectProps> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@action
|
||||||
reset = () => {
|
reset = () => {
|
||||||
this.isMultiSelection = false;
|
this.isMultiSelection = false;
|
||||||
this.isMenuOpen = false;
|
this.isMenuOpen = false;
|
||||||
@ -176,18 +212,22 @@ export class NamespaceSelectFilter extends React.Component<SelectProps> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div onKeyUp={this.onKeyUp} onKeyDown={this.onKeyDown} onClick={this.onClick}>
|
<div onKeyUp={this.onKeyUp} onKeyDown={this.onKeyDown} onClick={this.onClick} data-testid="namespace-select-filter">
|
||||||
<NamespaceSelect
|
<NamespaceSelect
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
|
inputValue={this.inputValue}
|
||||||
menuIsOpen={this.isMenuOpen}
|
menuIsOpen={this.isMenuOpen}
|
||||||
components={{ Placeholder }}
|
components={{
|
||||||
|
Placeholder: (props) => <Placeholder {...props} namespaceStore={this.namespaceStore} />,
|
||||||
|
}}
|
||||||
showAllNamespacesOption={true}
|
showAllNamespacesOption={true}
|
||||||
closeMenuOnSelect={false}
|
closeMenuOnSelect={false}
|
||||||
controlShouldRenderValue={false}
|
controlShouldRenderValue={false}
|
||||||
placeholder={""}
|
placeholder={""}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
onBlur={this.reset}
|
onBlur={this.reset}
|
||||||
formatOptionLabel={this.formatOptionLabel}
|
onInputChange={this.onInputChange}
|
||||||
|
formatOptionLabel={this.formatOptionLabel.bind(this)}
|
||||||
className="NamespaceSelectFilter"
|
className="NamespaceSelectFilter"
|
||||||
menuClass="NamespaceSelectFilterMenu"
|
menuClass="NamespaceSelectFilterMenu"
|
||||||
sort={(left, right) => +this.selected.has(right.value) - +this.selected.has(left.value)}
|
sort={(left, right) => +this.selected.has(right.value) - +this.selected.has(left.value)}
|
||||||
@ -196,3 +236,11 @@ export class NamespaceSelectFilter extends React.Component<SelectProps> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const NamespaceSelectFilter = withInjectables<NamespaceSelectFilterDependencies, NamespaceSelectFilterProps>(NonInjectedNamespaceSelectFilter, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
namespaceStore: di.inject(namespaceStoreInjectable),
|
||||||
|
maxItems: 500,
|
||||||
|
...props,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|||||||
@ -27,33 +27,53 @@ import { observer } from "mobx-react";
|
|||||||
import { Select, SelectOption, SelectProps } from "../select";
|
import { Select, SelectOption, SelectProps } from "../select";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { namespaceStore } from "./namespace.store";
|
import type { NamespaceStore } from "./namespace.store";
|
||||||
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
|
import namespaceStoreInjectable from "./namespace.store.injectable";
|
||||||
|
|
||||||
interface Props extends SelectProps {
|
export interface NamespaceSelectProps extends SelectProps {
|
||||||
showIcons?: boolean;
|
showIcons?: boolean;
|
||||||
sort?: (a: SelectOption<string>, b: SelectOption<string>) => number;
|
sort?: (a: SelectOption<string>, b: SelectOption<string>) => number;
|
||||||
showAllNamespacesOption?: boolean; // show "All namespaces" option on the top (default: false)
|
showAllNamespacesOption?: boolean; // show "All namespaces" option on the top (default: false)
|
||||||
customizeOptions?(options: SelectOption[]): SelectOption[];
|
customizeOptions?(options: SelectOption[]): SelectOption[];
|
||||||
|
maxItems?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProps: Partial<Props> = {
|
interface NamespaceSelectDependencies {
|
||||||
|
namespaceStore?: NamespaceStore
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultProps: Partial<NamespaceSelectProps> = {
|
||||||
showIcons: true,
|
showIcons: true,
|
||||||
|
maxItems: 500,
|
||||||
};
|
};
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class NamespaceSelect extends React.Component<Props> {
|
class NonInjectedNamespaceSelect extends React.Component<NamespaceSelectProps & NamespaceSelectDependencies> {
|
||||||
static defaultProps = defaultProps as object;
|
static defaultProps = defaultProps as object;
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: NamespaceSelectProps & NamespaceSelectDependencies) {
|
||||||
super(props);
|
super(props);
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get namespaceStore() {
|
||||||
|
return this.props.namespaceStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get maxItems() {
|
||||||
|
return this.props.maxItems;
|
||||||
|
}
|
||||||
|
|
||||||
// No subscribe here because the subscribe is in <App /> (the cluster frame root component)
|
// No subscribe here because the subscribe is in <App /> (the cluster frame root component)
|
||||||
|
|
||||||
@computed.struct get options(): SelectOption[] {
|
@computed.struct get options(): SelectOption[] {
|
||||||
const { customizeOptions, showAllNamespacesOption, sort } = this.props;
|
const { customizeOptions, showAllNamespacesOption, sort } = this.props;
|
||||||
let options: SelectOption[] = namespaceStore.items.map(ns => ({ value: ns.getName(), label: ns.getName() }));
|
let options: SelectOption[] = Array.from(this.namespaceStore.items.map(ns => ({ value: ns.getName(), label: ns.getName() })));
|
||||||
|
|
||||||
|
if (this.namespaceStore.selectedNames.size > this.maxItems) {
|
||||||
|
options = options.slice(0, this.maxItems); // need to protect UI from freezing
|
||||||
|
}
|
||||||
|
|
||||||
if (sort) {
|
if (sort) {
|
||||||
options.sort(sort);
|
options.sort(sort);
|
||||||
@ -83,11 +103,11 @@ export class NamespaceSelect extends React.Component<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
filterOption = (option: SelectOption, rawInput: string) => {
|
filterOption = (option: SelectOption, rawInput: string) => {
|
||||||
if (option.value === "" || (!namespaceStore.areAllSelectedImplicitly && namespaceStore.selectedNames.has(option.value))) {
|
if (option.value === "" || (this.namespaceStore !== undefined && !this.namespaceStore.areAllSelectedImplicitly && this.namespaceStore.selectedNames.has(option.value))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (namespaceStore.items.length > 500 && rawInput.length < 3) {
|
if (this.namespaceStore.items.length > this.maxItems && rawInput.length < 3) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,8 +129,16 @@ export class NamespaceSelect extends React.Component<Props> {
|
|||||||
options={this.options}
|
options={this.options}
|
||||||
components={components}
|
components={components}
|
||||||
filterOption={this.filterOption}
|
filterOption={this.filterOption}
|
||||||
|
blurInputOnSelect={false}
|
||||||
{...selectProps}
|
{...selectProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const NamespaceSelect = withInjectables(NonInjectedNamespaceSelect, {
|
||||||
|
getProps: (di, props) => ({
|
||||||
|
namespaceStore: di.inject(namespaceStoreInjectable),
|
||||||
|
...props,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2021 OpenLens Authors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
|
import { namespaceStore, NamespaceStore } from "./namespace.store";
|
||||||
|
|
||||||
|
const namespaceStoreInjectable: Injectable<
|
||||||
|
NamespaceStore
|
||||||
|
> = {
|
||||||
|
getDependencies: di => ({}),
|
||||||
|
|
||||||
|
instantiate: () => namespaceStore,
|
||||||
|
lifecycle: lifecycleEnum.singleton,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default namespaceStoreInjectable;
|
||||||
@ -20,21 +20,33 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { action, comparer, computed, IReactionDisposer, makeObservable, reaction } from "mobx";
|
import { action, comparer, computed, IReactionDisposer, makeObservable, reaction } from "mobx";
|
||||||
import { autoBind, createStorage, noop, ToggleSet } from "../../utils";
|
import { autoBind, createStorage, noop, StorageHelper, ToggleSet } from "../../utils";
|
||||||
import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../../common/k8s-api/kube-object.store";
|
import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../../common/k8s-api/kube-object.store";
|
||||||
import { Namespace, namespacesApi } from "../../../common/k8s-api/endpoints/namespaces.api";
|
import { Namespace, namespacesApi } from "../../../common/k8s-api/endpoints/namespaces.api";
|
||||||
import { apiManager } from "../../../common/k8s-api/api-manager";
|
import { apiManager } from "../../../common/k8s-api/api-manager";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export type NamespaceStoreConfig = {
|
||||||
|
storage?: StorageHelper<string[] | undefined>;
|
||||||
|
autoInit?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export class NamespaceStore extends KubeObjectStore<Namespace> {
|
export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||||
api = namespacesApi;
|
api = namespacesApi;
|
||||||
private storage = createStorage<string[] | undefined>("selected_namespaces", undefined);
|
|
||||||
|
|
||||||
constructor() {
|
constructor(private config: NamespaceStoreConfig) {
|
||||||
super();
|
super();
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
autoBind(this);
|
autoBind(this);
|
||||||
|
|
||||||
this.init();
|
if (config.autoInit !== false) {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private get storage() {
|
||||||
|
return this.config.storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async init() {
|
private async init() {
|
||||||
@ -247,7 +259,9 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const namespaceStore = new NamespaceStore();
|
export const namespaceStore = new NamespaceStore({
|
||||||
|
storage: createStorage<string[] | undefined>("selected_namespaces", undefined),
|
||||||
|
});
|
||||||
apiManager.registerStore(namespaceStore);
|
apiManager.registerStore(namespaceStore);
|
||||||
|
|
||||||
export function getDummyNamespace(name: string) {
|
export function getDummyNamespace(name: string) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user