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';