From a4a777441de6f1619e19bcefc7b958ee127335d2 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Mon, 7 Feb 2022 11:28:26 +0100 Subject: [PATCH] [Home] homepage component starred entities (#9378) * implement starred entities home page component + add to storybook Signed-off-by: Emma Indal * update default template to include starred entities Signed-off-by: Emma Indal * add changeset Signed-off-by: Emma Indal * api report Signed-off-by: Emma Indal --- .changeset/wise-plants-tease.md | 5 ++ plugins/home/api-report.md | 7 ++ plugins/home/package.json | 4 +- .../StarredEntities/Content.test.tsx | 70 +++++++++++++++++ .../StarredEntities/Content.tsx | 75 +++++++++++++++++++ .../StarredEntities.stories.tsx | 73 ++++++++++++++++++ .../StarredEntities/index.ts | 17 +++++ plugins/home/src/index.ts | 1 + plugins/home/src/plugin.ts | 13 ++++ .../src/templates/DefaultTemplate.stories.tsx | 60 +++++++++++---- 10 files changed, 308 insertions(+), 17 deletions(-) create mode 100644 .changeset/wise-plants-tease.md create mode 100644 plugins/home/src/homePageComponents/StarredEntities/Content.test.tsx create mode 100644 plugins/home/src/homePageComponents/StarredEntities/Content.tsx create mode 100644 plugins/home/src/homePageComponents/StarredEntities/StarredEntities.stories.tsx create mode 100644 plugins/home/src/homePageComponents/StarredEntities/index.ts diff --git a/.changeset/wise-plants-tease.md b/.changeset/wise-plants-tease.md new file mode 100644 index 0000000000..206704435d --- /dev/null +++ b/.changeset/wise-plants-tease.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Adds new StarredEntities component responsible for rendering a list of starred entities on the home page diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index fab2b05bb6..264189ee07 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -113,6 +113,13 @@ export const HomePageRandomJoke: ( }, ) => JSX.Element; +// @public +export const HomePageStarredEntities: ( + props: ComponentRenderer & { + title?: string | undefined; + }, +) => JSX.Element; + // Warning: (ae-forgotten-export) The symbol "ToolkitContentProps" needs to be exported by the entry point index.d.ts // // @public diff --git a/plugins/home/package.json b/plugins/home/package.json index a1a09329f8..b1d4fc362e 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -21,10 +21,12 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/catalog-model": "^0.9.10", "@backstage/core-components": "^0.8.7", "@backstage/core-plugin-api": "^0.6.0", - "@backstage/theme": "^0.2.14", + "@backstage/plugin-catalog-react": "^0.6.13", "@backstage/plugin-search": "^0.6.1", + "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", diff --git a/plugins/home/src/homePageComponents/StarredEntities/Content.test.tsx b/plugins/home/src/homePageComponents/StarredEntities/Content.test.tsx new file mode 100644 index 0000000000..ccc36f6964 --- /dev/null +++ b/plugins/home/src/homePageComponents/StarredEntities/Content.test.tsx @@ -0,0 +1,70 @@ +/* + * 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 { + renderInTestApp, + TestApiProvider, + MockStorageApi, +} from '@backstage/test-utils'; +import { + starredEntitiesApiRef, + entityRouteRef, + DefaultStarredEntitiesApi, +} from '@backstage/plugin-catalog-react'; +import React from 'react'; +import { Content } from './Content'; + +describe('StarredEntitiesContent', () => { + it('should render list of tools', async () => { + const mockStorageApi = MockStorageApi.create(); + await mockStorageApi + .forBucket('starredEntities') + .set('entityRefs', [ + 'component:default/mock-starred-entity', + 'component:default/mock-starred-entity-2', + ]); + + const { getByText } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + + expect(getByText('mock-starred-entity')).toBeInTheDocument(); + expect(getByText('mock-starred-entity-2')).toBeInTheDocument(); + expect(getByText('mock-starred-entity').closest('a')).toHaveAttribute( + 'href', + '/catalog/default/component/mock-starred-entity', + ); + expect(getByText('mock-starred-entity-2').closest('a')).toHaveAttribute( + 'href', + '/catalog/default/component/mock-starred-entity-2', + ); + }); +}); diff --git a/plugins/home/src/homePageComponents/StarredEntities/Content.tsx b/plugins/home/src/homePageComponents/StarredEntities/Content.tsx new file mode 100644 index 0000000000..6c9775c842 --- /dev/null +++ b/plugins/home/src/homePageComponents/StarredEntities/Content.tsx @@ -0,0 +1,75 @@ +/* + * 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 { + useStarredEntities, + entityRouteRef, +} from '@backstage/plugin-catalog-react'; +import { parseEntityRef } from '@backstage/catalog-model'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import { Link } from '@backstage/core-components'; +import { + List, + ListItem, + ListItemSecondaryAction, + IconButton, + ListItemText, + Tooltip, + Typography, +} from '@material-ui/core'; +import StarIcon from '@material-ui/icons/Star'; +import React from 'react'; + +/** + * A component to display a list of starred entities for the user. + * + * @public + */ + +export const Content = () => { + const catalogEntityRoute = useRouteRef(entityRouteRef); + const { starredEntities, toggleStarredEntity } = useStarredEntities(); + + if (starredEntities.size === 0) + return ( + + You do not have any starred entities yet! + + ); + + return ( + + {Array.from(starredEntities).map(entity => ( + + + + + + + toggleStarredEntity(entity)} + > + + + + + + ))} + + ); +}; diff --git a/plugins/home/src/homePageComponents/StarredEntities/StarredEntities.stories.tsx b/plugins/home/src/homePageComponents/StarredEntities/StarredEntities.stories.tsx new file mode 100644 index 0000000000..2e63762369 --- /dev/null +++ b/plugins/home/src/homePageComponents/StarredEntities/StarredEntities.stories.tsx @@ -0,0 +1,73 @@ +/* + * 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 { HomePageStarredEntities } from '../../plugin'; +import { + wrapInTestApp, + TestApiProvider, + MockStorageApi, +} from '@backstage/test-utils'; +import { + starredEntitiesApiRef, + entityRouteRef, + DefaultStarredEntitiesApi, +} from '@backstage/plugin-catalog-react'; +import { Grid } from '@material-ui/core'; +import React, { ComponentType } from 'react'; + +const mockStorageApi = MockStorageApi.create(); +mockStorageApi + .forBucket('starredEntities') + .set('entityRefs', [ + 'component:default/example-starred-entity', + 'component:default/example-starred-entity-2', + 'component:default/example-starred-entity-3', + 'component:default/example-starred-entity-4', + ]); + +export default { + title: 'Plugins/Home/Components/StarredEntities', + decorators: [ + (Story: ComponentType<{}>) => + wrapInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ), + ], +}; + +export const Default = () => { + return ( + + + + ); +}; diff --git a/plugins/home/src/homePageComponents/StarredEntities/index.ts b/plugins/home/src/homePageComponents/StarredEntities/index.ts new file mode 100644 index 0000000000..1faa9a2426 --- /dev/null +++ b/plugins/home/src/homePageComponents/StarredEntities/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 { Content } from './Content'; diff --git a/plugins/home/src/index.ts b/plugins/home/src/index.ts index dcd61f1fa3..4871323677 100644 --- a/plugins/home/src/index.ts +++ b/plugins/home/src/index.ts @@ -26,6 +26,7 @@ export { HomePageRandomJoke, HomePageToolkit, HomePageCompanyLogo, + HomePageStarredEntities, ComponentAccordion, ComponentTabs, ComponentTab, diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index 135ac97af0..32005a9064 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -115,3 +115,16 @@ export const HomePageToolkit = homePlugin.provide( components: () => import('./homePageComponents/Toolkit'), }), ); + +/** + * A component to display a list of starred entities for the user. + * + * @public + */ +export const HomePageStarredEntities = homePlugin.provide( + createCardExtension({ + name: 'HomePageStarredEntities', + title: 'Your Starred Entities', + components: () => import('./homePageComponents/StarredEntities'), + }), +); diff --git a/plugins/home/src/templates/DefaultTemplate.stories.tsx b/plugins/home/src/templates/DefaultTemplate.stories.tsx index fb62591fbd..ba5e1f33fd 100644 --- a/plugins/home/src/templates/DefaultTemplate.stories.tsx +++ b/plugins/home/src/templates/DefaultTemplate.stories.tsx @@ -14,20 +14,38 @@ * limitations under the License. */ -import {TemplateBackstageLogo} from './TemplateBackstageLogo'; -import {TemplateBackstageLogoIcon} from './TemplateBackstageLogoIcon'; -import { HomePageToolkit, HomePageCompanyLogo } from '../plugin'; -import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { TemplateBackstageLogo } from './TemplateBackstageLogo'; +import { TemplateBackstageLogoIcon } from './TemplateBackstageLogoIcon'; +import { + HomePageToolkit, + HomePageCompanyLogo, + HomePageStarredEntities, +} from '../plugin'; +import { wrapInTestApp, TestApiProvider, MockStorageApi} from '@backstage/test-utils'; import { Content, Page, InfoCard } from '@backstage/core-components'; +import { + starredEntitiesApiRef, + entityRouteRef, + DefaultStarredEntitiesApi +} from '@backstage/plugin-catalog-react'; import { HomePageSearchBar, SearchContextProvider, searchApiRef, - searchPlugin + searchPlugin, } from '@backstage/plugin-search'; import { Grid, makeStyles } from '@material-ui/core'; -import React, { ComponentType} from 'react'; +import React, { ComponentType } from 'react'; +const mockStorageApi = MockStorageApi.create(); +mockStorageApi + .forBucket('starredEntities') + .set('entityRefs', [ + 'component:default/example-starred-entity', + 'component:default/example-starred-entity-2', + 'component:default/example-starred-entity-3', + 'component:default/example-starred-entity-4' + ]); export default { title: 'Plugins/Home/Templates', @@ -35,13 +53,26 @@ export default { (Story: ComponentType<{}>) => wrapInTestApp( <> - Promise.resolve({results: []})}]]}> - + Promise.resolve({ results: [] }) }], + ]} + > + , { - mountedRoutes: {'/hello-company': searchPlugin.routes.root } - } + mountedRoutes: { + '/hello-company': searchPlugin.routes.root, + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, ), ], }; @@ -82,20 +113,17 @@ export const DefaultTemplate = () => { } + logo={} /> - - {/* placeholder for content */} -
- +