1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Allow specifying ResourceName in KubeObjectListLayout (#6926)

* Allow specifying ResourceName in KubeObjectListLayout

Signed-off-by: Panu Horsmalahti <phorsmalahti@mirantis.com>

* Fix comment syntax.

Signed-off-by: Panu Horsmalahti <phorsmalahti@mirantis.com>

Signed-off-by: Panu Horsmalahti <phorsmalahti@mirantis.com>
This commit is contained in:
Panu Horsmalahti 2023-01-12 18:38:50 +02:00 committed by GitHub
parent 904200f72f
commit c9418f6362
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 237 additions and 2 deletions

View File

@ -0,0 +1,121 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`kube-object-list-layout given pod store renders 1`] = `
<body>
<div>
<div>
<div
class="ItemListLayout flex column KubeObjectListLayout Pods"
>
<div
class="header flex gaps align-center"
>
<h5
class="title"
>
Pods
</h5>
<div
class="info-panel box grow"
>
0 items
</div>
<div
class="NamespaceSelectFilterParent"
data-testid="namespace-select-filter"
>
<div
class="Select theme-dark NamespaceSelect NamespaceSelectFilter css-b62m3t-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-kube-object-list-layout-namespace-select-input-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-13cymwt-control"
>
<div
class="Select__value-container Select__value-container--is-multi css-1fdsijx-ValueContainer"
>
<div
class="Select__placeholder css-1jqq78o-placeholder"
id="react-select-kube-object-list-layout-namespace-select-input-placeholder"
>
All namespaces
</div>
<div
class="Select__input-container css-qbdosj-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-kube-object-list-layout-namespace-select-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="kube-object-list-layout-namespace-select-input"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1u9des2-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-1xc3v61-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="items box grow flex column"
>
<div
class="Table flex column KubeObjectListLayout Pods box grow dark selectable scrollable autoSize virtual"
>
<div
class="Spinner singleColor center"
/>
</div>
<div
class="AddRemoveButtons flex gaps"
/>
</div>
</div>
</div>
</div>
</body>
`;

View File

@ -0,0 +1,108 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DiContainer } from "@ogre-tools/injectable";
import "@testing-library/jest-dom/extend-expect";
import type { RenderResult } from "@testing-library/react";
import React from "react";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
import kubeSelectedUrlParamInjectable from "../kube-detail-params/kube-selected-url.injectable";
import toggleKubeDetailsPaneInjectable from "../kube-detail-params/toggle-details.injectable";
import type { DiRender } from "../test-utils/renderFor";
import { renderFor } from "../test-utils/renderFor";
import { KubeObjectListLayout } from "./index";
import appPathsStateInjectable from "../../../common/app-paths/app-paths-state.injectable";
import podStoreInjectable from "../+workloads-pods/store.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
import createClusterInjectable from "../../../main/create-cluster/create-cluster.injectable";
import type { PodStore } from "../+workloads-pods/store";
describe("kube-object-list-layout", () => {
let di: DiContainer;
let render: DiRender;
let podStore: PodStore;
beforeEach(() => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
di.override(storesAndApisCanBeCreatedInjectable, () => true);
const createCluster = di.inject(createClusterInjectable);
di.override(hostedClusterInjectable, () => createCluster({
contextName: "some-context-name",
id: "some-cluster-id",
kubeConfigPath: "/some-path-to-a-kubeconfig",
}, {
clusterServerUrl: "https://localhost:8080",
}));
render = renderFor(di);
di.override(subscribeStoresInjectable, () => jest.fn().mockImplementation(() => jest.fn()));
di.override(kubeSelectedUrlParamInjectable, () => ({
get: () => "path",
}));
di.override(toggleKubeDetailsPaneInjectable, () => null);
di.override(appPathsStateInjectable, () => ({
get: () => ({}),
}));
podStore = di.inject(podStoreInjectable);
});
describe("given pod store", () => {
let result: RenderResult;
it("renders", () => {
result = render((
<div>
<KubeObjectListLayout
className="Pods"
store={podStore}
tableId = "workloads_pods"
isConfigurable
renderHeaderTitle="Pods"
renderTableContents={pod => [
<div key={pod.getName()}>{pod.getName()}</div>,
]}
/>
</div>
));
expect(result.baseElement).toMatchSnapshot();
});
describe("given resourcename", () => {
it("uses resourcename in search placeholder", () => {
result = render((
<div>
<KubeObjectListLayout
className="Pods"
store={podStore}
tableId = "workloads_pods"
isConfigurable
renderHeaderTitle="Pods"
renderTableContents={pod => [
<div key={pod.getName()}>{pod.getName()}</div>,
]}
resourceName="My Custom Items"
searchFilters={[() => null]}
/>
</div>
));
expect(result.getByPlaceholderText("Search My Custom Items...")).toBeInTheDocument();
});
});
});
});

View File

@ -40,6 +40,12 @@ export interface KubeObjectListLayoutProps<
store: KubeObjectStore<K, A, D>; store: KubeObjectStore<K, A, D>;
dependentStores?: SubscribableStore[]; dependentStores?: SubscribableStore[];
subscribeStores?: boolean; subscribeStores?: boolean;
/**
* Customize resource name for e.g. search input ("Search <ResourceName>..."")
* If not provided, ResourceNames is used instead with a fallback to resource kind.
*/
resourceName?: string;
} }
interface Dependencies { interface Dependencies {
@ -132,7 +138,7 @@ class NonInjectedKubeObjectListLayout<
onDetails, onDetails,
...layoutProps ...layoutProps
} = this.props; } = this.props;
const placeholderString = ResourceNames[ResourceKindMap[store.api.kind]] || store.api.kind; const resourceName = this.props.resourceName || ResourceNames[ResourceKindMap[store.api.kind]] || store.api.kind;
return ( return (
<ItemListLayout<K, false> <ItemListLayout<K, false>
@ -151,7 +157,7 @@ class NonInjectedKubeObjectListLayout<
), ),
searchProps: { searchProps: {
...searchProps, ...searchProps,
placeholder: `Search ${placeholderString}...`, placeholder: `Search ${resourceName}...`,
}, },
info: ( info: (
<> <>