diff --git a/.changeset/fifty-berries-learn.md b/.changeset/fifty-berries-learn.md new file mode 100644 index 0000000000..eb95d1febd --- /dev/null +++ b/.changeset/fifty-berries-learn.md @@ -0,0 +1,40 @@ +--- +'@backstage/plugin-catalog': patch +--- + +--- + +This change would allow consumers of plugin catalog to make use of labels just like tags from metadata. + +The intent of change is to show customised columns based on selected label with which entity is associated. +For example: In example below category and visibility are type of labels associated with API entity. +YAML for API entity + +```yaml +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: sample-api + description: API for sample + links: + - url: http://localhost:8080/swagger-ui.html + title: Swagger UI + tags: + - http + labels: + category: legacy + visibility: protected +``` + +Consumers can customise columns to include label column and show in api-docs list + +```typescript +const columns = [ + CatalogTable.columns.createNameColumn({ defaultKind: 'API' }), + CatalogTable.columns.createLabelColumn('category', { title: 'Category' }), + CatalogTable.columns.createLabelColumn('visibility', { + title: 'Visibility', + defaultValue: 'public', + }), +]; +``` diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 377b62a1e6..71006b53b5 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -148,6 +148,15 @@ export const CatalogTable: { } | undefined, ): TableColumn; + createLabelColumn( + key: string, + options?: + | { + title?: string | undefined; + defaultValue?: string | undefined; + } + | undefined, + ): TableColumn; }>; }; diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index bd78cecaf0..bb2f302a37 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -331,4 +331,42 @@ describe('CatalogTable component', () => { expect(getByText('Should be rendered')).toBeInTheDocument(); }); + + it('should render the label column with customised title and value as specified', async () => { + const columns = [ + 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']; + + const { getAllByRole, getByText } = await renderInTestApp( + + + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + + const columnHeader = getAllByRole('button').filter( + c => c.tagName === 'SPAN', + ); + const columnHeaderLabels = columnHeader.map(c => c.textContent); + expect(columnHeaderLabels).toEqual(expectedColumns); + + const labelCellValue = getByText('generic'); + expect(labelCellValue).toBeInTheDocument(); + }); }); diff --git a/plugins/catalog/src/components/CatalogTable/columns.tsx b/plugins/catalog/src/components/CatalogTable/columns.tsx index 19d160c0e2..68491f2e07 100644 --- a/plugins/catalog/src/components/CatalogTable/columns.tsx +++ b/plugins/catalog/src/components/CatalogTable/columns.tsx @@ -25,6 +25,31 @@ import { OverflowTooltip, TableColumn } from '@backstage/core-components'; import { Entity } from '@backstage/catalog-model'; import { JsonArray } from '@backstage/types'; +/** + * Renders label if exists in entities or default + * @param labels - Key value pairs of labels specified in entity metadata + * @param key - specified label key to be extracted from all labels + * @param defaultValue - shows default in case label key does not exist + * @returns Chip style component when label value exists undefined otherwise. + */ +const renderLabel = ( + labels: Record | undefined, + key: string, + defaultValue?: string, +) => { + const specifiedLabelValue = (labels && labels[key]) || defaultValue; + return ( + specifiedLabelValue && ( + + ) + ); +}; + // The columnFactories symbol is not directly exported, but through the // CatalogTable.columns field. /** @public */ @@ -162,4 +187,20 @@ export const columnFactories = Object.freeze({ searchable: true, }; }, + createLabelColumn( + key: string, + options?: { title?: string; defaultValue?: string }, + ): TableColumn { + return { + title: options?.title || 'Label', + field: 'entity.metadata.labels', + cellStyle: { + padding: '0px 16px 0px 20px', + }, + render: ({ entity }: { entity: Entity }) => ( + <>{renderLabel(entity.metadata?.labels, key, options?.defaultValue)} + ), + width: 'auto', + }; + }, });