diff --git a/.changeset/lemon-bulldogs-study.md b/.changeset/lemon-bulldogs-study.md new file mode 100644 index 0000000000..7bb15b8193 --- /dev/null +++ b/.changeset/lemon-bulldogs-study.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': minor +--- + +Consistent title behaviour across CatalogTable, CursorPaginatedCatalogTable, and OffsetPaginatedCatalogTable. diff --git a/plugins/catalog/report.api.md b/plugins/catalog/report.api.md index 6b8f246514..8bc620d221 100644 --- a/plugins/catalog/report.api.md +++ b/plugins/catalog/report.api.md @@ -156,7 +156,7 @@ export interface CatalogSearchResultListItemProps { result?: IndexableDocument; } -// @public (undocumented) +// @public export const CatalogTable: { (props: CatalogTableProps): React_2.JSX.Element; columns: Readonly<{ @@ -214,6 +214,7 @@ export interface CatalogTableProps { subtitle?: string; // (undocumented) tableOptions?: TableProps['options']; + title?: string; } // @public (undocumented) diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index 5b7f28e26a..13ee7593b9 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -84,6 +84,19 @@ describe('CatalogTable component', () => { ).resolves.toBeInTheDocument(); }); + it('should a custom title and subtitle when passed in', async () => { + await renderInTestApp( + + + + + , + ); + + expect(screen.queryByText('My Title')).toBeInTheDocument(); + expect(screen.queryByText('My Subtitle')).toBeInTheDocument(); + }); + it('should display entity names when loading has finished and no error occurred', async () => { await renderInTestApp( diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 2d5e78a0d6..944e795709 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -49,6 +49,7 @@ import { defaultCatalogTableColumnsFunc } from './defaultCatalogTableColumnsFunc import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { catalogTranslationRef } from '../../alpha/translation'; import { FavoriteToggleIcon } from '@backstage/core-components'; +import { CatalogTableToolbar } from './CatalogTableToolbar'; /** * Props for {@link CatalogTable}. @@ -60,6 +61,11 @@ export interface CatalogTableProps { actions?: TableProps['actions']; tableOptions?: TableProps['options']; emptyContent?: ReactNode; + /** + * A static title to use for the table. If not provided, a title will be + * generated based on the current Kind and Type filters and total number of items. + */ + title?: string; subtitle?: string; } @@ -73,7 +79,16 @@ const refCompare = (a: Entity, b: Entity) => { return toRef(a).localeCompare(toRef(b)); }; -/** @public */ +/** + * CatalogTable is a wrapper around the Table component that is pre-configured + * to display catalog entities. + * + * @remarks + * + * See {@link https://backstage.io/docs/features/software-catalog/catalog-customization} + * + * @public + */ export const CatalogTable = (props: CatalogTableProps) => { const { columns = defaultCatalogTableColumnsFunc, @@ -173,14 +188,11 @@ export const CatalogTable = (props: CatalogTableProps) => { const currentCount = typeof totalItems === 'number' ? `(${totalItems})` : ''; // TODO(timbonicus): remove the title from the CatalogTable once using EntitySearchBar const titlePreamble = capitalize(filters.user?.value ?? 'all'); - const title = [ - titlePreamble, - currentType, - pluralize(currentKind), - currentCount, - ] - .filter(s => s) - .join(' '); + const title = + props.title || + [titlePreamble, currentType, pluralize(currentKind), currentCount] + .filter(s => s) + .join(' '); const actions = props.actions || defaultActions; const options = { @@ -235,6 +247,9 @@ export const CatalogTable = (props: CatalogTableProps) => { pageSizeOptions: [20, 50, 100], ...options, }} + components={{ + Toolbar: CatalogTableToolbar, + }} title={title} data={rows} actions={actions} diff --git a/plugins/catalog/src/components/CatalogTable/CursorPaginatedCatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CursorPaginatedCatalogTable.test.tsx index adc69794ae..b0deec9679 100644 --- a/plugins/catalog/src/components/CatalogTable/CursorPaginatedCatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CursorPaginatedCatalogTable.test.tsx @@ -63,6 +63,22 @@ describe('CursorPaginatedCatalogTable', () => { ); }; + it('should display the title and subtitle when passed in', async () => { + await renderInTestApp( + wrapInContext( + , + ), + ); + + expect(screen.queryByText('My Title')).toBeInTheDocument(); + expect(screen.queryByText('My Subtitle')).toBeInTheDocument(); + }); + it('should display all the items', async () => { await renderInTestApp( wrapInContext( diff --git a/plugins/catalog/src/components/CatalogTable/CursorPaginatedCatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CursorPaginatedCatalogTable.tsx index 7198f8a60c..843d5be2c8 100644 --- a/plugins/catalog/src/components/CatalogTable/CursorPaginatedCatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CursorPaginatedCatalogTable.tsx @@ -30,12 +30,10 @@ type PaginatedCatalogTableProps = { */ export function CursorPaginatedCatalogTable(props: PaginatedCatalogTableProps) { - const { columns, data, next, prev, title, isLoading, options, ...restProps } = - props; + const { columns, data, next, prev, options, ...restProps } = props; return ( ); diff --git a/plugins/catalog/src/components/CatalogTable/OffsetPaginatedCatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/OffsetPaginatedCatalogTable.test.tsx index aad9b7cb3d..e2e73c5af8 100644 --- a/plugins/catalog/src/components/CatalogTable/OffsetPaginatedCatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/OffsetPaginatedCatalogTable.test.tsx @@ -62,6 +62,28 @@ describe('OffsetPaginatedCatalogTable', () => { ); }; + it('should display the title and subtitle when passed in', async () => { + await renderInTestApp( + wrapInContext( + , + { + setOffset: jest.fn(), + limit: Number.MAX_SAFE_INTEGER, + offset: 0, + totalItems: data.length, + }, + ), + ); + + expect(screen.queryByText('My Title')).toBeInTheDocument(); + expect(screen.queryByText('My Subtitle')).toBeInTheDocument(); + }); + it('should display all the items', async () => { await renderInTestApp( wrapInContext( diff --git a/plugins/catalog/src/components/CatalogTable/OffsetPaginatedCatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/OffsetPaginatedCatalogTable.tsx index 7febfc2d32..7ebf791046 100644 --- a/plugins/catalog/src/components/CatalogTable/OffsetPaginatedCatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/OffsetPaginatedCatalogTable.tsx @@ -27,7 +27,7 @@ import { CatalogTableToolbar } from './CatalogTableToolbar'; export function OffsetPaginatedCatalogTable( props: TableProps, ) { - const { columns, data, isLoading, options } = props; + const { columns, data, options, ...restProps } = props; const { setLimit, setOffset, limit, totalItems, offset } = useEntityList(); const [page, setPage] = React.useState( @@ -65,7 +65,7 @@ export function OffsetPaginatedCatalogTable( }} totalCount={totalItems} localization={{ pagination: { labelDisplayedRows: '' } }} - isLoading={isLoading} + {...restProps} /> ); }