diff --git a/packages/core/src/features/catalog/__snapshots__/custom-columns.test.tsx.snap b/packages/core/src/features/catalog/__snapshots__/custom-columns.test.tsx.snap new file mode 100644 index 0000000000..e97ed826d7 --- /dev/null +++ b/packages/core/src/features/catalog/__snapshots__/custom-columns.test.tsx.snap @@ -0,0 +1,1864 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`custom category columns for catalog renders 1`] = ` + +
+
+
+
+
+ + + home + + +
+
+
+ + + arrow_back + + +
+
+
+ + + arrow_forward + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Ca +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + arrow_left + + +
+
+ 1 +
+
+ + + arrow_right + + +
+
+
+
+
+
+
+
+
+ +`; + +exports[`custom category columns for catalog when category is added using default colemns renders 1`] = ` + +
+
+
+
+
+ + + home + + +
+
+
+ + + arrow_back + + +
+
+
+ + + arrow_forward + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Ca +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + arrow_left + + +
+
+ 1 +
+
+ + + arrow_right + + +
+
+
+
+
+
+
+
+
+ +`; + +exports[`custom category columns for catalog when category is added using default colemns when the Test category tab is clicked renders 1`] = ` + +
+
+
+
+
+ + + home + + +
+
+
+ + + arrow_back + + +
+
+
+ + + arrow_forward + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Ca +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + arrow_left + + +
+
+ 1 +
+
+ + + arrow_right + + +
+
+
+
+
+
+
+
+
+ +`; diff --git a/packages/core/src/features/catalog/custom-columns.test.tsx b/packages/core/src/features/catalog/custom-columns.test.tsx new file mode 100644 index 0000000000..4cf0cefc8c --- /dev/null +++ b/packages/core/src/features/catalog/custom-columns.test.tsx @@ -0,0 +1,98 @@ +/** + * 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 { CatalogCategory, type CatalogCategorySpec, type CategoryColumnRegistration } from "../../common/catalog"; +import catalogCategoryRegistryInjectable from "../../common/catalog/category-registry.injectable"; +import navigateToCatalogInjectable from "../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; +import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; + +describe("custom category columns for catalog", () => { + let builder: ApplicationBuilder; + let renderResult: RenderResult; + + beforeEach(async () => { + builder = getApplicationBuilder(); + + renderResult = await builder.render(); + + const navigateToCatalog = builder.applicationWindow.only.di.inject(navigateToCatalogInjectable); + + navigateToCatalog(); + }); + + it("renders", () => { + expect(renderResult.baseElement).toMatchSnapshot(); + }); + + it("shows 'Browse All' view", () => { + expect(renderResult.queryByTestId("catalog-list-for-browse-all")).toBeInTheDocument(); + }); + + it("should show the 'Kind' column", () => { + expect(renderResult.queryByTestId("browse-all-category-column")).toBeInTheDocument(); + }); + + describe("when category is added using default colemns", () => { + beforeEach(() => { + const catalogCategoryRegistry = builder.applicationWindow.only.di.inject(catalogCategoryRegistryInjectable); + + catalogCategoryRegistry.add(new TestCategory()); + }); + + it("renders", () => { + expect(renderResult.baseElement).toMatchSnapshot(); + }); + + it("shows category in sidebar", () => { + expect(renderResult.queryByTestId("foo.bar.bat/Test-tab")).toBeInTheDocument(); + }); + + it("still shows 'Browse All' view", () => { + expect(renderResult.queryByTestId("catalog-list-for-browse-all")).toBeInTheDocument(); + }); + + describe.only("when the Test category tab is clicked", () => { + beforeEach(async () => { + const testCategory = renderResult.getByTestId("foo.bar.bat/Test-tab"); + + testCategory.click(); + }); + + it("renders", () => { + expect(renderResult.baseElement).toMatchSnapshot(); + }); + + it.only("shows view for category", () => { + expect(renderResult.queryByTestId("catalog-list-for-Test")).toBeInTheDocument(); + }); + }); + }); +}); + +class TestCategory extends CatalogCategory { + apiVersion = "catalog.k8slens.dev/v1alpha1"; + kind = "CatalogCategory"; + metadata = { + name: "Test", + icon: "question_mark", + }; + spec: CatalogCategorySpec = { + group: "foo.bar.bat", + names: { + kind: "Test", + }, + versions: [], + }; + + constructor(columns?: CategoryColumnRegistration[]) { + super(); + this.spec = { + displayColumns: columns, + ...this.spec, + }; + } +} diff --git a/packages/core/src/renderer/components/+catalog/__tests__/catalog-entity-store.test.ts b/packages/core/src/renderer/components/+catalog/__tests__/catalog-entity-store.test.ts index 6794fed899..6d8a1828f4 100644 --- a/packages/core/src/renderer/components/+catalog/__tests__/catalog-entity-store.test.ts +++ b/packages/core/src/renderer/components/+catalog/__tests__/catalog-entity-store.test.ts @@ -3,12 +3,17 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import type { DiContainer } from "@ogre-tools/injectable"; import type { CatalogCategoryMetadata, CatalogCategorySpec } from "../../../../common/catalog"; import { CatalogEntity, categoryVersion } from "../../../../common/catalog"; +import catalogCategoryRegistryInjectable from "../../../../common/catalog/category-registry.injectable"; import { CatalogCategory } from "../../../api/catalog-entity"; +import type { CatalogEntityRegistry } from "../../../api/catalog/entity/registry"; +import catalogEntityRegistryInjectable from "../../../api/catalog/entity/registry.injectable"; +import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; import { noop } from "../../../utils"; -import type { CatalogEntityStore } from "../catalog-entity-store/catalog-entity.store"; -import { catalogEntityStore } from "../catalog-entity-store/catalog-entity.store"; +import type { CatalogEntityStore } from "../catalog-entity-store.injectable"; +import catalogEntityStoreInjectable from "../catalog-entity-store.injectable"; class TestEntityOne extends CatalogEntity { public static readonly apiVersion: string = "entity.k8slens.dev/v1alpha1"; @@ -63,6 +68,12 @@ class TestCategoryTwo extends CatalogCategory { } describe("CatalogEntityStore", () => { + let di: DiContainer; + + beforeEach(() => { + di = getDiForUnitTesting({ doGeneralOverrides: true }); + }); + describe("getTotalCount", () => { let store: CatalogEntityStore; let testCategoryOne: TestCategoryOne; @@ -129,21 +140,22 @@ describe("CatalogEntityStore", () => { testCategoryOne = new TestCategoryOne(); testCategoryTwo = new TestCategoryTwo(); - store = catalogEntityStore({ - catalogRegistry: { - items: [ - testCategoryOne, - testCategoryTwo, - ], + + di.override(catalogCategoryRegistryInjectable, () => ({ + items: [ + testCategoryOne, + testCategoryTwo, + ], + })); + di.override(catalogEntityRegistryInjectable, () => ({ + onRun: noop, + filteredItems: entityItems, + getItemsForCategory: (category: CatalogCategory): T[] => { + return entityItems.filter(item => category.spec.versions.some(version => item instanceof version.entityClass)) as T[]; }, - entityRegistry: { - onRun: noop, - filteredItems: entityItems, - getItemsForCategory: (category: CatalogCategory): T[] => { - return entityItems.filter(item => category.spec.versions.some(version => item instanceof version.entityClass)) as T[]; - }, - }, - }); + } as CatalogEntityRegistry)); + + store = di.inject(catalogEntityStoreInjectable); }); it("given no active category, returns count of all kinds", () => { diff --git a/packages/core/src/renderer/components/+catalog/catalog-entity-store.injectable.ts b/packages/core/src/renderer/components/+catalog/catalog-entity-store.injectable.ts new file mode 100644 index 0000000000..221e7b77f5 --- /dev/null +++ b/packages/core/src/renderer/components/+catalog/catalog-entity-store.injectable.ts @@ -0,0 +1,91 @@ +/** + * 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 catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable"; +import catalogCategoryRegistryInjectable from "../../../common/catalog/category-registry.injectable"; +import type { IComputedValue, IObservableValue } from "mobx"; +import { computed, observable, reaction } from "mobx"; +import type { CatalogEntity, CatalogCategory } from "../../api/catalog-entity"; +import type { Disposer } from "../../utils"; +import { disposer } from "../../utils"; +import type { ItemListStore } from "../item-object-list"; + +export type CatalogEntityStore = ItemListStore & { + readonly entities: IComputedValue; + readonly activeCategory: IComputedValue; + readonly selectedItemId: IObservableValue; + readonly selectedItem: IComputedValue; + watch(): Disposer; + onRun(entity: CatalogEntity): void; +}; + +export type ActiveCategory = { + browseAll: true; +} | { + browseAll: false; + activeTab: string; +}; + +const catalogEntityStoreInjectable = getInjectable({ + id: "catalog-entity-store", + + instantiate: (di): CatalogEntityStore => { + const entityRegistry = di.inject(catalogEntityRegistryInjectable); + const catalogRegistry = di.inject(catalogCategoryRegistryInjectable); + + const activeCategory = observable.box(undefined); + const selectedItemId = observable.box(undefined); + const entities = computed(() => { + const category = activeCategory.get(); + + return category + ? entityRegistry.getItemsForCategory(category, { filtered: true }) + : entityRegistry.filteredItems; + }); + const selectedItem = computed(() => { + const id = selectedItemId.get(); + + if (!id) { + return undefined; + } + + return entities.get().find(entity => entity.getId() === id); + }); + const loadAll = () => { + const category = activeCategory.get(); + + if (category) { + category.emit("load"); + } else { + for (const category of catalogRegistry.items) { + category.emit("load"); + } + } + }; + + return { + entities, + selectedItem, + activeCategory, + selectedItemId, + watch: () => disposer( + reaction(() => entities.get(), loadAll), + reaction(() => activeCategory.get(), loadAll, { delay: 100 }), + ), + onRun: entity => entityRegistry.onRun(entity), + failedLoading: false, + getTotalCount: () => entities.get().length, + isLoaded: true, + isSelected: (item) => item.getId() === selectedItemId.get(), + isSelectedAll: () => false, + pickOnlySelected: () => [], + toggleSelection: () => {}, + toggleSelectionAll: () => {}, + removeSelectedItems: async () => {}, + }; + }, +}); + +export default catalogEntityStoreInjectable; diff --git a/packages/core/src/renderer/components/+catalog/catalog-entity-store/catalog-entity-store.injectable.ts b/packages/core/src/renderer/components/+catalog/catalog-entity-store/catalog-entity-store.injectable.ts deleted file mode 100644 index 6e3cb979e4..0000000000 --- a/packages/core/src/renderer/components/+catalog/catalog-entity-store/catalog-entity-store.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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 { catalogEntityStore } from "./catalog-entity.store"; -import catalogEntityRegistryInjectable from "../../../api/catalog/entity/registry.injectable"; -import catalogCategoryRegistryInjectable from "../../../../common/catalog/category-registry.injectable"; - -const catalogEntityStoreInjectable = getInjectable({ - id: "catalog-entity-store", - - instantiate: (di) => catalogEntityStore({ - entityRegistry: di.inject(catalogEntityRegistryInjectable), - catalogRegistry: di.inject(catalogCategoryRegistryInjectable), - }), -}); - -export default catalogEntityStoreInjectable; diff --git a/packages/core/src/renderer/components/+catalog/catalog-entity-store/catalog-entity.store.tsx b/packages/core/src/renderer/components/+catalog/catalog-entity-store/catalog-entity.store.tsx deleted file mode 100644 index 3209428cba..0000000000 --- a/packages/core/src/renderer/components/+catalog/catalog-entity-store/catalog-entity.store.tsx +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { IComputedValue, IObservableValue } from "mobx"; -import { computed, observable, reaction } from "mobx"; -import type { CatalogEntityRegistry } from "../../../api/catalog/entity/registry"; -import type { CatalogEntity } from "../../../api/catalog-entity"; -import type { CatalogCategory, CatalogCategoryRegistry } from "../../../../common/catalog"; -import type { Disposer } from "../../../../common/utils"; -import { disposer } from "../../../../common/utils"; -import type { ItemListStore } from "../../item-object-list"; - -type EntityRegistry = Pick; -type CatalogRegistry = Pick; - -interface Dependencies { - entityRegistry: EntityRegistry; - catalogRegistry: CatalogRegistry; -} - -export type CatalogEntityStore = ItemListStore & { - readonly entities: IComputedValue; - readonly activeCategory: IObservableValue; - readonly selectedItemId: IObservableValue; - readonly selectedItem: IComputedValue; - watch(): Disposer; - onRun(entity: CatalogEntity): void; -}; - -export function catalogEntityStore({ - entityRegistry, - catalogRegistry, -}: Dependencies): CatalogEntityStore { - const activeCategory = observable.box(undefined); - const selectedItemId = observable.box(undefined); - const entities = computed(() => { - const category = activeCategory.get(); - - return category - ? entityRegistry.getItemsForCategory(category, { filtered: true }) - : entityRegistry.filteredItems; - }); - const selectedItem = computed(() => { - const id = selectedItemId.get(); - - if (!id) { - return undefined; - } - - return entities.get().find(entity => entity.getId() === id); - }); - const loadAll = () => { - const category = activeCategory.get(); - - if (category) { - category.emit("load"); - } else { - for (const category of catalogRegistry.items) { - category.emit("load"); - } - } - }; - - return { - entities, - selectedItem, - activeCategory, - selectedItemId, - watch: () => disposer( - reaction(() => entities.get(), loadAll), - reaction(() => activeCategory.get(), loadAll, { delay: 100 }), - ), - onRun: entity => entityRegistry.onRun(entity), - failedLoading: false, - getTotalCount: () => entities.get().length, - isLoaded: true, - isSelected: (item) => item.getId() === selectedItemId.get(), - isSelectedAll: () => false, - pickOnlySelected: () => [], - toggleSelection: () => {}, - toggleSelectionAll: () => {}, - removeSelectedItems: async () => {}, - }; -} diff --git a/packages/core/src/renderer/components/+catalog/catalog-menu.tsx b/packages/core/src/renderer/components/+catalog/catalog-menu.tsx index 5947f0a301..278bbcea3e 100644 --- a/packages/core/src/renderer/components/+catalog/catalog-menu.tsx +++ b/packages/core/src/renderer/components/+catalog/catalog-menu.tsx @@ -7,7 +7,6 @@ import treeStyles from "./catalog-tree.module.scss"; import styles from "./catalog-menu.module.scss"; import React from "react"; -import type { TreeItemProps } from "@material-ui/lab"; import { TreeItem, TreeView } from "@material-ui/lab"; import { Icon } from "../icon"; import { StylesProvider } from "@material-ui/core"; @@ -36,12 +35,6 @@ function getCategoryIcon(category: CatalogCategory) { return null; } -function Item(props: TreeItemProps) { - return ( - - ); -} - interface Dependencies { filteredCategories: IComputedValue; } @@ -60,13 +53,17 @@ const NonInjectedCatalogMenu = observer(({ defaultCollapseIcon={} defaultExpandIcon={} selected={activeTab || "browse"} + onNodeSelect={console.log} > - onItemClick("*")} /> - onItemClick("*")} + /> + Categories
} className={cssNames(styles.bordered)} @@ -74,16 +71,23 @@ const NonInjectedCatalogMenu = observer(({ { filteredCategories.get() .map(category => ( - } data-testid={`${category.getId()}-tab`} - onClick={() => onItemClick(category.getId())} /> + onLabelClick={console.log} + onIconClick={console.log} + onClick={() => { + console.log("clicking", category); + onItemClick(category.getId()); + }} + /> )) } - +
diff --git a/packages/core/src/renderer/components/+catalog/catalog.test.tsx b/packages/core/src/renderer/components/+catalog/catalog.test.tsx index 334970e166..ec35fb1d4a 100644 --- a/packages/core/src/renderer/components/+catalog/catalog.test.tsx +++ b/packages/core/src/renderer/components/+catalog/catalog.test.tsx @@ -10,10 +10,10 @@ import { Catalog } from "./catalog"; import type { CatalogEntityActionContext, CatalogEntityData } from "../../../common/catalog"; import { CatalogEntity } from "../../../common/catalog"; import type { CatalogEntityOnBeforeRun, CatalogEntityRegistry } from "../../api/catalog/entity/registry"; -import type { CatalogEntityStore } from "./catalog-entity-store/catalog-entity.store"; +import type { CatalogEntityStore } from "./catalog-entity-store.injectable"; import { getDiForUnitTesting } from "../../getDiForUnitTesting"; import type { DiContainer } from "@ogre-tools/injectable"; -import catalogEntityStoreInjectable from "./catalog-entity-store/catalog-entity-store.injectable"; +import catalogEntityStoreInjectable from "./catalog-entity-store.injectable"; import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable"; import type { DiRender } from "../test-utils/renderFor"; import { renderFor } from "../test-utils/renderFor"; diff --git a/packages/core/src/renderer/components/+catalog/catalog.tsx b/packages/core/src/renderer/components/+catalog/catalog.tsx index acd5346b17..42578dcad7 100644 --- a/packages/core/src/renderer/components/+catalog/catalog.tsx +++ b/packages/core/src/renderer/components/+catalog/catalog.tsx @@ -10,7 +10,7 @@ import { disposeOnUnmount, observer } from "mobx-react"; import { ItemListLayout } from "../item-object-list"; import type { IComputedValue } from "mobx"; import { action, computed, makeObservable, observable, reaction, runInAction, when } from "mobx"; -import type { CatalogEntityStore } from "./catalog-entity-store/catalog-entity.store"; +import type { CatalogEntityStore } from "./catalog-entity-store.injectable"; import { MenuItem, MenuActions } from "../menu"; import type { CatalogEntityContextMenu } from "../../api/catalog-entity"; import type { CatalogCategory, CatalogCategoryRegistry, CatalogEntity } from "../../../common/catalog"; @@ -27,7 +27,7 @@ import { HotbarToggleMenuItem } from "./hotbar-toggle-menu-item"; import { Avatar } from "../avatar"; import { withInjectables } from "@ogre-tools/injectable-react"; import catalogPreviousActiveTabStorageInjectable from "./catalog-previous-active-tab-storage/catalog-previous-active-tab-storage.injectable"; -import catalogEntityStoreInjectable from "./catalog-entity-store/catalog-entity-store.injectable"; +import catalogEntityStoreInjectable from "./catalog-entity-store.injectable"; import type { GetCategoryColumnsParams, CategoryColumns } from "./columns/get.injectable"; import getCategoryColumnsInjectable from "./columns/get.injectable"; import type { RegisteredCustomCategoryViewDecl } from "./custom-views.injectable"; @@ -133,16 +133,19 @@ class NonInjectedCatalog extends React.Component { } }, { fireImmediately: true }), // If active category is filtered out, automatically switch to the first category - reaction(() => catalogCategoryRegistry.filteredItems, () => { - if (!catalogCategoryRegistry.filteredItems.find(item => item.getId() === catalogEntityStore.activeCategory.get()?.getId())) { - const item = catalogCategoryRegistry.filteredItems[0]; + reaction(() => [...catalogCategoryRegistry.filteredItems], (categories) => { + const currentCategory = catalogEntityStore.activeCategory.get(); + const someCategory = categories[0]; - runInAction(() => { - if (item) { - this.activeTab = item.getId(); - this.props.catalogEntityStore.activeCategory.set(item); - } - }); + if (this.routeActiveTab === browseCatalogTab || !someCategory) { + return; + } + + const currentCategoryShouldBeShown = Boolean(categories.find(item => item.getId() === someCategory.getId())); + + if (!currentCategory || !currentCategoryShouldBeShown) { + this.activeTab = someCategory.getId(); + this.props.catalogEntityStore.activeCategory.set(someCategory); } }), ]); @@ -174,6 +177,7 @@ class NonInjectedCatalog extends React.Component { } onTabChange = action((tabId: string | null) => { + console.log(tabId); const activeCategory = this.categories.find(category => category.getId() === tabId); this.props.emitEvent({ diff --git a/packages/core/src/renderer/components/+catalog/columns/browse-all.injectable.tsx b/packages/core/src/renderer/components/+catalog/columns/browse-all.injectable.tsx index b7e1ddddb7..8d1c4b9315 100644 --- a/packages/core/src/renderer/components/+catalog/columns/browse-all.injectable.tsx +++ b/packages/core/src/renderer/components/+catalog/columns/browse-all.injectable.tsx @@ -17,6 +17,7 @@ const defaultBrowseAllColumns: RegisteredAdditionalCategoryColumn[] = [ id: "kind", sortBy: "kind", title: "Kind", + "data-testid": "browse-all-category-column", }, sortCallback: entity => entity.kind, }, diff --git a/packages/core/src/renderer/components/cluster-manager/cluster-manager.tsx b/packages/core/src/renderer/components/cluster-manager/cluster-manager.tsx index ab418ec827..2463a48f4f 100644 --- a/packages/core/src/renderer/components/cluster-manager/cluster-manager.tsx +++ b/packages/core/src/renderer/components/cluster-manager/cluster-manager.tsx @@ -13,18 +13,15 @@ import { HotbarMenu } from "../hotbar/hotbar-menu"; import { DeleteClusterDialog } from "../delete-cluster-dialog"; import { withInjectables } from "@ogre-tools/injectable-react"; import { TopBar } from "../layout/top-bar/top-bar"; -import catalogPreviousActiveTabStorageInjectable from "../+catalog/catalog-previous-active-tab-storage/catalog-previous-active-tab-storage.injectable"; import type { IComputedValue } from "mobx"; import currentRouteComponentInjectable from "../../routes/current-route-component.injectable"; import welcomeRouteInjectable from "../../../common/front-end-routing/routes/welcome/welcome-route.injectable"; import { buildURL } from "../../../common/utils/buildUrl"; -import type { StorageLayer } from "../../utils"; import type { WatchForGeneralEntityNavigation } from "../../api/helpers/watch-for-general-entity-navigation.injectable"; import watchForGeneralEntityNavigationInjectable from "../../api/helpers/watch-for-general-entity-navigation.injectable"; import currentPathInjectable from "../../routes/current-path.injectable"; interface Dependencies { - catalogPreviousActiveTabStorage: StorageLayer; currentRouteComponent: IComputedValue; welcomeUrl: string; watchForGeneralEntityNavigation: WatchForGeneralEntityNavigation; @@ -84,7 +81,6 @@ class NonInjectedClusterManager extends React.Component { export const ClusterManager = withInjectables(NonInjectedClusterManager, { getProps: (di) => ({ - catalogPreviousActiveTabStorage: di.inject(catalogPreviousActiveTabStorageInjectable), currentRouteComponent: di.inject(currentRouteComponentInjectable), welcomeUrl: buildURL(di.inject(welcomeRouteInjectable).path), watchForGeneralEntityNavigation: di.inject(watchForGeneralEntityNavigationInjectable), diff --git a/packages/core/src/renderer/components/table/table-cell.tsx b/packages/core/src/renderer/components/table/table-cell.tsx index 758c5cd951..a01e6fae09 100644 --- a/packages/core/src/renderer/components/table/table-cell.tsx +++ b/packages/core/src/renderer/components/table/table-cell.tsx @@ -70,6 +70,11 @@ 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; } export class TableCell extends React.Component {