diff --git a/.changeset/weak-jeans-live.md b/.changeset/weak-jeans-live.md index 041021b052..0d25f07cca 100644 --- a/.changeset/weak-jeans-live.md +++ b/.changeset/weak-jeans-live.md @@ -2,7 +2,11 @@ '@backstage/plugin-catalog': patch --- -- Added `EntityHasResourcesCard` to display resources that are part of a system. -- Added `EntityDependsOnComponentsCard` to display components that are dependencies of a component. -- Added `EntityDependsOnResourcesCard` to display resources that are dependencies of a component. +- Added `RelatedEntitesCard` as a base implementation of displaying entities that are related to another entity. +- Added `HasResourcesCard` to display resources that are part of a system. +- Added `DependsOnComponentsCard` to display components that are dependencies of a component. +- Added `DependsOnResourcesCard` to display resources that are dependencies of a component. +- Refactored `HasComponentsCard` to use base `RelatedEntitiesCard`. Card remains backwards compatible. +- Refactored `HasSubcomponentsCard` to use base `RelatedEntitiesCard`. Card remains backwards compatible. +- Refactored `HasSystemsCard` to use base `RelatedEntitiesCard`. Card remains backwards compatible. - Updated the example app to take advantage of these new components. diff --git a/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.tsx b/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.tsx index 18c6173e17..41b4117792 100644 --- a/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.tsx +++ b/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.tsx @@ -14,79 +14,30 @@ * limitations under the License. */ -import { ComponentEntity, RELATION_DEPENDS_ON } from '@backstage/catalog-model'; -import { - CodeSnippet, - InfoCard, - Link, - Progress, - WarningPanel, -} from '@backstage/core'; -import { Typography } from '@material-ui/core'; -import { - EntityTable, - useEntity, - useRelatedEntities, -} from '@backstage/plugin-catalog-react'; +import { RELATION_DEPENDS_ON } from '@backstage/catalog-model'; import React from 'react'; +import { + asComponentEntities, + componentColumns, + componentHelpLink, + RelatedEntitiesCard, +} from '../RelatedEntitiesCard'; type Props = { variant?: 'gridItem'; }; -const columns = [ - EntityTable.columns.createEntityRefColumn({ defaultKind: 'component' }), - EntityTable.columns.createOwnerColumn(), - EntityTable.columns.createSpecTypeColumn(), - EntityTable.columns.createSpecLifecycleColumn(), - EntityTable.columns.createMetadataDescriptionColumn(), -]; - export const DependsOnComponentsCard = ({ variant = 'gridItem' }: Props) => { - const { entity } = useEntity(); - const { entities, loading, error } = useRelatedEntities(entity, { - type: RELATION_DEPENDS_ON, - kind: 'Component', - }); - - if (loading) { - return ( - - - - ); - } - - if (error || !entities) { - return ( - - } - /> - - ); - } - return ( - - - No component is a dependency of this component. - - - - Learn how to change this. - - - - } - columns={columns} - entities={entities as ComponentEntity[]} + title="Components" + entityKind="Component" + relationType={RELATION_DEPENDS_ON} + columns={componentColumns} + emptyMessage="No component is a dependency of this component" + emptyHelpLink={componentHelpLink} + asRenderableEntities={asComponentEntities} /> ); }; diff --git a/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.tsx b/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.tsx index 798a579b9a..7658af1456 100644 --- a/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.tsx +++ b/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.tsx @@ -14,79 +14,30 @@ * limitations under the License. */ -import { ComponentEntity, RELATION_DEPENDS_ON } from '@backstage/catalog-model'; -import { - CodeSnippet, - InfoCard, - Link, - Progress, - WarningPanel, -} from '@backstage/core'; -import { Typography } from '@material-ui/core'; -import { - EntityTable, - useEntity, - useRelatedEntities, -} from '@backstage/plugin-catalog-react'; +import { RELATION_DEPENDS_ON } from '@backstage/catalog-model'; import React from 'react'; +import { + asResourceEntities, + componentHelpLink, + RelatedEntitiesCard, + resourceColumns, +} from '../RelatedEntitiesCard'; type Props = { variant?: 'gridItem'; }; -const columns = [ - EntityTable.columns.createEntityRefColumn({ defaultKind: 'resource' }), - EntityTable.columns.createOwnerColumn(), - EntityTable.columns.createSpecTypeColumn(), - EntityTable.columns.createSpecLifecycleColumn(), - EntityTable.columns.createMetadataDescriptionColumn(), -]; - export const DependsOnResourcesCard = ({ variant = 'gridItem' }: Props) => { - const { entity } = useEntity(); - const { entities, loading, error } = useRelatedEntities(entity, { - type: RELATION_DEPENDS_ON, - kind: 'Resource', - }); - - if (loading) { - return ( - - - - ); - } - - if (error || !entities) { - return ( - - } - /> - - ); - } - return ( - - - No resource is a dependency of this component. - - - - Learn how to change this. - - - - } - columns={columns} - entities={entities as ComponentEntity[]} + title="Resources" + entityKind="Resource" + relationType={RELATION_DEPENDS_ON} + columns={resourceColumns} + emptyMessage="No resource is a dependency of this component" + emptyHelpLink={componentHelpLink} + asRenderableEntities={asResourceEntities} /> ); }; diff --git a/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.tsx b/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.tsx index b6eae81f30..2f3a0572d2 100644 --- a/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.tsx +++ b/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.tsx @@ -14,79 +14,30 @@ * limitations under the License. */ -import { ComponentEntity, RELATION_HAS_PART } from '@backstage/catalog-model'; -import { - CodeSnippet, - InfoCard, - Link, - Progress, - WarningPanel, -} from '@backstage/core'; -import { Typography } from '@material-ui/core'; -import { - EntityTable, - useEntity, - useRelatedEntities, -} from '@backstage/plugin-catalog-react'; +import { RELATION_HAS_PART } from '@backstage/catalog-model'; import React from 'react'; +import { + asComponentEntities, + componentColumns, + componentHelpLink, + RelatedEntitiesCard, +} from '../RelatedEntitiesCard'; type Props = { variant?: 'gridItem'; }; -const columns = [ - EntityTable.columns.createEntityRefColumn({ defaultKind: 'component' }), - EntityTable.columns.createOwnerColumn(), - EntityTable.columns.createSpecTypeColumn(), - EntityTable.columns.createSpecLifecycleColumn(), - EntityTable.columns.createMetadataDescriptionColumn(), -]; - export const HasComponentsCard = ({ variant = 'gridItem' }: Props) => { - const { entity } = useEntity(); - const { entities, loading, error } = useRelatedEntities(entity, { - type: RELATION_HAS_PART, - kind: 'Component', - }); - - if (loading) { - return ( - - - - ); - } - - if (error || !entities) { - return ( - - } - /> - - ); - } - return ( - - - No component is part of this system. - - - - Learn how to change this. - - - - } - columns={columns} - entities={entities as ComponentEntity[]} + title="Components" + entityKind="Component" + relationType={RELATION_HAS_PART} + columns={componentColumns} + emptyMessage="No component is part of this system" + emptyHelpLink={componentHelpLink} + asRenderableEntities={asComponentEntities} /> ); }; diff --git a/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.tsx b/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.tsx index ad53d9ce7f..6d33dcb419 100644 --- a/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.tsx +++ b/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.tsx @@ -14,79 +14,30 @@ * limitations under the License. */ -import { ResourceEntity, RELATION_HAS_PART } from '@backstage/catalog-model'; -import { - CodeSnippet, - InfoCard, - Link, - Progress, - WarningPanel, -} from '@backstage/core'; -import { Typography } from '@material-ui/core'; -import { - EntityTable, - useEntity, - useRelatedEntities, -} from '@backstage/plugin-catalog-react'; +import { RELATION_HAS_PART } from '@backstage/catalog-model'; import React from 'react'; +import { + asResourceEntities, + RelatedEntitiesCard, + resourceColumns, + resourceHelpLink, +} from '../RelatedEntitiesCard'; type Props = { variant?: 'gridItem'; }; -const columns = [ - EntityTable.columns.createEntityRefColumn({ defaultKind: 'resource' }), - EntityTable.columns.createOwnerColumn(), - EntityTable.columns.createSpecTypeColumn(), - EntityTable.columns.createSpecLifecycleColumn(), - EntityTable.columns.createMetadataDescriptionColumn(), -]; - export const HasResourcesCard = ({ variant = 'gridItem' }: Props) => { - const { entity } = useEntity(); - const { entities, loading, error } = useRelatedEntities(entity, { - type: RELATION_HAS_PART, - kind: 'Resource', - }); - - if (loading) { - return ( - - - - ); - } - - if (error || !entities) { - return ( - - } - /> - - ); - } - return ( - - - No resource is part of this system. - - - - Learn how to change this. - - - - } - columns={columns} - entities={entities as ResourceEntity[]} + title="Resources" + entityKind="Resource" + relationType={RELATION_HAS_PART} + columns={resourceColumns} + asRenderableEntities={asResourceEntities} + emptyMessage="No resource is part of this system" + emptyHelpLink={resourceHelpLink} /> ); }; diff --git a/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.tsx b/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.tsx index 41244ba3d4..5613c6a9cc 100644 --- a/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.tsx +++ b/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.tsx @@ -14,79 +14,29 @@ * limitations under the License. */ -import { ComponentEntity, RELATION_HAS_PART } from '@backstage/catalog-model'; -import { - CodeSnippet, - InfoCard, - Link, - Progress, - WarningPanel, -} from '@backstage/core'; -import { Typography } from '@material-ui/core'; -import { - EntityTable, - useEntity, - useRelatedEntities, -} from '@backstage/plugin-catalog-react'; +import { RELATION_HAS_PART } from '@backstage/catalog-model'; import React from 'react'; +import { + asComponentEntities, + componentColumns, + RelatedEntitiesCard, +} from '../RelatedEntitiesCard'; type Props = { variant?: 'gridItem'; }; -const columns = [ - EntityTable.columns.createEntityRefColumn({ defaultKind: 'component' }), - EntityTable.columns.createOwnerColumn(), - EntityTable.columns.createSpecTypeColumn(), - EntityTable.columns.createSpecLifecycleColumn(), - EntityTable.columns.createMetadataDescriptionColumn(), -]; - export const HasSubcomponentsCard = ({ variant = 'gridItem' }: Props) => { - const { entity } = useEntity(); - const { entities, loading, error } = useRelatedEntities(entity, { - type: RELATION_HAS_PART, - kind: 'Component', - }); - - if (loading) { - return ( - - - - ); - } - - if (error || !entities) { - return ( - - } - /> - - ); - } - return ( - - - No subcomponent is part of this component. - - - - Learn how to change this. - - - - } - columns={columns} - entities={entities as ComponentEntity[]} + title="Subcomponents" + entityKind="Component" + relationType={RELATION_HAS_PART} + columns={componentColumns} + asRenderableEntities={asComponentEntities} + emptyMessage="No subcomponent is part of this component" + emptyHelpLink="https://backstage.io/docs/features/software-catalog/descriptor-format#specsubcomponentof-optional" /> ); }; diff --git a/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.tsx b/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.tsx index ea4de474de..1ddb8cad5c 100644 --- a/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.tsx +++ b/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.tsx @@ -14,76 +14,30 @@ * limitations under the License. */ -import { RELATION_HAS_PART, SystemEntity } from '@backstage/catalog-model'; -import { - CodeSnippet, - InfoCard, - Link, - Progress, - WarningPanel, -} from '@backstage/core'; -import { Typography } from '@material-ui/core'; -import { - EntityTable, - useEntity, - useRelatedEntities, -} from '@backstage/plugin-catalog-react'; +import { RELATION_HAS_PART } from '@backstage/catalog-model'; import React from 'react'; +import { + asSystemEntities, + RelatedEntitiesCard, + systemColumns, + systemHelpLink, +} from '../RelatedEntitiesCard'; type Props = { variant?: 'gridItem'; }; -const columns = [ - EntityTable.columns.createEntityRefColumn({ defaultKind: 'system' }), - EntityTable.columns.createOwnerColumn(), - EntityTable.columns.createMetadataDescriptionColumn(), -]; - export const HasSystemsCard = ({ variant = 'gridItem' }: Props) => { - const { entity } = useEntity(); - const { entities, loading, error } = useRelatedEntities(entity, { - type: RELATION_HAS_PART, - }); - - if (loading) { - return ( - - - - ); - } - - if (error || !entities) { - return ( - - } - /> - - ); - } - return ( - - - No system is part of this domain. - - - - Learn how to change this. - - - - } - columns={columns} - entities={entities as SystemEntity[]} + title="Systems" + entityKind="System" + relationType={RELATION_HAS_PART} + columns={systemColumns} + asRenderableEntities={asSystemEntities} + emptyMessage="No system is part of this domain" + emptyHelpLink={systemHelpLink} /> ); }; diff --git a/plugins/catalog/src/components/RelatedEntitiesCard/RelatedEntitiesCard.tsx b/plugins/catalog/src/components/RelatedEntitiesCard/RelatedEntitiesCard.tsx new file mode 100644 index 0000000000..6405faad17 --- /dev/null +++ b/plugins/catalog/src/components/RelatedEntitiesCard/RelatedEntitiesCard.tsx @@ -0,0 +1,97 @@ +/* + * 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 { Entity } from '@backstage/catalog-model'; +import { + CodeSnippet, + InfoCard, + Link, + Progress, + TableColumn, + WarningPanel, +} from '@backstage/core'; +import { Typography } from '@material-ui/core'; +import { + EntityTable, + useEntity, + useRelatedEntities, +} from '@backstage/plugin-catalog-react'; +import React from 'react'; + +type Props = { + variant?: 'gridItem'; + title: string; + columns: TableColumn[]; + entityKind: string; + relationType: string; + emptyMessage: string; + emptyHelpLink: string; + asRenderableEntities: (entities: Entity[]) => Entity[]; +}; + +export const RelatedEntitiesCard = ({ + variant = 'gridItem', + title, + columns, + entityKind, + relationType, + emptyMessage, + emptyHelpLink, + asRenderableEntities, +}: Props) => { + const { entity } = useEntity(); + const { entities, loading, error } = useRelatedEntities(entity, { + type: relationType, + kind: entityKind, + }); + + if (loading) { + return ( + + + + ); + } + + if (error || !entities) { + return ( + + } + /> + + ); + } + + return ( + + {emptyMessage} + + Learn how to change this. + + + } + columns={columns} + entities={asRenderableEntities(entities)} + /> + ); +}; diff --git a/plugins/catalog/src/components/RelatedEntitiesCard/constants.ts b/plugins/catalog/src/components/RelatedEntitiesCard/constants.ts new file mode 100644 index 0000000000..78e11edd9b --- /dev/null +++ b/plugins/catalog/src/components/RelatedEntitiesCard/constants.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2021 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, + Entity, + ResourceEntity, + SystemEntity, +} from '@backstage/catalog-model'; +import { EntityTable } from '@backstage/plugin-catalog-react'; + +export const componentColumns = [ + EntityTable.columns.createEntityRefColumn({ defaultKind: 'component' }), + EntityTable.columns.createOwnerColumn(), + EntityTable.columns.createSpecTypeColumn(), + EntityTable.columns.createSpecLifecycleColumn(), + EntityTable.columns.createMetadataDescriptionColumn(), +]; +export const componentHelpLink = + 'https://backstage.io/docs/features/software-catalog/descriptor-format#kind-component'; +export const asComponentEntities = (entities: Entity[]) => + entities as ComponentEntity[]; + +export const resourceColumns = [ + EntityTable.columns.createEntityRefColumn({ defaultKind: 'resource' }), + EntityTable.columns.createOwnerColumn(), + EntityTable.columns.createSpecTypeColumn(), + EntityTable.columns.createSpecLifecycleColumn(), + EntityTable.columns.createMetadataDescriptionColumn(), +]; +export const resourceHelpLink = + 'https://backstage.io/docs/features/software-catalog/descriptor-format#kind-resource'; +export const asResourceEntities = (entities: Entity[]) => + entities as ResourceEntity[]; + +export const systemColumns = [ + EntityTable.columns.createEntityRefColumn({ defaultKind: 'system' }), + EntityTable.columns.createOwnerColumn(), + EntityTable.columns.createMetadataDescriptionColumn(), +]; +export const systemHelpLink = + 'https://backstage.io/docs/features/software-catalog/descriptor-format#kind-system'; +export const asSystemEntities = (entities: Entity[]) => + entities as SystemEntity[]; diff --git a/plugins/catalog/src/components/RelatedEntitiesCard/index.ts b/plugins/catalog/src/components/RelatedEntitiesCard/index.ts new file mode 100644 index 0000000000..b2cb93c5cf --- /dev/null +++ b/plugins/catalog/src/components/RelatedEntitiesCard/index.ts @@ -0,0 +1,18 @@ +/* + * 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 { RelatedEntitiesCard } from './RelatedEntitiesCard'; +export * from './constants';