[Home] homepage component starred entities (#9378)

* implement starred entities home page component + add to storybook

Signed-off-by: Emma Indal <emma.indahl@gmail.com>

* update default template to include starred entities

Signed-off-by: Emma Indal <emma.indahl@gmail.com>

* add changeset

Signed-off-by: Emma Indal <emma.indahl@gmail.com>

* api report

Signed-off-by: Emma Indal <emma.indahl@gmail.com>
This commit is contained in:
Emma Indal
2022-02-07 11:28:26 +01:00
committed by GitHub
parent 709bc85ca1
commit a4a777441d
10 changed files with 308 additions and 17 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-home': patch
---
Adds new StarredEntities component responsible for rendering a list of starred entities on the home page
+7
View File
@@ -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
+3 -1
View File
@@ -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",
@@ -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(
<TestApiProvider
apis={[
[
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({
storageApi: mockStorageApi,
}),
],
]}
>
<Content />
</TestApiProvider>,
{
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',
);
});
});
@@ -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 (
<Typography variant="body1">
You do not have any starred entities yet!
</Typography>
);
return (
<List>
{Array.from(starredEntities).map(entity => (
<ListItem key={entity}>
<Link to={catalogEntityRoute(parseEntityRef(entity))}>
<ListItemText primary={parseEntityRef(entity).name} />
</Link>
<ListItemSecondaryAction>
<Tooltip title="Remove from starred">
<IconButton
edge="end"
aria-label="unstar"
onClick={() => toggleStarredEntity(entity)}
>
<StarIcon />
</IconButton>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
))}
</List>
);
};
@@ -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(
<TestApiProvider
apis={[
[
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({
storageApi: mockStorageApi,
}),
],
]}
>
<Story />
</TestApiProvider>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
),
],
};
export const Default = () => {
return (
<Grid item xs={12} md={6}>
<HomePageStarredEntities />
</Grid>
);
};
@@ -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';
+1
View File
@@ -26,6 +26,7 @@ export {
HomePageRandomJoke,
HomePageToolkit,
HomePageCompanyLogo,
HomePageStarredEntities,
ComponentAccordion,
ComponentTabs,
ComponentTab,
+13
View File
@@ -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'),
}),
);
@@ -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(
<>
<TestApiProvider apis={[[searchApiRef, {query: () => Promise.resolve({results: []})}]]}>
<Story />
<TestApiProvider
apis={[
[
starredEntitiesApiRef,
new DefaultStarredEntitiesApi({
storageApi: mockStorageApi,
}),
],
[searchApiRef, { query: () => Promise.resolve({ results: [] }) }],
]}
>
<Story />
</TestApiProvider>
</>,
{
mountedRoutes: {'/hello-company': searchPlugin.routes.root }
}
mountedRoutes: {
'/hello-company': searchPlugin.routes.root,
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
),
],
};
@@ -82,20 +113,17 @@ export const DefaultTemplate = () => {
<Grid container justifyContent="center" spacing={6}>
<HomePageCompanyLogo
className={container}
logo={<TemplateBackstageLogo classes={{svg, path}} />}
logo={<TemplateBackstageLogo classes={{ svg, path }} />}
/>
<Grid container item xs={12} alignItems="center" direction="row">
<HomePageSearchBar
classes={{root: classes.searchBar}}
classes={{ root: classes.searchBar }}
placeholder="Search"
/>
</Grid>
<Grid container item xs={12}>
<Grid item xs={12} md={6}>
<InfoCard title="Composable Section">
{/* placeholder for content */}
<div style={{ height: 210 }} />
</InfoCard>
<HomePageStarredEntities />
</Grid>
<Grid item xs={12} md={6}>
<HomePageToolkit