Merge pull request #6983 from SDA-SE/feat/better-about

Update the `AboutCard` to properly support non-standard entity types and rework the defaults for the build-in kinds
This commit is contained in:
Dominik Henneke
2021-08-30 10:44:13 +02:00
committed by GitHub
7 changed files with 715 additions and 49 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-catalog': patch
---
Update the `AboutCard` to properly support non-standard entity types and rework the defaults for the build-in kinds.
This change also uses `useElementFilter(...)` instead of `React.children.count(...)` in `AboutField` to properly recognize whether children are available.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog': patch
---
Use a `Link` for the edit button on the `AboutCard` instead of doing `window.open(...)`
@@ -15,21 +15,20 @@
*/
import { RELATION_OWNED_BY } from '@backstage/catalog-model';
import {
ApiProvider,
ApiRegistry,
ConfigReader,
} from '@backstage/core-app-api';
import {
ScmIntegrationsApi,
scmIntegrationsApiRef,
} from '@backstage/integration-react';
import { EntityProvider } from '@backstage/plugin-catalog-react';
import { renderInTestApp } from '@backstage/test-utils';
import { act, fireEvent } from '@testing-library/react';
import React from 'react';
import { AboutCard } from './AboutCard';
import {
ApiProvider,
ApiRegistry,
ConfigReader,
} from '@backstage/core-app-api';
import { viewTechDocRouteRef } from '../../routes';
import { AboutCard } from './AboutCard';
describe('<AboutCard />', () => {
it('renders info', async () => {
@@ -166,14 +165,10 @@ describe('<AboutCard />', () => {
</ApiProvider>,
);
const editButton = getByTitle('Edit Metadata');
window.open = jest.fn();
await act(async () => {
fireEvent.click(editButton);
});
expect(window.open).toHaveBeenCalledWith(
`https://github.com/backstage/backstage/edit/master/software.yaml`,
'_blank',
const editLink = getByTitle('Edit Metadata').closest('a');
expect(editLink).toHaveAttribute(
'href',
'https://github.com/backstage/backstage/edit/master/software.yaml',
);
});
@@ -20,6 +20,13 @@ import {
RELATION_CONSUMES_API,
RELATION_PROVIDES_API,
} from '@backstage/catalog-model';
import {
HeaderIconLinkRow,
IconLinkVerticalProps,
InfoCardVariants,
Link,
} from '@backstage/core-components';
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
import {
ScmIntegrationIcon,
scmIntegrationsApiRef,
@@ -42,15 +49,8 @@ import DocsIcon from '@material-ui/icons/Description';
import EditIcon from '@material-ui/icons/Edit';
import ExtensionIcon from '@material-ui/icons/Extension';
import React from 'react';
import { AboutContent } from './AboutContent';
import {
HeaderIconLinkRow,
IconLinkVerticalProps,
InfoCardVariants,
} from '@backstage/core-components';
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
import { viewTechDocRouteRef } from '../../routes';
import { AboutContent } from './AboutContent';
const useStyles = makeStyles({
gridItemCard: {
@@ -148,12 +148,11 @@ export function AboutCard({ variant }: AboutCardProps) {
title="About"
action={
<IconButton
component={Link}
aria-label="Edit"
disabled={!entityMetadataEditUrl}
title="Edit Metadata"
onClick={() => {
window.open(entityMetadataEditUrl ?? '#', '_blank');
}}
to={entityMetadataEditUrl ?? '#'}
>
<EditIcon />
</IconButton>
@@ -0,0 +1,631 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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,
RELATION_OWNED_BY,
RELATION_PART_OF,
} from '@backstage/catalog-model';
import { renderInTestApp } from '@backstage/test-utils';
import React from 'react';
import { AboutContent } from './AboutContent';
describe('<AboutContent />', () => {
describe('An unknown entity', () => {
let entity: Entity;
beforeEach(() => {
entity = {
apiVersion: 'custom.company/v1',
kind: 'Unknown',
metadata: {
name: 'software',
description: 'This is the description',
tags: ['tag-1'],
},
spec: {
owner: 'o',
domain: 'd',
system: 's',
type: 't',
lifecycle: 'l',
},
relations: [
{
type: RELATION_OWNED_BY,
target: {
kind: 'user',
name: 'o',
namespace: 'default',
},
},
{
type: RELATION_PART_OF,
target: {
kind: 'system',
name: 's',
namespace: 'default',
},
},
{
type: RELATION_PART_OF,
target: {
kind: 'domain',
name: 'd',
namespace: 'default',
},
},
],
};
});
it('renders info', async () => {
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('user:o');
expect(getByText('Domain')).toBeInTheDocument();
expect(getByText('Domain').nextSibling).toHaveTextContent('d');
expect(getByText('System')).toBeInTheDocument();
expect(getByText('System').nextSibling).toHaveTextContent('s');
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(getByText('Type')).toBeInTheDocument();
expect(getByText('Type').nextSibling).toHaveTextContent('t');
expect(getByText('Lifecycle')).toBeInTheDocument();
expect(getByText('Lifecycle').nextSibling).toHaveTextContent('l');
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1');
});
it('highlights missing required fields', async () => {
delete entity.metadata.tags;
entity.spec = {};
entity.relations = [];
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner');
expect(queryByText('Domain')).not.toBeInTheDocument();
expect(queryByText('System')).not.toBeInTheDocument();
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(queryByText('Type')).not.toBeInTheDocument();
expect(queryByText('Lifecycle')).not.toBeInTheDocument();
});
});
describe('API entity', () => {
let entity: Entity;
beforeEach(() => {
entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'API',
metadata: {
name: 'software',
description: 'This is the description',
tags: ['tag-1'],
},
spec: {
type: 'openapi',
lifecycle: 'production',
owner: 'guest',
definition: '...',
system: 'system',
},
relations: [
{
type: RELATION_OWNED_BY,
target: {
kind: 'user',
name: 'guest',
namespace: 'default',
},
},
{
type: RELATION_PART_OF,
target: {
kind: 'system',
name: 'system',
namespace: 'default',
},
},
],
};
});
it('renders info', async () => {
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest');
expect(queryByText('Domain')).not.toBeInTheDocument();
expect(getByText('System')).toBeInTheDocument();
expect(getByText('System').nextSibling).toHaveTextContent('system');
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(getByText('Type')).toBeInTheDocument();
expect(getByText('Type').nextSibling).toHaveTextContent('openapi');
expect(getByText('Lifecycle')).toBeInTheDocument();
expect(getByText('Lifecycle').nextSibling).toHaveTextContent(
'production',
);
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1');
});
it('highlights missing required fields', async () => {
delete entity.metadata.tags;
delete entity.spec!.type;
delete entity.spec!.lifecycle;
delete entity.spec!.system;
entity.relations = [];
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner');
expect(queryByText('Domain')).not.toBeInTheDocument();
expect(getByText('System')).toBeInTheDocument();
expect(getByText('System').nextSibling).toHaveTextContent('No System');
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(getByText('Type')).toBeInTheDocument();
expect(getByText('Type').nextSibling).toHaveTextContent('unknown');
expect(getByText('Lifecycle')).toBeInTheDocument();
expect(getByText('Lifecycle').nextSibling).toHaveTextContent('unknown');
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags');
});
});
describe('Component entity', () => {
let entity: Entity;
beforeEach(() => {
entity = {
apiVersion: 'v1',
kind: 'Component',
metadata: {
name: 'software',
description: 'This is the description',
tags: ['tag-1'],
},
spec: {
owner: 'guest',
type: 'service',
lifecycle: 'production',
system: 'system',
subcomponentOf: ['parent-software'],
},
relations: [
{
type: RELATION_OWNED_BY,
target: {
kind: 'user',
name: 'guest',
namespace: 'default',
},
},
{
type: RELATION_PART_OF,
target: {
kind: 'system',
name: 'system',
namespace: 'default',
},
},
{
type: RELATION_PART_OF,
target: {
kind: 'component',
name: 'parent-software',
namespace: 'default',
},
},
],
};
});
it('renders info', async () => {
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest');
expect(queryByText('Domain')).not.toBeInTheDocument();
expect(getByText('System')).toBeInTheDocument();
expect(getByText('System').nextSibling).toHaveTextContent('system');
expect(getByText('Parent Component')).toBeInTheDocument();
expect(getByText('Parent Component').nextSibling).toHaveTextContent(
'parent-software',
);
expect(getByText('Type')).toBeInTheDocument();
expect(getByText('Type').nextSibling).toHaveTextContent('service');
expect(getByText('Lifecycle')).toBeInTheDocument();
expect(getByText('Lifecycle').nextSibling).toHaveTextContent(
'production',
);
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1');
});
it('highlights missing required fields', async () => {
delete entity.metadata.tags;
delete entity.spec!.type;
delete entity.spec!.lifecycle;
delete entity.spec!.system;
entity.relations = [];
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner');
expect(queryByText('Domain')).not.toBeInTheDocument();
expect(getByText('System')).toBeInTheDocument();
expect(getByText('System').nextSibling).toHaveTextContent('No System');
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(getByText('Type')).toBeInTheDocument();
expect(getByText('Type').nextSibling).toHaveTextContent('unknown');
expect(getByText('Lifecycle')).toBeInTheDocument();
expect(getByText('Lifecycle').nextSibling).toHaveTextContent('unknown');
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags');
});
});
describe('Domain entity', () => {
let entity: Entity;
beforeEach(() => {
entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Domain',
metadata: {
name: 'software',
description: 'This is the description',
tags: ['tag-1'],
},
spec: {
owner: 'guest',
},
relations: [
{
type: RELATION_OWNED_BY,
target: {
kind: 'user',
name: 'guest',
namespace: 'default',
},
},
],
};
});
it('renders info', async () => {
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest');
expect(queryByText('Domain')).not.toBeInTheDocument();
expect(queryByText('System')).not.toBeInTheDocument();
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(queryByText('Type')).not.toBeInTheDocument();
expect(queryByText('Lifecycle')).not.toBeInTheDocument();
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1');
});
it('highlights missing required fields', async () => {
delete entity.metadata.tags;
entity.relations = [];
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner');
expect(queryByText('Domain')).not.toBeInTheDocument();
expect(queryByText('System')).not.toBeInTheDocument();
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(queryByText('Type')).not.toBeInTheDocument();
expect(queryByText('Lifecycle')).not.toBeInTheDocument();
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags');
});
});
describe('Location entity', () => {
let entity: Entity;
beforeEach(() => {
entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Location',
metadata: {
name: 'software',
description: 'This is the description',
tags: ['tag-1'],
},
spec: {
type: 'root',
},
relations: [],
};
});
it('renders info', async () => {
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner');
expect(queryByText('Domain')).not.toBeInTheDocument();
expect(queryByText('System')).not.toBeInTheDocument();
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(getByText('Type')).toBeInTheDocument();
expect(getByText('Type').nextSibling).toHaveTextContent('root');
expect(queryByText('Lifecycle')).not.toBeInTheDocument();
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1');
});
it('highlights missing required fields', async () => {
delete entity.metadata.tags;
delete entity.spec!.type;
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner');
expect(queryByText('Domain')).not.toBeInTheDocument();
expect(queryByText('System')).not.toBeInTheDocument();
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(getByText('Type')).toBeInTheDocument();
expect(getByText('Type').nextSibling).toHaveTextContent('unknown');
expect(queryByText('Lifecycle')).not.toBeInTheDocument();
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags');
});
});
describe('Resource entity', () => {
let entity: Entity;
beforeEach(() => {
entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Resource',
metadata: {
name: 'software',
description: 'This is the description',
tags: ['tag-1'],
},
spec: {
type: 's3',
owner: 'guest',
system: 'system',
},
relations: [
{
type: RELATION_OWNED_BY,
target: {
kind: 'user',
name: 'guest',
namespace: 'default',
},
},
{
type: RELATION_PART_OF,
target: {
kind: 'system',
name: 'system',
namespace: 'default',
},
},
],
};
});
it('renders info', async () => {
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest');
expect(queryByText('Domain')).not.toBeInTheDocument();
expect(getByText('System')).toBeInTheDocument();
expect(getByText('System').nextSibling).toHaveTextContent('system');
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(getByText('Type')).toBeInTheDocument();
expect(getByText('Type').nextSibling).toHaveTextContent('s3');
expect(queryByText('Lifecycle')).not.toBeInTheDocument();
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1');
});
it('highlights missing required fields', async () => {
delete entity.metadata.tags;
delete entity.spec!.type;
delete entity.spec!.system;
entity.relations = [];
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner');
expect(queryByText('Domain')).not.toBeInTheDocument();
expect(getByText('System')).toBeInTheDocument();
expect(getByText('System').nextSibling).toHaveTextContent('No System');
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(getByText('Type')).toBeInTheDocument();
expect(getByText('Type').nextSibling).toHaveTextContent('unknown');
expect(queryByText('Lifecycle')).not.toBeInTheDocument();
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags');
});
});
describe('System entity', () => {
let entity: Entity;
beforeEach(() => {
entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'System',
metadata: {
name: 'software',
description: 'This is the description',
tags: ['tag-1'],
},
spec: {
owner: 'guest',
domain: 'domain',
},
relations: [
{
type: RELATION_OWNED_BY,
target: {
kind: 'user',
name: 'guest',
namespace: 'default',
},
},
{
type: RELATION_PART_OF,
target: {
kind: 'domain',
name: 'domain',
namespace: 'default',
},
},
],
};
});
it('renders info', async () => {
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest');
expect(getByText('Domain')).toBeInTheDocument();
expect(getByText('Domain').nextSibling).toHaveTextContent('domain');
expect(queryByText('System')).not.toBeInTheDocument();
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(queryByText('Type')).not.toBeInTheDocument();
expect(queryByText('Lifecycle')).not.toBeInTheDocument();
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1');
});
it('highlights missing required fields', async () => {
delete entity.metadata.tags;
delete entity.spec!.domain;
entity.relations = [];
const { getByText, queryByText } = await renderInTestApp(
<AboutContent entity={entity} />,
);
expect(getByText('Description')).toBeInTheDocument();
expect(getByText('Description').nextSibling).toHaveTextContent(
'This is the description',
);
expect(getByText('Owner')).toBeInTheDocument();
expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner');
expect(getByText('Domain')).toBeInTheDocument();
expect(getByText('Domain').nextSibling).toHaveTextContent('No Domain');
expect(queryByText('System')).not.toBeInTheDocument();
expect(queryByText('Parent Component')).not.toBeInTheDocument();
expect(queryByText('Type')).not.toBeInTheDocument();
expect(queryByText('Lifecycle')).not.toBeInTheDocument();
expect(getByText('Tags')).toBeInTheDocument();
expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags');
});
});
});
@@ -40,9 +40,13 @@ type Props = {
export const AboutContent = ({ entity }: Props) => {
const classes = useStyles();
const isSystem = entity.kind.toLocaleLowerCase('en-US') === 'system';
const isDomain = entity.kind.toLocaleLowerCase('en-US') === 'domain';
const isResource = entity.kind.toLocaleLowerCase('en-US') === 'resource';
const isComponent = entity.kind.toLocaleLowerCase('en-US') === 'component';
const isAPI = entity.kind.toLocaleLowerCase('en-US') === 'api';
const isTemplate = entity.kind.toLocaleLowerCase('en-US') === 'template';
const isLocation = entity.kind.toLocaleLowerCase('en-US') === 'location';
const isGroup = entity.kind.toLocaleLowerCase('en-US') === 'group';
const partOfSystemRelations = getEntityRelations(entity, RELATION_PART_OF, {
kind: 'system',
});
@@ -65,31 +69,44 @@ export const AboutContent = ({ entity }: Props) => {
{entity?.metadata?.description || 'No description'}
</Typography>
</AboutField>
<AboutField label="Owner" gridSizes={{ xs: 12, sm: 6, lg: 4 }}>
<EntityRefLinks entityRefs={ownedByRelations} defaultKind="group" />
<AboutField
label="Owner"
value="No Owner"
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
>
{ownedByRelations.length > 0 && (
<EntityRefLinks entityRefs={ownedByRelations} defaultKind="group" />
)}
</AboutField>
{isSystem && (
{(isSystem || partOfDomainRelations.length > 0) && (
<AboutField
label="Domain"
value="No Domain"
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
>
<EntityRefLinks
entityRefs={partOfDomainRelations}
defaultKind="domain"
/>
{partOfDomainRelations.length > 0 && (
<EntityRefLinks
entityRefs={partOfDomainRelations}
defaultKind="domain"
/>
)}
</AboutField>
)}
{!isSystem && !isDomain && (
{(isAPI ||
isComponent ||
isResource ||
partOfSystemRelations.length > 0) && (
<AboutField
label="System"
value="No System"
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
>
<EntityRefLinks
entityRefs={partOfSystemRelations}
defaultKind="system"
/>
{partOfSystemRelations.length > 0 && (
<EntityRefLinks
entityRefs={partOfSystemRelations}
defaultKind="system"
/>
)}
</AboutField>
)}
{isComponent && partOfComponentRelations.length > 0 && (
@@ -104,14 +121,22 @@ export const AboutContent = ({ entity }: Props) => {
/>
</AboutField>
)}
{!isSystem && !isDomain && (
{(isAPI ||
isComponent ||
isResource ||
isTemplate ||
isGroup ||
isLocation ||
typeof entity?.spec?.type === 'string') && (
<AboutField
label="Type"
value={entity?.spec?.type as string}
gridSizes={{ xs: 12, sm: 6, lg: 4 }}
/>
)}
{!isSystem && !isDomain && !isResource && (
{(isAPI ||
isComponent ||
typeof entity?.spec?.lifecycle === 'string') && (
<AboutField
label="Lifecycle"
value={entity?.spec?.lifecycle as string}
@@ -14,8 +14,9 @@
* limitations under the License.
*/
import { useElementFilter } from '@backstage/core-plugin-api';
import { Grid, makeStyles, Typography } from '@material-ui/core';
import React from 'react';
import { makeStyles, Typography, Grid } from '@material-ui/core';
const useStyles = makeStyles(theme => ({
value: {
@@ -45,14 +46,17 @@ type Props = {
export const AboutField = ({ label, value, gridSizes, children }: Props) => {
const classes = useStyles();
const childElements = useElementFilter(children, c => c.getElements());
// Content is either children or a string prop `value`
const content = React.Children.count(children) ? (
children
) : (
<Typography variant="body2" className={classes.value}>
{value || `unknown`}
</Typography>
);
const content =
childElements.length > 0 ? (
childElements
) : (
<Typography variant="body2" className={classes.value}>
{value || `unknown`}
</Typography>
);
return (
<Grid item {...gridSizes}>
<Typography variant="subtitle2" className={classes.label}>