mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add tests to cover rearragement
- Fixup styling - Move isSelected out of `Options` to prevent reflow and into seperate model function Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
7381c0fcf7
commit
6c8220e140
@ -191,7 +191,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-1"
|
id="react-select-namespace-select-filter-option-1"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -226,7 +226,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-2"
|
id="react-select-namespace-select-filter-option-2"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -261,7 +261,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-3"
|
id="react-select-namespace-select-filter-option-3"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -296,7 +296,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-4"
|
id="react-select-namespace-select-filter-option-4"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -331,7 +331,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-5"
|
id="react-select-namespace-select-filter-option-5"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -366,7 +366,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-6"
|
id="react-select-namespace-select-filter-option-6"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -401,7 +401,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-7"
|
id="react-select-namespace-select-filter-option-7"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -436,7 +436,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-8"
|
id="react-select-namespace-select-filter-option-8"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -471,7 +471,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-9"
|
id="react-select-namespace-select-filter-option-9"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -506,7 +506,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-10"
|
id="react-select-namespace-select-filter-option-10"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -541,7 +541,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-11"
|
id="react-select-namespace-select-filter-option-11"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -576,7 +576,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-12"
|
id="react-select-namespace-select-filter-option-12"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -611,7 +611,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-13"
|
id="react-select-namespace-select-filter-option-13"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -841,7 +841,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-1"
|
id="react-select-namespace-select-filter-option-1"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -1254,7 +1254,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
|
|||||||
</body>
|
</body>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked when clicked again, then holding down multi select key, and then clicking 'test-3' when 'test-3' is clicked renders 1`] = `
|
exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked when clicked again, then holding down multi select key when 'test-3' is clicked renders 1`] = `
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
@ -1359,7 +1359,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-1"
|
id="react-select-namespace-select-filter-option-1"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
@ -1418,7 +1418,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
class="Select__option css-yt9ioa-option"
|
class="Select__option Select__option--is-selected css-9gakcf-option"
|
||||||
id="react-select-namespace-select-filter-option-3"
|
id="react-select-namespace-select-filter-option-3"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -22,39 +22,20 @@ export const selectAllNamespaces = Symbol("all-namespaces-selected");
|
|||||||
export type SelectAllNamespaces = typeof selectAllNamespaces;
|
export type SelectAllNamespaces = typeof selectAllNamespaces;
|
||||||
export type NamespaceSelectFilterOption = SelectOption<string | SelectAllNamespaces>;
|
export type NamespaceSelectFilterOption = SelectOption<string | SelectAllNamespaces>;
|
||||||
|
|
||||||
export function formatOptionLabel({ value, isSelected }: NamespaceSelectFilterOption) {
|
|
||||||
if (value === selectAllNamespaces) {
|
|
||||||
return <>All Namespaces</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex gaps align-center">
|
|
||||||
<Icon small material="layers" />
|
|
||||||
<span>{value}</span>
|
|
||||||
{isSelected && (
|
|
||||||
<Icon
|
|
||||||
small
|
|
||||||
material="check"
|
|
||||||
className="box right"
|
|
||||||
data-testid={`namespace-select-filter-option-${value}-selected`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NamespaceSelectFilterModel {
|
export interface NamespaceSelectFilterModel {
|
||||||
readonly options: IComputedValue<readonly NamespaceSelectFilterOption[]>;
|
readonly options: IComputedValue<readonly NamespaceSelectFilterOption[]>;
|
||||||
readonly menu: {
|
readonly menu: {
|
||||||
open: () => void;
|
open: () => void;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
readonly isOpen: boolean;
|
readonly isOpen: IComputedValue<boolean>;
|
||||||
};
|
};
|
||||||
onChange: (newValue: MultiValue<NamespaceSelectFilterOption>, actionMeta: ActionMeta<NamespaceSelectFilterOption>) => void;
|
onChange: (newValue: MultiValue<NamespaceSelectFilterOption>, actionMeta: ActionMeta<NamespaceSelectFilterOption>) => void;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
onKeyDown: React.KeyboardEventHandler;
|
onKeyDown: React.KeyboardEventHandler;
|
||||||
onKeyUp: React.KeyboardEventHandler;
|
onKeyUp: React.KeyboardEventHandler;
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
|
isOptionSelected: (option: NamespaceSelectFilterOption) => boolean;
|
||||||
|
formatOptionLabel: (option: NamespaceSelectFilterOption) => JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SelectMenuState {
|
enum SelectMenuState {
|
||||||
@ -81,7 +62,7 @@ export function namespaceSelectFilterModelFor(dependencies: Dependencies): Names
|
|||||||
const optionsSortingSelected = observable.set(selectedNames.get());
|
const optionsSortingSelected = observable.set(selectedNames.get());
|
||||||
const options = computed((): readonly NamespaceSelectFilterOption[] => {
|
const options = computed((): readonly NamespaceSelectFilterOption[] => {
|
||||||
const baseOptions = namespaceStore.items.map(ns => ns.getName());
|
const baseOptions = namespaceStore.items.map(ns => ns.getName());
|
||||||
const namespaces = selectedNames.get();
|
// const namespaces = selectedNames.get();
|
||||||
|
|
||||||
baseOptions.sort((
|
baseOptions.sort((
|
||||||
(left, right) =>
|
(left, right) =>
|
||||||
@ -94,17 +75,24 @@ export function namespaceSelectFilterModelFor(dependencies: Dependencies): Names
|
|||||||
value: selectAllNamespaces,
|
value: selectAllNamespaces,
|
||||||
label: "All Namespaces",
|
label: "All Namespaces",
|
||||||
id: "all-namespaces",
|
id: "all-namespaces",
|
||||||
isSelected: false,
|
// isSelected: false,
|
||||||
},
|
},
|
||||||
...baseOptions.map(namespace => ({
|
...baseOptions.map(namespace => ({
|
||||||
value: namespace,
|
value: namespace,
|
||||||
label: namespace,
|
label: namespace,
|
||||||
id: namespace,
|
id: namespace,
|
||||||
isSelected: namespaces.has(namespace),
|
// isSelected: namespaces.has(namespace),
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
const menuIsOpen = computed(() => menuState.get() === SelectMenuState.Open);
|
const menuIsOpen = computed(() => menuState.get() === SelectMenuState.Open);
|
||||||
|
const isOptionSelected: NamespaceSelectFilterModel["isOptionSelected"] = (option) => {
|
||||||
|
if (option.value === selectAllNamespaces) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedNames.get().has(option.value);
|
||||||
|
};
|
||||||
|
|
||||||
const model: NamespaceSelectFilterModel = {
|
const model: NamespaceSelectFilterModel = {
|
||||||
options,
|
options,
|
||||||
@ -115,9 +103,7 @@ export function namespaceSelectFilterModelFor(dependencies: Dependencies): Names
|
|||||||
open: action(() => {
|
open: action(() => {
|
||||||
menuState.set(SelectMenuState.Open);
|
menuState.set(SelectMenuState.Open);
|
||||||
}),
|
}),
|
||||||
get isOpen() {
|
isOpen: menuIsOpen,
|
||||||
return menuIsOpen.get();
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
onChange: (_, action) => {
|
onChange: (_, action) => {
|
||||||
switch (action.action) {
|
switch (action.action) {
|
||||||
@ -125,19 +111,13 @@ export function namespaceSelectFilterModelFor(dependencies: Dependencies): Names
|
|||||||
namespaceStore.selectAll();
|
namespaceStore.selectAll();
|
||||||
break;
|
break;
|
||||||
case "deselect-option":
|
case "deselect-option":
|
||||||
if (typeof action.option === "string") {
|
|
||||||
didToggle = true;
|
|
||||||
namespaceStore.toggleSingle(action.option);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "select-option":
|
case "select-option":
|
||||||
if (action.option?.value === selectAllNamespaces) {
|
if (action.option) {
|
||||||
didToggle = true;
|
|
||||||
namespaceStore.selectAll();
|
|
||||||
} else if (action.option) {
|
|
||||||
didToggle = true;
|
didToggle = true;
|
||||||
|
|
||||||
if (isMultiSelection) {
|
if (action.option.value === selectAllNamespaces) {
|
||||||
|
namespaceStore.selectAll();
|
||||||
|
} else if (isMultiSelection) {
|
||||||
namespaceStore.toggleSingle(action.option.value);
|
namespaceStore.toggleSingle(action.option.value);
|
||||||
} else {
|
} else {
|
||||||
namespaceStore.selectSingle(action.option.value);
|
namespaceStore.selectSingle(action.option.value);
|
||||||
@ -171,6 +151,27 @@ export function namespaceSelectFilterModelFor(dependencies: Dependencies): Names
|
|||||||
isMultiSelection = false;
|
isMultiSelection = false;
|
||||||
model.menu.close();
|
model.menu.close();
|
||||||
}),
|
}),
|
||||||
|
isOptionSelected,
|
||||||
|
formatOptionLabel: (option) => {
|
||||||
|
if (option.value === selectAllNamespaces) {
|
||||||
|
return <>All Namespaces</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex gaps align-center">
|
||||||
|
<Icon small material="layers" />
|
||||||
|
<span>{option.value}</span>
|
||||||
|
{isOptionSelected(option) && (
|
||||||
|
<Icon
|
||||||
|
small
|
||||||
|
material="check"
|
||||||
|
className="box right"
|
||||||
|
data-testid={`namespace-select-filter-option-${option.value}-selected`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
|
|||||||
@ -64,6 +64,10 @@
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
||||||
|
&--is-selected:not(&--is-focused) {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -131,7 +131,7 @@ describe("<NamespaceSelectFilter />", () => {
|
|||||||
expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull();
|
expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when clicked again, then holding down multi select key, and then clicking 'test-3'", () => {
|
describe("when clicked again, then holding down multi select key", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const filter = result.getByTestId("namespace-select-filter");
|
const filter = result.getByTestId("namespace-select-filter");
|
||||||
|
|
||||||
@ -160,6 +160,22 @@ describe("<NamespaceSelectFilter />", () => {
|
|||||||
expect(result.queryByTestId("namespace-select-filter-option-kube-system-selected")).toBeNull();
|
expect(result.queryByTestId("namespace-select-filter-option-kube-system-selected")).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("when 'test-13' is clicked", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
result.getByText("test-13").click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has all of 'test-1', 'test-3', and 'test-13' selected in the store", () => {
|
||||||
|
expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3", "test-13"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("'test-13' is not sorted to the top of the list", () => {
|
||||||
|
const topLevelElement = result.getByText("test-13").parentElement?.parentElement as HTMLElement;
|
||||||
|
|
||||||
|
expect(topLevelElement.nextSibling).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("when releasing multi select key", () => {
|
describe("when releasing multi select key", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const filter = result.getByTestId("namespace-select-filter");
|
const filter = result.getByTestId("namespace-select-filter");
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import type { NamespaceStore } from "./store";
|
|||||||
import { Select } from "../select";
|
import { Select } from "../select";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import type { NamespaceSelectFilterModel, NamespaceSelectFilterOption, SelectAllNamespaces } from "./namespace-select-filter-model/namespace-select-filter-model";
|
import type { NamespaceSelectFilterModel, NamespaceSelectFilterOption, SelectAllNamespaces } from "./namespace-select-filter-model/namespace-select-filter-model";
|
||||||
import { formatOptionLabel } from "./namespace-select-filter-model/namespace-select-filter-model";
|
|
||||||
import namespaceSelectFilterModelInjectable from "./namespace-select-filter-model/namespace-select-filter-model.injectable";
|
import namespaceSelectFilterModelInjectable from "./namespace-select-filter-model/namespace-select-filter-model.injectable";
|
||||||
import namespaceStoreInjectable from "./store.injectable";
|
import namespaceStoreInjectable from "./store.injectable";
|
||||||
|
|
||||||
@ -37,16 +36,18 @@ const NonInjectedNamespaceSelectFilter = observer(({ model, id }: Dependencies &
|
|||||||
id={id}
|
id={id}
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
isClearable={false}
|
isClearable={false}
|
||||||
menuIsOpen={model.menu.isOpen}
|
menuIsOpen={model.menu.isOpen.get()}
|
||||||
components={{ Placeholder }}
|
components={{ Placeholder }}
|
||||||
closeMenuOnSelect={false}
|
closeMenuOnSelect={false}
|
||||||
controlShouldRenderValue={false}
|
controlShouldRenderValue={false}
|
||||||
onChange={model.onChange}
|
onChange={model.onChange}
|
||||||
onBlur={model.reset}
|
onBlur={model.reset}
|
||||||
formatOptionLabel={formatOptionLabel}
|
formatOptionLabel={model.formatOptionLabel}
|
||||||
options={model.options.get()}
|
options={model.options.get()}
|
||||||
className="NamespaceSelect NamespaceSelectFilter"
|
className="NamespaceSelect NamespaceSelectFilter"
|
||||||
menuClass="NamespaceSelectFilterMenu"
|
menuClass="NamespaceSelectFilterMenu"
|
||||||
|
isOptionSelected={model.isOptionSelected}
|
||||||
|
hideSelectedOptions={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user