From 302316d231f17d8ff7501c402cd49b9343f93f34 Mon Sep 17 00:00:00 2001 From: nikolar Date: Tue, 3 Oct 2023 17:26:59 -0700 Subject: [PATCH 01/10] add featured docs Signed-off-by: nikolar --- .changeset/wet-cats-allow.md | 5 + plugins/home/package.json | 2 + .../FeaturedDocs/FeaturedDocs.stories.tsx | 118 ++++++++++++++ .../FeaturedDocs/FeaturedDocs.test.tsx | 77 +++++++++ .../FeaturedDocs/FeaturedDocs.tsx | 154 ++++++++++++++++++ .../homePageComponents/FeaturedDocs/index.ts | 17 ++ plugins/home/src/index.ts | 1 + plugins/home/src/plugin.ts | 15 ++ yarn.lock | 2 + 9 files changed, 391 insertions(+) create mode 100644 .changeset/wet-cats-allow.md create mode 100644 plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.stories.tsx create mode 100644 plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.test.tsx create mode 100644 plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx create mode 100644 plugins/home/src/homePageComponents/FeaturedDocs/index.ts diff --git a/.changeset/wet-cats-allow.md b/.changeset/wet-cats-allow.md new file mode 100644 index 0000000000..5c642109b0 --- /dev/null +++ b/.changeset/wet-cats-allow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Added a new Featured Docs component to plugin-home, which can display any entity given a filter diff --git a/plugins/home/package.json b/plugins/home/package.json index 5db7347f7c..49d61d0c16 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -47,6 +47,7 @@ "clean": "backstage-cli package clean" }, "dependencies": { + "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/core-app-api": "workspace:^", @@ -60,6 +61,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", + "@material-ui/styles": "^4.11.5", "@rjsf/core": "5.13.0", "@rjsf/material-ui": "5.13.0", "@rjsf/utils": "5.13.0", diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.stories.tsx b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.stories.tsx new file mode 100644 index 0000000000..c7b9a77be5 --- /dev/null +++ b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.stories.tsx @@ -0,0 +1,118 @@ +/* + * 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 { FeaturedDocs } from '../../plugin'; +import React, { ComponentType, PropsWithChildren } from 'react'; +import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; +import { Grid, makeStyles, Theme } from '@material-ui/core'; +import WarningIcon from '@material-ui/icons/Warning'; + +const docsEntities = [ + { + apiVersion: '1', + kind: 'Location', + metadata: { + name: 'getting-started-with-backstage', + title: 'Getting Started Docs', + description: + 'An awesome doc you want to feature to help out your customers. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pretium magna ut molestie lacinia. Nullam eget bibendum est, vitae finibus neque.', + }, + spec: { + type: 'documentation', + }, + }, +]; + +const mockCatalogApi = { + getEntities: async () => ({ items: docsEntities }), +}; + +const useStyles = makeStyles(() => ({ + cardTitleIcon: { + verticalAlign: 'bottom', + marginLeft: '-4px', + }, + docDescription: { + marginBottom: '16px', + marginTop: '12px', + }, + docSubLink: { + fontSize: 10, + fontWeight: 500, + lineHeight: 2, + }, + docsTitleLink: { + fontSize: 18, + fontWeight: 600, + lineHeight: 3, + }, +})); + +export default { + title: 'Plugins/Home/Components/FeaturedDocs', + decorators: [ + (Story: ComponentType>) => + wrapInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ), + ], +}; + +export const Default = () => { + return ( + + + + ); +}; + +export const ExampleCustomized = () => { + const styles = useStyles(); + const cardTitle = ( + <> + +   Important + + ); + return ( + + + + ); +}; diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.test.tsx b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.test.tsx new file mode 100644 index 0000000000..435e4a3686 --- /dev/null +++ b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.test.tsx @@ -0,0 +1,77 @@ +/* + * 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 { FeaturedDocs } from './FeaturedDocs'; +import React from 'react'; +import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; + +const docsEntities = [ + { + apiVersion: '1', + kind: 'Location', + metadata: { + name: 'getting-started-with-idp', + title: 'Getting Started Docs', + }, + spec: { + type: 'documentation', + }, + }, +]; + +describe('', () => { + const mockCatalogApi = { + getEntities: jest + .fn() + .mockImplementation(async () => ({ items: docsEntities })), + }; + let Wrapper: React.ComponentType; + + beforeAll(() => { + Wrapper = ({ children }: { children?: React.ReactNode }) => ( + + {children} + + ); + }); + it('should show expected featured doc and title', async () => { + const { getByTestId, getByText } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/home': entityRouteRef, + }, + }, + ); + const docsCardContent = getByTestId('docs-card-content'); + const docsEntity = getByText('Getting Started Docs'); + const docsTitle = getByText('Featured Doc'); + + expect(docsCardContent).toContainElement(docsEntity); + expect(docsTitle).toBeInTheDocument(); + }); +}); diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx new file mode 100644 index 0000000000..459dc7bdb6 --- /dev/null +++ b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx @@ -0,0 +1,154 @@ +/* + * 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 React from 'react'; +import useAsync from 'react-use/lib/useAsync'; +import { + LinkButton, + EmptyState, + Link, + CodeSnippet, + InfoCard, + Progress, + WarningPanel, +} from '@backstage/core-components'; +import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog-react'; +import { useApi } from '@backstage/core-plugin-api'; +import { EntityFilterQuery } from '@backstage/catalog-client'; +import { ClassNameMap } from '@material-ui/styles'; + +import { makeStyles, Theme, Typography } from '@material-ui/core'; + +type DocsCardProps = { + filter: EntityFilterQuery; + color?: 'inherit' | 'primary' | 'secondary' | undefined; + customStyles?: ClassNameMap | undefined; + emptyState?: React.ReactNode | undefined; + subLinkText?: string | undefined; + title?: React.ReactNode | string | undefined; +}; + +const useStyles = makeStyles(() => ({ + docDescription: { + fontSize: 16, + fontWeight: 400, + marginBottom: '16px', + marginTop: '12px', + }, + docSubLink: { + fontSize: 12, + fontWeight: 700, + lineHeight: 2, + }, + docsTitleLink: { + fontSize: 16, + fontWeight: 600, + lineHeight: 2, + }, +})); + +/** + * A component to display specific Featured Docs. + * @param {EntityFilterQuery} filter - The entity filter used to display only the intended item/s + * @param {'inherit' | 'primary' | 'secondary' | undefined} [color] - An optional color which can be customized through themes + * @param {ClassNameMap | undefined} [customStyles] - An optional ClassNameMap created with makeStyles + * @param {React.ReactNode | undefined} [emptyState] - An optional ReactNode for empty states + * @param {string | undefined} [subLinkText] - An optional string to customize sublink text + * @param {React.ReactNode | string | undefined} [title] - An optional string or ReactNode to customize the card title + * + * @public + */ +export const FeaturedDocs = (props: DocsCardProps) => { + const { color, customStyles, emptyState, filter, subLinkText, title } = props; + const linkText = subLinkText || 'LEARN MORE'; + const defaultStyles = useStyles(); + const styles = customStyles || defaultStyles; + const catalogApi: CatalogApi = useApi(catalogApiRef); + const { + value: entities, + loading, + error, + } = useAsync(async () => { + const response = await catalogApi.getEntities({ + filter: filter, + }); + return response.items; + }); + + if (loading) { + return ; + } + + if (error) { + return ( + + + + ); + } + + return ( + + {entities?.length + ? entities.map(d => ( +
+ + {d.metadata.title} + + + {d.metadata.description} + + + {linkText} + +
+ )) + : emptyState || ( + + DOCS + + } + /> + )} +
+ ); +}; diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/index.ts b/plugins/home/src/homePageComponents/FeaturedDocs/index.ts new file mode 100644 index 0000000000..08ca030dca --- /dev/null +++ b/plugins/home/src/homePageComponents/FeaturedDocs/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { FeaturedDocs } from './FeaturedDocs'; diff --git a/plugins/home/src/index.ts b/plugins/home/src/index.ts index a80f8bd93d..c7ae5c2467 100644 --- a/plugins/home/src/index.ts +++ b/plugins/home/src/index.ts @@ -34,6 +34,7 @@ export { HeaderWorldClock, HomePageTopVisited, HomePageRecentlyVisited, + FeaturedDocs, } from './plugin'; export * from './components'; export * from './assets'; diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index ab8c46baa1..d4ee89b707 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -210,3 +210,18 @@ export const HomePageRecentlyVisited = homePlugin.provide( import('./homePageComponents/VisitedByType/RecentlyVisited'), }), ); + +/** + * A component to display specific Featured Docs. + * + * @public + */ +export const FeaturedDocs = homePlugin.provide( + createComponentExtension({ + name: 'FeaturedDocs', + component: { + lazy: () => + import('./homePageComponents/FeaturedDocs').then(m => m.FeaturedDocs), + }, + }), +); diff --git a/yarn.lock b/yarn.lock index d5abffa914..6683bdd76c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7246,6 +7246,7 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-home@workspace:plugins/home" dependencies: + "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" @@ -7262,6 +7263,7 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 + "@material-ui/styles": ^4.11.5 "@rjsf/core": 5.13.0 "@rjsf/material-ui": 5.13.0 "@rjsf/utils": 5.13.0 From be94a7ca85aa976519451da3e6d7643dc988098b Mon Sep 17 00:00:00 2001 From: nikolar Date: Thu, 5 Oct 2023 15:34:37 -0700 Subject: [PATCH 02/10] update changeset and use createReactExtension Signed-off-by: nikolar --- .changeset/wet-cats-allow.md | 20 +++++++++++++++++++- plugins/home/src/plugin.ts | 3 ++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.changeset/wet-cats-allow.md b/.changeset/wet-cats-allow.md index 5c642109b0..c3e79ac25a 100644 --- a/.changeset/wet-cats-allow.md +++ b/.changeset/wet-cats-allow.md @@ -2,4 +2,22 @@ '@backstage/plugin-home': patch --- -Added a new Featured Docs component to plugin-home, which can display any entity given a filter +Added a new Featured Docs component to `plugin-home`, which can display any entity given a filter. + +``` +import { FeaturedDocs } from '@backstage/plugin-home'; + + +``` + +See the [storybook examples](https://backstage.io/storybook/?path=/story/plugins-home-components-featureddocs--default) diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index d4ee89b707..ca024faabd 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -19,6 +19,7 @@ import { createComponentExtension, createPlugin, createRoutableExtension, + createReactExtension, identityApiRef, storageApiRef, } from '@backstage/core-plugin-api'; @@ -217,7 +218,7 @@ export const HomePageRecentlyVisited = homePlugin.provide( * @public */ export const FeaturedDocs = homePlugin.provide( - createComponentExtension({ + createReactExtension({ name: 'FeaturedDocs', component: { lazy: () => From 6915b29931a6f030f811132f3ac03d148f7867bb Mon Sep 17 00:00:00 2001 From: nikolar Date: Wed, 18 Oct 2023 14:45:07 -0700 Subject: [PATCH 03/10] address pr comments Signed-off-by: nikolar --- plugins/home/api-report.md | 17 ++++ .../FeaturedDocs/FeaturedDocs.tsx | 81 +++++++++++-------- .../homePageComponents/FeaturedDocs/index.ts | 1 + plugins/home/src/homePageComponents/index.ts | 1 + 4 files changed, 65 insertions(+), 35 deletions(-) diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 14f685cfc1..09b1be16ba 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -11,9 +11,11 @@ import { CardConfig as CardConfig_2 } from '@backstage/plugin-home-react'; import { CardExtensionProps as CardExtensionProps_2 } from '@backstage/plugin-home-react'; import { CardLayout as CardLayout_2 } from '@backstage/plugin-home-react'; import { CardSettings as CardSettings_2 } from '@backstage/plugin-home-react'; +import { ClassNameMap } from '@material-ui/styles'; import { ComponentParts as ComponentParts_2 } from '@backstage/plugin-home-react'; import { ComponentRenderer as ComponentRenderer_2 } from '@backstage/plugin-home-react'; import { createCardExtension as createCardExtension_2 } from '@backstage/plugin-home-react'; +import { EntityFilterQuery } from '@backstage/catalog-client'; import { ErrorApi } from '@backstage/core-plugin-api'; import { IdentityApi } from '@backstage/core-plugin-api'; import { JSX as JSX_2 } from 'react'; @@ -101,6 +103,21 @@ export type CustomHomepageGridProps = { preventCollision?: boolean; }; +// @public +export const FeaturedDocs: (props: FeaturedDocsProps) => JSX_2.Element; + +// @public +export type FeaturedDocsProps = { + filter: EntityFilterQuery; + color?: 'inherit' | 'primary' | 'secondary' | undefined; + customStyles?: ClassNameMap | undefined; + emptyState?: React_2.ReactNode | undefined; + path?: string | undefined; + responseLimit?: number | undefined; + subLinkText?: string | undefined; + title?: React_2.ReactNode | string | undefined; +}; + // @public export const HeaderWorldClock: (props: { clockConfigs: ClockConfig[]; diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx index 459dc7bdb6..7b1f3f9d3e 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx @@ -20,10 +20,9 @@ import { LinkButton, EmptyState, Link, - CodeSnippet, InfoCard, Progress, - WarningPanel, + ErrorPanel, } from '@backstage/core-components'; import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog-react'; import { useApi } from '@backstage/core-plugin-api'; @@ -32,21 +31,36 @@ import { ClassNameMap } from '@material-ui/styles'; import { makeStyles, Theme, Typography } from '@material-ui/core'; -type DocsCardProps = { +/** + * Props customizing the component. + * + * @public + */ +export type FeaturedDocsProps = { + /** The entity filter used to display only the intended item/s */ filter: EntityFilterQuery; + /** An optional color which can be customized through themes */ color?: 'inherit' | 'primary' | 'secondary' | undefined; + /** An optional ClassNameMap created with makeStyles */ customStyles?: ClassNameMap | undefined; + /** An optional ReactNode for empty states */ emptyState?: React.ReactNode | undefined; + /** An optional path to set for entity entry */ + path?: string | undefined; + /** An optional limit to set for link destination */ + responseLimit?: number | undefined; + /** An optional string to customize sublink text */ subLinkText?: string | undefined; + /** An optional string or ReactNode to customize the card title */ title?: React.ReactNode | string | undefined; }; -const useStyles = makeStyles(() => ({ +const useStyles = makeStyles(theme => ({ docDescription: { fontSize: 16, fontWeight: 400, - marginBottom: '16px', - marginTop: '12px', + marginBottom: theme.spacing(2), + marginTop: theme.spacing(2), }, docSubLink: { fontSize: 12, @@ -62,17 +76,20 @@ const useStyles = makeStyles(() => ({ /** * A component to display specific Featured Docs. - * @param {EntityFilterQuery} filter - The entity filter used to display only the intended item/s - * @param {'inherit' | 'primary' | 'secondary' | undefined} [color] - An optional color which can be customized through themes - * @param {ClassNameMap | undefined} [customStyles] - An optional ClassNameMap created with makeStyles - * @param {React.ReactNode | undefined} [emptyState] - An optional ReactNode for empty states - * @param {string | undefined} [subLinkText] - An optional string to customize sublink text - * @param {React.ReactNode | string | undefined} [title] - An optional string or ReactNode to customize the card title * * @public */ -export const FeaturedDocs = (props: DocsCardProps) => { - const { color, customStyles, emptyState, filter, subLinkText, title } = props; +export const FeaturedDocs = (props: FeaturedDocsProps) => { + const { + color, + customStyles, + emptyState, + filter, + path, + responseLimit, + subLinkText, + title, + } = props; const linkText = subLinkText || 'LEARN MORE'; const defaultStyles = useStyles(); const styles = customStyles || defaultStyles; @@ -84,27 +101,15 @@ export const FeaturedDocs = (props: DocsCardProps) => { } = useAsync(async () => { const response = await catalogApi.getEntities({ filter: filter, + limit: responseLimit || 10, }); return response.items; }); - if (loading) { - return ; - } - - if (error) { - return ( - - - - ); - } - return ( + {loading && } + {error && } {entities?.length ? entities.map(d => (
@@ -112,9 +117,12 @@ export const FeaturedDocs = (props: DocsCardProps) => { className={styles.docsTitleLink} data-testid="docs-card-title" color={color || 'primary'} - to={`/docs/${d.metadata.namespace || 'default'}/${d.kind}/${ - d.metadata.name - }/`} + to={ + path || + `/docs/${d.metadata.namespace || 'default'}/${d.kind}/${ + d.metadata.name + }/` + } > {d.metadata.title} @@ -125,9 +133,12 @@ export const FeaturedDocs = (props: DocsCardProps) => { className={styles.docSubLink} data-testid="docs-card-sub-link" color={color || 'primary'} - to={`/docs/${d.metadata.namespace || 'default'}/${d.kind}/${ - d.metadata.name - }/`} + to={ + path || + `/docs/${d.metadata.namespace || 'default'}/${d.kind}/${ + d.metadata.name + }/` + } > {linkText} diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/index.ts b/plugins/home/src/homePageComponents/FeaturedDocs/index.ts index 08ca030dca..b4efed3268 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocs/index.ts +++ b/plugins/home/src/homePageComponents/FeaturedDocs/index.ts @@ -15,3 +15,4 @@ */ export { FeaturedDocs } from './FeaturedDocs'; +export type { FeaturedDocsProps } from './FeaturedDocs'; diff --git a/plugins/home/src/homePageComponents/index.ts b/plugins/home/src/homePageComponents/index.ts index 9149afa295..b3c7d77c46 100644 --- a/plugins/home/src/homePageComponents/index.ts +++ b/plugins/home/src/homePageComponents/index.ts @@ -18,3 +18,4 @@ export type { ToolkitContentProps, Tool } from './Toolkit'; export type { ClockConfig } from './HeaderWorldClock'; export type { WelcomeTitleLanguageProps } from './WelcomeTitle'; export type { VisitedByTypeProps, VisitedByTypeKind } from './VisitedByType'; +export type { FeaturedDocsProps } from './FeaturedDocs'; From c1352d8ef67851545bf6d11a00af2dbe76a4367e Mon Sep 17 00:00:00 2001 From: nikolar Date: Mon, 23 Oct 2023 21:45:06 -0700 Subject: [PATCH 04/10] update with feedback and signoff Signed-off-by: nikolar --- .../FeaturedDocs/FeaturedDocs.test.tsx | 2 +- .../FeaturedDocs/FeaturedDocs.tsx | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.test.tsx b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.test.tsx index 435e4a3686..f613aa8948 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.test.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.test.tsx @@ -39,7 +39,7 @@ describe('', () => { .fn() .mockImplementation(async () => ({ items: docsEntities })), }; - let Wrapper: React.ComponentType; + let Wrapper: React.ComponentType>; beforeAll(() => { Wrapper = ({ children }: { children?: React.ReactNode }) => ( diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx index 7b1f3f9d3e..656c7acc82 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx @@ -40,37 +40,37 @@ export type FeaturedDocsProps = { /** The entity filter used to display only the intended item/s */ filter: EntityFilterQuery; /** An optional color which can be customized through themes */ - color?: 'inherit' | 'primary' | 'secondary' | undefined; + color?: 'inherit' | 'primary' | 'secondary'; /** An optional ClassNameMap created with makeStyles */ - customStyles?: ClassNameMap | undefined; + customStyles?: ClassNameMap; /** An optional ReactNode for empty states */ - emptyState?: React.ReactNode | undefined; + emptyState?: React.ReactNode; /** An optional path to set for entity entry */ - path?: string | undefined; + path?: string; /** An optional limit to set for link destination */ - responseLimit?: number | undefined; + responseLimit?: number; /** An optional string to customize sublink text */ - subLinkText?: string | undefined; + subLinkText?: string; /** An optional string or ReactNode to customize the card title */ - title?: React.ReactNode | string | undefined; + title?: React.ReactNode | string; }; const useStyles = makeStyles(theme => ({ docDescription: { - fontSize: 16, - fontWeight: 400, + fontSize: theme.typography.body1.fontSize, + fontWeight: theme.typography.body1.fontWeight, marginBottom: theme.spacing(2), marginTop: theme.spacing(2), }, docSubLink: { - fontSize: 12, - fontWeight: 700, - lineHeight: 2, + fontSize: theme.typography.subtitle1.fontSize, + fontWeight: theme.typography.subtitle1.fontWeight, + lineHeight: theme.typography.subtitle1.lineHeight, }, docsTitleLink: { - fontSize: 16, - fontWeight: 600, - lineHeight: 2, + fontSize: theme.typography.h6.fontSize, + fontWeight: theme.typography.h6.fontWeight, + lineHeight: theme.typography.h6.lineHeight, }, })); @@ -116,7 +116,7 @@ export const FeaturedDocs = (props: FeaturedDocsProps) => { { Date: Mon, 23 Oct 2023 22:03:47 -0700 Subject: [PATCH 05/10] update changeset and api report Signed-off-by: nikolar --- .changeset/wet-cats-allow.md | 2 ++ plugins/home/api-report.md | 14 +++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.changeset/wet-cats-allow.md b/.changeset/wet-cats-allow.md index c3e79ac25a..b7de0f71ef 100644 --- a/.changeset/wet-cats-allow.md +++ b/.changeset/wet-cats-allow.md @@ -17,6 +17,8 @@ import { FeaturedDocs } from '@backstage/plugin-home'; subLinkText="More Details" color="secondary" emptyState={emptyStateReactNode} + path={'/customPath'} + responseLimit={5} /> ``` diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 09b1be16ba..779ece75fe 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -109,13 +109,13 @@ export const FeaturedDocs: (props: FeaturedDocsProps) => JSX_2.Element; // @public export type FeaturedDocsProps = { filter: EntityFilterQuery; - color?: 'inherit' | 'primary' | 'secondary' | undefined; - customStyles?: ClassNameMap | undefined; - emptyState?: React_2.ReactNode | undefined; - path?: string | undefined; - responseLimit?: number | undefined; - subLinkText?: string | undefined; - title?: React_2.ReactNode | string | undefined; + color?: 'inherit' | 'primary' | 'secondary'; + customStyles?: ClassNameMap; + emptyState?: React_2.ReactNode; + path?: string; + responseLimit?: number; + subLinkText?: string; + title?: React_2.ReactNode | string; }; // @public From 78c2cdf02def5a80589be19c7d6a0819947195f3 Mon Sep 17 00:00:00 2001 From: nikolar Date: Mon, 30 Oct 2023 19:10:57 -0700 Subject: [PATCH 06/10] add name BackstageFeaturedDocs to styles Signed-off-by: nikolar --- .../FeaturedDocs/FeaturedDocs.tsx | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx index 656c7acc82..74cc3934b5 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx @@ -55,24 +55,27 @@ export type FeaturedDocsProps = { title?: React.ReactNode | string; }; -const useStyles = makeStyles(theme => ({ - docDescription: { - fontSize: theme.typography.body1.fontSize, - fontWeight: theme.typography.body1.fontWeight, - marginBottom: theme.spacing(2), - marginTop: theme.spacing(2), - }, - docSubLink: { - fontSize: theme.typography.subtitle1.fontSize, - fontWeight: theme.typography.subtitle1.fontWeight, - lineHeight: theme.typography.subtitle1.lineHeight, - }, - docsTitleLink: { - fontSize: theme.typography.h6.fontSize, - fontWeight: theme.typography.h6.fontWeight, - lineHeight: theme.typography.h6.lineHeight, - }, -})); +const useStyles = makeStyles( + theme => ({ + docDescription: { + fontSize: theme.typography.body1.fontSize, + fontWeight: theme.typography.body1.fontWeight, + marginBottom: theme.spacing(2), + marginTop: theme.spacing(2), + }, + docSubLink: { + fontSize: theme.typography.subtitle1.fontSize, + fontWeight: theme.typography.subtitle1.fontWeight, + lineHeight: theme.typography.subtitle1.lineHeight, + }, + docsTitleLink: { + fontSize: theme.typography.h6.fontSize, + fontWeight: theme.typography.h6.fontWeight, + lineHeight: theme.typography.h6.lineHeight, + }, + }), + { name: 'BackstageFeaturedDocs' }, +); /** * A component to display specific Featured Docs. From 7393c441487ae06c37ac94d0b80bb39c15e40dd2 Mon Sep 17 00:00:00 2001 From: nikolar Date: Wed, 1 Nov 2023 08:17:18 -0700 Subject: [PATCH 07/10] return loading and error before entities and empty state Signed-off-by: nikolar --- .../src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx index 74cc3934b5..479c2a6292 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.tsx @@ -109,10 +109,15 @@ export const FeaturedDocs = (props: FeaturedDocsProps) => { return response.items; }); + if (loading) { + return ; + } + if (error) { + return ; + } + return ( - {loading && } - {error && } {entities?.length ? entities.map(d => (
From 0dbaa6221cc680e116b97fb1d82438fd492f3504 Mon Sep 17 00:00:00 2001 From: nikolar Date: Wed, 8 Nov 2023 16:02:18 -0800 Subject: [PATCH 08/10] review feedback and update package.json changeset and api-report as a result of feedback changes Signed-off-by: nikolar --- .changeset/wet-cats-allow.md | 8 ++- plugins/home/api-report.md | 11 ++-- plugins/home/package.json | 1 - .../FeaturedDocsCard.stories.tsx} | 54 ++----------------- .../FeaturedDocsCard.test.tsx} | 7 ++- .../FeaturedDocsCard.tsx} | 41 ++++++-------- .../index.ts | 4 +- plugins/home/src/homePageComponents/index.ts | 2 +- plugins/home/src/index.ts | 2 +- plugins/home/src/plugin.ts | 6 ++- yarn.lock | 1 - 11 files changed, 38 insertions(+), 99 deletions(-) rename plugins/home/src/homePageComponents/{FeaturedDocs/FeaturedDocs.stories.tsx => FeaturedDocsCard/FeaturedDocsCard.stories.tsx} (63%) rename plugins/home/src/homePageComponents/{FeaturedDocs/FeaturedDocs.test.tsx => FeaturedDocsCard/FeaturedDocsCard.test.tsx} (93%) rename plugins/home/src/homePageComponents/{FeaturedDocs/FeaturedDocs.tsx => FeaturedDocsCard/FeaturedDocsCard.tsx} (81%) rename plugins/home/src/homePageComponents/{FeaturedDocs => FeaturedDocsCard}/index.ts (83%) diff --git a/.changeset/wet-cats-allow.md b/.changeset/wet-cats-allow.md index b7de0f71ef..815f7fdc30 100644 --- a/.changeset/wet-cats-allow.md +++ b/.changeset/wet-cats-allow.md @@ -5,19 +5,17 @@ Added a new Featured Docs component to `plugin-home`, which can display any entity given a filter. ``` -import { FeaturedDocs } from '@backstage/plugin-home'; +import { FeaturedDocsCard } from '@backstage/plugin-home'; - ``` diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 779ece75fe..6a6022170b 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -11,7 +11,6 @@ import { CardConfig as CardConfig_2 } from '@backstage/plugin-home-react'; import { CardExtensionProps as CardExtensionProps_2 } from '@backstage/plugin-home-react'; import { CardLayout as CardLayout_2 } from '@backstage/plugin-home-react'; import { CardSettings as CardSettings_2 } from '@backstage/plugin-home-react'; -import { ClassNameMap } from '@material-ui/styles'; import { ComponentParts as ComponentParts_2 } from '@backstage/plugin-home-react'; import { ComponentRenderer as ComponentRenderer_2 } from '@backstage/plugin-home-react'; import { createCardExtension as createCardExtension_2 } from '@backstage/plugin-home-react'; @@ -104,18 +103,16 @@ export type CustomHomepageGridProps = { }; // @public -export const FeaturedDocs: (props: FeaturedDocsProps) => JSX_2.Element; +export const FeaturedDocsCard: (props: FeaturedDocsCardProps) => JSX_2.Element; // @public -export type FeaturedDocsProps = { +export type FeaturedDocsCardProps = { filter: EntityFilterQuery; - color?: 'inherit' | 'primary' | 'secondary'; - customStyles?: ClassNameMap; emptyState?: React_2.ReactNode; - path?: string; + linkDestination?: string; responseLimit?: number; subLinkText?: string; - title?: React_2.ReactNode | string; + title?: React_2.ReactNode; }; // @public diff --git a/plugins/home/package.json b/plugins/home/package.json index 49d61d0c16..b5110104b0 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -61,7 +61,6 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@material-ui/styles": "^4.11.5", "@rjsf/core": "5.13.0", "@rjsf/material-ui": "5.13.0", "@rjsf/utils": "5.13.0", diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.stories.tsx b/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.stories.tsx similarity index 63% rename from plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.stories.tsx rename to plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.stories.tsx index c7b9a77be5..498d177c07 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.stories.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.stories.tsx @@ -14,12 +14,11 @@ * limitations under the License. */ -import { FeaturedDocs } from '../../plugin'; +import { FeaturedDocsCard } from '../../plugin'; import React, { ComponentType, PropsWithChildren } from 'react'; import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils'; import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; -import { Grid, makeStyles, Theme } from '@material-ui/core'; -import WarningIcon from '@material-ui/icons/Warning'; +import { Grid } from '@material-ui/core'; const docsEntities = [ { @@ -41,29 +40,8 @@ const mockCatalogApi = { getEntities: async () => ({ items: docsEntities }), }; -const useStyles = makeStyles(() => ({ - cardTitleIcon: { - verticalAlign: 'bottom', - marginLeft: '-4px', - }, - docDescription: { - marginBottom: '16px', - marginTop: '12px', - }, - docSubLink: { - fontSize: 10, - fontWeight: 500, - lineHeight: 2, - }, - docsTitleLink: { - fontSize: 18, - fontWeight: 600, - lineHeight: 3, - }, -})); - export default { - title: 'Plugins/Home/Components/FeaturedDocs', + title: 'Plugins/Home/Components/FeaturedDocsCard', decorators: [ (Story: ComponentType>) => wrapInTestApp( @@ -82,7 +60,7 @@ export default { export const Default = () => { return ( - { ); }; - -export const ExampleCustomized = () => { - const styles = useStyles(); - const cardTitle = ( - <> - -   Important - - ); - return ( - - - - ); -}; diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.test.tsx b/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.test.tsx similarity index 93% rename from plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.test.tsx rename to plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.test.tsx index f613aa8948..175b1423b0 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocs/FeaturedDocs.test.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { FeaturedDocs } from './FeaturedDocs'; +import { FeaturedDocsCard } from './FeaturedDocsCard'; import React from 'react'; import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; @@ -33,7 +33,7 @@ const docsEntities = [ }, ]; -describe('', () => { +describe('', () => { const mockCatalogApi = { getEntities: jest .fn() @@ -51,8 +51,7 @@ describe('', () => { it('should show expected featured doc and title', async () => { const { getByTestId, getByText } = await renderInTestApp( - component. + * Props customizing the component. * * @public */ -export type FeaturedDocsProps = { +export type FeaturedDocsCardProps = { /** The entity filter used to display only the intended item/s */ filter: EntityFilterQuery; - /** An optional color which can be customized through themes */ - color?: 'inherit' | 'primary' | 'secondary'; - /** An optional ClassNameMap created with makeStyles */ - customStyles?: ClassNameMap; /** An optional ReactNode for empty states */ emptyState?: React.ReactNode; - /** An optional path to set for entity entry */ - path?: string; + /** An optional linkDestination to set for the Featured Doc */ + linkDestination?: string; /** An optional limit to set for link destination */ responseLimit?: number; /** An optional string to customize sublink text */ subLinkText?: string; /** An optional string or ReactNode to customize the card title */ - title?: React.ReactNode | string; + title?: React.ReactNode; }; const useStyles = makeStyles( @@ -74,7 +69,7 @@ const useStyles = makeStyles( lineHeight: theme.typography.h6.lineHeight, }, }), - { name: 'BackstageFeaturedDocs' }, + { name: 'HomeFeaturedDocsCard' }, ); /** @@ -82,20 +77,17 @@ const useStyles = makeStyles( * * @public */ -export const FeaturedDocs = (props: FeaturedDocsProps) => { +export const FeaturedDocsCard = (props: FeaturedDocsCardProps) => { const { - color, - customStyles, emptyState, filter, - path, + linkDestination, responseLimit, subLinkText, title, } = props; const linkText = subLinkText || 'LEARN MORE'; - const defaultStyles = useStyles(); - const styles = customStyles || defaultStyles; + const styles = useStyles(); const catalogApi: CatalogApi = useApi(catalogApiRef); const { value: entities, @@ -124,9 +116,8 @@ export const FeaturedDocs = (props: FeaturedDocsProps) => { { > {d.metadata.title} - - {d.metadata.description} - + {d.metadata.description && ( + + {d.metadata.description} + + )} { description="Create your own document. Check out our Getting Started Information" action={ diff --git a/plugins/home/src/homePageComponents/FeaturedDocs/index.ts b/plugins/home/src/homePageComponents/FeaturedDocsCard/index.ts similarity index 83% rename from plugins/home/src/homePageComponents/FeaturedDocs/index.ts rename to plugins/home/src/homePageComponents/FeaturedDocsCard/index.ts index b4efed3268..99169327f9 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocs/index.ts +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export { FeaturedDocs } from './FeaturedDocs'; -export type { FeaturedDocsProps } from './FeaturedDocs'; +export { FeaturedDocsCard } from './FeaturedDocsCard'; +export type { FeaturedDocsCardProps } from './FeaturedDocsCard'; diff --git a/plugins/home/src/homePageComponents/index.ts b/plugins/home/src/homePageComponents/index.ts index b3c7d77c46..dc611291fb 100644 --- a/plugins/home/src/homePageComponents/index.ts +++ b/plugins/home/src/homePageComponents/index.ts @@ -18,4 +18,4 @@ export type { ToolkitContentProps, Tool } from './Toolkit'; export type { ClockConfig } from './HeaderWorldClock'; export type { WelcomeTitleLanguageProps } from './WelcomeTitle'; export type { VisitedByTypeProps, VisitedByTypeKind } from './VisitedByType'; -export type { FeaturedDocsProps } from './FeaturedDocs'; +export type { FeaturedDocsCardProps } from './FeaturedDocsCard'; diff --git a/plugins/home/src/index.ts b/plugins/home/src/index.ts index c7ae5c2467..154186f169 100644 --- a/plugins/home/src/index.ts +++ b/plugins/home/src/index.ts @@ -34,7 +34,7 @@ export { HeaderWorldClock, HomePageTopVisited, HomePageRecentlyVisited, - FeaturedDocs, + FeaturedDocsCard, } from './plugin'; export * from './components'; export * from './assets'; diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index ca024faabd..42e0237589 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -217,12 +217,14 @@ export const HomePageRecentlyVisited = homePlugin.provide( * * @public */ -export const FeaturedDocs = homePlugin.provide( +export const FeaturedDocsCard = homePlugin.provide( createReactExtension({ name: 'FeaturedDocs', component: { lazy: () => - import('./homePageComponents/FeaturedDocs').then(m => m.FeaturedDocs), + import('./homePageComponents/FeaturedDocsCard').then( + m => m.FeaturedDocsCard, + ), }, }), ); diff --git a/yarn.lock b/yarn.lock index 6683bdd76c..1f9df5c3b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7263,7 +7263,6 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 - "@material-ui/styles": ^4.11.5 "@rjsf/core": 5.13.0 "@rjsf/material-ui": 5.13.0 "@rjsf/utils": 5.13.0 From 73f927b60cb42e840dc218d99c3f5b9311cc78e9 Mon Sep 17 00:00:00 2001 From: nikolar Date: Thu, 9 Nov 2023 13:20:32 -0800 Subject: [PATCH 09/10] add more specific key Signed-off-by: nikolar --- .../homePageComponents/FeaturedDocsCard/FeaturedDocsCard.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.tsx b/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.tsx index d48d041606..715608c62a 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.tsx @@ -112,7 +112,10 @@ export const FeaturedDocsCard = (props: FeaturedDocsCardProps) => { {entities?.length ? entities.map(d => ( -
+
Date: Mon, 13 Nov 2023 12:42:23 -0800 Subject: [PATCH 10/10] use createCardExtension Signed-off-by: nikolar --- .changeset/wet-cats-allow.md | 1 - plugins/home/api-report.md | 7 +- ...uredDocsCard.test.tsx => Content.test.tsx} | 7 +- .../{FeaturedDocsCard.tsx => Content.tsx} | 125 ++++++++---------- .../FeaturedDocsCard.stories.tsx | 1 - .../FeaturedDocsCard/index.ts | 4 +- plugins/home/src/plugin.ts | 19 ++- 7 files changed, 75 insertions(+), 89 deletions(-) rename plugins/home/src/homePageComponents/FeaturedDocsCard/{FeaturedDocsCard.test.tsx => Content.test.tsx} (91%) rename plugins/home/src/homePageComponents/FeaturedDocsCard/{FeaturedDocsCard.tsx => Content.tsx} (56%) diff --git a/.changeset/wet-cats-allow.md b/.changeset/wet-cats-allow.md index 815f7fdc30..12d0eb7069 100644 --- a/.changeset/wet-cats-allow.md +++ b/.changeset/wet-cats-allow.md @@ -12,7 +12,6 @@ import { FeaturedDocsCard } from '@backstage/plugin-home'; 'spec.type': 'documentation', 'metadata.name': 'getting-started-with-backstage', }} - title={cardTitleReactNode} subLinkText="More Details" emptyState={emptyStateReactNode} linkDestination={'/customPath'} diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 6a6022170b..18049cae84 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -103,16 +103,17 @@ export type CustomHomepageGridProps = { }; // @public -export const FeaturedDocsCard: (props: FeaturedDocsCardProps) => JSX_2.Element; +export const FeaturedDocsCard: ( + props: CardExtensionProps_2, +) => JSX_2.Element; // @public export type FeaturedDocsCardProps = { filter: EntityFilterQuery; - emptyState?: React_2.ReactNode; + emptyState?: React_2.JSX.Element; linkDestination?: string; responseLimit?: number; subLinkText?: string; - title?: React_2.ReactNode; }; // @public diff --git a/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.test.tsx b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.test.tsx similarity index 91% rename from plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.test.tsx rename to plugins/home/src/homePageComponents/FeaturedDocsCard/Content.test.tsx index 175b1423b0..427a1484d5 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.test.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { FeaturedDocsCard } from './FeaturedDocsCard'; +import { Content } from './Content'; import React from 'react'; import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; @@ -51,13 +51,12 @@ describe('', () => { it('should show expected featured doc and title', async () => { const { getByTestId, getByText } = await renderInTestApp( - , { @@ -68,9 +67,7 @@ describe('', () => { ); const docsCardContent = getByTestId('docs-card-content'); const docsEntity = getByText('Getting Started Docs'); - const docsTitle = getByText('Featured Doc'); expect(docsCardContent).toContainElement(docsEntity); - expect(docsTitle).toBeInTheDocument(); }); }); diff --git a/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.tsx b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx similarity index 56% rename from plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.tsx rename to plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx index 715608c62a..dfa83ce074 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx @@ -20,7 +20,6 @@ import { LinkButton, EmptyState, Link, - InfoCard, Progress, ErrorPanel, } from '@backstage/core-components'; @@ -39,15 +38,13 @@ export type FeaturedDocsCardProps = { /** The entity filter used to display only the intended item/s */ filter: EntityFilterQuery; /** An optional ReactNode for empty states */ - emptyState?: React.ReactNode; + emptyState?: React.JSX.Element; /** An optional linkDestination to set for the Featured Doc */ linkDestination?: string; /** An optional limit to set for link destination */ responseLimit?: number; /** An optional string to customize sublink text */ subLinkText?: string; - /** An optional string or ReactNode to customize the card title */ - title?: React.ReactNode; }; const useStyles = makeStyles( @@ -77,15 +74,9 @@ const useStyles = makeStyles( * * @public */ -export const FeaturedDocsCard = (props: FeaturedDocsCardProps) => { - const { - emptyState, - filter, - linkDestination, - responseLimit, - subLinkText, - title, - } = props; +export const Content = (props: FeaturedDocsCardProps): JSX.Element => { + const { emptyState, filter, linkDestination, responseLimit, subLinkText } = + props; const linkText = subLinkText || 'LEARN MORE'; const styles = useStyles(); const catalogApi: CatalogApi = useApi(catalogApiRef); @@ -108,60 +99,60 @@ export const FeaturedDocsCard = (props: FeaturedDocsCardProps) => { return ; } - return ( - - {entities?.length - ? entities.map(d => ( -
- - {d.metadata.title} - - {d.metadata.description && ( - - {d.metadata.description} - - )} - - {linkText} - -
- )) - : emptyState || ( - - DOCS - - } - /> + return entities?.length ? ( + <> + {entities.map(d => ( +
+ + {d.metadata.title} + + {d.metadata.description && ( + + {d.metadata.description} + )} - + + {linkText} + +
+ ))} + + ) : ( + emptyState || ( + + DOCS + + } + /> + ) ); }; diff --git a/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.stories.tsx b/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.stories.tsx index 498d177c07..91f0dbf27e 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.stories.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/FeaturedDocsCard.stories.tsx @@ -65,7 +65,6 @@ export const Default = () => { 'spec.type': 'documentation', 'metadata.name': 'getting-started-with-backstage', }} - title="Featured Doc" /> ); diff --git a/plugins/home/src/homePageComponents/FeaturedDocsCard/index.ts b/plugins/home/src/homePageComponents/FeaturedDocsCard/index.ts index 99169327f9..5151fa370c 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocsCard/index.ts +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export { FeaturedDocsCard } from './FeaturedDocsCard'; -export type { FeaturedDocsCardProps } from './FeaturedDocsCard'; +export { Content } from './Content'; +export type { FeaturedDocsCardProps } from './Content'; diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index 42e0237589..5361ecd9c0 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -19,12 +19,15 @@ import { createComponentExtension, createPlugin, createRoutableExtension, - createReactExtension, identityApiRef, storageApiRef, } from '@backstage/core-plugin-api'; import { createCardExtension } from '@backstage/plugin-home-react'; -import { ToolkitContentProps, VisitedByTypeProps } from './homePageComponents'; +import { + ToolkitContentProps, + VisitedByTypeProps, + FeaturedDocsCardProps, +} from './homePageComponents'; import { rootRouteRef } from './routes'; import { VisitsStorageApi, visitsApiRef } from './api'; @@ -218,13 +221,9 @@ export const HomePageRecentlyVisited = homePlugin.provide( * @public */ export const FeaturedDocsCard = homePlugin.provide( - createReactExtension({ - name: 'FeaturedDocs', - component: { - lazy: () => - import('./homePageComponents/FeaturedDocsCard').then( - m => m.FeaturedDocsCard, - ), - }, + createCardExtension({ + name: 'FeaturedDocsCard', + title: 'Featured Docs', + components: () => import('./homePageComponents/FeaturedDocsCard'), }), );