mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
List layout column injection token package (#7544)
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> Signed-off-by: Sami Tiilikainen <97873007+samitiilikainen@users.noreply.github.com> Co-authored-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
4bb37df1cd
commit
5eb41e8a6c
19
package-lock.json
generated
19
package-lock.json
generated
@ -3935,6 +3935,10 @@
|
|||||||
"resolved": "packages/technical-features/application/legacy-extensions",
|
"resolved": "packages/technical-features/application/legacy-extensions",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@k8slens/list-layout": {
|
||||||
|
"resolved": "packages/list-layout",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@k8slens/messaging": {
|
"node_modules/@k8slens/messaging": {
|
||||||
"resolved": "packages/technical-features/messaging/agnostic",
|
"resolved": "packages/technical-features/messaging/agnostic",
|
||||||
"link": true
|
"link": true
|
||||||
@ -35719,6 +35723,7 @@
|
|||||||
"@astronautlabs/jsonpath": "^1.1.0",
|
"@astronautlabs/jsonpath": "^1.1.0",
|
||||||
"@hapi/call": "^9.0.1",
|
"@hapi/call": "^9.0.1",
|
||||||
"@hapi/subtext": "^7.1.0",
|
"@hapi/subtext": "^7.1.0",
|
||||||
|
"@k8slens/list-layout": "^1.0.0-alpha.0",
|
||||||
"@k8slens/metrics": "^6.5.0-alpha.3",
|
"@k8slens/metrics": "^6.5.0-alpha.3",
|
||||||
"@k8slens/node-fetch": "^6.5.0-alpha.3",
|
"@k8slens/node-fetch": "^6.5.0-alpha.3",
|
||||||
"@k8slens/react-application": "^1.0.0-alpha.2",
|
"@k8slens/react-application": "^1.0.0-alpha.2",
|
||||||
@ -36657,6 +36662,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"packages/list-layout": {
|
||||||
|
"name": "@k8slens/list-layout",
|
||||||
|
"version": "1.0.0-alpha.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@k8slens/eslint-config": "^6.5.0-alpha.2",
|
||||||
|
"@k8slens/jest": "^6.5.0-alpha.2",
|
||||||
|
"@k8slens/typescript": "^6.5.0-alpha.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@ogre-tools/injectable": "^15.1.2",
|
||||||
|
"react": "^17.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"packages/metrics": {
|
"packages/metrics": {
|
||||||
"name": "@k8slens/metrics",
|
"name": "@k8slens/metrics",
|
||||||
"version": "6.5.0-alpha.3",
|
"version": "6.5.0-alpha.3",
|
||||||
|
|||||||
@ -115,6 +115,7 @@
|
|||||||
"@k8slens/metrics": "^6.5.0-alpha.3",
|
"@k8slens/metrics": "^6.5.0-alpha.3",
|
||||||
"@k8slens/node-fetch": "^6.5.0-alpha.3",
|
"@k8slens/node-fetch": "^6.5.0-alpha.3",
|
||||||
"@k8slens/react-application": "^1.0.0-alpha.2",
|
"@k8slens/react-application": "^1.0.0-alpha.2",
|
||||||
|
"@k8slens/list-layout": "^1.0.0-alpha.0",
|
||||||
"@kubernetes/client-node": "^0.18.1",
|
"@kubernetes/client-node": "^0.18.1",
|
||||||
"@material-ui/styles": "^4.11.5",
|
"@material-ui/styles": "^4.11.5",
|
||||||
"@sentry/electron": "^3.0.8",
|
"@sentry/electron": "^3.0.8",
|
||||||
|
|||||||
@ -3,14 +3,11 @@
|
|||||||
* 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 type { ItemObject } from "@k8slens/list-layout";
|
||||||
import autoBind from "auto-bind";
|
import autoBind from "auto-bind";
|
||||||
import orderBy from "lodash/orderBy";
|
import orderBy from "lodash/orderBy";
|
||||||
import { action, computed, observable, when, makeObservable } from "mobx";
|
import { action, computed, observable, when, makeObservable } from "mobx";
|
||||||
|
|
||||||
export interface ItemObject {
|
|
||||||
getId(): string;
|
|
||||||
getName(): string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class ItemStore<Item extends ItemObject> {
|
export abstract class ItemStore<Item extends ItemObject> {
|
||||||
protected defaultSorting = (item: Item) => item.getName();
|
protected defaultSorting = (item: Item) => item.getName();
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* 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 type { ItemObject } from "../../item.store";
|
import type { ItemObject } from "@k8slens/list-layout";
|
||||||
import type { HelmReleaseDetails } from "./helm-releases.api/request-details.injectable";
|
import type { HelmReleaseDetails } from "./helm-releases.api/request-details.injectable";
|
||||||
|
|
||||||
export interface HelmReleaseUpdateDetails {
|
export interface HelmReleaseUpdateDetails {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import {
|
|||||||
isTypedArray,
|
isTypedArray,
|
||||||
isRecord,
|
isRecord,
|
||||||
} from "@k8slens/utilities";
|
} from "@k8slens/utilities";
|
||||||
import type { ItemObject } from "../item.store";
|
import type { ItemObject } from "@k8slens/list-layout";
|
||||||
import type { Patch } from "rfc6902";
|
import type { Patch } from "rfc6902";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import type { JsonObject } from "type-fest";
|
import type { JsonObject } from "type-fest";
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
132
packages/core/src/features/cluster/workloads/pods.test.tsx
Normal file
132
packages/core/src/features/cluster/workloads/pods.test.tsx
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import navigateToPodsInjectable from "../../../common/front-end-routing/routes/cluster/workloads/pods/navigate-to-pods.injectable";
|
||||||
|
import { type ApplicationBuilder, getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import podStoreInjectable from "../../../renderer/components/+workloads-pods/store.injectable";
|
||||||
|
import type { PodMetrics } from "../../../common/k8s-api/endpoints";
|
||||||
|
import { Pod } from "../../../common/k8s-api/endpoints";
|
||||||
|
import podMetricsApiInjectable from "../../../common/k8s-api/endpoints/pod-metrics.api.injectable";
|
||||||
|
import requestMetricsInjectable from "../../../common/k8s-api/endpoints/metrics.api/request-metrics.injectable";
|
||||||
|
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
||||||
|
|
||||||
|
describe("workloads / pods", () => {
|
||||||
|
let rendered: RenderResult;
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
const podMetrics: PodMetrics[] = [];
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder = getApplicationBuilder().setEnvironmentToClusterFrame();
|
||||||
|
applicationBuilder.namespaces.add("default");
|
||||||
|
applicationBuilder.beforeWindowStart(({ windowDi }) => {
|
||||||
|
applicationBuilder.allowKubeResource({
|
||||||
|
apiName: "pods",
|
||||||
|
group: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
windowDi.override(podMetricsApiInjectable, () => ({
|
||||||
|
list: async () => podMetrics,
|
||||||
|
} as any));
|
||||||
|
|
||||||
|
const apiManager = windowDi.inject(apiManagerInjectable);
|
||||||
|
const podStore = windowDi.inject(podStoreInjectable);
|
||||||
|
|
||||||
|
apiManager.registerStore(podStore);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when navigating to workloads / pods view", () => {
|
||||||
|
describe("given pods are loading", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.afterWindowStart(({ windowDi }) => {
|
||||||
|
const podStore = windowDi.inject(podStoreInjectable);
|
||||||
|
|
||||||
|
podStore.items.clear();
|
||||||
|
podStore.isLoaded = false;
|
||||||
|
podStore.isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
applicationBuilder.navigateWith(navigateToPodsInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows loading spinner", async () => {
|
||||||
|
expect(await rendered.findByTestId("kube-object-list-layout-spinner")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given no pods", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.afterWindowStart(({ windowDi }) => {
|
||||||
|
const podStore = windowDi.inject(podStoreInjectable);
|
||||||
|
|
||||||
|
podStore.items.clear();
|
||||||
|
podStore.isLoaded = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
applicationBuilder.navigateWith(navigateToPodsInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows item list is empty", async () => {
|
||||||
|
expect(rendered.getByText("Item list is empty")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given a namespace has pods", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.afterWindowStart(({ windowDi }) => {
|
||||||
|
windowDi.override(requestMetricsInjectable, () => () => ({} as any));
|
||||||
|
|
||||||
|
const podStore = windowDi.inject(podStoreInjectable);
|
||||||
|
|
||||||
|
podStore.items.push(new Pod({
|
||||||
|
apiVersion: "v1",
|
||||||
|
kind: "Pod",
|
||||||
|
metadata: {
|
||||||
|
name: "test-pod-1",
|
||||||
|
namespace: "default",
|
||||||
|
resourceVersion: "irrelevant",
|
||||||
|
selfLink: "/api/v1/namespaces/default/pods/test-pod-1",
|
||||||
|
uid: "uuid-1",
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
containers: [
|
||||||
|
{
|
||||||
|
name: "container-1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "container-2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
status: {} as any,
|
||||||
|
}));
|
||||||
|
podStore.isLoaded = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
applicationBuilder.navigateWith(navigateToPodsInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders the pod list", async () => {
|
||||||
|
expect(await rendered.findByTestId(`list-pod-name-uuid-1`)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -13,7 +13,7 @@ import { Icon } from "../icon";
|
|||||||
import { SubHeader } from "../layout/sub-header";
|
import { SubHeader } from "../layout/sub-header";
|
||||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||||
import { cssNames, prevDefault } from "@k8slens/utilities";
|
import { cssNames, prevDefault } from "@k8slens/utilities";
|
||||||
import type { ItemObject } from "../../../common/item.store";
|
import type { ItemObject } from "@k8slens/list-layout";
|
||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||||
import { KubeObjectAge } from "../kube-object/age";
|
import { KubeObjectAge } from "../kube-object/age";
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import React from "react";
|
||||||
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import type { KubeObjectListLayoutColumn } from "@k8slens/list-layout";
|
||||||
|
import { kubeObjectListLayoutColumnInjectionToken } from "@k8slens/list-layout";
|
||||||
|
import { KubeObjectAge } from "../../kube-object/age";
|
||||||
|
|
||||||
|
export const podsAgeColumnInjectable = getInjectable({
|
||||||
|
id: "pods-age-column",
|
||||||
|
instantiate: (): KubeObjectListLayoutColumn<Pod> => {
|
||||||
|
const columnId = "age";
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: columnId,
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersion: "v1",
|
||||||
|
priority: 30,
|
||||||
|
content: (pod: Pod) => {
|
||||||
|
return <KubeObjectAge key="age" object={pod} />;
|
||||||
|
},
|
||||||
|
header: { title: "Age", className: "age", sortBy: columnId, id: columnId },
|
||||||
|
sortingCallBack: (pod: Pod) => -pod.getCreationTimestamp(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: kubeObjectListLayoutColumnInjectionToken,
|
||||||
|
});
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { cssNames } from "@k8slens/utilities";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import startCase from "lodash/startCase";
|
||||||
|
import React from "react";
|
||||||
|
import type { ContainerStateValues, Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import type { KubeObjectListLayoutColumn } from "@k8slens/list-layout";
|
||||||
|
import { kubeObjectListLayoutColumnInjectionToken } from "@k8slens/list-layout";
|
||||||
|
import { StatusBrick } from "../../status-brick";
|
||||||
|
|
||||||
|
function renderState(name: string, ready: boolean, key: string, data?: ContainerStateValues) {
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="title">
|
||||||
|
{name}
|
||||||
|
{" "}
|
||||||
|
<span className="text-secondary">
|
||||||
|
{key}
|
||||||
|
{ready ? ", ready" : ""}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{Object.entries(data).map(([name, value]) => (
|
||||||
|
<React.Fragment key={name}>
|
||||||
|
<div className="name">{startCase(name)}</div>
|
||||||
|
<div className="value">{value}</div>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderContainersStatus(pod: Pod) {
|
||||||
|
return pod.getContainerStatuses().map(({ name, state, ready }) => {
|
||||||
|
return (
|
||||||
|
<StatusBrick
|
||||||
|
key={name}
|
||||||
|
className={cssNames(state, { ready })}
|
||||||
|
tooltip={{
|
||||||
|
formatters: {
|
||||||
|
tableView: true,
|
||||||
|
nowrap: true,
|
||||||
|
},
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
{renderState(name, ready, "running", state?.running)}
|
||||||
|
{renderState(name, ready, "waiting", state?.waiting)}
|
||||||
|
{renderState(name, ready, "terminated", state?.terminated)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const podsContainersColumnInjectable = getInjectable({
|
||||||
|
id: "pods-containers-column",
|
||||||
|
instantiate: (): KubeObjectListLayoutColumn<Pod> => {
|
||||||
|
const columnId = "containers";
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: columnId,
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersion: "v1",
|
||||||
|
priority: 80,
|
||||||
|
content: (pod: Pod) => {
|
||||||
|
return renderContainersStatus(pod);
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
title: "Containers",
|
||||||
|
className: "containers",
|
||||||
|
sortBy: columnId,
|
||||||
|
id: columnId,
|
||||||
|
},
|
||||||
|
sortingCallBack: (pod: Pod) => pod.getContainerStatuses().length,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: kubeObjectListLayoutColumnInjectionToken,
|
||||||
|
});
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getConvertedParts } from "@k8slens/utilities";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import React from "react";
|
||||||
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import type { KubeObjectListLayoutColumn } from "@k8slens/list-layout";
|
||||||
|
import { kubeObjectListLayoutColumnInjectionToken } from "@k8slens/list-layout";
|
||||||
|
import { Tooltip } from "@k8slens/tooltip";
|
||||||
|
|
||||||
|
export const podsNameColumnInjectable = getInjectable({
|
||||||
|
id: "pods-name-column",
|
||||||
|
instantiate: (): KubeObjectListLayoutColumn<Pod> => {
|
||||||
|
const columnId = "name";
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: columnId,
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersion: "v1",
|
||||||
|
priority: 100,
|
||||||
|
content: (pod: Pod) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span id={`list-pod-name-${pod.getId()}`} data-testid={`list-pod-name-${pod.getId()}`}>
|
||||||
|
{pod.getName()}
|
||||||
|
</span>
|
||||||
|
<Tooltip targetId={`list-pod-name-${pod.getId()}`}>
|
||||||
|
{pod.getName()}
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
header: { title: "Name", className: "name", sortBy: columnId, id: columnId },
|
||||||
|
sortingCallBack: (pod: Pod) => getConvertedParts(pod.getName()),
|
||||||
|
searchFilter: (pod: Pod) => pod.getSearchFields(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: kubeObjectListLayoutColumnInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import React from "react";
|
||||||
|
import { NamespaceSelectBadge } from "../../+namespaces/namespace-select-badge";
|
||||||
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import type { KubeObjectListLayoutColumn } from "@k8slens/list-layout";
|
||||||
|
import { kubeObjectListLayoutColumnInjectionToken } from "@k8slens/list-layout";
|
||||||
|
|
||||||
|
export const podsNamespaceColumnInjectable = getInjectable({
|
||||||
|
id: "pods-namespace-column",
|
||||||
|
instantiate: (): KubeObjectListLayoutColumn<Pod> => {
|
||||||
|
const columnId = "namespace";
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: columnId,
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersion: "v1",
|
||||||
|
priority: 90,
|
||||||
|
content: (pod: Pod) => {
|
||||||
|
return (<NamespaceSelectBadge key="namespace" namespace={pod.getNs()} />);
|
||||||
|
},
|
||||||
|
header: { title: "Namespace", className: "namespace", sortBy: columnId, id: columnId },
|
||||||
|
sortingCallBack: (pod: Pod) => pod.getNs(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: kubeObjectListLayoutColumnInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import React from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import nodeApiInjectable from "../../../../common/k8s-api/endpoints/node.api.injectable";
|
||||||
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import { Badge } from "../../badge";
|
||||||
|
import getDetailsUrlInjectable from "../../kube-detail-params/get-details-url.injectable";
|
||||||
|
import type { KubeObjectListLayoutColumn } from "@k8slens/list-layout";
|
||||||
|
import { kubeObjectListLayoutColumnInjectionToken } from "@k8slens/list-layout";
|
||||||
|
import { stopPropagation } from "@k8slens/utilities";
|
||||||
|
|
||||||
|
export const podsNodeColumnInjectable = getInjectable({
|
||||||
|
id: "pods-node-column",
|
||||||
|
instantiate: (di): KubeObjectListLayoutColumn<Pod> => {
|
||||||
|
const getDetailsUrl = di.inject(getDetailsUrlInjectable);
|
||||||
|
const nodeApi = di.inject(nodeApiInjectable);
|
||||||
|
const columnId = "node";
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: columnId,
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersion: "v1",
|
||||||
|
priority: 50,
|
||||||
|
content: (pod: Pod) => {
|
||||||
|
return pod.getNodeName() ? (
|
||||||
|
<Badge
|
||||||
|
flat
|
||||||
|
key="node"
|
||||||
|
className="node"
|
||||||
|
tooltip={pod.getNodeName()}
|
||||||
|
expandable={false}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
to={getDetailsUrl(nodeApi.getUrl({ name: pod.getNodeName() }))}
|
||||||
|
onClick={stopPropagation}>
|
||||||
|
{pod.getNodeName()}
|
||||||
|
</Link>
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
: "";
|
||||||
|
},
|
||||||
|
header: { title: "Node", className: "node", sortBy: columnId, id: columnId },
|
||||||
|
sortingCallBack: (pod: Pod) => pod.getNodeName(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: kubeObjectListLayoutColumnInjectionToken,
|
||||||
|
});
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { stopPropagation } from "@k8slens/utilities";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import React from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";
|
||||||
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import { Badge } from "../../badge";
|
||||||
|
import getDetailsUrlInjectable from "../../kube-detail-params/get-details-url.injectable";
|
||||||
|
import type { KubeObjectListLayoutColumn } from "@k8slens/list-layout";
|
||||||
|
import { kubeObjectListLayoutColumnInjectionToken } from "@k8slens/list-layout";
|
||||||
|
|
||||||
|
export const podsOwnersColumnInjectable = getInjectable({
|
||||||
|
id: "pods-owners-column",
|
||||||
|
instantiate: (di): KubeObjectListLayoutColumn<Pod> => {
|
||||||
|
const getDetailsUrl = di.inject(getDetailsUrlInjectable);
|
||||||
|
const apiManager = di.inject(apiManagerInjectable);
|
||||||
|
const columnId = "owners";
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: columnId,
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersion: "v1",
|
||||||
|
priority: 60,
|
||||||
|
content: (pod: Pod) => {
|
||||||
|
return pod.getOwnerRefs().map(ref => {
|
||||||
|
const { kind, name } = ref;
|
||||||
|
const detailsLink = getDetailsUrl(apiManager.lookupApiLink(ref, pod));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge
|
||||||
|
flat
|
||||||
|
key={name}
|
||||||
|
className="owner"
|
||||||
|
tooltip={name}
|
||||||
|
>
|
||||||
|
<Link to={detailsLink} onClick={stopPropagation}>
|
||||||
|
{kind}
|
||||||
|
</Link>
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
header: { title: "Controlled By", className: "owners", sortBy: columnId, id: columnId },
|
||||||
|
sortingCallBack: (pod: Pod) => pod.getOwnerRefs().map(ref => ref.kind),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: kubeObjectListLayoutColumnInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import type { KubeObjectListLayoutColumn } from "@k8slens/list-layout";
|
||||||
|
import { kubeObjectListLayoutColumnInjectionToken } from "@k8slens/list-layout";
|
||||||
|
|
||||||
|
export const podsQosColumnInjectable = getInjectable({
|
||||||
|
id: "pods-qos-column",
|
||||||
|
instantiate: (): KubeObjectListLayoutColumn<Pod> => {
|
||||||
|
const columnId = "qos";
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: columnId,
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersion: "v1",
|
||||||
|
priority: 40,
|
||||||
|
content: (pod: Pod) => {
|
||||||
|
return pod.getQosClass();
|
||||||
|
},
|
||||||
|
header: { title: "QoS", className: "qos", sortBy: columnId, id: columnId },
|
||||||
|
sortingCallBack: (pod: Pod) => pod.getQosClass(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: kubeObjectListLayoutColumnInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import type { KubeObjectListLayoutColumn } from "@k8slens/list-layout";
|
||||||
|
import { kubeObjectListLayoutColumnInjectionToken } from "@k8slens/list-layout";
|
||||||
|
|
||||||
|
export const podsRestartsColumnInjectable = getInjectable({
|
||||||
|
id: "pods-restarts-column",
|
||||||
|
instantiate: (): KubeObjectListLayoutColumn<Pod> => {
|
||||||
|
const columnId = "restarts";
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: columnId,
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersion: "v1",
|
||||||
|
priority: 70,
|
||||||
|
content: (pod: Pod) => {
|
||||||
|
return pod.getRestartsCount();
|
||||||
|
},
|
||||||
|
header: { title: "Restarts", className: "restarts", sortBy: columnId, id: columnId },
|
||||||
|
sortingCallBack: (pod: Pod) => pod.getRestartsCount(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: kubeObjectListLayoutColumnInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { kebabCase } from "lodash";
|
||||||
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import type { KubeObjectListLayoutColumn } from "@k8slens/list-layout";
|
||||||
|
import { kubeObjectListLayoutColumnInjectionToken } from "@k8slens/list-layout";
|
||||||
|
|
||||||
|
export const podsStatusColumnInjectable = getInjectable({
|
||||||
|
id: "pods-status-column",
|
||||||
|
instantiate: (): KubeObjectListLayoutColumn<Pod> => {
|
||||||
|
const columnId = "status";
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: columnId,
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersion: "v1",
|
||||||
|
priority: 0,
|
||||||
|
content: (pod: Pod) => {
|
||||||
|
return { title: pod.getStatusMessage(), className: kebabCase(pod.getStatusMessage()) };
|
||||||
|
},
|
||||||
|
header: { title: "Status", className: "status", sortBy: columnId, id: columnId },
|
||||||
|
sortingCallBack: (pod: Pod) => pod.getStatusMessage(),
|
||||||
|
searchFilter: (pod: Pod) => pod.getStatusMessage(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: kubeObjectListLayoutColumnInjectionToken,
|
||||||
|
});
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import React from "react";
|
||||||
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
|
import type { KubeObjectListLayoutColumn } from "@k8slens/list-layout";
|
||||||
|
import { kubeObjectListLayoutColumnInjectionToken } from "@k8slens/list-layout";
|
||||||
|
import { KubeObjectStatusIcon } from "../../kube-object-status-icon";
|
||||||
|
|
||||||
|
export const podsQosColumnInjectable = getInjectable({
|
||||||
|
id: "pods-status-icon-column",
|
||||||
|
instantiate: (): KubeObjectListLayoutColumn<Pod> => {
|
||||||
|
const columnId = "qos";
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: columnId,
|
||||||
|
kind: "Pod",
|
||||||
|
apiVersion: "v1",
|
||||||
|
priority: 99,
|
||||||
|
content: (pod: Pod) => {
|
||||||
|
return <KubeObjectStatusIcon key="icon" object={pod} />;
|
||||||
|
},
|
||||||
|
header: { className: "warning", showWithColumn: "name" },
|
||||||
|
sortingCallBack: (pod: Pod) => pod.getQosClass(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: kubeObjectListLayoutColumnInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
@ -7,103 +7,23 @@ import "./pods.scss";
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||||
import type { ContainerStateValues, NodeApi, Pod } from "../../../common/k8s-api/endpoints";
|
|
||||||
import { StatusBrick } from "../status-brick";
|
|
||||||
import { cssNames, getConvertedParts, object, stopPropagation } from "@k8slens/utilities";
|
|
||||||
import startCase from "lodash/startCase";
|
|
||||||
import kebabCase from "lodash/kebabCase";
|
|
||||||
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
|
||||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
|
||||||
import { Badge } from "../badge";
|
|
||||||
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
|
||||||
import { KubeObjectAge } from "../kube-object/age";
|
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
|
|
||||||
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
|
|
||||||
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
|
||||||
import type { EventStore } from "../+events/store";
|
import type { EventStore } from "../+events/store";
|
||||||
import type { PodStore } from "./store";
|
import type { PodStore } from "./store";
|
||||||
import nodeApiInjectable from "../../../common/k8s-api/endpoints/node.api.injectable";
|
|
||||||
import eventStoreInjectable from "../+events/store.injectable";
|
import eventStoreInjectable from "../+events/store.injectable";
|
||||||
import podStoreInjectable from "./store.injectable";
|
import podStoreInjectable from "./store.injectable";
|
||||||
import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge";
|
|
||||||
import { Tooltip } from "@k8slens/tooltip";
|
|
||||||
|
|
||||||
enum columnId {
|
|
||||||
name = "name",
|
|
||||||
namespace = "namespace",
|
|
||||||
containers = "containers",
|
|
||||||
restarts = "restarts",
|
|
||||||
age = "age",
|
|
||||||
qos = "qos",
|
|
||||||
node = "node",
|
|
||||||
owners = "owners",
|
|
||||||
status = "status",
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
getDetailsUrl: GetDetailsUrl;
|
|
||||||
apiManager: ApiManager;
|
|
||||||
eventStore: EventStore;
|
eventStore: EventStore;
|
||||||
podStore: PodStore;
|
podStore: PodStore;
|
||||||
nodeApi: NodeApi;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class NonInjectedPods extends React.Component<Dependencies> {
|
class NonInjectedPods extends React.Component<Dependencies> {
|
||||||
renderState(name: string, ready: boolean, key: string, data?: ContainerStateValues) {
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="title">
|
|
||||||
{name}
|
|
||||||
{" "}
|
|
||||||
<span className="text-secondary">
|
|
||||||
{key}
|
|
||||||
{ready ? ", ready" : ""}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{object.entries(data).map(([name, value]) => (
|
|
||||||
<React.Fragment key={name}>
|
|
||||||
<div className="name">{startCase(name)}</div>
|
|
||||||
<div className="value">{value}</div>
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderContainersStatus(pod: Pod) {
|
|
||||||
return pod.getContainerStatuses().map(({ name, state, ready }) => {
|
|
||||||
return (
|
|
||||||
<StatusBrick
|
|
||||||
key={name}
|
|
||||||
className={cssNames(state, { ready })}
|
|
||||||
tooltip={{
|
|
||||||
formatters: {
|
|
||||||
tableView: true,
|
|
||||||
nowrap: true,
|
|
||||||
},
|
|
||||||
children: (
|
|
||||||
<>
|
|
||||||
{this.renderState(name, ready, "running", state?.running)}
|
|
||||||
{this.renderState(name, ready, "waiting", state?.waiting)}
|
|
||||||
{this.renderState(name, ready, "terminated", state?.terminated)}
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { apiManager, getDetailsUrl, podStore, eventStore, nodeApi } = this.props;
|
const { podStore, eventStore } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SiblingsInTabLayout>
|
<SiblingsInTabLayout>
|
||||||
@ -113,109 +33,12 @@ class NonInjectedPods extends React.Component<Dependencies> {
|
|||||||
dependentStores={[eventStore]} // status icon component uses event store
|
dependentStores={[eventStore]} // status icon component uses event store
|
||||||
tableId="workloads_pods"
|
tableId="workloads_pods"
|
||||||
isConfigurable
|
isConfigurable
|
||||||
sortingCallbacks={{
|
|
||||||
[columnId.name]: pod => getConvertedParts(pod.getName()),
|
|
||||||
[columnId.namespace]: pod => pod.getNs(),
|
|
||||||
[columnId.containers]: pod => pod.getContainerStatuses().length,
|
|
||||||
[columnId.restarts]: pod => pod.getRestartsCount(),
|
|
||||||
[columnId.owners]: pod => pod.getOwnerRefs().map(ref => ref.kind),
|
|
||||||
[columnId.qos]: pod => pod.getQosClass(),
|
|
||||||
[columnId.node]: pod => pod.getNodeName(),
|
|
||||||
[columnId.age]: pod => -pod.getCreationTimestamp(),
|
|
||||||
[columnId.status]: pod => pod.getStatusMessage(),
|
|
||||||
}}
|
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
pod => pod.getSearchFields(),
|
|
||||||
pod => pod.getStatusMessage(),
|
|
||||||
pod => pod.status?.podIP,
|
pod => pod.status?.podIP,
|
||||||
pod => pod.getNodeName(),
|
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Pods"
|
renderHeaderTitle="Pods"
|
||||||
renderTableHeader={[
|
renderTableHeader={[]}
|
||||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
renderTableContents={() => []}
|
||||||
{ className: "warning", showWithColumn: columnId.name },
|
|
||||||
{
|
|
||||||
title: "Namespace",
|
|
||||||
className: "namespace",
|
|
||||||
sortBy: columnId.namespace,
|
|
||||||
id: columnId.namespace,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Containers",
|
|
||||||
className: "containers",
|
|
||||||
sortBy: columnId.containers,
|
|
||||||
id: columnId.containers,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Restarts",
|
|
||||||
className: "restarts",
|
|
||||||
sortBy: columnId.restarts,
|
|
||||||
id: columnId.restarts,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Controlled By",
|
|
||||||
className: "owners",
|
|
||||||
sortBy: columnId.owners,
|
|
||||||
id: columnId.owners,
|
|
||||||
},
|
|
||||||
{ title: "Node", className: "node", sortBy: columnId.node, id: columnId.node },
|
|
||||||
{ title: "QoS", className: "qos", sortBy: columnId.qos, id: columnId.qos },
|
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
|
||||||
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
|
||||||
]}
|
|
||||||
renderTableContents={pod => [
|
|
||||||
<>
|
|
||||||
<span id={`list-pod-${pod.getId()}`}>
|
|
||||||
{pod.getName()}
|
|
||||||
</span>
|
|
||||||
<Tooltip targetId={`list-pod-${pod.getId()}`}>
|
|
||||||
{pod.getName()}
|
|
||||||
</Tooltip>
|
|
||||||
</>,
|
|
||||||
<KubeObjectStatusIcon key="icon" object={pod} />,
|
|
||||||
<NamespaceSelectBadge
|
|
||||||
key="namespace"
|
|
||||||
namespace={pod.getNs()}
|
|
||||||
/>,
|
|
||||||
this.renderContainersStatus(pod),
|
|
||||||
pod.getRestartsCount(),
|
|
||||||
pod.getOwnerRefs().map(ref => {
|
|
||||||
const { kind, name } = ref;
|
|
||||||
const detailsLink = getDetailsUrl(apiManager.lookupApiLink(ref, pod));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Badge
|
|
||||||
flat
|
|
||||||
key={name}
|
|
||||||
className="owner"
|
|
||||||
tooltip={name}
|
|
||||||
>
|
|
||||||
<Link to={detailsLink} onClick={stopPropagation}>
|
|
||||||
{kind}
|
|
||||||
</Link>
|
|
||||||
</Badge>
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
pod.getNodeName() ? (
|
|
||||||
<Badge
|
|
||||||
flat
|
|
||||||
key="node"
|
|
||||||
className="node"
|
|
||||||
tooltip={pod.getNodeName()}
|
|
||||||
expandable={false}
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
to={getDetailsUrl(nodeApi.getUrl({ name: pod.getNodeName() }))}
|
|
||||||
onClick={stopPropagation}>
|
|
||||||
{pod.getNodeName()}
|
|
||||||
</Link>
|
|
||||||
</Badge>
|
|
||||||
)
|
|
||||||
: "",
|
|
||||||
pod.getQosClass(),
|
|
||||||
<KubeObjectAge key="age" object={pod} />,
|
|
||||||
{ title: pod.getStatusMessage(), className: kebabCase(pod.getStatusMessage()) },
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
</SiblingsInTabLayout>
|
</SiblingsInTabLayout>
|
||||||
);
|
);
|
||||||
@ -225,9 +48,6 @@ class NonInjectedPods extends React.Component<Dependencies> {
|
|||||||
export const Pods = withInjectables<Dependencies>(NonInjectedPods, {
|
export const Pods = withInjectables<Dependencies>(NonInjectedPods, {
|
||||||
getProps: (di, props) => ({
|
getProps: (di, props) => ({
|
||||||
...props,
|
...props,
|
||||||
apiManager: di.inject(apiManagerInjectable),
|
|
||||||
getDetailsUrl: di.inject(getDetailsUrlInjectable),
|
|
||||||
nodeApi: di.inject(nodeApiInjectable),
|
|
||||||
eventStore: di.inject(eventStoreInjectable),
|
eventStore: di.inject(eventStoreInjectable),
|
||||||
podStore: di.inject(podStoreInjectable),
|
podStore: di.inject(podStoreInjectable),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import type { AddRemoveButtonsProps } from "../add-remove-buttons";
|
|||||||
import { AddRemoveButtons } from "../add-remove-buttons";
|
import { AddRemoveButtons } from "../add-remove-buttons";
|
||||||
import { NoItems } from "../no-items";
|
import { NoItems } from "../no-items";
|
||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
import type { ItemObject } from "../../../common/item.store";
|
import type { ItemObject } from "@k8slens/list-layout";
|
||||||
import type { Filter, PageFiltersStore } from "./page-filters/store";
|
import type { Filter, PageFiltersStore } from "./page-filters/store";
|
||||||
import type { LensTheme } from "../../themes/lens-theme";
|
import type { LensTheme } from "../../themes/lens-theme";
|
||||||
import { MenuActions } from "../menu/menu-actions";
|
import { MenuActions } from "../menu/menu-actions";
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import React from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { IClassName } from "@k8slens/utilities";
|
import type { IClassName } from "@k8slens/utilities";
|
||||||
import { cssNames, isDefined } from "@k8slens/utilities";
|
import { cssNames, isDefined } from "@k8slens/utilities";
|
||||||
import type { ItemObject } from "../../../common/item.store";
|
import type { ItemObject } from "@k8slens/list-layout";
|
||||||
import type { Filter } from "./page-filters/store";
|
import type { Filter } from "./page-filters/store";
|
||||||
import type { HeaderCustomizer, HeaderPlaceholders, ItemListStore, SearchFilter } from "./list-layout";
|
import type { HeaderCustomizer, HeaderPlaceholders, ItemListStore, SearchFilter } from "./list-layout";
|
||||||
import { SearchInputUrl } from "../input";
|
import { SearchInputUrl } from "../input";
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import type { TableCellProps, TableProps, TableRowProps, TableSortCallbacks } fr
|
|||||||
import type { IClassName, SingleOrMany } from "@k8slens/utilities";
|
import type { IClassName, SingleOrMany } from "@k8slens/utilities";
|
||||||
import { cssNames, noop } from "@k8slens/utilities";
|
import { cssNames, noop } from "@k8slens/utilities";
|
||||||
import type { AddRemoveButtonsProps } from "../add-remove-buttons";
|
import type { AddRemoveButtonsProps } from "../add-remove-buttons";
|
||||||
import type { ItemObject } from "../../../common/item.store";
|
import type { ItemObject } from "@k8slens/list-layout";
|
||||||
import type { SearchInputUrlProps } from "../input";
|
import type { SearchInputUrlProps } from "../input";
|
||||||
import type { PageFiltersStore } from "./page-filters/store";
|
import type { PageFiltersStore } from "./page-filters/store";
|
||||||
import { FilterType } from "./page-filters/store";
|
import { FilterType } from "./page-filters/store";
|
||||||
|
|||||||
@ -104,10 +104,236 @@ exports[`kube-object-list-layout given pod store renders 1`] = `
|
|||||||
class="items box grow flex column"
|
class="items box grow flex column"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Table flex column KubeObjectListLayout Pods box grow dark selectable scrollable autoSize virtual"
|
class="Table flex column KubeObjectListLayout Pods box grow dark selectable scrollable sortable autoSize virtual"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
class="TableHead sticky nowrap topLine"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="TableCell checkbox"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="Checkbox flex align-center"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
<i
|
||||||
|
class="box flex align-center"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell name nowrap sorting"
|
||||||
|
id="name"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell warning nowrap"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell namespace nowrap sorting"
|
||||||
|
id="namespace"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Namespace
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell containers nowrap sorting"
|
||||||
|
id="containers"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Containers
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell restarts nowrap sorting"
|
||||||
|
id="restarts"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Restarts
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell owners nowrap sorting"
|
||||||
|
id="owners"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Controlled By
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell node nowrap sorting"
|
||||||
|
id="node"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Node
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell qos nowrap sorting"
|
||||||
|
id="qos"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
QoS
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell age nowrap sorting"
|
||||||
|
id="age"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Age
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell status nowrap sorting"
|
||||||
|
id="status"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
Status
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="Icon sortIcon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="arrow_drop_down"
|
||||||
|
>
|
||||||
|
arrow_drop_down
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="TableCell menu nowrap"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material interactive focusable"
|
||||||
|
id="menu-actions-for-item-object-list-content"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="more_vert"
|
||||||
|
>
|
||||||
|
more_vert
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Spinner singleColor center"
|
class="Spinner singleColor center"
|
||||||
|
data-testid="kube-object-list-layout-spinner"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import directoryForKubeConfigsInjectable from "../../../common/app-paths/directo
|
|||||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||||
import type { PodStore } from "../+workloads-pods/store";
|
import type { PodStore } from "../+workloads-pods/store";
|
||||||
import { Cluster } from "../../../common/cluster/cluster";
|
import { Cluster } from "../../../common/cluster/cluster";
|
||||||
|
import isTableColumnHiddenInjectable from "../../../features/user-preferences/common/is-table-column-hidden.injectable";
|
||||||
|
|
||||||
describe("kube-object-list-layout", () => {
|
describe("kube-object-list-layout", () => {
|
||||||
let di: DiContainer;
|
let di: DiContainer;
|
||||||
@ -33,6 +34,7 @@ describe("kube-object-list-layout", () => {
|
|||||||
di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
|
di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
|
||||||
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
|
||||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||||
|
di.override(isTableColumnHiddenInjectable, () => () => false);
|
||||||
|
|
||||||
di.override(hostedClusterInjectable, () => new Cluster({
|
di.override(hostedClusterInjectable, () => new Cluster({
|
||||||
contextName: "some-context-name",
|
contextName: "some-context-name",
|
||||||
|
|||||||
@ -29,6 +29,9 @@ import type { ToggleKubeDetailsPane } from "../kube-detail-params/toggle-details
|
|||||||
import kubeSelectedUrlParamInjectable from "../kube-detail-params/kube-selected-url.injectable";
|
import kubeSelectedUrlParamInjectable from "../kube-detail-params/kube-selected-url.injectable";
|
||||||
import toggleKubeDetailsPaneInjectable from "../kube-detail-params/toggle-details.injectable";
|
import toggleKubeDetailsPaneInjectable from "../kube-detail-params/toggle-details.injectable";
|
||||||
import type { ClusterContext } from "../../cluster-frame-context/cluster-frame-context";
|
import type { ClusterContext } from "../../cluster-frame-context/cluster-frame-context";
|
||||||
|
import type { KubeObjectListLayoutColumn, ItemObject } from "@k8slens/list-layout";
|
||||||
|
import { kubeObjectListLayoutColumnInjectionToken } from "@k8slens/list-layout";
|
||||||
|
import { sortBy } from "lodash";
|
||||||
|
|
||||||
export interface KubeObjectListLayoutProps<
|
export interface KubeObjectListLayoutProps<
|
||||||
K extends KubeObject,
|
K extends KubeObject,
|
||||||
@ -53,6 +56,7 @@ interface Dependencies {
|
|||||||
subscribeToStores: SubscribeStores;
|
subscribeToStores: SubscribeStores;
|
||||||
kubeSelectedUrlParam: PageParam<string>;
|
kubeSelectedUrlParam: PageParam<string>;
|
||||||
toggleKubeDetailsPane: ToggleKubeDetailsPane;
|
toggleKubeDetailsPane: ToggleKubeDetailsPane;
|
||||||
|
columns: KubeObjectListLayoutColumn<ItemObject>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLoadErrorMessage = (error: unknown): string => {
|
const getLoadErrorMessage = (error: unknown): string => {
|
||||||
@ -140,9 +144,25 @@ class NonInjectedKubeObjectListLayout<
|
|||||||
dependentStores,
|
dependentStores,
|
||||||
toggleKubeDetailsPane: toggleDetails,
|
toggleKubeDetailsPane: toggleDetails,
|
||||||
onDetails,
|
onDetails,
|
||||||
|
renderTableContents,
|
||||||
|
renderTableHeader,
|
||||||
|
columns,
|
||||||
|
sortingCallbacks = {},
|
||||||
...layoutProps
|
...layoutProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const resourceName = this.props.resourceName || ResourceNames[ResourceKindMap[store.api.kind]] || store.api.kind;
|
const resourceName = this.props.resourceName || ResourceNames[ResourceKindMap[store.api.kind]] || store.api.kind;
|
||||||
|
const targetColumns = columns.filter((col) => col.kind === store.api.kind && col.apiVersion === store.api.apiVersionWithGroup);
|
||||||
|
|
||||||
|
targetColumns.forEach((col) => {
|
||||||
|
if (col.sortingCallBack) {
|
||||||
|
sortingCallbacks[col.id] = col.sortingCallBack;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const headers = sortBy([
|
||||||
|
...(renderTableHeader || []).map((header, index) => ({ priority: (20 - index), header })),
|
||||||
|
...targetColumns,
|
||||||
|
], (v) => -v.priority).map((col) => col.header);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ItemListLayout<K, false>
|
<ItemListLayout<K, false>
|
||||||
@ -175,6 +195,15 @@ class NonInjectedKubeObjectListLayout<
|
|||||||
]}
|
]}
|
||||||
renderItemMenu={item => <KubeObjectMenu object={item} />}
|
renderItemMenu={item => <KubeObjectMenu object={item} />}
|
||||||
onDetails={onDetails ?? ((item) => toggleDetails(item.selfLink))}
|
onDetails={onDetails ?? ((item) => toggleDetails(item.selfLink))}
|
||||||
|
sortingCallbacks={sortingCallbacks}
|
||||||
|
renderTableHeader={headers}
|
||||||
|
renderTableContents={(item) => {
|
||||||
|
return sortBy([
|
||||||
|
...(renderTableContents(item).map((content, index) => ({ priority: (20 - index), content }))),
|
||||||
|
...targetColumns.map((col) => ({ priority: col.priority, content: col.content(item) })),
|
||||||
|
], (item) => -item.priority).map((value) => value.content);
|
||||||
|
}}
|
||||||
|
spinnerTestId="kube-object-list-layout-spinner"
|
||||||
{...layoutProps}
|
{...layoutProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -191,6 +220,7 @@ export const KubeObjectListLayout = withInjectables<
|
|||||||
subscribeToStores: di.inject(subscribeStoresInjectable),
|
subscribeToStores: di.inject(subscribeStoresInjectable),
|
||||||
kubeSelectedUrlParam: di.inject(kubeSelectedUrlParamInjectable),
|
kubeSelectedUrlParam: di.inject(kubeSelectedUrlParamInjectable),
|
||||||
toggleKubeDetailsPane: di.inject(toggleKubeDetailsPaneInjectable),
|
toggleKubeDetailsPane: di.inject(toggleKubeDetailsPaneInjectable),
|
||||||
|
columns: di.injectMany(kubeObjectListLayoutColumnInjectionToken),
|
||||||
}),
|
}),
|
||||||
}) as <
|
}) as <
|
||||||
K extends KubeObject,
|
K extends KubeObject,
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import { getSorted } from "./sorting";
|
|||||||
import type { TableModel } from "./table-model/table-model";
|
import type { TableModel } from "./table-model/table-model";
|
||||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||||
import tableModelInjectable from "./table-model/table-model.injectable";
|
import tableModelInjectable from "./table-model/table-model.injectable";
|
||||||
import type { ItemObject } from "../../../common/item.store";
|
import type { ItemObject } from "@k8slens/list-layout";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import orderByUrlParamInjectable from "./order-by-url-param.injectable";
|
import orderByUrlParamInjectable from "./order-by-url-param.injectable";
|
||||||
import sortByUrlParamInjectable from "./sort-by-url-param.injectable";
|
import sortByUrlParamInjectable from "./sort-by-url-param.injectable";
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import autoBind from "auto-bind";
|
import autoBind from "auto-bind";
|
||||||
import type { ItemObject } from "../../common/item.store";
|
import type { ItemObject } from "@k8slens/list-layout";
|
||||||
|
|
||||||
export type ForwardedPortStatus = "Active" | "Disabled";
|
export type ForwardedPortStatus = "Active" | "Disabled";
|
||||||
export interface ForwardedPort {
|
export interface ForwardedPort {
|
||||||
|
|||||||
6
packages/list-layout/.eslintrc.js
Normal file
6
packages/list-layout/.eslintrc.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: "@k8slens/eslint-config/eslint",
|
||||||
|
parserOptions: {
|
||||||
|
project: "./tsconfig.json",
|
||||||
|
},
|
||||||
|
};
|
||||||
1
packages/list-layout/.prettierrc
Normal file
1
packages/list-layout/.prettierrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
"@k8slens/eslint-config/prettier"
|
||||||
1
packages/list-layout/index.ts
Normal file
1
packages/list-layout/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./kube-object-list-layout-column-injection-token";
|
||||||
1
packages/list-layout/jest.config.js
Normal file
1
packages/list-layout/jest.config.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require("@k8slens/jest").monorepoPackageConfig(__dirname).configForReact;
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import type { SingleOrMany } from "@k8slens/utilities";
|
||||||
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
|
|
||||||
|
export interface ItemObject {
|
||||||
|
getId(): string;
|
||||||
|
getName(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TableSortBy = string;
|
||||||
|
export type TableOrderBy = "asc" | "desc";
|
||||||
|
export interface TableSortParams {
|
||||||
|
sortBy: TableSortBy;
|
||||||
|
orderBy: TableOrderBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TableSortCallback<Item> = (data: Item) => undefined | string | number | (string | number)[];
|
||||||
|
export type TableSortCallbacks<Item> = Record<string, TableSortCallback<Item>>;
|
||||||
|
|
||||||
|
export type SearchFilter<I extends ItemObject> = (item: I) => SingleOrMany<string | number | undefined | null>;
|
||||||
|
|
||||||
|
export interface TableCellProps extends React.DOMAttributes<HTMLDivElement> {
|
||||||
|
/**
|
||||||
|
* used for configuration visibility of columns
|
||||||
|
*/
|
||||||
|
id?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any css class names for this table cell. Only used if `title` is a "simple" react node
|
||||||
|
*/
|
||||||
|
className?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual value of the cell
|
||||||
|
*/
|
||||||
|
title?: ReactNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* content inside could be scrolled
|
||||||
|
*/
|
||||||
|
scrollable?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* render cell with a checkbox
|
||||||
|
*/
|
||||||
|
checkbox?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mark checkbox as checked or not
|
||||||
|
*/
|
||||||
|
isChecked?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* column name, must be same as key in sortable object <Table sortable={}/>
|
||||||
|
*/
|
||||||
|
sortBy?: TableSortBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id of the column which follow same visibility rules
|
||||||
|
*/
|
||||||
|
showWithColumn?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
_sorting?: Partial<TableSortParams>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
_sort?(sortBy: TableSortBy): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* indicator, might come from parent <TableHead>, don't use this prop outside (!)
|
||||||
|
*/
|
||||||
|
_nowrap?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KubeObjectListLayoutColumn<Item extends ItemObject> {
|
||||||
|
id: string;
|
||||||
|
kind: string;
|
||||||
|
apiVersion: string;
|
||||||
|
priority: number;
|
||||||
|
sortingCallBack?: TableSortCallback<Item>;
|
||||||
|
searchFilter?: SearchFilter<Item>;
|
||||||
|
header: TableCellProps | undefined | null;
|
||||||
|
content: (item: Item) => ReactNode | TableCellProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const kubeObjectListLayoutColumnInjectionToken = getInjectionToken<KubeObjectListLayoutColumn<any>>({
|
||||||
|
id: "kube-object-list-layout-column",
|
||||||
|
});
|
||||||
43
packages/list-layout/package.json
Normal file
43
packages/list-layout/package.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "@k8slens/list-layout",
|
||||||
|
"private": false,
|
||||||
|
"version": "1.0.0-alpha.0",
|
||||||
|
"description": "Injection tokens for list layout",
|
||||||
|
"type": "commonjs",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public",
|
||||||
|
"registry": "https://registry.npmjs.org/"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lensapp/lens.git"
|
||||||
|
},
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"author": {
|
||||||
|
"name": "OpenLens Authors",
|
||||||
|
"email": "info@k8slens.dev"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://github.com/lensapp/lens",
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack",
|
||||||
|
"clean": "rimraf dist/",
|
||||||
|
"dev": "webpack --mode=development --watch",
|
||||||
|
"test": "jest --coverage --runInBand",
|
||||||
|
"lint": "lens-lint",
|
||||||
|
"lint:fix": "lens-lint --fix"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@ogre-tools/injectable": "^15.1.2",
|
||||||
|
"react": "^17.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@k8slens/eslint-config": "^6.5.0-alpha.2",
|
||||||
|
"@k8slens/jest": "^6.5.0-alpha.2",
|
||||||
|
"@k8slens/typescript": "^6.5.0-alpha.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
packages/list-layout/tsconfig.json
Normal file
4
packages/list-layout/tsconfig.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "@k8slens/typescript/config/base.json",
|
||||||
|
"include": ["**/*.ts"]
|
||||||
|
}
|
||||||
1
packages/list-layout/webpack.config.js
Normal file
1
packages/list-layout/webpack.config.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require("@k8slens/webpack").configForReact;
|
||||||
Loading…
Reference in New Issue
Block a user