diff --git a/.changeset/slow-jobs-live.md b/.changeset/slow-jobs-live.md new file mode 100644 index 0000000000..dd640a54f3 --- /dev/null +++ b/.changeset/slow-jobs-live.md @@ -0,0 +1,6 @@ +--- +'@backstage/core': patch +'@backstage/theme': patch +--- + +Deprecated `ItemCard`. Added `ItemCardGrid` and `ItemCardHeader` instead, that can be used to compose functionality around regular Material-UI `Card` components instead. diff --git a/.changeset/strong-wasps-watch.md b/.changeset/strong-wasps-watch.md new file mode 100644 index 0000000000..44923d5335 --- /dev/null +++ b/.changeset/strong-wasps-watch.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-explore': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-techdocs': patch +--- + +Make use of the new core `ItemCardGrid` and `ItemCardHeader` instead of the deprecated `ItemCard`. diff --git a/packages/core/src/layout/Breadcrumbs/Breadcrumbs.tsx b/packages/core/src/layout/Breadcrumbs/Breadcrumbs.tsx index d8cf88497f..a64b462377 100644 --- a/packages/core/src/layout/Breadcrumbs/Breadcrumbs.tsx +++ b/packages/core/src/layout/Breadcrumbs/Breadcrumbs.tsx @@ -87,8 +87,8 @@ export const Breadcrumbs = ({ children, ...props }: Props) => { }} > - {expandablePages.map(pageLink => ( - + {expandablePages.map((pageLink, index) => ( + {pageLink} ))} diff --git a/packages/core/src/layout/ItemCard/ItemCard.stories.tsx b/packages/core/src/layout/ItemCard/ItemCard.stories.tsx index eea78562e8..56d2328454 100644 --- a/packages/core/src/layout/ItemCard/ItemCard.stories.tsx +++ b/packages/core/src/layout/ItemCard/ItemCard.stories.tsx @@ -13,86 +13,101 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Grid } from '@material-ui/core'; + +import { + Card, + CardActions, + CardContent, + CardMedia, + makeStyles, + Typography, +} from '@material-ui/core'; import React from 'react'; import { MemoryRouter } from 'react-router'; -import { ItemCard } from '.'; +import { Button } from '../../components'; +import { ItemCardGrid } from './ItemCardGrid'; +import { ItemCardHeader } from './ItemCardHeader'; export default { - title: 'Layout/Item Card', - component: ItemCard, + title: 'Layout/Item Cards', }; +const text = + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; + +const useStyles = makeStyles({ + grid: { + gridTemplateColumns: 'repeat(auto-fill, 12em)', + }, + header: { + color: 'black', + backgroundImage: 'linear-gradient(to bottom right, red, yellow)', + }, +}); + export const Default = () => ( - - - {}} - /> - - - {}} - /> - - -); - -export const Tags = () => ( - - - - - - - - - - - -); - -export const Link = () => ( - - - - - - - - + + The most basic setup is to place a bunch of cards into a large grid, + leaving styling to the defaults. Try to resize the window to see how they + rearrange themselves to fit the viewport. + + + {[...Array(10).keys()].map(index => ( + + + + + + {text + .split(' ') + .slice(0, 5 + Math.floor(Math.random() * 30)) + .join(' ')} + + + + + + ))} + ); + +export const Styling = () => { + const classes = useStyles(); + return ( + + + Both the grid and the header can be styled, using the{' '} + classes property. This lets + you for example tweak the column sizes and the background of the header. + + + {[...Array(10).keys()].map(index => ( + + + + + + {text + .split(' ') + .slice(0, 5 + Math.floor(Math.random() * 30)) + .join(' ')} + + + + + + ))} + + + ); +}; diff --git a/packages/core/src/layout/ItemCard/ItemCard.tsx b/packages/core/src/layout/ItemCard/ItemCard.tsx index 9e16def5d3..8c26e6a9f1 100644 --- a/packages/core/src/layout/ItemCard/ItemCard.tsx +++ b/packages/core/src/layout/ItemCard/ItemCard.tsx @@ -13,33 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Button, Card, Chip, makeStyles, Typography } from '@material-ui/core'; -import clsx from 'clsx'; +import { + Box, + Card, + CardActions, + CardContent, + CardMedia, + Chip, +} from '@material-ui/core'; import React, { ReactNode } from 'react'; -import { Link } from '../../components'; - -const useStyles = makeStyles(theme => ({ - header: { - color: theme.palette.common.white, - padding: theme.spacing(2, 2, 6), - backgroundImage: 'linear-gradient(-137deg, #4BB8A5 0%, #187656 100%)', - }, - content: { - padding: theme.spacing(2), - }, - description: { - height: 175, - overflow: 'hidden', - textOverflow: 'ellipsis', - }, - withTags: { - height: 'calc(175px - 32px - 8px)', - }, - footer: { - display: 'flex', - flexDirection: 'row-reverse', - }, -})); +import { Button } from '../../components'; +import { ItemCardHeader } from './ItemCardHeader'; type ItemCardProps = { description?: string; @@ -53,6 +37,31 @@ type ItemCardProps = { href?: string; }; +/** + * This card type has been deprecated. Instead use plain MUI Card and helpers + * where appropriate. + * + * + * + * + * + * @deprecated Use plain MUI and composable helpers instead. + * @see https://material-ui.com/components/cards/ + */ export const ItemCard = ({ description, tags, @@ -63,43 +72,33 @@ export const ItemCard = ({ onClick, href, }: ItemCardProps) => { - const classes = useStyles(); - return ( -
- {(subtitle || type) && ( - {subtitle ?? type} + + + + + {tags?.length ? ( + + {tags.map((tag, i) => ( + + ))} + + ) : null} + {description} + + + {!href && ( + )} - {title} -
-
- {tags?.map((tag, i) => ( - - ))} - 0 && classes.withTags, - )} - > - {description} - -
- {!href && ( - - )} - {href && ( - - )} -
-
+ {href && ( + + )} +
); }; diff --git a/packages/core/src/layout/ItemCard/ItemCardGrid.test.tsx b/packages/core/src/layout/ItemCard/ItemCardGrid.test.tsx new file mode 100644 index 0000000000..51ef8c544f --- /dev/null +++ b/packages/core/src/layout/ItemCard/ItemCardGrid.test.tsx @@ -0,0 +1,50 @@ +/* + * 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 { renderInTestApp } from '@backstage/test-utils'; +import { Card } from '@material-ui/core'; +import { screen } from '@testing-library/react'; +import React from 'react'; +import { ItemCardGrid } from './ItemCardGrid'; + +describe('', () => { + it('renders default without exploding', async () => { + await renderInTestApp( + + Hello! + , + ); + expect(screen.getByRole('grid')).toBeInTheDocument(); + expect(screen.getByText('Hello!')).toBeInTheDocument(); + }); + + it('renders custom styles', async () => { + await renderInTestApp( + <> + + Hello! + + + Goodbye! + + , + ); + expect(screen.getAllByRole('grid')[0]).toHaveStyle({ + gridTemplateColumns: 'repeat(auto-fill, minmax(22em, 1fr))', + }); + expect(screen.getAllByRole('grid')[1]).toHaveClass('my-css-class'); + }); +}); diff --git a/packages/core/src/layout/ItemCard/ItemCardGrid.tsx b/packages/core/src/layout/ItemCard/ItemCardGrid.tsx new file mode 100644 index 0000000000..551c3c67d5 --- /dev/null +++ b/packages/core/src/layout/ItemCard/ItemCardGrid.tsx @@ -0,0 +1,62 @@ +/* + * 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 { createStyles, makeStyles, Theme, WithStyles } from '@material-ui/core'; +import React from 'react'; + +const styles = (theme: Theme) => + createStyles({ + root: { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(22em, 1fr))', + gridAutoRows: '1fr', + gridGap: theme.spacing(2), + }, + }); + +const useStyles = makeStyles(styles); + +export type ItemCardGridProps = Partial> & { + /** + * The Card items of the grid. + */ + children?: React.ReactNode; +}; + +/** + * A default grid to use when arranging "item cards" - cards that let users + * select among several options. + * + * The immediate children are expected to be MUI Card components. + * + * Styles for the grid can be overridden using the `classes` prop, e.g.: + * + * + * + * + * + * This can be useful for e.g. overriding gridTemplateColumns to adapt the + * minimum size of the cells to fit the content better. + */ +export const ItemCardGrid = (props: ItemCardGridProps) => { + const { children, ...otherProps } = props; + const classes = useStyles(otherProps); + return ( +
+ {children} +
+ ); +}; diff --git a/packages/core/src/layout/ItemCard/ItemCardHeader.test.tsx b/packages/core/src/layout/ItemCard/ItemCardHeader.test.tsx new file mode 100644 index 0000000000..ec9af6436e --- /dev/null +++ b/packages/core/src/layout/ItemCard/ItemCardHeader.test.tsx @@ -0,0 +1,60 @@ +/* + * 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 { renderInTestApp } from '@backstage/test-utils'; +import { Card, CardMedia } from '@material-ui/core'; +import { screen } from '@testing-library/react'; +import React from 'react'; +import { ItemCardHeader } from './ItemCardHeader'; + +describe('', () => { + it('renders default without exploding', async () => { + await renderInTestApp( + + + + + , + ); + expect(screen.getByText('My Title')).toBeInTheDocument(); + expect(screen.getByText('My Subtitle')).toBeInTheDocument(); + }); + + it('renders custom children', async () => { + await renderInTestApp( + + + My Custom Text + + , + ); + expect(screen.getByText('My Title')).toBeInTheDocument(); + expect(screen.getByText('My Custom Text')).toBeInTheDocument(); + }); + + it('renders custom styles', async () => { + await renderInTestApp( + + + + My Custom Text + + + , + ); + expect(screen.getByText('My Custom Text')).toHaveClass('my-css-class'); + }); +}); diff --git a/packages/core/src/layout/ItemCard/ItemCardHeader.tsx b/packages/core/src/layout/ItemCard/ItemCardHeader.tsx new file mode 100644 index 0000000000..17c4a19e58 --- /dev/null +++ b/packages/core/src/layout/ItemCard/ItemCardHeader.tsx @@ -0,0 +1,85 @@ +/* + * 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 { + createStyles, + makeStyles, + Theme, + Typography, + WithStyles, +} from '@material-ui/core'; +import React from 'react'; + +const styles = (theme: Theme) => + createStyles({ + root: { + color: theme.palette.common.white, + padding: theme.spacing(2, 2, 3), + backgroundImage: 'linear-gradient(-137deg, #4BB8A5 0%, #187656 100%)', + backgroundPosition: 0, + backgroundSize: 'inherit', + }, + }); + +const useStyles = makeStyles(styles); + +export type ItemCardHeaderProps = Partial> & { + /** + * A large title to show in the header, providing the main heading. + * + * Use this if you want to have the default styling and placement of a title. + */ + title?: React.ReactNode; + /** + * A slightly smaller title to show in the header, providing additional + * details. + * + * Use this if you want to have the default styling and placement of a + * subtitle. + */ + subtitle?: React.ReactNode; + /** + * Custom children to draw in the header. + * + * If the title and/or subtitle were specified, the children are drawn below + * those. + */ + children?: React.ReactNode; +}; + +/** + * A simple card header, rendering a default look for "item cards" - cards that + * are arranged in a grid for users to select among several options. + * + * This component expects to be placed within a MUI . + * + * Styles for the header can be overridden using the `classes` prop, e.g.: + * + * + * + * + */ +export const ItemCardHeader = (props: ItemCardHeaderProps) => { + const { title, subtitle, children } = props; + const classes = useStyles(props); + return ( +
+ {subtitle && {subtitle}} + {title && {title}} + {children} +
+ ); +}; diff --git a/packages/core/src/layout/ItemCard/index.ts b/packages/core/src/layout/ItemCard/index.ts index b38dd2acc2..da2c1dd546 100644 --- a/packages/core/src/layout/ItemCard/index.ts +++ b/packages/core/src/layout/ItemCard/index.ts @@ -15,3 +15,7 @@ */ export { ItemCard } from './ItemCard'; +export { ItemCardGrid } from './ItemCardGrid'; +export type { ItemCardGridProps } from './ItemCardGrid'; +export { ItemCardHeader } from './ItemCardHeader'; +export type { ItemCardHeaderProps } from './ItemCardHeader'; diff --git a/packages/theme/src/baseTheme.ts b/packages/theme/src/baseTheme.ts index 37b8e3b47f..e7bb7e58b6 100644 --- a/packages/theme/src/baseTheme.ts +++ b/packages/theme/src/baseTheme.ts @@ -234,12 +234,31 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides { margin: `0 ${theme.spacing(0.5)}px 0 -${theme.spacing(0.5)}px`, }, }, + MuiCard: { + root: { + // When cards have a forced size, such as when they are arranged in a + // CSS grid, the content needs to flex such that the actions (buttons + // etc) end up at the bottom of the card instead of just below the body + // contents. + display: 'flex', + flexDirection: 'column', + }, + }, MuiCardHeader: { root: { // Reduce padding between header and content paddingBottom: 0, }, }, + MuiCardContent: { + root: { + // When cards have a forced size, such as when they are arranged in a + // CSS grid, the content needs to flex such that the actions (buttons + // etc) end up at the bottom of the card instead of just below the body + // contents. + flexGrow: 1, + }, + }, MuiCardActions: { root: { // We default to putting the card actions at the end diff --git a/plugins/explore/src/components/DomainCard/DomainCard.tsx b/plugins/explore/src/components/DomainCard/DomainCard.tsx index 67bee13696..ee06fd39fd 100644 --- a/plugins/explore/src/components/DomainCard/DomainCard.tsx +++ b/plugins/explore/src/components/DomainCard/DomainCard.tsx @@ -14,12 +14,20 @@ * limitations under the License. */ import { DomainEntity, RELATION_OWNED_BY } from '@backstage/catalog-model'; -import { ItemCard, useRouteRef } from '@backstage/core'; +import { Button, ItemCardHeader, useRouteRef } from '@backstage/core'; import { EntityRefLinks, entityRouteParams, getEntityRelations, } from '@backstage/plugin-catalog-react'; +import { + Box, + Card, + CardActions, + CardContent, + CardMedia, + Chip, +} from '@material-ui/core'; import React from 'react'; import { catalogEntityRouteRef } from '../../routes'; @@ -28,23 +36,39 @@ type DomainCardProps = { }; export const DomainCard = ({ entity }: DomainCardProps) => { - const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); const catalogEntityRoute = useRouteRef(catalogEntityRouteRef); - return ( - - } - label="Explore" - href={catalogEntityRoute(entityRouteParams(entity))} + const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); + const url = catalogEntityRoute(entityRouteParams(entity)); + + const owner = ( + ); + + return ( + + + + + + {entity.metadata.tags?.length ? ( + + {entity.metadata.tags.map(tag => ( + + ))} + + ) : null} + {entity.metadata.description} + + + + + + ); }; diff --git a/plugins/explore/src/components/DomainCard/DomainCardGrid.test.tsx b/plugins/explore/src/components/DomainCard/DomainCardGrid.test.tsx deleted file mode 100644 index ee06a50427..0000000000 --- a/plugins/explore/src/components/DomainCard/DomainCardGrid.test.tsx +++ /dev/null @@ -1,59 +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 { DomainEntity } from '@backstage/catalog-model'; -import { renderInTestApp } from '@backstage/test-utils'; -import React from 'react'; -import { catalogEntityRouteRef } from '../../routes'; -import { DomainCardGrid } from './DomainCardGrid'; - -describe('', () => { - it('renders a grid of domain cards', async () => { - const entities: DomainEntity[] = [ - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Domain', - metadata: { - name: 'playback', - }, - spec: { - owner: 'guest', - }, - }, - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Domain', - metadata: { - name: 'artists', - }, - spec: { - owner: 'guest', - }, - }, - ]; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': catalogEntityRouteRef, - }, - }, - ); - - expect(getByText('artists')).toBeInTheDocument(); - expect(getByText('playback')).toBeInTheDocument(); - }); -}); diff --git a/plugins/explore/src/components/DomainCard/DomainCardGrid.tsx b/plugins/explore/src/components/DomainCard/DomainCardGrid.tsx deleted file mode 100644 index b4a13aa39f..0000000000 --- a/plugins/explore/src/components/DomainCard/DomainCardGrid.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 { DomainEntity } from '@backstage/catalog-model'; -import { Grid } from '@material-ui/core'; -import React from 'react'; -import { DomainCard } from '.'; - -type DomainCardGridProps = { - entities: DomainEntity[]; -}; - -export const DomainCardGrid = ({ entities }: DomainCardGridProps) => ( - - {entities.map((e, i) => ( - - - - ))} - -); diff --git a/plugins/explore/src/components/DomainCard/index.ts b/plugins/explore/src/components/DomainCard/index.ts index 6acf10800a..3511ff023d 100644 --- a/plugins/explore/src/components/DomainCard/index.ts +++ b/plugins/explore/src/components/DomainCard/index.ts @@ -14,4 +14,3 @@ * limitations under the License. */ export { DomainCard } from './DomainCard'; -export { DomainCardGrid } from './DomainCardGrid'; diff --git a/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.tsx b/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.tsx index 182adf70ea..a12811e486 100644 --- a/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.tsx +++ b/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.tsx @@ -18,6 +18,7 @@ import { Content, ContentHeader, EmptyState, + ItemCardGrid, Progress, SupportButton, useApi, @@ -27,47 +28,64 @@ import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { Button } from '@material-ui/core'; import React from 'react'; import { useAsync } from 'react-use'; -import { DomainCardGrid } from '../DomainCard'; +import { DomainCard } from '../DomainCard'; -export const DomainExplorerContent = () => { +const Body = () => { const catalogApi = useApi(catalogApiRef); const { value: entities, loading, error } = useAsync(async () => { const response = await catalogApi.getEntities({ filter: { kind: 'domain' }, }); - return response.items as DomainEntity[]; }, [catalogApi]); + if (loading) { + return ; + } + + if (error) { + return ( + + {error.message} + + ); + } + + if (!entities?.length) { + return ( + + Read more + + } + /> + ); + } + + return ( + + {entities.map((entity, index) => ( + + ))} + + ); +}; + +export const DomainExplorerContent = () => { return ( Discover the domains in your ecosystem. - - {loading && } - {error && ( - - {error.message} - - )} - {!loading && !error && (!entities || entities.length === 0) && ( - - Read more - - } - /> - )} - {!loading && entities && } + ); }; diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index ebce35a91f..8e8ba15ddc 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -14,14 +14,13 @@ * limitations under the License. */ -import React, { useEffect, useMemo, useState } from 'react'; -import { Link as RouterLink } from 'react-router-dom'; import { EntityMeta, TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { configApiRef, Content, ContentHeader, Header, + ItemCardGrid, Lifecycle, Page, Progress, @@ -30,14 +29,16 @@ import { WarningPanel, } from '@backstage/core'; import { useStarredEntities } from '@backstage/plugin-catalog-react'; -import { Box, Button, Link, makeStyles, Typography } from '@material-ui/core'; +import { Button, Link, makeStyles, Typography } from '@material-ui/core'; import StarIcon from '@material-ui/icons/Star'; +import React, { useEffect, useMemo, useState } from 'react'; +import { Link as RouterLink } from 'react-router-dom'; import { EntityFilterGroupsProvider, useFilteredEntities } from '../../filter'; -import { TemplateCard, TemplateCardProps } from '../TemplateCard'; import { ResultsFilter } from '../ResultsFilter/ResultsFilter'; import { ScaffolderFilter } from '../ScaffolderFilter'; import { ButtonGroup } from '../ScaffolderFilter/ScaffolderFilter'; import SearchToolbar from '../SearchToolbar/SearchToolbar'; +import { TemplateCard, TemplateCardProps } from '../TemplateCard'; const useStyles = makeStyles(theme => ({ contentWrapper: { @@ -46,12 +47,6 @@ const useStyles = makeStyles(theme => ({ gridTemplateColumns: '250px 1fr', gridColumnGap: theme.spacing(2), }, - templateGrid: { - display: 'grid', - gridTemplateColumns: 'repeat(auto-fill, minmax(22em, 1fr))', - gridAutoRows: '1fr', - gridGap: theme.spacing(2), - }, })); const getTemplateCardProps = ( @@ -183,13 +178,13 @@ export const ScaffolderPageContents = () => { )} - + {matchingEntities && matchingEntities?.length > 0 && matchingEntities.map(template => ( ))} - +
diff --git a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx index 26d5c79cc7..25f89f20ab 100644 --- a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx @@ -13,29 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Button, useRouteRef } from '@backstage/core'; +import { Button, ItemCardHeader, useRouteRef } from '@backstage/core'; import { BackstageTheme, pageTheme } from '@backstage/theme'; import { + Box, Card, CardActions, CardContent, CardMedia, Chip, makeStyles, - Typography, useTheme, } from '@material-ui/core'; import React from 'react'; import { generatePath } from 'react-router'; import { rootRouteRef } from '../../routes'; -const useStyles = makeStyles(theme => ({ - header: { - color: theme.palette.common.white, - padding: theme.spacing(2, 2, 3), - backgroundImage: (props: { backgroundImage: string }) => - props.backgroundImage, - backgroundPosition: 0, +const useStyles = makeStyles({ + title: { + backgroundImage: ({ backgroundImage }: any) => backgroundImage, }, description: { overflow: 'hidden', @@ -44,14 +40,7 @@ const useStyles = makeStyles(theme => ({ '-webkit-line-clamp': 10, '-webkit-box-orient': 'vertical', }, - card: { - display: 'flex', - flexDirection: 'column', - }, - cardContent: { - flexGrow: 1, - }, -})); +}); export type TemplateCardProps = { description: string; @@ -79,18 +68,21 @@ export const TemplateCard = ({ }); return ( - - - {type} - {title} + + + - - {tags?.map(tag => ( - - ))} - - {description} - + + + {tags?.map(tag => ( + + ))} + + {description} + + + ))} + );