mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Move everything else related to selecting namespaces into context
- Use only the context for the namespace-select-filter-model Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
d7474fe443
commit
3fd68bfcc1
@ -7,10 +7,25 @@
|
|||||||
* This type is used for KubeObjectStores
|
* This type is used for KubeObjectStores
|
||||||
*/
|
*/
|
||||||
export interface ClusterContext {
|
export interface ClusterContext {
|
||||||
readonly allNamespaces: string[]; // available / allowed namespaces from cluster.ts
|
/**
|
||||||
readonly contextNamespaces: string[]; // selected by user (see: namespace-select.tsx)
|
* A computed getter for all the namespaces that this cluster knows about
|
||||||
|
*/
|
||||||
|
readonly allNamespaces: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The computed getter of namespaces that are currently selected
|
||||||
|
*/
|
||||||
|
readonly contextNamespaces: string[];
|
||||||
|
|
||||||
readonly hasSelectedAll: boolean;
|
readonly hasSelectedAll: boolean;
|
||||||
|
|
||||||
isLoadingAll(namespaces: string[]): boolean;
|
isLoadingAll(namespaces: string[]): boolean;
|
||||||
isGlobalWatchEnabled(): boolean;
|
isGlobalWatchEnabled(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NamespaceScopedClusterContext extends ClusterContext {
|
||||||
|
selectAllNamespaces(): void;
|
||||||
|
toggleNamespace(namespace: string): void;
|
||||||
|
selectNamespace(namespace: string): void;
|
||||||
|
deselectNamespace(namespace: string): void;
|
||||||
|
}
|
||||||
|
|||||||
@ -3,18 +3,21 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import type { ClusterContext } from "./cluster-frame-context";
|
import type { NamespaceScopedClusterContext } from "./cluster-frame-context";
|
||||||
import namespaceStoreInjectable from "../components/+namespaces/store.injectable";
|
import namespaceStoreInjectable from "../components/+namespaces/store.injectable";
|
||||||
import hostedClusterInjectable from "./hosted-cluster.injectable";
|
import hostedClusterInjectable from "./hosted-cluster.injectable";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import { computed } from "mobx";
|
import { computed } from "mobx";
|
||||||
|
import selectedNamespaceStorageInjectable from "../components/+namespaces/namespace-storage.injectable";
|
||||||
|
import { toggle } from "../utils";
|
||||||
|
|
||||||
const clusterFrameContextForNamespacedResourcesInjectable = getInjectable({
|
const clusterFrameContextForNamespacedResourcesInjectable = getInjectable({
|
||||||
id: "cluster-frame-context-for-namespaced-resources",
|
id: "cluster-frame-context-for-namespaced-resources",
|
||||||
|
|
||||||
instantiate: (di): ClusterContext => {
|
instantiate: (di): NamespaceScopedClusterContext => {
|
||||||
const cluster = di.inject(hostedClusterInjectable);
|
const cluster = di.inject(hostedClusterInjectable);
|
||||||
const namespaceStore = di.inject(namespaceStoreInjectable);
|
const namespaceStore = di.inject(namespaceStoreInjectable);
|
||||||
|
const selectedNamespaceStorage = di.inject(selectedNamespaceStorageInjectable);
|
||||||
|
|
||||||
assert(cluster, "This can only be injected within a cluster frame");
|
assert(cluster, "This can only be injected within a cluster frame");
|
||||||
|
|
||||||
@ -25,20 +28,37 @@ const clusterFrameContextForNamespacedResourcesInjectable = getInjectable({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (namespaceStore.items.length > 0) {
|
if (namespaceStore.items.length > 0) {
|
||||||
// namespaces from kubernetes api
|
// namespaces from kubernetes api
|
||||||
return namespaceStore.items.map((namespace) => namespace.getName());
|
return namespaceStore.items.map((namespace) => namespace.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback to cluster resolved namespaces because we could not load list
|
// fallback to cluster resolved namespaces because we could not load list
|
||||||
return cluster.allowedNamespaces.slice();
|
return cluster.allowedNamespaces.slice();
|
||||||
});
|
});
|
||||||
const contextNamespaces = computed(() => namespaceStore.contextNamespaces);
|
const contextNamespaces = computed(() => {
|
||||||
|
const storedState = selectedNamespaceStorage.get();
|
||||||
|
|
||||||
|
if (!storedState || storedState.length === 0) {
|
||||||
|
return allNamespaces.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = new Set(storedState);
|
||||||
|
const currentlyKnownNamespaces = new Set(allNamespaces.get());
|
||||||
|
|
||||||
|
for (const namespace of storedState) {
|
||||||
|
if (!currentlyKnownNamespaces.has(namespace)) {
|
||||||
|
state.delete(namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...state];
|
||||||
|
});
|
||||||
const hasSelectedAll = computed(() => {
|
const hasSelectedAll = computed(() => {
|
||||||
const namespaces = new Set(contextNamespaces.get());
|
const namespaces = new Set(contextNamespaces.get());
|
||||||
|
|
||||||
return allNamespaces.get().length > 1
|
return allNamespaces.get().length > 1
|
||||||
&& cluster.accessibleNamespaces.length === 0
|
&& cluster.accessibleNamespaces.length === 0
|
||||||
&& allNamespaces.get().every(ns => namespaces.has(ns));
|
&& allNamespaces.get().every(ns => namespaces.has(ns));
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -48,6 +68,24 @@ const clusterFrameContextForNamespacedResourcesInjectable = getInjectable({
|
|||||||
&& allNamespaces.get().every(ns => namespaces.includes(ns))
|
&& allNamespaces.get().every(ns => namespaces.includes(ns))
|
||||||
),
|
),
|
||||||
isGlobalWatchEnabled: () => cluster.isGlobalWatchEnabled,
|
isGlobalWatchEnabled: () => cluster.isGlobalWatchEnabled,
|
||||||
|
selectAllNamespaces: () => {
|
||||||
|
selectedNamespaceStorage.set([]);
|
||||||
|
},
|
||||||
|
selectNamespace: (namespace) => {
|
||||||
|
selectedNamespaceStorage.set([namespace]);
|
||||||
|
},
|
||||||
|
toggleNamespace: (namespace) => {
|
||||||
|
const nextState = new Set(contextNamespaces.get());
|
||||||
|
|
||||||
|
toggle(nextState, namespace);
|
||||||
|
selectedNamespaceStorage.set([...nextState]);
|
||||||
|
},
|
||||||
|
deselectNamespace: (namespace) => {
|
||||||
|
const nextState = new Set(contextNamespaces.get());
|
||||||
|
|
||||||
|
nextState.delete(namespace);
|
||||||
|
selectedNamespaceStorage.set([...nextState]);
|
||||||
|
},
|
||||||
get allNamespaces() {
|
get allNamespaces() {
|
||||||
return allNamespaces.get();
|
return allNamespaces.get();
|
||||||
},
|
},
|
||||||
|
|||||||
@ -3,16 +3,16 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import namespaceStoreInjectable from "../store.injectable";
|
import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
|
||||||
|
|
||||||
export type FilterByNamespace = (namespace: string) => void;
|
export type FilterByNamespace = (namespace: string) => void;
|
||||||
|
|
||||||
const filterByNamespaceInjectable = getInjectable({
|
const filterByNamespaceInjectable = getInjectable({
|
||||||
id: "filter-by-namespace",
|
id: "filter-by-namespace",
|
||||||
instantiate: (di): FilterByNamespace => {
|
instantiate: (di): FilterByNamespace => {
|
||||||
const namespaceStore = di.inject(namespaceStoreInjectable);
|
const context = di.inject(clusterFrameContextForNamespacedResourcesInjectable);
|
||||||
|
|
||||||
return (namespace) => namespaceStore.selectSingle(namespace);
|
return (namespace) => context.selectNamespace(namespace);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
import { namespaceSelectFilterModelFor } from "./namespace-select-filter-model";
|
import { namespaceSelectFilterModelFor } from "./namespace-select-filter-model";
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import namespaceStoreInjectable from "../store.injectable";
|
|
||||||
import isMultiSelectionKeyInjectable from "./is-selection-key.injectable";
|
import isMultiSelectionKeyInjectable from "./is-selection-key.injectable";
|
||||||
import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
|
import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
|
||||||
|
|
||||||
@ -12,7 +11,6 @@ const namespaceSelectFilterModelInjectable = getInjectable({
|
|||||||
id: "namespace-select-filter-model",
|
id: "namespace-select-filter-model",
|
||||||
|
|
||||||
instantiate: (di) => namespaceSelectFilterModelFor({
|
instantiate: (di) => namespaceSelectFilterModelFor({
|
||||||
namespaceStore: di.inject(namespaceStoreInjectable),
|
|
||||||
isMultiSelectionKey: di.inject(isMultiSelectionKeyInjectable),
|
isMultiSelectionKey: di.inject(isMultiSelectionKeyInjectable),
|
||||||
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
|
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -5,16 +5,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import type { IComputedValue } from "mobx";
|
import type { IComputedValue } from "mobx";
|
||||||
import { observable, action, computed, comparer } from "mobx";
|
import { observable, action, computed, comparer } from "mobx";
|
||||||
import type { NamespaceStore } from "../store";
|
|
||||||
import type { ActionMeta, MultiValue } from "react-select";
|
import type { ActionMeta, MultiValue } from "react-select";
|
||||||
import { Icon } from "../../icon";
|
import { Icon } from "../../icon";
|
||||||
import type { SelectOption } from "../../select";
|
import type { SelectOption } from "../../select";
|
||||||
import { observableCrate } from "../../../utils";
|
import { observableCrate } from "../../../utils";
|
||||||
import type { IsMultiSelectionKey } from "./is-selection-key.injectable";
|
import type { IsMultiSelectionKey } from "./is-selection-key.injectable";
|
||||||
|
import type { NamespaceScopedClusterContext } from "../../../cluster-frame-context/cluster-frame-context";
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
context: NamespaceScopedClusterContext;
|
context: NamespaceScopedClusterContext;
|
||||||
namespaceStore: NamespaceStore;
|
|
||||||
isMultiSelectionKey: IsMultiSelectionKey;
|
isMultiSelectionKey: IsMultiSelectionKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ enum SelectMenuState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function namespaceSelectFilterModelFor(dependencies: Dependencies): NamespaceSelectFilterModel {
|
export function namespaceSelectFilterModelFor(dependencies: Dependencies): NamespaceSelectFilterModel {
|
||||||
const { isMultiSelectionKey, namespaceStore, context } = dependencies;
|
const { isMultiSelectionKey, context } = dependencies;
|
||||||
|
|
||||||
let didToggle = false;
|
let didToggle = false;
|
||||||
let isMultiSelection = false;
|
let isMultiSelection = false;
|
||||||
@ -111,7 +110,7 @@ export function namespaceSelectFilterModelFor(dependencies: Dependencies): Names
|
|||||||
onChange: (_, action) => {
|
onChange: (_, action) => {
|
||||||
switch (action.action) {
|
switch (action.action) {
|
||||||
case "clear":
|
case "clear":
|
||||||
namespaceStore.selectAll();
|
context.selectAllNamespaces();
|
||||||
break;
|
break;
|
||||||
case "deselect-option":
|
case "deselect-option":
|
||||||
case "select-option":
|
case "select-option":
|
||||||
@ -119,11 +118,11 @@ export function namespaceSelectFilterModelFor(dependencies: Dependencies): Names
|
|||||||
didToggle = true;
|
didToggle = true;
|
||||||
|
|
||||||
if (action.option.value === selectAllNamespaces) {
|
if (action.option.value === selectAllNamespaces) {
|
||||||
namespaceStore.selectAll();
|
context.selectAllNamespaces();
|
||||||
} else if (isMultiSelection) {
|
} else if (isMultiSelection) {
|
||||||
namespaceStore.toggleSingle(action.option.value);
|
context.toggleNamespace(action.option.value);
|
||||||
} else {
|
} else {
|
||||||
namespaceStore.selectSingle(action.option.value);
|
context.selectNamespace(action.option.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -15,6 +15,8 @@ import type { Fetch } from "../../../common/fetch/fetch.injectable";
|
|||||||
import fetchInjectable from "../../../common/fetch/fetch.injectable";
|
import fetchInjectable from "../../../common/fetch/fetch.injectable";
|
||||||
import { Namespace } from "../../../common/k8s-api/endpoints";
|
import { Namespace } from "../../../common/k8s-api/endpoints";
|
||||||
import { createMockResponseFromString } from "../../../test-utils/mock-responses";
|
import { createMockResponseFromString } from "../../../test-utils/mock-responses";
|
||||||
|
import type { NamespaceScopedClusterContext } from "../../cluster-frame-context/cluster-frame-context";
|
||||||
|
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
|
||||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||||
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
import createClusterInjectable from "../../cluster/create-cluster.injectable";
|
||||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||||
@ -43,6 +45,7 @@ function createNamespace(name: string): Namespace {
|
|||||||
describe("<NamespaceSelectFilter />", () => {
|
describe("<NamespaceSelectFilter />", () => {
|
||||||
let di: DiContainer;
|
let di: DiContainer;
|
||||||
let namespaceStore: NamespaceStore;
|
let namespaceStore: NamespaceStore;
|
||||||
|
let context: NamespaceScopedClusterContext;
|
||||||
let fetchMock: AsyncFnMock<Fetch>;
|
let fetchMock: AsyncFnMock<Fetch>;
|
||||||
let result: RenderResult;
|
let result: RenderResult;
|
||||||
let cleanup: Disposer;
|
let cleanup: Disposer;
|
||||||
@ -69,6 +72,7 @@ describe("<NamespaceSelectFilter />", () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
namespaceStore = di.inject(namespaceStoreInjectable);
|
namespaceStore = di.inject(namespaceStoreInjectable);
|
||||||
|
context = di.inject(clusterFrameContextForNamespacedResourcesInjectable);
|
||||||
|
|
||||||
const subscribeStores = di.inject(subscribeStoresInjectable);
|
const subscribeStores = di.inject(subscribeStoresInjectable);
|
||||||
|
|
||||||
@ -138,7 +142,7 @@ describe("<NamespaceSelectFilter />", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("has only 'test-2' is selected in the store", () => {
|
it("has only 'test-2' is selected in the store", () => {
|
||||||
expect(namespaceStore.contextNamespaces).toEqual(["test-2"]);
|
expect(context.contextNamespaces).toEqual(["test-2"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("closes menu", () => {
|
it("closes menu", () => {
|
||||||
@ -172,7 +176,7 @@ describe("<NamespaceSelectFilter />", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("has only 'test-1' is selected in the store", () => {
|
it("has only 'test-1' is selected in the store", () => {
|
||||||
expect(namespaceStore.contextNamespaces).toEqual(["test-1"]);
|
expect(context.contextNamespaces).toEqual(["test-1"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("closes menu", () => {
|
it("closes menu", () => {
|
||||||
@ -197,7 +201,7 @@ describe("<NamespaceSelectFilter />", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("has both 'test-1' and 'test-3' as selected in the store", () => {
|
it("has both 'test-1' and 'test-3' as selected in the store", () => {
|
||||||
expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3"]));
|
expect(new Set(context.contextNamespaces)).toEqual(new Set(["test-1", "test-3"]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps menu open", () => {
|
it("keeps menu open", () => {
|
||||||
@ -214,7 +218,7 @@ describe("<NamespaceSelectFilter />", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("has all of 'test-1', 'test-3', and 'test-13' selected in the store", () => {
|
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"]));
|
expect(new Set(context.contextNamespaces)).toEqual(new Set(["test-1", "test-3", "test-13"]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("'test-13' is not sorted to the top of the list", () => {
|
it("'test-13' is not sorted to the top of the list", () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user