From db19eeac70c7ec2d091a042ec42f50305d65bbe1 Mon Sep 17 00:00:00 2001 From: Kalle Ericson <7943407+kalleericson@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:30:51 +0100 Subject: [PATCH] Feat: Additional test coverage and component structure for TemplateCard Signed-off-by: Kalle Ericson <7943407+kalleericson@users.noreply.github.com> --- .../next/components/TemplateCard/CardLink.tsx | 2 +- .../components/TemplateCard/TemplateCard.tsx | 176 ++++-------------- .../TemplateCard/TemplateCardActions.tsx | 86 +++++++++ .../TemplateCard/TemplateCardContent.tsx | 59 ++++++ .../TemplateCard/TemplateCardLinks.tsx | 82 ++++++++ .../TemplateCard/TemplateCardTags.tsx | 55 ++++++ .../src/next/components/TemplateCard/index.ts | 24 ++- 7 files changed, 340 insertions(+), 144 deletions(-) create mode 100644 plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardActions.tsx create mode 100644 plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardContent.tsx create mode 100644 plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardLinks.tsx create mode 100644 plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardTags.tsx diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/CardLink.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/CardLink.tsx index 263a1356b7..4aa63e4555 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateCard/CardLink.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/CardLink.tsx @@ -19,7 +19,7 @@ import { Link } from '@backstage/core-components'; import { makeStyles } from '@material-ui/core/styles'; import React from 'react'; -interface CardLinkProps { +export interface CardLinkProps { icon: IconComponent; text: string; url: string; diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx index 5d2c8aa7fe..73b8e71de9 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx @@ -15,81 +15,41 @@ */ import { RELATION_OWNED_BY } from '@backstage/catalog-model'; -import { MarkdownContent, UserIcon } from '@backstage/core-components'; -import { - IconComponent, - useAnalytics, - useApp, -} from '@backstage/core-plugin-api'; -import { - EntityRefLinks, - getEntityRelations, -} from '@backstage/plugin-catalog-react'; +import { IconComponent, useAnalytics } from '@backstage/core-plugin-api'; +import { getEntityRelations } from '@backstage/plugin-catalog-react'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; -import Box from '@material-ui/core/Box'; import Card from '@material-ui/core/Card'; import CardActions from '@material-ui/core/CardActions'; import CardContent from '@material-ui/core/CardContent'; -import Chip from '@material-ui/core/Chip'; import Divider from '@material-ui/core/Divider'; -import Button from '@material-ui/core/Button'; import Grid from '@material-ui/core/Grid'; import { makeStyles, Theme } from '@material-ui/core/styles'; -import LanguageIcon from '@material-ui/icons/Language'; import React, { useCallback } from 'react'; import { CardHeader } from './CardHeader'; -import { CardLink } from './CardLink'; import { usePermission } from '@backstage/plugin-permission-react'; import { taskCreatePermission } from '@backstage/plugin-scaffolder-common/alpha'; +import { TemplateCardContent } from './TemplateCardContent'; +import { TemplateCardTags } from './TemplateCardTags'; +import { TemplateCardLinks } from './TemplateCardLinks'; +import { TemplateCardActions } from './TemplateCardActions'; -const useStyles = makeStyles(theme => ({ - box: { - overflow: 'hidden', - textOverflow: 'ellipsis', - display: '-webkit-box', - '-webkit-line-clamp': 10, - '-webkit-box-orient': 'vertical', - }, - markdown: { - /** to make the styles for React Markdown not leak into the description */ - '& :first-child': { - margin: 0, - }, - }, - label: { - color: theme.palette.text.secondary, - textTransform: 'uppercase', - fontWeight: 'bold', - letterSpacing: 0.5, - lineHeight: 1, - fontSize: '0.75rem', - }, - footer: { - display: 'flex', - justifyContent: 'space-between', - flex: 1, - alignItems: 'center', - }, - ownedBy: { - display: 'flex', - alignItems: 'center', - flex: 1, - color: theme.palette.link, - }, +const useStyles = makeStyles(() => ({ + actionContainer: { padding: '16px', flex: 1, alignItems: 'flex-end' }, })); +export interface TemplateCardAdditionalLink { + icon: IconComponent; + text: string; + url: string; +} + /** * The Props for the {@link TemplateCard} component * @alpha */ export interface TemplateCardProps { template: TemplateEntityV1beta3; - additionalLinks?: { - icon: IconComponent; - text: string; - url: string; - }[]; - + additionalLinks?: TemplateCardAdditionalLink[]; onSelected?: (template: TemplateEntityV1beta3) => void; } @@ -98,16 +58,13 @@ export interface TemplateCardProps { * @alpha */ export const TemplateCard = (props: TemplateCardProps) => { - const { onSelected, template } = props; + const { additionalLinks, onSelected, template } = props; const styles = useStyles(); const analytics = useAnalytics(); const ownedByRelations = getEntityRelations(template, RELATION_OWNED_BY); - const app = useApp(); - const iconResolver = (key?: string): IconComponent => - key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon; const hasTags = !!template.metadata.tags?.length; const hasLinks = - !!props.additionalLinks?.length || !!template.metadata.links?.length; + !!additionalLinks?.length || !!template.metadata.links?.length; const displayDefaultDivider = !hasTags && !hasLinks; const { allowed: canCreateTask } = usePermission({ @@ -120,98 +77,33 @@ export const TemplateCard = (props: TemplateCardProps) => { return ( - + - - - - - - + + {displayDefaultDivider && ( )} - {hasTags && ( - <> - - - - - - {template.metadata.tags?.map(tag => ( - - - - ))} - - - - )} + {hasTags && } {hasLinks && ( - <> - - - - - - {props.additionalLinks?.map(({ icon, text, url }, index) => ( - - - - ))} - {template.metadata.links?.map( - ({ url, icon, title }, index) => ( - - - - ), - )} - - - + )} - -
-
- {ownedByRelations.length > 0 && ( - <> - - - - )} -
- {canCreateTask ? ( - - ) : null} -
+ +
); diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardActions.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardActions.tsx new file mode 100644 index 0000000000..e8a84487df --- /dev/null +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardActions.tsx @@ -0,0 +1,86 @@ +/* + * Copyright 2022 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 { UserIcon } from '@backstage/core-components'; +import { EntityRefLinks } from '@backstage/plugin-catalog-react'; +import Button from '@material-ui/core/Button'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import React from 'react'; + +const useStyles = makeStyles(theme => ({ + footer: { + display: 'flex', + justifyContent: 'space-between', + flex: 1, + alignItems: 'center', + }, + ownedBy: { + display: 'flex', + alignItems: 'center', + flex: 1, + color: theme.palette.link, + }, + actionContainer: { padding: '16px', flex: 1, alignItems: 'flex-end' }, +})); + +/** + * The Props for the {@link TemplateCardActions} component + * @alpha + */ +export interface TemplateCardActionsProps { + ownedByRelations: any; + canCreateTask: boolean; + handleChoose: () => void; +} +export const TemplateCardActions = ({ + canCreateTask, + handleChoose, + ownedByRelations, +}: TemplateCardActionsProps) => { + const styles = useStyles(); + + return ( +
+
+ {ownedByRelations.length > 0 && ( + <> + + + + )} +
+ {canCreateTask ? ( + + ) : null} +
+ ); +}; diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardContent.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardContent.tsx new file mode 100644 index 0000000000..fad5c85474 --- /dev/null +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardContent.tsx @@ -0,0 +1,59 @@ +/* + * Copyright 2022 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 { MarkdownContent } from '@backstage/core-components'; +import type { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; +import { makeStyles } from '@material-ui/core/styles'; +import Box from '@material-ui/core/Box'; +import Grid from '@material-ui/core/Grid'; +import React from 'react'; + +const useStyles = makeStyles(() => ({ + box: { + overflow: 'hidden', + textOverflow: 'ellipsis', + display: '-webkit-box', + '-webkit-line-clamp': 10, + '-webkit-box-orient': 'vertical', + }, + markdown: { + /** to make the styles for React Markdown not leak into the description */ + '& :first-child': { + margin: 0, + }, + }, +})); + +/** + * The Props for the {@link TemplateCardContent} component + * @alpha + */ +export interface TemplateCardContentProps { + template: TemplateEntityV1beta3; +} +export const TemplateCardContent = ({ template }: TemplateCardContentProps) => { + const styles = useStyles(); + return ( + + + + + + ); +}; diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardLinks.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardLinks.tsx new file mode 100644 index 0000000000..845c4c1621 --- /dev/null +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardLinks.tsx @@ -0,0 +1,82 @@ +/* + * Copyright 2022 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 { IconComponent, useApp } from '@backstage/core-plugin-api'; +import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; +import Divider from '@material-ui/core/Divider'; +import Grid from '@material-ui/core/Grid'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import LanguageIcon from '@material-ui/icons/Language'; +import React from 'react'; +import { CardLink } from './CardLink'; +import { TemplateCardAdditionalLink } from './TemplateCard'; + +const useStyles = makeStyles({}); + +/** + * The Props for the {@link TemplateCardLinks} component + * @alpha + */ +export interface TemplateCardLinksProps { + template: TemplateEntityV1beta3; + additionalLinks?: TemplateCardAdditionalLink[]; +} +export const TemplateCardLinks = ({ + template, + additionalLinks, +}: TemplateCardLinksProps) => { + const styles = useStyles(); + const app = useApp(); + const iconResolver = (key?: string): IconComponent => + key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon; + return ( + <> + + + + + + {additionalLinks?.map(({ icon, text, url }, index) => ( + + + + ))} + {template.metadata.links?.map(({ url, icon, title }, index) => ( + + + + ))} + + + + ); +}; diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardTags.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardTags.tsx new file mode 100644 index 0000000000..88550e2104 --- /dev/null +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCardTags.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2022 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 type { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; +import Chip from '@material-ui/core/Chip'; +import Divider from '@material-ui/core/Divider'; +import Grid from '@material-ui/core/Grid'; +import React from 'react'; + +/** + * The Props for the {@link TemplateCardTags} component + * @alpha + */ +export interface TemplateCardTagsProps { + template: TemplateEntityV1beta3; +} +export const TemplateCardTags = ({ template }: TemplateCardTagsProps) => ( + <> + + + + + + {template.metadata.tags?.map(tag => ( + + + + ))} + + + +); diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/index.ts b/plugins/scaffolder-react/src/next/components/TemplateCard/index.ts index 529aa938e4..aa13b79284 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateCard/index.ts +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/index.ts @@ -13,4 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { TemplateCard, type TemplateCardProps } from './TemplateCard'; +export { + TemplateCard, + type TemplateCardProps, + type TemplateCardAdditionalLink, +} from './TemplateCard'; +export { + TemplateCardContent, + type TemplateCardContentProps, +} from './TemplateCardContent'; +export { + TemplateCardTags, + type TemplateCardTagsProps, +} from './TemplateCardTags'; +export { + TemplateCardLinks, + type TemplateCardLinksProps, +} from './TemplateCardLinks'; +export { + TemplateCardActions, + type TemplateCardActionsProps, +} from './TemplateCardActions'; +export { CardHeader, type CardHeaderProps } from './CardHeader'; +export { CardLink, type CardLinkProps } from './CardLink';