From 660de68e7e41b4c855ee084973ceabf96583fc0f Mon Sep 17 00:00:00 2001 From: nikek Date: Mon, 31 Aug 2020 23:01:40 +0200 Subject: [PATCH] Add AboutCard component to EntityPageOverview --- packages/core/src/layout/Header/Header.tsx | 2 +- .../components/AboutCard/AboutCard.test.tsx | 46 +++++ .../src/components/AboutCard/AboutCard.tsx | 182 ++++++++++++++++++ .../IconLinkVertical/IconLinkVertical.tsx | 53 +++++ .../IconLinkVertical/index.ts} | 14 +- .../index.ts} | 17 +- .../EntityPageOverview/EntityPageOverview.tsx | 4 +- 7 files changed, 286 insertions(+), 32 deletions(-) create mode 100644 plugins/catalog/src/components/AboutCard/AboutCard.test.tsx create mode 100644 plugins/catalog/src/components/AboutCard/AboutCard.tsx create mode 100644 plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx rename plugins/catalog/src/components/{EntityMetadataCard/EntityMetadataCard.tsx => AboutCard/IconLinkVertical/index.ts} (61%) rename plugins/catalog/src/components/{EntityMetadataCard/EntityMetadataCard.test.tsx => AboutCard/index.ts} (50%) diff --git a/packages/core/src/layout/Header/Header.tsx b/packages/core/src/layout/Header/Header.tsx index 7aafc2730e..6b9ea05245 100644 --- a/packages/core/src/layout/Header/Header.tsx +++ b/packages/core/src/layout/Header/Header.tsx @@ -38,7 +38,7 @@ const useStyles = makeStyles( alignItems: 'center', backgroundImage: props => props.backgroundImage, backgroundPosition: 'center', - backgroundSize: '100% 400px', + backgroundSize: 'cover', }, leftItemsBox: { flex: '1 1 auto', diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx new file mode 100644 index 0000000000..bf69818995 --- /dev/null +++ b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx @@ -0,0 +1,46 @@ +/* + * 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 React from 'react'; +import { render } from '@testing-library/react'; +import { AboutCard } from './AboutCard'; + +describe('', () => { + it('renders info and "view source" link', () => { + const entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'software', + annotations: { + 'backstage.io/managed-by-location': + 'github:https://github.com/spotify/backstage/blob/master/software.yaml', + }, + }, + spec: { + owner: 'guest', + type: 'service', + lifecycle: 'production', + }, + }; + const { getByText } = render(); + expect(getByText('service')).toBeInTheDocument(); + expect(getByText('View Source').closest('a')).toHaveAttribute( + 'href', + 'https://github.com/spotify/backstage/blob/master/software.yaml', + ); + }); +}); diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx new file mode 100644 index 0000000000..d27175fcf3 --- /dev/null +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -0,0 +1,182 @@ +/* + * 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 React from 'react'; +import { + Grid, + Typography, + makeStyles, + Chip, + IconButton, + Card, + CardContent, + CardHeader, + Divider, +} from '@material-ui/core'; +import { Entity } from '@backstage/catalog-model'; + +import GitHubIcon from '@material-ui/icons/GitHub'; +import { IconLinkVertical } from './IconLinkVertical'; +import EditIcon from '@material-ui/icons/Edit'; +import DocsIcon from '@material-ui/icons/Description'; + +const useStyles = makeStyles(theme => ({ + links: { + margin: theme.spacing(2, 0, 2), + display: 'grid', + gridAutoFlow: 'column', + gridAutoColumns: 'min-content', + gridGap: theme.spacing(2), + }, + label: { + color: '#9e9e9e', + textTransform: 'uppercase', + fontSize: '12px', + fontWeight: 'bold', + overflow: 'hidden', + whiteSpace: 'nowrap', + }, + value: { + fontWeight: 'bold', + overflow: 'hidden', + lineHeight: '24px', + wordBreak: 'break-word', + }, + description: { + wordBreak: 'break-word', + }, +})); + +const iconMap: Record = { + github: , +}; + +type CodeLinkInfo = { icon?: React.ReactNode; href?: string }; + +function getCodeLinkInfo(entity: Entity): CodeLinkInfo { + const location = + entity?.metadata?.annotations?.['backstage.io/managed-by-location']; + + if (location) { + // split by first `:` + // e.g. "github:https://github.com/spotify/backstage/blob/master/software.yaml" + const [type, target] = location.split(/:(.+)/); + + return { icon: iconMap[type], href: target }; + } + return {}; +} + +type AboutCardProps = { + entity: Entity; +}; + +export function AboutCard({ entity }: AboutCardProps) { + const classes = useStyles(); + const codeLink = getCodeLinkInfo(entity); + + return ( + + + + + } + subheader={ + + } + /> + + + + + + {entity?.metadata?.description || 'No description'} + + + + + + + {(entity?.metadata?.tags || []).map(t => ( + + ))} + + + + + ); +} + +function AboutField({ + label, + value, + gridSizes, + children, +}: { + label: string; + value?: string; + gridSizes?: Record; + children?: React.ReactNode; +}) { + const classes = useStyles(); + + // Content is either children or a string prop `value` + const content = React.Children.count(children) ? ( + children + ) : ( + + {value || `unknown`} + + ); + return ( + + + {label} + + {content} + + ); +} diff --git a/plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx b/plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx new file mode 100644 index 0000000000..91a5211925 --- /dev/null +++ b/plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx @@ -0,0 +1,53 @@ +/* + * 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 * as React from 'react'; +import { makeStyles, Link } from '@material-ui/core'; +import LinkIcon from '@material-ui/icons/Link'; + +export type IconLinkVerticalProps = { + icon?: React.ReactNode; + href?: string; + label: string; +}; + +const useIconStyles = makeStyles({ + link: { + display: 'grid', + justifyItems: 'center', + gridGap: 4, + textAlign: 'center', + }, + label: { + fontSize: '0.7rem', + textTransform: 'uppercase', + fontWeight: 600, + letterSpacing: 1.2, + }, +}); + +export function IconLinkVertical({ + icon = , + href = '#', + ...props +}: IconLinkVerticalProps) { + const classes = useIconStyles(); + return ( + + {icon} + {props.label} + + ); +} diff --git a/plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.tsx b/plugins/catalog/src/components/AboutCard/IconLinkVertical/index.ts similarity index 61% rename from plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.tsx rename to plugins/catalog/src/components/AboutCard/IconLinkVertical/index.ts index 57082a9c91..ddc146dbd8 100644 --- a/plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.tsx +++ b/plugins/catalog/src/components/AboutCard/IconLinkVertical/index.ts @@ -14,16 +14,4 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; -import { InfoCard, StructuredMetadataTable } from '@backstage/core'; -import React, { FC } from 'react'; - -type Props = { - entity: Entity; -}; - -export const EntityMetadataCard: FC = ({ entity }) => ( - - - -); +export { IconLinkVertical } from './IconLinkVertical'; diff --git a/plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.test.tsx b/plugins/catalog/src/components/AboutCard/index.ts similarity index 50% rename from plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.test.tsx rename to plugins/catalog/src/components/AboutCard/index.ts index 4bb64f221d..93765ff942 100644 --- a/plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.test.tsx +++ b/plugins/catalog/src/components/AboutCard/index.ts @@ -14,19 +14,4 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; -import { render } from '@testing-library/react'; -import React from 'react'; -import { EntityMetadataCard } from './EntityMetadataCard'; - -describe('EntityMetadataCard component', () => { - it('should display entity name if provided', async () => { - const testEntity: Entity = { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { name: 'test' }, - }; - const rendered = await render(); - expect(await rendered.findByText('test')).toBeInTheDocument(); - }); -}); +export { AboutCard } from './AboutCard'; diff --git a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx index 45a62198c7..4067fef29a 100644 --- a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx +++ b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx @@ -24,14 +24,14 @@ import { } from '@backstage/plugin-jenkins'; import { Grid } from '@material-ui/core'; import React, { FC } from 'react'; -import { EntityMetadataCard } from '../EntityMetadataCard/EntityMetadataCard'; +import { AboutCard } from '../AboutCard'; export const EntityPageOverview: FC<{ entity: Entity }> = ({ entity }) => { return ( - + {entity.metadata?.annotations?.[ 'backstage.io/jenkins-github-folder'