diff --git a/packages/core/src/features/custom-resources/__snapshots__/view-with-extra-columns.test.ts.snap b/packages/core/src/features/custom-resources/__snapshots__/view-with-extra-columns.test.ts.snap
new file mode 100644
index 0000000000..250ff548ab
--- /dev/null
+++ b/packages/core/src/features/custom-resources/__snapshots__/view-with-extra-columns.test.ts.snap
@@ -0,0 +1,663 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Viewing Custom Resources with extra columns renders 1`] = `
+
+
+
+
+
+
+
+
+
+ close
+
+
+
+ Close
+
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/core/src/features/custom-resources/view-with-extra-columns.test.ts b/packages/core/src/features/custom-resources/view-with-extra-columns.test.ts
new file mode 100644
index 0000000000..1ab539d22d
--- /dev/null
+++ b/packages/core/src/features/custom-resources/view-with-extra-columns.test.ts
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+
+import type { KubeObjectMetadata, KubeObjectScope } from "@k8slens/kube-object";
+import { KubeObject, CustomResourceDefinition } from "@k8slens/kube-object";
+import type { RenderResult } from "@testing-library/react";
+import navigateToCustomResourcesInjectable from "../../common/front-end-routing/routes/cluster/custom-resources/custom-resources/navigate-to-custom-resources.injectable";
+import apiManagerInjectable from "../../common/k8s-api/api-manager/manager.injectable";
+import type { CustomResourceStore } from "../../common/k8s-api/api-manager/resource.store";
+import type { CustomResourceDefinitionStore } from "../../renderer/components/custom-resources/definition.store";
+import customResourceDefinitionStoreInjectable from "../../renderer/components/custom-resources/definition.store.injectable";
+import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
+import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
+
+describe("Viewing Custom Resources with extra columns", () => {
+ let builder: ApplicationBuilder;
+ let result: RenderResult;
+ let customResourceDefinitionStore: CustomResourceDefinitionStore;
+ let customResourceStore: CustomResourceStore, void, { someColumn: Record }>>;
+ let customResource: CustomResourceDefinition;
+
+ beforeEach(async () => {
+ builder = getApplicationBuilder();
+ builder.setEnvironmentToClusterFrame();
+
+ builder.afterWindowStart(({ windowDi }) => {
+ const apiManager = windowDi.inject(apiManagerInjectable);
+
+ customResourceDefinitionStore = windowDi.inject(customResourceDefinitionStoreInjectable);
+ customResource = new CustomResourceDefinition({
+ apiVersion: "apiextensions.k8s.io/v1",
+ kind: "CustomResourceDefinition",
+ metadata: {
+ name: "some-crd",
+ selfLink: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/some-crd",
+ uid: "some-uid",
+ resourceVersion: "1",
+ },
+ spec: {
+ group: "some-group",
+ scope: "Cluster",
+ names: {
+ kind: "SomeResource",
+ plural: "some-resources",
+ singular: "some-resource",
+ },
+ versions: [
+ {
+ storage: true,
+ name: "v1",
+ served: true,
+ additionalPrinterColumns: [
+ {
+ name: "Some Column",
+ type: "string",
+ description: "Some description",
+ jsonPath: ".spec.someColumn",
+ },
+ ],
+ },
+ ],
+ },
+ });
+
+ customResourceDefinitionStore.items.replace([
+ customResource,
+ ]);
+
+ customResourceStore = apiManager.getStore(customResource.getResourceApiBase()) as typeof customResourceStore;
+
+ customResourceStore.items.replace([
+ new KubeObject({
+ apiVersion: "/some-group/v1",
+ kind: "SomeResource",
+ metadata: {
+ name: "some-resource",
+ selfLink: "/apis/some-group/v1/namespaces/default/some-resources/some-resource",
+ uid: "some-uid-2",
+ resourceVersion: "1",
+ },
+ spec: {
+ someColumn: {
+ "foo": "bar",
+ },
+ },
+ }),
+ ]);
+ });
+
+ result = await builder.render();
+
+ builder.allowKubeResource({
+ group: "/apis/apiextensions.k8s.io/v1",
+ apiName: "customresourcedefinitions",
+ });
+
+ const windowDi = builder.applicationWindow.only.di;
+ const navigateToCustomResources = windowDi.inject(navigateToCustomResourcesInjectable);
+
+ navigateToCustomResources({
+ group: "some-group",
+ name: "some-resources",
+ });
+ });
+
+ it("renders", () => {
+ expect(result.container).toMatchSnapshot();
+ });
+
+ it("shows the table for the custom resource SomeResource", () => {
+ expect(result.getByText("SomeResource")).toBeInTheDocument();
+ });
+
+ it("shows the 'some-column' column", () => {
+ expect(result.getByTestId("custom-resource-column-title-some-column")).toBeInTheDocument();
+ });
+
+ it("shows some value for in the cells of 'some-column' column", () => {
+ expect(result.getByTestId("custom-resource-column-cell-some-column-for-some-resource")).toHaveTextContent(JSON.stringify({ foo: "bar" }));
+ });
+});
diff --git a/packages/core/src/renderer/components/catalog/custom-category-columns.ts b/packages/core/src/renderer/components/catalog/custom-category-columns.ts
index 05939d2126..efe6278270 100644
--- a/packages/core/src/renderer/components/catalog/custom-category-columns.ts
+++ b/packages/core/src/renderer/components/catalog/custom-category-columns.ts
@@ -3,9 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
+import type { TableCellProps } from "@k8slens/list-layout";
import type { StrictReactNode } from "@k8slens/utilities";
import type { CatalogEntity } from "../../../common/catalog";
-import type { TableCellProps } from "../table";
/**
* These are the supported props for the title cell
diff --git a/packages/core/src/renderer/components/custom-resources/crd-resources.tsx b/packages/core/src/renderer/components/custom-resources/crd-resources.tsx
index 70a77c67b9..7de4f2230b 100644
--- a/packages/core/src/renderer/components/custom-resources/crd-resources.tsx
+++ b/packages/core/src/renderer/components/custom-resources/crd-resources.tsx
@@ -20,6 +20,7 @@ import type { CustomResourceDefinitionStore } from "./definition.store";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import customResourceDefinitionStoreInjectable from "./definition.store.injectable";
import { NamespaceSelectBadge } from "../namespaces/namespace-select-badge";
+import type { TableCellProps } from "@k8slens/list-layout";
enum columnId {
name = "name",
@@ -95,9 +96,10 @@ class NonInjectedCustomResources extends React.Component {
: undefined,
...extraColumns.map(({ name }) => ({
title: name,
- className: name.toLowerCase(),
+ className: name.toLowerCase().replace(/\s+/g, "-"),
sortBy: name,
id: name,
+ "data-testid": `custom-resource-column-title-${name.toLowerCase().replace(/\s+/g, "-")}`,
})),
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
]}
@@ -106,11 +108,10 @@ class NonInjectedCustomResources extends React.Component {
isNamespaced && (
),
- ...(
- extraColumns
- .map((column) => safeJSONPathValue(customResource, column.jsonPath))
- .map(formatJSONValue)
- ),
+ ...extraColumns.map((column): TableCellProps => ({
+ "data-testid": `custom-resource-column-cell-${column.name.toLowerCase().replace(/\s+/g, "-")}-for-${customResource.getScopedName()}`,
+ title: formatJSONValue(safeJSONPathValue(customResource, column.jsonPath)),
+ })),
,
]}
failedToLoadMessage={(
diff --git a/packages/core/src/renderer/components/item-object-list/content.tsx b/packages/core/src/renderer/components/item-object-list/content.tsx
index 32fcca5b71..de19c1c058 100644
--- a/packages/core/src/renderer/components/item-object-list/content.tsx
+++ b/packages/core/src/renderer/components/item-object-list/content.tsx
@@ -10,7 +10,7 @@ import type { IComputedValue } from "mobx";
import { computed, makeObservable } from "mobx";
import { Observer, observer } from "mobx-react";
import type { ConfirmDialogParams } from "../confirm-dialog";
-import type { TableCellProps, TableProps, TableRowProps, TableSortCallbacks } from "../table";
+import type { TableProps, TableRowProps, TableSortCallbacks } from "../table";
import { Table, TableCell, TableHead, TableRow } from "../table";
import type { IClassName, StrictReactNode } from "@k8slens/utilities";
import { cssNames, isDefined, isReactNode, noop, prevDefault, stopPropagation } from "@k8slens/utilities";
@@ -18,7 +18,7 @@ import type { AddRemoveButtonsProps } from "../add-remove-buttons";
import { AddRemoveButtons } from "../add-remove-buttons";
import { NoItems } from "../no-items";
import { Spinner } from "../spinner";
-import type { ItemObject } from "@k8slens/list-layout";
+import type { ItemObject, TableCellProps } from "@k8slens/list-layout";
import type { Filter, PageFiltersStore } from "./page-filters/store";
import type { LensTheme } from "../../themes/lens-theme";
import { MenuActions } from "../menu/menu-actions";
diff --git a/packages/core/src/renderer/components/item-object-list/list-layout.tsx b/packages/core/src/renderer/components/item-object-list/list-layout.tsx
index 470cbf6eb8..32f46e7a2a 100644
--- a/packages/core/src/renderer/components/item-object-list/list-layout.tsx
+++ b/packages/core/src/renderer/components/item-object-list/list-layout.tsx
@@ -9,11 +9,11 @@ import React from "react";
import type { IComputedValue } from "mobx";
import { computed, makeObservable, untracked } from "mobx";
import type { ConfirmDialogParams } from "../confirm-dialog";
-import type { TableCellProps, TableProps, TableRowProps, TableSortCallbacks } from "../table";
+import type { TableProps, TableRowProps, TableSortCallbacks } from "../table";
import type { IClassName, StrictReactNode, SingleOrMany } from "@k8slens/utilities";
import { cssNames, noop } from "@k8slens/utilities";
import type { AddRemoveButtonsProps } from "../add-remove-buttons";
-import type { ItemObject } from "@k8slens/list-layout";
+import type { ItemObject, TableCellProps } from "@k8slens/list-layout";
import type { SearchInputUrlProps } from "../input";
import type { PageFiltersStore } from "./page-filters/store";
import { FilterType } from "./page-filters/store";
diff --git a/packages/core/src/renderer/components/table/table-cell.tsx b/packages/core/src/renderer/components/table/table-cell.tsx
index cfd969c064..5794f67cbf 100644
--- a/packages/core/src/renderer/components/table/table-cell.tsx
+++ b/packages/core/src/renderer/components/table/table-cell.tsx
@@ -4,80 +4,16 @@
*/
import "./table-cell.scss";
-import type { TableSortBy, TableSortParams } from "./table";
import React from "react";
-import type { StrictReactNode } from "@k8slens/utilities";
import { cssNames } from "@k8slens/utilities";
import { Icon } from "../icon";
import { Checkbox } from "../checkbox";
import autoBindReact from "auto-bind/react";
+import type { TableCellProps } from "@k8slens/list-layout";
export type TableCellElem = React.ReactElement;
-export interface TableCellProps extends React.DOMAttributes {
- /**
- * 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?: StrictReactNode;
-
- /**
- * 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 ``
- */
- sortBy?: TableSortBy;
-
- /**
- * id of the column which follow same visibility rules
- */
- showWithColumn?: string;
-
- /**
- * @internal
- */
- _sorting?: Partial;
-
- /**
- * @internal
- */
- _sort?(sortBy: TableSortBy): void;
-
- /**
- * @internal
- * indicator, might come from parent , don't use this prop outside (!)
- */
- _nowrap?: boolean;
-
- /**
- * For passing in the testid
- */
- "data-testid"?: string;
-}
-
export class TableCell extends React.Component {
constructor(props: TableCellProps) {
super(props);
diff --git a/packages/kube-object/src/specifics/custom-resource-definition.ts b/packages/kube-object/src/specifics/custom-resource-definition.ts
index 65ce591941..83ddd0f9f6 100644
--- a/packages/kube-object/src/specifics/custom-resource-definition.ts
+++ b/packages/kube-object/src/specifics/custom-resource-definition.ts
@@ -141,13 +141,6 @@ export class CustomResourceDefinition extends KubeObject<
}
getPreferredVersion(): CustomResourceDefinitionVersion {
- return this.getPreferedVersion();
- }
-
- /**
- * @deprecated Switch to using {@link getPreferredVersion} instead (which fixes the is a typo)
- */
- getPreferedVersion(): CustomResourceDefinitionVersion {
const { apiVersion } = this;
switch (apiVersion) {
@@ -179,8 +172,15 @@ export class CustomResourceDefinition extends KubeObject<
);
}
+ /**
+ * @deprecated Switch to using {@link getPreferredVersion} instead (which fixes the is a typo)
+ */
+ getPreferedVersion(): CustomResourceDefinitionVersion {
+ return this.getPreferredVersion();
+ }
+
getVersion() {
- return this.getPreferedVersion().name;
+ return this.getPreferredVersion().name;
}
isNamespaced() {
@@ -200,13 +200,13 @@ export class CustomResourceDefinition extends KubeObject<
}
getPrinterColumns(ignorePriority = true): AdditionalPrinterColumnsV1[] {
- const columns = this.getPreferedVersion().additionalPrinterColumns ?? [];
+ const columns = this.getPreferredVersion().additionalPrinterColumns ?? [];
return columns.filter((column) => column.name.toLowerCase() !== "age" && (ignorePriority || !column.priority));
}
getValidation() {
- return JSON.stringify(this.getPreferedVersion().schema, null, 2);
+ return JSON.stringify(this.getPreferredVersion().schema, null, 2);
}
getConditions() {
diff --git a/packages/list-layout/src/list-layout-column.ts b/packages/list-layout/src/list-layout-column.ts
index 3855d79d8e..b326d9a019 100644
--- a/packages/list-layout/src/list-layout-column.ts
+++ b/packages/list-layout/src/list-layout-column.ts
@@ -54,7 +54,7 @@ export interface TableCellProps extends React.DOMAttributes {
isChecked?: boolean;
/**
- * column name, must be same as key in sortable object
+ * column name, must be same as key in sortable object ``
*/
sortBy?: TableSortBy;
@@ -78,4 +78,9 @@ export interface TableCellProps extends React.DOMAttributes {
* indicator, might come from parent , don't use this prop outside (!)
*/
_nowrap?: boolean;
+
+ /**
+ * For passing in the testid
+ */
+ "data-testid"?: string;
}