From b8e1eb2c0fa588fd93f50b7e833f47bbd09e0022 Mon Sep 17 00:00:00 2001 From: Paul Stoker Date: Wed, 8 Nov 2023 13:57:14 +0100 Subject: [PATCH 1/9] Accept columns prop as an array or a function Signed-off-by: Paul Stoker --- .changeset/tricky-islands-wonder.md | 5 ++ .../CatalogPage/DefaultCatalogPage.test.tsx | 24 ++++++++ .../CatalogPage/DefaultCatalogPage.tsx | 3 +- .../CatalogTable/CatalogTable.test.tsx | 58 ++++++++++++++++++- .../components/CatalogTable/CatalogTable.tsx | 26 +++++++-- 5 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 .changeset/tricky-islands-wonder.md diff --git a/.changeset/tricky-islands-wonder.md b/.changeset/tricky-islands-wonder.md new file mode 100644 index 0000000000..5b9c37281b --- /dev/null +++ b/.changeset/tricky-islands-wonder.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': minor +--- + +The `columns` prop can be an array or a function that returns an array in order to override the default columns of the `CatalogIndexPage`. diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx index 0ced712344..a4b1148a6f 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx @@ -44,6 +44,8 @@ import React from 'react'; import { createComponentRouteRef } from '../../routes'; import { CatalogTableRow } from '../CatalogTable'; import { DefaultCatalogPage } from './DefaultCatalogPage'; +import { ColumnsFunc } from '../CatalogTable/CatalogTable'; +import { Entity } from '@backstage/catalog-model/'; describe('DefaultCatalogPage', () => { const origReplaceState = window.history.replaceState; @@ -217,6 +219,28 @@ describe('DefaultCatalogPage', () => { expect(columnHeaderLabels).toEqual(['Foo', 'Bar', 'Baz', 'Actions']); }, 20_000); + it('should render the custom column function passed as prop', async () => { + const columns: ColumnsFunc = ( + kind: string | undefined, + entities: Entity[], + ) => { + return kind === 'component' && entities.length + ? [ + { title: 'Foo', field: 'entity.foo' }, + { title: 'Bar', field: 'entity.bar' }, + { title: 'Baz', field: 'entity.spec.lifecycle' }, + ] + : []; + }; + await renderWrapped(); + + const columnHeader = screen + .getAllByRole('button') + .filter(c => c.tagName === 'SPAN'); + const columnHeaderLabels = columnHeader.map(c => c.textContent); + expect(columnHeaderLabels).toEqual(['Foo', 'Bar', 'Baz', 'Actions']); + }, 20_000); + it('should render the default actions of an item in the grid', async () => { await renderWrapped(); await waitFor(() => expect(catalogApi.queryEntities).toHaveBeenCalled()); diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx index e50c69b8ec..bcabbed991 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx @@ -43,6 +43,7 @@ import { createComponentRouteRef } from '../../routes'; import { CatalogTable, CatalogTableRow } from '../CatalogTable'; import { catalogTranslationRef } from '../../translation'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { ColumnsFunc } from '../CatalogTable/CatalogTable'; /** @internal */ export interface BaseCatalogPageProps { @@ -86,7 +87,7 @@ export function BaseCatalogPage(props: BaseCatalogPageProps) { */ export interface DefaultCatalogPageProps { initiallySelectedFilter?: UserListFilterKind; - columns?: TableColumn[]; + columns?: TableColumn[] | ColumnsFunc; actions?: TableProps['actions']; initialKind?: string; tableOptions?: TableProps['options']; diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index c242372a97..35f7a94355 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -31,7 +31,7 @@ import { import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; import { act, fireEvent, screen } from '@testing-library/react'; import * as React from 'react'; -import { CatalogTable } from './CatalogTable'; +import { CatalogTable, ColumnsFunc } from './CatalogTable'; const entities: Entity[] = [ { @@ -379,4 +379,60 @@ describe('CatalogTable component', () => { const labelCellValue = screen.getByText('generic'); expect(labelCellValue).toBeInTheDocument(); }); + + it('should render the label column with customised title and value as specified using function', async () => { + const columns: ColumnsFunc = (kind, entities1) => { + return kind === 'api' && entities1.length + ? [ + CatalogTable.columns.createNameColumn({ defaultKind: 'API' }), + CatalogTable.columns.createLabelColumn('category', { + title: 'Category', + }), + ] + : []; + }; + + const entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { + name: 'APIWithLabel', + labels: { category: 'generic' }, + }, + }; + const expectedColumns = ['Name', 'Category', 'Actions']; + + await renderInTestApp( + + ({ kind: 'api' }), + toQueryValue: () => 'api', + }, + }, + }} + > + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + + const columnHeader = screen + .getAllByRole('button') + .filter(c => c.tagName === 'SPAN'); + const columnHeaderLabels = columnHeader.map(c => c.textContent); + expect(columnHeaderLabels).toEqual(expectedColumns); + + const labelCellValue = screen.getByText('generic'); + expect(labelCellValue).toBeInTheDocument(); + }); }); diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 24ccb22908..25a3535d22 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -40,19 +40,29 @@ import Edit from '@material-ui/icons/Edit'; import OpenInNew from '@material-ui/icons/OpenInNew'; import Star from '@material-ui/icons/Star'; import StarBorder from '@material-ui/icons/StarBorder'; -import { capitalize } from 'lodash'; +import { capitalize, isFunction } from 'lodash'; import React, { ReactNode, useMemo } from 'react'; import { columnFactories } from './columns'; import { CatalogTableRow } from './types'; import pluralize from 'pluralize'; +/** + * Typed columns function to dynamically render columns based on entities and chosen kind. + * + * @public + */ +export type ColumnsFunc = ( + kind: string | undefined, + entities: Entity[], +) => TableColumn[]; + /** * Props for {@link CatalogTable}. * * @public */ export interface CatalogTableProps { - columns?: TableColumn[]; + columns?: TableColumn[] | ColumnsFunc; actions?: TableProps['actions']; tableOptions?: TableProps['options']; emptyContent?: ReactNode; @@ -121,6 +131,12 @@ export const CatalogTable = (props: CatalogTableProps) => { } }, [filters.kind?.value, entities]); + const overrideColumns = useMemo(() => { + return isFunction(columns) + ? columns(filters.kind?.value, entities) + : columns; + }, [columns, filters.kind?.value, entities]); + const showTypeColumn = filters.type === undefined; // TODO(timbonicus): remove the title from the CatalogTable once using EntitySearchBar const titlePreamble = capitalize(filters.user?.value ?? 'all'); @@ -227,7 +243,9 @@ export const CatalogTable = (props: CatalogTableProps) => { }; }); - const typeColumn = (columns || defaultColumns).find(c => c.title === 'Type'); + const typeColumn = (overrideColumns || defaultColumns).find( + c => c.title === 'Type', + ); if (typeColumn) { typeColumn.hidden = !showTypeColumn; } @@ -241,7 +259,7 @@ export const CatalogTable = (props: CatalogTableProps) => { return ( isLoading={loading} - columns={columns || defaultColumns} + columns={overrideColumns || defaultColumns} options={{ paging: showPagination, pageSize: 20, From 68c66c72a86c18590e973160fc691445477b5b93 Mon Sep 17 00:00:00 2001 From: Paul Stoker Date: Thu, 9 Nov 2023 09:50:31 +0100 Subject: [PATCH 2/9] Pass entire entity list context as a parameter Signed-off-by: Paul Stoker --- .../CatalogPage/DefaultCatalogPage.test.tsx | 8 ++------ .../components/CatalogTable/CatalogTable.test.tsx | 4 ++-- .../src/components/CatalogTable/CatalogTable.tsx | 15 +++++++-------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx index a4b1148a6f..cb4c2a2533 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx @@ -45,7 +45,6 @@ import { createComponentRouteRef } from '../../routes'; import { CatalogTableRow } from '../CatalogTable'; import { DefaultCatalogPage } from './DefaultCatalogPage'; import { ColumnsFunc } from '../CatalogTable/CatalogTable'; -import { Entity } from '@backstage/catalog-model/'; describe('DefaultCatalogPage', () => { const origReplaceState = window.history.replaceState; @@ -220,11 +219,8 @@ describe('DefaultCatalogPage', () => { }, 20_000); it('should render the custom column function passed as prop', async () => { - const columns: ColumnsFunc = ( - kind: string | undefined, - entities: Entity[], - ) => { - return kind === 'component' && entities.length + const columns: ColumnsFunc = ({ filters, entities }) => { + return filters.kind?.value === 'component' && entities.length ? [ { title: 'Foo', field: 'entity.foo' }, { title: 'Bar', field: 'entity.bar' }, diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index 35f7a94355..cfcba1fedd 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -381,8 +381,8 @@ describe('CatalogTable component', () => { }); it('should render the label column with customised title and value as specified using function', async () => { - const columns: ColumnsFunc = (kind, entities1) => { - return kind === 'api' && entities1.length + const columns: ColumnsFunc = ({ filters, entities: entities1 }) => { + return filters.kind?.value === 'api' && entities1.length ? [ CatalogTable.columns.createNameColumn({ defaultKind: 'API' }), CatalogTable.columns.createLabelColumn('category', { diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 25a3535d22..f75791481f 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -29,6 +29,7 @@ import { WarningPanel, } from '@backstage/core-components'; import { + EntityListContextProps, getEntityRelations, humanizeEntityRef, useEntityList, @@ -47,13 +48,12 @@ import { CatalogTableRow } from './types'; import pluralize from 'pluralize'; /** - * Typed columns function to dynamically render columns based on entities and chosen kind. + * Typed columns function to dynamically render columns based on entity list context. * * @public */ export type ColumnsFunc = ( - kind: string | undefined, - entities: Entity[], + entityListContext: EntityListContextProps, ) => TableColumn[]; /** @@ -89,7 +89,8 @@ const refCompare = (a: Entity, b: Entity) => { export const CatalogTable = (props: CatalogTableProps) => { const { columns, actions, tableOptions, subtitle, emptyContent } = props; const { isStarredEntity, toggleStarredEntity } = useStarredEntities(); - const { loading, error, entities, filters } = useEntityList(); + const entityListContext = useEntityList(); + const { loading, error, entities, filters } = entityListContext; const defaultColumns: TableColumn[] = useMemo(() => { return [ @@ -132,10 +133,8 @@ export const CatalogTable = (props: CatalogTableProps) => { }, [filters.kind?.value, entities]); const overrideColumns = useMemo(() => { - return isFunction(columns) - ? columns(filters.kind?.value, entities) - : columns; - }, [columns, filters.kind?.value, entities]); + return isFunction(columns) ? columns(entityListContext) : columns; + }, [columns, entityListContext]); const showTypeColumn = filters.type === undefined; // TODO(timbonicus): remove the title from the CatalogTable once using EntitySearchBar From edb12882dfb548196590b1902a02b7154b239472 Mon Sep 17 00:00:00 2001 From: Paul Stoker Date: Fri, 17 Nov 2023 09:30:16 +0100 Subject: [PATCH 3/9] Rename columns func type Signed-off-by: Paul Stoker --- .../src/components/CatalogPage/DefaultCatalogPage.test.tsx | 4 ++-- .../src/components/CatalogPage/DefaultCatalogPage.tsx | 4 ++-- .../src/components/CatalogTable/CatalogTable.test.tsx | 7 +++++-- .../catalog/src/components/CatalogTable/CatalogTable.tsx | 4 ++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx index cb4c2a2533..2663dc7fd2 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx @@ -44,7 +44,7 @@ import React from 'react'; import { createComponentRouteRef } from '../../routes'; import { CatalogTableRow } from '../CatalogTable'; import { DefaultCatalogPage } from './DefaultCatalogPage'; -import { ColumnsFunc } from '../CatalogTable/CatalogTable'; +import { CatalogTableColumnsFunc } from '../CatalogTable/CatalogTable'; describe('DefaultCatalogPage', () => { const origReplaceState = window.history.replaceState; @@ -219,7 +219,7 @@ describe('DefaultCatalogPage', () => { }, 20_000); it('should render the custom column function passed as prop', async () => { - const columns: ColumnsFunc = ({ filters, entities }) => { + const columns: CatalogTableColumnsFunc = ({ filters, entities }) => { return filters.kind?.value === 'component' && entities.length ? [ { title: 'Foo', field: 'entity.foo' }, diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx index bcabbed991..9aca36a4e1 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx @@ -43,7 +43,7 @@ import { createComponentRouteRef } from '../../routes'; import { CatalogTable, CatalogTableRow } from '../CatalogTable'; import { catalogTranslationRef } from '../../translation'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; -import { ColumnsFunc } from '../CatalogTable/CatalogTable'; +import { CatalogTableColumnsFunc } from '../CatalogTable/CatalogTable'; /** @internal */ export interface BaseCatalogPageProps { @@ -87,7 +87,7 @@ export function BaseCatalogPage(props: BaseCatalogPageProps) { */ export interface DefaultCatalogPageProps { initiallySelectedFilter?: UserListFilterKind; - columns?: TableColumn[] | ColumnsFunc; + columns?: TableColumn[] | CatalogTableColumnsFunc; actions?: TableProps['actions']; initialKind?: string; tableOptions?: TableProps['options']; diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index cfcba1fedd..c55b058ec5 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -31,7 +31,7 @@ import { import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; import { act, fireEvent, screen } from '@testing-library/react'; import * as React from 'react'; -import { CatalogTable, ColumnsFunc } from './CatalogTable'; +import { CatalogTable, CatalogTableColumnsFunc } from './CatalogTable'; const entities: Entity[] = [ { @@ -381,7 +381,10 @@ describe('CatalogTable component', () => { }); it('should render the label column with customised title and value as specified using function', async () => { - const columns: ColumnsFunc = ({ filters, entities: entities1 }) => { + const columns: CatalogTableColumnsFunc = ({ + filters, + entities: entities1, + }) => { return filters.kind?.value === 'api' && entities1.length ? [ CatalogTable.columns.createNameColumn({ defaultKind: 'API' }), diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 4cbdf64a88..dd18984b8e 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -53,7 +53,7 @@ import { CatalogTableRow } from './types'; * * @public */ -export type ColumnsFunc = ( +export type CatalogTableColumnsFunc = ( entityListContext: EntityListContextProps, ) => TableColumn[]; @@ -63,7 +63,7 @@ export type ColumnsFunc = ( * @public */ export interface CatalogTableProps { - columns?: TableColumn[] | ColumnsFunc; + columns?: TableColumn[] | CatalogTableColumnsFunc; actions?: TableProps['actions']; tableOptions?: TableProps['options']; emptyContent?: ReactNode; From e41cb05b57b6ddc11439295c485c79d70988d2e6 Mon Sep 17 00:00:00 2001 From: Paul Stoker Date: Fri, 17 Nov 2023 09:35:32 +0100 Subject: [PATCH 4/9] Simplify is function check Signed-off-by: Paul Stoker --- plugins/catalog/src/components/CatalogTable/CatalogTable.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index dd18984b8e..9f94993cdc 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -42,7 +42,7 @@ import Edit from '@material-ui/icons/Edit'; import OpenInNew from '@material-ui/icons/OpenInNew'; import Star from '@material-ui/icons/Star'; import StarBorder from '@material-ui/icons/StarBorder'; -import { capitalize, isFunction } from 'lodash'; +import { capitalize } from 'lodash'; import pluralize from 'pluralize'; import React, { ReactNode, useMemo } from 'react'; import { columnFactories } from './columns'; @@ -134,7 +134,7 @@ export const CatalogTable = (props: CatalogTableProps) => { }, [filters.kind?.value, entities]); const overrideColumns = useMemo(() => { - return isFunction(columns) ? columns(entityListContext) : columns; + return typeof columns === 'function' ? columns(entityListContext) : columns; }, [columns, entityListContext]); const showTypeColumn = filters.type === undefined; From 61a9d35578d43b7551230b7648c0c33db40ec4e7 Mon Sep 17 00:00:00 2001 From: Paul Stoker Date: Fri, 17 Nov 2023 09:43:18 +0100 Subject: [PATCH 5/9] Pass default columns func on prop Signed-off-by: Paul Stoker --- .../components/CatalogTable/CatalogTable.tsx | 92 ++++++++++--------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 9f94993cdc..7aa6d43f6c 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -86,53 +86,57 @@ const refCompare = (a: Entity, b: Entity) => { return toRef(a).localeCompare(toRef(b)); }; +const defaultColumnsFunc: CatalogTableColumnsFunc = ({ filters, entities }) => { + return [ + columnFactories.createTitleColumn({ hidden: true }), + columnFactories.createNameColumn({ defaultKind: filters.kind?.value }), + ...createEntitySpecificColumns(), + columnFactories.createMetadataDescriptionColumn(), + columnFactories.createTagsColumn(), + ]; + + function createEntitySpecificColumns(): TableColumn[] { + const baseColumns = [ + columnFactories.createSystemColumn(), + columnFactories.createOwnerColumn(), + columnFactories.createSpecTypeColumn(), + columnFactories.createSpecLifecycleColumn(), + ]; + switch (filters.kind?.value) { + case 'user': + return []; + case 'domain': + case 'system': + return [columnFactories.createOwnerColumn()]; + case 'group': + case 'template': + return [columnFactories.createSpecTypeColumn()]; + case 'location': + return [ + columnFactories.createSpecTypeColumn(), + columnFactories.createSpecTargetsColumn(), + ]; + default: + return entities.every(entity => entity.metadata.namespace === 'default') + ? baseColumns + : [...baseColumns, columnFactories.createNamespaceColumn()]; + } + } +}; + /** @public */ export const CatalogTable = (props: CatalogTableProps) => { - const { columns, actions, tableOptions, subtitle, emptyContent } = props; + const { + columns = defaultColumnsFunc, + actions, + tableOptions, + subtitle, + emptyContent, + } = props; const { isStarredEntity, toggleStarredEntity } = useStarredEntities(); const entityListContext = useEntityList(); const { loading, error, entities, filters } = entityListContext; - const defaultColumns: TableColumn[] = useMemo(() => { - return [ - columnFactories.createTitleColumn({ hidden: true }), - columnFactories.createNameColumn({ defaultKind: filters.kind?.value }), - ...createEntitySpecificColumns(), - columnFactories.createMetadataDescriptionColumn(), - columnFactories.createTagsColumn(), - ]; - - function createEntitySpecificColumns(): TableColumn[] { - const baseColumns = [ - columnFactories.createSystemColumn(), - columnFactories.createOwnerColumn(), - columnFactories.createSpecTypeColumn(), - columnFactories.createSpecLifecycleColumn(), - ]; - switch (filters.kind?.value) { - case 'user': - return []; - case 'domain': - case 'system': - return [columnFactories.createOwnerColumn()]; - case 'group': - case 'template': - return [columnFactories.createSpecTypeColumn()]; - case 'location': - return [ - columnFactories.createSpecTypeColumn(), - columnFactories.createSpecTargetsColumn(), - ]; - default: - return entities.every( - entity => entity.metadata.namespace === 'default', - ) - ? baseColumns - : [...baseColumns, columnFactories.createNamespaceColumn()]; - } - } - }, [filters.kind?.value, entities]); - const overrideColumns = useMemo(() => { return typeof columns === 'function' ? columns(entityListContext) : columns; }, [columns, entityListContext]); @@ -243,9 +247,7 @@ export const CatalogTable = (props: CatalogTableProps) => { }; }); - const typeColumn = (overrideColumns || defaultColumns).find( - c => c.title === 'Type', - ); + const typeColumn = overrideColumns.find(c => c.title === 'Type'); if (typeColumn) { typeColumn.hidden = !showTypeColumn; } @@ -259,7 +261,7 @@ export const CatalogTable = (props: CatalogTableProps) => { return ( isLoading={loading} - columns={overrideColumns || defaultColumns} + columns={overrideColumns} options={{ paging: showPagination, pageSize: 20, From 5d298865fb9afc76fef11b516b0c448174b3baea Mon Sep 17 00:00:00 2001 From: Paul Stoker Date: Fri, 17 Nov 2023 09:47:23 +0100 Subject: [PATCH 6/9] Rename variable Signed-off-by: Paul Stoker --- .../catalog/src/components/CatalogTable/CatalogTable.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 7aa6d43f6c..602aad38c0 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -137,7 +137,7 @@ export const CatalogTable = (props: CatalogTableProps) => { const entityListContext = useEntityList(); const { loading, error, entities, filters } = entityListContext; - const overrideColumns = useMemo(() => { + const tableColumns = useMemo(() => { return typeof columns === 'function' ? columns(entityListContext) : columns; }, [columns, entityListContext]); @@ -247,7 +247,7 @@ export const CatalogTable = (props: CatalogTableProps) => { }; }); - const typeColumn = overrideColumns.find(c => c.title === 'Type'); + const typeColumn = tableColumns.find(c => c.title === 'Type'); if (typeColumn) { typeColumn.hidden = !showTypeColumn; } @@ -261,7 +261,7 @@ export const CatalogTable = (props: CatalogTableProps) => { return ( isLoading={loading} - columns={overrideColumns} + columns={tableColumns} options={{ paging: showPagination, pageSize: 20, From 2cdfd5fe82624ee29a46d2193ea83a69fc5dddad Mon Sep 17 00:00:00 2001 From: Paul Stoker Date: Fri, 17 Nov 2023 09:53:54 +0100 Subject: [PATCH 7/9] Simplify func Signed-off-by: Paul Stoker --- .../catalog/src/components/CatalogTable/CatalogTable.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 602aad38c0..d8eb2cf04e 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -137,9 +137,11 @@ export const CatalogTable = (props: CatalogTableProps) => { const entityListContext = useEntityList(); const { loading, error, entities, filters } = entityListContext; - const tableColumns = useMemo(() => { - return typeof columns === 'function' ? columns(entityListContext) : columns; - }, [columns, entityListContext]); + const tableColumns = useMemo( + () => + typeof columns === 'function' ? columns(entityListContext) : columns, + [columns, entityListContext], + ); const showTypeColumn = filters.type === undefined; // TODO(timbonicus): remove the title from the CatalogTable once using EntitySearchBar From 8903ab5b043a7f8c4c66c6ea8438e1d46d60fdaf Mon Sep 17 00:00:00 2001 From: Paul Stoker Date: Fri, 17 Nov 2023 15:43:39 +0100 Subject: [PATCH 8/9] API reports Signed-off-by: Paul Stoker --- plugins/catalog/api-report.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 6647f8deaa..f1a704e0d4 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -11,6 +11,7 @@ import { CatalogApi } from '@backstage/plugin-catalog-react'; import { ComponentEntity } from '@backstage/catalog-model'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Entity } from '@backstage/catalog-model'; +import { EntityListContextProps } from '@backstage/plugin-catalog-react'; import { EntityOwnerPickerProps } from '@backstage/plugin-catalog-react'; import { EntityPresentationApi } from '@backstage/plugin-catalog-react'; import { EntityRefPresentation } from '@backstage/plugin-catalog-react'; @@ -184,8 +185,10 @@ export const CatalogTable: { export interface CatalogTableProps { // (undocumented) actions?: TableProps['actions']; + // Warning: (ae-forgotten-export) The symbol "CatalogTableColumnsFunc" needs to be exported by the entry point index.d.ts + // // (undocumented) - columns?: TableColumn[]; + columns?: TableColumn[] | CatalogTableColumnsFunc; // (undocumented) emptyContent?: ReactNode; // (undocumented) @@ -217,7 +220,7 @@ export interface DefaultCatalogPageProps { // (undocumented) actions?: TableProps['actions']; // (undocumented) - columns?: TableColumn[]; + columns?: TableColumn[] | CatalogTableColumnsFunc; // (undocumented) emptyContent?: ReactNode; // (undocumented) From 36bd2059f179845073545f0813799d89678a1ee4 Mon Sep 17 00:00:00 2001 From: Paul Stoker Date: Fri, 17 Nov 2023 17:09:22 +0100 Subject: [PATCH 9/9] Move and make type public Signed-off-by: Paul Stoker --- plugins/catalog/api-report.md | 7 +++++-- .../CatalogPage/DefaultCatalogPage.test.tsx | 3 ++- .../components/CatalogPage/DefaultCatalogPage.tsx | 3 ++- .../components/CatalogTable/CatalogTable.test.tsx | 3 ++- .../src/components/CatalogTable/CatalogTable.tsx | 12 +----------- .../catalog/src/components/CatalogTable/index.ts | 2 +- .../catalog/src/components/CatalogTable/types.ts | 13 ++++++++++++- 7 files changed, 25 insertions(+), 18 deletions(-) diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index f1a704e0d4..76aa64b6a3 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -181,12 +181,15 @@ export const CatalogTable: { }>; }; +// @public +export type CatalogTableColumnsFunc = ( + entityListContext: EntityListContextProps, +) => TableColumn[]; + // @public export interface CatalogTableProps { // (undocumented) actions?: TableProps['actions']; - // Warning: (ae-forgotten-export) The symbol "CatalogTableColumnsFunc" needs to be exported by the entry point index.d.ts - // // (undocumented) columns?: TableColumn[] | CatalogTableColumnsFunc; // (undocumented) diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx index 2663dc7fd2..f3cc0cb042 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx @@ -44,7 +44,8 @@ import React from 'react'; import { createComponentRouteRef } from '../../routes'; import { CatalogTableRow } from '../CatalogTable'; import { DefaultCatalogPage } from './DefaultCatalogPage'; -import { CatalogTableColumnsFunc } from '../CatalogTable/CatalogTable'; + +import { CatalogTableColumnsFunc } from '../CatalogTable/types'; describe('DefaultCatalogPage', () => { const origReplaceState = window.history.replaceState; diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx index 9aca36a4e1..a50cd4fa2a 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx @@ -43,7 +43,8 @@ import { createComponentRouteRef } from '../../routes'; import { CatalogTable, CatalogTableRow } from '../CatalogTable'; import { catalogTranslationRef } from '../../translation'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; -import { CatalogTableColumnsFunc } from '../CatalogTable/CatalogTable'; + +import { CatalogTableColumnsFunc } from '../CatalogTable/types'; /** @internal */ export interface BaseCatalogPageProps { diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index c55b058ec5..052d8f4787 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -31,7 +31,8 @@ import { import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; import { act, fireEvent, screen } from '@testing-library/react'; import * as React from 'react'; -import { CatalogTable, CatalogTableColumnsFunc } from './CatalogTable'; +import { CatalogTable } from './CatalogTable'; +import { CatalogTableColumnsFunc } from './types'; const entities: Entity[] = [ { diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index d8eb2cf04e..4dd3fa257d 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -29,7 +29,6 @@ import { WarningPanel, } from '@backstage/core-components'; import { - EntityListContextProps, getEntityRelations, humanizeEntityRef, useEntityList, @@ -46,16 +45,7 @@ import { capitalize } from 'lodash'; import pluralize from 'pluralize'; import React, { ReactNode, useMemo } from 'react'; import { columnFactories } from './columns'; -import { CatalogTableRow } from './types'; - -/** - * Typed columns function to dynamically render columns based on entity list context. - * - * @public - */ -export type CatalogTableColumnsFunc = ( - entityListContext: EntityListContextProps, -) => TableColumn[]; +import { CatalogTableColumnsFunc, CatalogTableRow } from './types'; /** * Props for {@link CatalogTable}. diff --git a/plugins/catalog/src/components/CatalogTable/index.ts b/plugins/catalog/src/components/CatalogTable/index.ts index 4e1b90f80a..f5d1ca1bb7 100644 --- a/plugins/catalog/src/components/CatalogTable/index.ts +++ b/plugins/catalog/src/components/CatalogTable/index.ts @@ -16,4 +16,4 @@ export { CatalogTable } from './CatalogTable'; export type { CatalogTableProps } from './CatalogTable'; -export type { CatalogTableRow } from './types'; +export type { CatalogTableRow, CatalogTableColumnsFunc } from './types'; diff --git a/plugins/catalog/src/components/CatalogTable/types.ts b/plugins/catalog/src/components/CatalogTable/types.ts index f2b3324169..c991205b11 100644 --- a/plugins/catalog/src/components/CatalogTable/types.ts +++ b/plugins/catalog/src/components/CatalogTable/types.ts @@ -14,7 +14,9 @@ * limitations under the License. */ -import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; +import { CompoundEntityRef, Entity } from '@backstage/catalog-model'; +import { EntityListContextProps } from '@backstage/plugin-catalog-react'; +import { TableColumn } from '@backstage/core-components'; /** @public */ export interface CatalogTableRow { @@ -31,3 +33,12 @@ export interface CatalogTableRow { ownedByRelations: CompoundEntityRef[]; }; } + +/** + * Typed columns function to dynamically render columns based on entity list context. + * + * @public + */ +export type CatalogTableColumnsFunc = ( + entityListContext: EntityListContextProps, +) => TableColumn[];