diff --git a/.changeset/pink-coins-sniff.md b/.changeset/pink-coins-sniff.md index 83fa280475..b9714f116a 100644 --- a/.changeset/pink-coins-sniff.md +++ b/.changeset/pink-coins-sniff.md @@ -11,6 +11,6 @@ Introduce new cards to `@backstage/plugin-catalog` that can be added to entity p - `EntityHasSubcomponentsCard` to display subcomponents of a subcomponent. - In addition, `EntityHasApisCard` to display APIs of a system is added to `@backstage/plugin-api-docs`. -`@backstage/plugin-catalog-react` now provides `ComponentsTable` and `SystemsTable` to build own cards for components and systems. +`@backstage/plugin-catalog-react` now provides an `EntityTable` to build own cards for entities. The styling of the tables and new cards was also applied to the existing `EntityConsumedApisCard`, `EntityConsumingComponentsCard`, `EntityProvidedApisCard`, and `EntityProvidingComponentsCard`. diff --git a/plugins/api-docs/src/components/ApisCards/ApisTable.tsx b/plugins/api-docs/src/components/ApisCards/ApisTable.tsx deleted file mode 100644 index 76daf15945..0000000000 --- a/plugins/api-docs/src/components/ApisCards/ApisTable.tsx +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - ApiEntity, - EntityName, - RELATION_OWNED_BY, - RELATION_PART_OF, -} from '@backstage/catalog-model'; -import { Table, TableColumn } from '@backstage/core'; -import { - EntityRefLink, - EntityRefLinks, - formatEntityRefTitle, - getEntityRelations, -} from '@backstage/plugin-catalog-react'; -import { makeStyles } from '@material-ui/core'; -import React from 'react'; -import { ApiTypeTitle } from '../ApiDefinitionCard'; - -type EntityRow = { - entity: ApiEntity; - resolved: { - name: string; - partOfSystemRelationTitle?: string; - partOfSystemRelations: EntityName[]; - ownedByRelationsTitle?: string; - ownedByRelations: EntityName[]; - }; -}; - -const columns: TableColumn[] = [ - { - title: 'Name', - field: 'resolved.name', - highlight: true, - render: ({ entity }) => ( - - ), - }, - { - title: 'System', - field: 'resolved.partOfSystemRelationTitle', - render: ({ resolved }) => ( - - ), - }, - { - title: 'Owner', - field: 'resolved.ownedByRelationsTitle', - render: ({ resolved }) => ( - - ), - }, - { - title: 'Lifecycle', - field: 'entity.spec.lifecycle', - }, - { - title: 'Type', - field: 'entity.spec.type', - render: ({ entity }) => , - }, - { - title: 'Description', - field: 'entity.metadata.description', - width: 'auto', - }, -]; - -type Props = { - title: string; - variant?: string; - entities: ApiEntity[]; - emptyComponent?: JSX.Element; -}; - -const useStyles = makeStyles(theme => ({ - empty: { - padding: theme.spacing(2), - display: 'flex', - justifyContent: 'center', - }, -})); - -export const ApisTable = ({ - entities, - title, - emptyComponent, - variant = 'gridItem', -}: Props) => { - const classes = useStyles(); - const tableStyle: React.CSSProperties = { - minWidth: '0', - width: '100%', - }; - - if (variant === 'gridItem') { - tableStyle.height = 'calc(100% - 10px)'; - } - - const rows = entities.map(entity => { - const partOfSystemRelations = getEntityRelations(entity, RELATION_PART_OF, { - kind: 'system', - }); - const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); - - return { - entity, - resolved: { - name: formatEntityRefTitle(entity, { - defaultKind: 'API', - }), - ownedByRelationsTitle: ownedByRelations - .map(r => formatEntityRefTitle(r, { defaultKind: 'group' })) - .join(', '), - ownedByRelations, - partOfSystemRelationTitle: partOfSystemRelations - .map(r => - formatEntityRefTitle(r, { - defaultKind: 'system', - }), - ) - .join(', '), - partOfSystemRelations, - }, - }; - }); - - return ( - - columns={columns} - title={title} - style={tableStyle} - emptyComponent={ - emptyComponent &&
{emptyComponent}
- } - options={{ - // TODO: Toolbar padding if off compared to other cards, should be: padding: 16px 24px; - search: false, - paging: false, - actionsColumnIndex: -1, - padding: 'dense', - }} - data={rows} - /> - ); -}; diff --git a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx index 7e57a23b06..922a133fde 100644 --- a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx +++ b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx @@ -26,9 +26,13 @@ import { Progress, WarningPanel, } from '@backstage/core'; -import { useEntity, useRelatedEntities } from '@backstage/plugin-catalog-react'; +import { + EntityTable, + useEntity, + useRelatedEntities, +} from '@backstage/plugin-catalog-react'; import React, { PropsWithChildren } from 'react'; -import { ApisTable } from './ApisTable'; +import { apiEntityColumns } from './presets'; const ApisCard = ({ children, @@ -74,7 +78,7 @@ export const ConsumedApisCard = ({ variant = 'gridItem' }: Props) => { } return ( - { } + columns={apiEntityColumns} entities={entities as ApiEntity[]} /> ); diff --git a/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx b/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx index 5ed38e91c7..a0e1dce052 100644 --- a/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx +++ b/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx @@ -22,9 +22,13 @@ import { Progress, WarningPanel, } from '@backstage/core'; -import { useEntity, useRelatedEntities } from '@backstage/plugin-catalog-react'; +import { + EntityTable, + useEntity, + useRelatedEntities, +} from '@backstage/plugin-catalog-react'; import React, { PropsWithChildren } from 'react'; -import { ApisTable } from './ApisTable'; +import { apiEntityColumns } from './presets'; const ApisCard = ({ children, @@ -69,7 +73,7 @@ export const HasApisCard = ({ variant = 'gridItem' }: Props) => { } return ( - { } + columns={apiEntityColumns} entities={entities as ApiEntity[]} /> ); diff --git a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx index e760d7a8f0..191eb0465c 100644 --- a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx +++ b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx @@ -26,9 +26,13 @@ import { Progress, WarningPanel, } from '@backstage/core'; -import { useEntity, useRelatedEntities } from '@backstage/plugin-catalog-react'; +import { + EntityTable, + useEntity, + useRelatedEntities, +} from '@backstage/plugin-catalog-react'; import React, { PropsWithChildren } from 'react'; -import { ApisTable } from './ApisTable'; +import { apiEntityColumns } from './presets'; const ApisCard = ({ children, @@ -74,7 +78,7 @@ export const ProvidedApisCard = ({ variant = 'gridItem' }: Props) => { } return ( - { } + columns={apiEntityColumns} entities={entities as ApiEntity[]} /> ); diff --git a/plugins/api-docs/src/components/ApisCards/presets.tsx b/plugins/api-docs/src/components/ApisCards/presets.tsx new file mode 100644 index 0000000000..c7fc4420df --- /dev/null +++ b/plugins/api-docs/src/components/ApisCards/presets.tsx @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ApiEntity } from '@backstage/catalog-model'; +import { TableColumn } from '@backstage/core'; +import { EntityTable } from '@backstage/plugin-catalog-react'; +import React from 'react'; +import { ApiTypeTitle } from '../ApiDefinitionCard'; + +export function createSpecApiTypeColumn(): TableColumn { + return { + title: 'Type', + field: 'spec.type', + render: entity => , + }; +} + +// TODO: This could be moved to plugin-catalog-react if we wouldn't have a +// special createSpecApiTypeColumn. But this is requited to use ApiTypeTitle to +// resolve the display name of an entity. Is the display name really worth it? + +export const apiEntityColumns: TableColumn[] = [ + EntityTable.columns.createEntityRefColumn({ defaultKind: 'API' }), + EntityTable.columns.createSystemColumn(), + EntityTable.columns.createOwnerColumn(), + EntityTable.columns.createSpecLifecycleColumn(), + createSpecApiTypeColumn(), + EntityTable.columns.createMetadataDescriptionColumn(), +]; diff --git a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx index ed987b33f5..a35c10b5cc 100644 --- a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx +++ b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.tsx @@ -27,11 +27,10 @@ import { WarningPanel, } from '@backstage/core'; import { - ComponentsTable, + EntityTable, useEntity, useRelatedEntities, } from '@backstage/plugin-catalog-react'; - import React, { PropsWithChildren } from 'react'; const ComponentsCard = ({ @@ -78,7 +77,7 @@ export const ConsumingComponentsCard = ({ variant = 'gridItem' }: Props) => { } return ( - { } + columns={EntityTable.componentEntityColumns} entities={entities as ComponentEntity[]} /> ); diff --git a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx index 39e179dd10..04239085f0 100644 --- a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx +++ b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.tsx @@ -27,7 +27,7 @@ import { WarningPanel, } from '@backstage/core'; import { - ComponentsTable, + EntityTable, useEntity, useRelatedEntities, } from '@backstage/plugin-catalog-react'; @@ -77,7 +77,7 @@ export const ProvidingComponentsCard = ({ variant = 'gridItem' }: Props) => { } return ( - { } + columns={EntityTable.componentEntityColumns} entities={entities as ComponentEntity[]} /> ); diff --git a/plugins/catalog-react/src/components/EntityTables/SystemsTable.test.tsx b/plugins/catalog-react/src/components/EntityTable/EntityTable.test.tsx similarity index 53% rename from plugins/catalog-react/src/components/EntityTables/SystemsTable.test.tsx rename to plugins/catalog-react/src/components/EntityTable/EntityTable.test.tsx index 9f89d7592e..96f9b4cdbd 100644 --- a/plugins/catalog-react/src/components/EntityTables/SystemsTable.test.tsx +++ b/plugins/catalog-react/src/components/EntityTable/EntityTable.test.tsx @@ -15,6 +15,7 @@ */ import { + Entity, RELATION_OWNED_BY, RELATION_PART_OF, SystemEntity, @@ -22,70 +23,51 @@ import { import { renderInTestApp } from '@backstage/test-utils'; import { waitFor } from '@testing-library/react'; import React from 'react'; -import { SystemsTable } from './SystemsTable'; +import { EntityTable } from './EntityTable'; -describe('', () => { +describe('', () => { it('shows empty table', async () => { const { getByText } = await renderInTestApp( - EMPTY} + columns={[]} />, ); - expect(getByText('My Systems')).toBeInTheDocument(); + expect(getByText('Entities')).toBeInTheDocument(); expect(getByText('EMPTY')).toBeInTheDocument(); }); - it('shows systems', async () => { - const entities: SystemEntity[] = [ + it('shows entities', async () => { + const entities: Entity[] = [ { apiVersion: 'backstage.io/v1alpha1', kind: 'System', metadata: { - name: 'my-system', - namespace: 'my-namespace', - description: 'Some description', + name: 'my-entity', }, - spec: { - owner: 'owner-data', - }, - relations: [ - { - type: RELATION_PART_OF, - target: { - kind: 'Domain', - name: 'my-domain', - namespace: 'my-namespace', - }, - }, - { - type: RELATION_OWNED_BY, - target: { - kind: 'Group', - name: 'Test', - namespace: 'default', - }, - }, - ], + spec: {}, }, ]; const { getByText } = await renderInTestApp( - EMPTY} + columns={[ + { + title: 'Name', + field: 'metadata.name', + }, + ]} />, ); await waitFor(() => { - expect(getByText('My Systems')).toBeInTheDocument(); - expect(getByText('my-namespace/my-system')).toBeInTheDocument(); - expect(getByText('my-namespace/my-domain')).toBeInTheDocument(); - expect(getByText('Test')).toBeInTheDocument(); - expect(getByText('Some description')).toBeInTheDocument(); + expect(getByText('my-entity')).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog-react/src/components/EntityTable/EntityTable.tsx b/plugins/catalog-react/src/components/EntityTable/EntityTable.tsx index a96f10fe2b..18de1ec9ab 100644 --- a/plugins/catalog-react/src/components/EntityTable/EntityTable.tsx +++ b/plugins/catalog-react/src/components/EntityTable/EntityTable.tsx @@ -19,7 +19,7 @@ import { Table, TableColumn } from '@backstage/core'; import { makeStyles } from '@material-ui/core'; import React from 'react'; import * as columnFactories from './columns'; -import { systemEntityColumns } from './presets'; +import { componentEntityColumns, systemEntityColumns } from './presets'; type Props = { title: string; @@ -77,3 +77,5 @@ export function EntityTable({ EntityTable.columns = columnFactories; EntityTable.systemEntityColumns = systemEntityColumns; + +EntityTable.componentEntityColumns = componentEntityColumns; diff --git a/plugins/catalog-react/src/components/EntityTable/columns.tsx b/plugins/catalog-react/src/components/EntityTable/columns.tsx index 011ba66d59..01ed288c13 100644 --- a/plugins/catalog-react/src/components/EntityTable/columns.tsx +++ b/plugins/catalog-react/src/components/EntityTable/columns.tsx @@ -16,6 +16,7 @@ import { Entity, + EntityName, RELATION_OWNED_BY, RELATION_PART_OF, } from '@backstage/catalog-model'; @@ -43,11 +44,17 @@ export function createEntityRefColumn({ title: 'Name', highlight: true, customFilterAndSearch(filter, entity) { - // TODO: We could implement this more efficiently, like searching over each field individually, but that migth confuse the user + // TODO: We could implement this more efficiently, like searching over + // each field that is displayed individually (kind, namespace, name). + // but that migth confuse the user as it will behave different than a + // simple text search. + // Another altnerative would be to cache the values. But writing them + // into the entity feels bad too. return formatContent(entity).includes(filter); }, customSort(entity1, entity2) { // TODO: We could implement this more efficiently by comparing field by field. + // This has similar issues as above. return formatContent(entity1).localeCompare(formatContent(entity2)); }, render: entity => ( @@ -56,16 +63,29 @@ export function createEntityRefColumn({ }; } -export function createOwnerColumn(): TableColumn { +export function createEntityRelationColumn({ + title, + relation, + defaultKind, + filter: entityFilter, +}: { + title: string; + relation: string; + defaultKind?: string; + filter?: { kind: string }; +}): TableColumn { + function getRelations(entity: T): EntityName[] { + return getEntityRelations(entity, relation, entityFilter); + } + function formatContent(entity: T): string { - const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); - return ownedByRelations - .map(r => formatEntityRefTitle(r, { defaultKind: 'group' })) + return getRelations(entity) + .map(r => formatEntityRefTitle(r, { defaultKind })) .join(', '); } return { - title: 'Owner', + title, customFilterAndSearch(filter, entity) { return formatContent(entity).includes(filter); }, @@ -73,48 +93,44 @@ export function createOwnerColumn(): TableColumn { return formatContent(entity1).localeCompare(formatContent(entity2)); }, render: entity => { - const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); return ( - + ); }, }; } -export function createDomainColumn(): TableColumn { - function formatContent(entity: T): string { - const partOfDomainRelations = getEntityRelations(entity, RELATION_PART_OF, { - kind: 'domain', - }); - return partOfDomainRelations - .map(r => formatEntityRefTitle(r, { defaultKind: 'domain' })) - .join(', '); - } +export function createOwnerColumn(): TableColumn { + return createEntityRelationColumn({ + title: 'Owner', + relation: RELATION_OWNED_BY, + defaultKind: 'group', + }); +} - return { +export function createDomainColumn(): TableColumn { + return createEntityRelationColumn({ title: 'Domain', - customFilterAndSearch(filter, entity) { - return formatContent(entity).includes(filter); + relation: RELATION_PART_OF, + defaultKind: 'domain', + filter: { + kind: 'domain', }, - customSort(entity1, entity2) { - return formatContent(entity1).localeCompare(formatContent(entity2)); + }); +} + +export function createSystemColumn(): TableColumn { + return createEntityRelationColumn({ + title: 'System', + relation: RELATION_PART_OF, + defaultKind: 'system', + filter: { + kind: 'system', }, - render: entity => { - const partOfDomainRelations = getEntityRelations( - entity, - RELATION_PART_OF, - { - kind: 'domain', - }, - ); - return ( - - ); - }, - }; + }); } export function createMetadataDescriptionColumn< @@ -126,3 +142,17 @@ export function createMetadataDescriptionColumn< width: 'auto', }; } + +export function createSpecLifecycleColumn(): TableColumn { + return { + title: 'Lifecycle', + field: 'spec.lifecycle', + }; +} + +export function createSpecTypeColumn(): TableColumn { + return { + title: 'Type', + field: 'spec.type', + }; +} diff --git a/plugins/catalog-react/src/components/EntityTables/ComponentsTable.test.tsx b/plugins/catalog-react/src/components/EntityTable/presets.test.tsx similarity index 61% rename from plugins/catalog-react/src/components/EntityTables/ComponentsTable.test.tsx rename to plugins/catalog-react/src/components/EntityTable/presets.test.tsx index f29d1cfe60..3edce87dac 100644 --- a/plugins/catalog-react/src/components/EntityTables/ComponentsTable.test.tsx +++ b/plugins/catalog-react/src/components/EntityTable/presets.test.tsx @@ -18,26 +18,68 @@ import { ComponentEntity, RELATION_OWNED_BY, RELATION_PART_OF, + SystemEntity, } from '@backstage/catalog-model'; import { renderInTestApp } from '@backstage/test-utils'; import { waitFor } from '@testing-library/react'; import React from 'react'; -import { ComponentsTable } from './ComponentsTable'; +import { EntityTable } from './EntityTable'; +import { componentEntityColumns, systemEntityColumns } from './presets'; + +describe('systemEntityColumns', () => { + it('shows systems', async () => { + const entities: SystemEntity[] = [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'System', + metadata: { + name: 'my-system', + namespace: 'my-namespace', + description: 'Some description', + }, + spec: { + owner: 'owner-data', + }, + relations: [ + { + type: RELATION_PART_OF, + target: { + kind: 'Domain', + name: 'my-domain', + namespace: 'my-namespace', + }, + }, + { + type: RELATION_OWNED_BY, + target: { + kind: 'Group', + name: 'Test', + namespace: 'default', + }, + }, + ], + }, + ]; -describe('', () => { - it('shows empty table', async () => { const { getByText } = await renderInTestApp( - EMPTY} + columns={systemEntityColumns} />, ); - expect(getByText('My Components')).toBeInTheDocument(); - expect(getByText('EMPTY')).toBeInTheDocument(); + await waitFor(() => { + expect(getByText('my-namespace/my-system')).toBeInTheDocument(); + expect(getByText('my-namespace/my-domain')).toBeInTheDocument(); + expect(getByText('Test')).toBeInTheDocument(); + expect(getByText('Some description')).toBeInTheDocument(); + }); }); +}); +describe('componentEntityColumns', () => { it('shows components', async () => { const entities: ComponentEntity[] = [ { @@ -75,15 +117,15 @@ describe('', () => { ]; const { getByText } = await renderInTestApp( - EMPTY} + columns={componentEntityColumns} />, ); await waitFor(() => { - expect(getByText('My Components')).toBeInTheDocument(); expect(getByText('my-namespace/my-component')).toBeInTheDocument(); expect(getByText('my-namespace/my-system')).toBeInTheDocument(); expect(getByText('Test')).toBeInTheDocument(); diff --git a/plugins/catalog-react/src/components/EntityTable/presets.tsx b/plugins/catalog-react/src/components/EntityTable/presets.tsx index e898185e5e..7ce6b75532 100644 --- a/plugins/catalog-react/src/components/EntityTable/presets.tsx +++ b/plugins/catalog-react/src/components/EntityTable/presets.tsx @@ -14,18 +14,30 @@ * limitations under the License. */ -import { SystemEntity } from '@backstage/catalog-model'; +import { ComponentEntity, SystemEntity } from '@backstage/catalog-model'; import { TableColumn } from '@backstage/core'; import { + createDomainColumn, createEntityRefColumn, createMetadataDescriptionColumn, createOwnerColumn, - createDomainColumn, + createSpecLifecycleColumn, + createSpecTypeColumn, + createSystemColumn, } from './columns'; export const systemEntityColumns: TableColumn[] = [ createEntityRefColumn({ defaultKind: 'system' }), - createOwnerColumn(), createDomainColumn(), + createOwnerColumn(), + createMetadataDescriptionColumn(), +]; + +export const componentEntityColumns: TableColumn[] = [ + createEntityRefColumn({ defaultKind: 'component' }), + createSystemColumn(), + createOwnerColumn(), + createSpecTypeColumn(), + createSpecLifecycleColumn(), createMetadataDescriptionColumn(), ]; diff --git a/plugins/catalog-react/src/components/EntityTables/ComponentsTable.tsx b/plugins/catalog-react/src/components/EntityTables/ComponentsTable.tsx deleted file mode 100644 index b79c7e705a..0000000000 --- a/plugins/catalog-react/src/components/EntityTables/ComponentsTable.tsx +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - ComponentEntity, - EntityName, - RELATION_OWNED_BY, - RELATION_PART_OF, -} from '@backstage/catalog-model'; -import { Table, TableColumn } from '@backstage/core'; -import { makeStyles } from '@material-ui/core'; -import React from 'react'; -import { getEntityRelations } from '../../utils'; -import { - EntityRefLink, - EntityRefLinks, - formatEntityRefTitle, -} from '../EntityRefLink'; - -type EntityRow = { - entity: ComponentEntity; - resolved: { - name: string; - partOfSystemRelationTitle?: string; - partOfSystemRelations: EntityName[]; - ownedByRelationsTitle?: string; - ownedByRelations: EntityName[]; - }; -}; - -const columns: TableColumn[] = [ - { - title: 'Name', - field: 'resolved.name', - highlight: true, - render: ({ entity }) => ( - - ), - }, - { - title: 'System', - field: 'resolved.partOfSystemRelationTitle', - render: ({ resolved }) => ( - - ), - }, - { - title: 'Owner', - field: 'resolved.ownedByRelationsTitle', - render: ({ resolved }) => ( - - ), - }, - { - title: 'Lifecycle', - field: 'entity.spec.lifecycle', - }, - { - title: 'Type', - field: 'entity.spec.type', - }, - { - title: 'Description', - field: 'entity.metadata.description', - width: 'auto', - }, -]; - -type Props = { - title: string; - variant?: string; - entities: ComponentEntity[]; - emptyComponent?: JSX.Element; -}; - -const useStyles = makeStyles(theme => ({ - empty: { - padding: theme.spacing(2), - display: 'flex', - justifyContent: 'center', - }, -})); - -export const ComponentsTable = ({ - entities, - title, - emptyComponent, - variant = 'gridItem', -}: Props) => { - const classes = useStyles(); - const tableStyle: React.CSSProperties = { - minWidth: '0', - width: '100%', - }; - - if (variant === 'gridItem') { - tableStyle.height = 'calc(100% - 10px)'; - } - - const rows = entities.map(entity => { - const partOfSystemRelations = getEntityRelations(entity, RELATION_PART_OF, { - kind: 'system', - }); - const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); - - return { - entity: entity, - resolved: { - name: formatEntityRefTitle(entity, { - defaultKind: 'Component', - }), - ownedByRelationsTitle: ownedByRelations - .map(r => formatEntityRefTitle(r, { defaultKind: 'group' })) - .join(', '), - ownedByRelations, - partOfSystemRelationTitle: partOfSystemRelations - .map(r => - formatEntityRefTitle(r, { - defaultKind: 'system', - }), - ) - .join(', '), - partOfSystemRelations, - }, - }; - }); - - return ( - - columns={columns} - title={title} - style={tableStyle} - emptyComponent={ - emptyComponent &&
{emptyComponent}
- } - options={{ - // TODO: Toolbar padding if off compared to other cards, should be: padding: 16px 24px; - search: false, - paging: false, - actionsColumnIndex: -1, - padding: 'dense', - }} - data={rows} - /> - ); -}; diff --git a/plugins/catalog-react/src/components/EntityTables/SystemsTable.tsx b/plugins/catalog-react/src/components/EntityTables/SystemsTable.tsx deleted file mode 100644 index 8f30ea0543..0000000000 --- a/plugins/catalog-react/src/components/EntityTables/SystemsTable.tsx +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - EntityName, - RELATION_OWNED_BY, - RELATION_PART_OF, - SystemEntity, -} from '@backstage/catalog-model'; -import { Table, TableColumn } from '@backstage/core'; -import { makeStyles } from '@material-ui/core'; -import React from 'react'; -import { getEntityRelations } from '../../utils'; -import { - EntityRefLink, - EntityRefLinks, - formatEntityRefTitle, -} from '../EntityRefLink'; - -type EntityRow = { - entity: SystemEntity; - resolved: { - name: string; - partOfDomainRelationTitle?: string; - partOfDomainRelations: EntityName[]; - ownedByRelationsTitle?: string; - ownedByRelations: EntityName[]; - }; -}; - -const columns: TableColumn[] = [ - { - title: 'Name', - field: 'resolved.name', - highlight: true, - render: ({ entity }) => ( - - ), - }, - { - title: 'Domain', - field: 'resolved.partOfDomainRelationTitle', - render: ({ resolved }) => ( - - ), - }, - { - title: 'Owner', - field: 'resolved.ownedByRelationsTitle', - render: ({ resolved }) => ( - - ), - }, - { - title: 'Description', - field: 'entity.metadata.description', - width: 'auto', - }, -]; - -type Props = { - title: string; - variant?: string; - entities: SystemEntity[]; - emptyComponent?: JSX.Element; -}; - -const useStyles = makeStyles(theme => ({ - empty: { - padding: theme.spacing(2), - display: 'flex', - justifyContent: 'center', - }, -})); - -export const SystemsTable = ({ - entities, - title, - emptyComponent, - variant = 'gridItem', -}: Props) => { - const classes = useStyles(); - const tableStyle: React.CSSProperties = { - minWidth: '0', - width: '100%', - }; - - if (variant === 'gridItem') { - tableStyle.height = 'calc(100% - 10px)'; - } - - const rows = entities.map(entity => { - const partOfDomainRelations = getEntityRelations(entity, RELATION_PART_OF, { - kind: 'domain', - }); - const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); - - return { - entity, - resolved: { - name: formatEntityRefTitle(entity, { - defaultKind: 'System', - }), - ownedByRelationsTitle: ownedByRelations - .map(r => formatEntityRefTitle(r, { defaultKind: 'group' })) - .join(', '), - ownedByRelations, - partOfDomainRelationTitle: partOfDomainRelations - .map(r => - formatEntityRefTitle(r, { - defaultKind: 'domain', - }), - ) - .join(', '), - partOfDomainRelations, - }, - }; - }); - - return ( - - columns={columns} - title={title} - style={tableStyle} - emptyComponent={ - emptyComponent &&
{emptyComponent}
- } - options={{ - // TODO: Toolbar padding if off compared to other cards, should be: padding: 16px 24px; - search: false, - paging: false, - actionsColumnIndex: -1, - padding: 'dense', - }} - data={rows} - /> - ); -}; diff --git a/plugins/catalog-react/src/components/EntityTables/index.ts b/plugins/catalog-react/src/components/EntityTables/index.ts deleted file mode 100644 index f3e4f416ac..0000000000 --- a/plugins/catalog-react/src/components/EntityTables/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export { ComponentsTable } from './ComponentsTable'; -export { SystemsTable } from './SystemsTable'; diff --git a/plugins/catalog-react/src/components/index.ts b/plugins/catalog-react/src/components/index.ts index 8508935dd6..5181b8f0ea 100644 --- a/plugins/catalog-react/src/components/index.ts +++ b/plugins/catalog-react/src/components/index.ts @@ -16,4 +16,3 @@ export * from './EntityProvider'; export * from './EntityRefLink'; export * from './EntityTable'; -export * from './EntityTables'; diff --git a/plugins/catalog/src/components/ComponentsCard/HasComponentsCard.tsx b/plugins/catalog/src/components/ComponentsCard/HasComponentsCard.tsx index c9b5879a05..c2652f8cff 100644 --- a/plugins/catalog/src/components/ComponentsCard/HasComponentsCard.tsx +++ b/plugins/catalog/src/components/ComponentsCard/HasComponentsCard.tsx @@ -23,7 +23,7 @@ import { WarningPanel, } from '@backstage/core'; import { - ComponentsTable, + EntityTable, useEntity, useRelatedEntities, } from '@backstage/plugin-catalog-react'; @@ -72,7 +72,7 @@ export const HasComponentsCard = ({ variant = 'gridItem' }: Props) => { } return ( - { } + columns={EntityTable.componentEntityColumns} entities={entities as ComponentEntity[]} /> ); diff --git a/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.tsx b/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.tsx index 541ccf795d..6bc555efc2 100644 --- a/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.tsx +++ b/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.tsx @@ -23,7 +23,7 @@ import { WarningPanel, } from '@backstage/core'; import { - ComponentsTable, + EntityTable, useEntity, useRelatedEntities, } from '@backstage/plugin-catalog-react'; @@ -72,7 +72,7 @@ export const HasSubcomponentsCard = ({ variant = 'gridItem' }: Props) => { } return ( - { } + columns={EntityTable.componentEntityColumns} entities={entities as ComponentEntity[]} /> );