feat(HomePlugin): split StarredEntities groupByKind in tabs

Signed-off-by: Antonio Bergas <anthonyprincedom@hotmail.com>
This commit is contained in:
Antonio Bergas
2023-11-11 12:21:36 +01:00
parent 6e0f2e771b
commit e0e10b272b
3 changed files with 117 additions and 42 deletions
@@ -0,0 +1,60 @@
/*
* Copyright 2023 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 { Entity, stringifyEntityRef } from '@backstage/catalog-model';
import { entityRouteParams } from '@backstage/plugin-catalog-react';
import {
ListItem,
ListItemIcon,
Tooltip,
IconButton,
ListItemText,
} from '@material-ui/core';
import React from 'react';
import { Link } from 'react-router-dom';
import { entityRouteRef } from '@backstage/plugin-catalog-react';
import { useRouteRef } from '@backstage/core-plugin-api';
import StarIcon from '@material-ui/icons/Star';
type EntityListItemProps = {
entity: Entity;
onToggleStarredEntity: (entity: Entity) => void;
};
export const StarredEntityListItem = ({
entity,
onToggleStarredEntity,
}: EntityListItemProps) => {
const catalogEntityRoute = useRouteRef(entityRouteRef);
return (
<ListItem key={stringifyEntityRef(entity)}>
<ListItemIcon>
<Tooltip title="Remove from starred">
<IconButton
edge="end"
aria-label="unstar"
onClick={() => onToggleStarredEntity(entity)}
>
<StarIcon style={{ color: '#f3ba37' }} />
</IconButton>
</Tooltip>
</ListItemIcon>
<Link to={catalogEntityRoute(entityRouteParams(entity))}>
<ListItemText primary={entity.metadata.title ?? entity.metadata.name} />
</Link>
</ListItem>
);
};
@@ -17,28 +17,18 @@
import {
catalogApiRef,
useStarredEntities,
entityRouteParams,
entityRouteRef,
} from '@backstage/plugin-catalog-react';
import {
Entity,
parseEntityRef,
stringifyEntityRef,
} from '@backstage/catalog-model';
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
import { Link, Progress, ResponseErrorPanel } from '@backstage/core-components';
import {
List,
ListItem,
ListItemSecondaryAction,
IconButton,
ListItemText,
Tooltip,
Typography,
} from '@material-ui/core';
import StarIcon from '@material-ui/icons/Star';
import { useApi } from '@backstage/core-plugin-api';
import { Progress, ResponseErrorPanel } from '@backstage/core-components';
import { List, Typography, Tabs, Tab } from '@material-ui/core';
import React from 'react';
import useAsync from 'react-use/lib/useAsync';
import { StarredEntityListItem } from '../../components/StarredEntityListItem/StarredEntityListItem';
/**
* A component to display a list of starred entities for the user.
@@ -46,12 +36,18 @@ import useAsync from 'react-use/lib/useAsync';
* @public
*/
export const Content = (props: {
export type StarredEntitiesProps = {
noStarredEntitiesMessage?: React.ReactNode | undefined;
}) => {
groupByKind?: boolean;
};
export const Content = ({
noStarredEntitiesMessage,
groupByKind,
}: StarredEntitiesProps) => {
const catalogApi = useApi(catalogApiRef);
const catalogEntityRoute = useRouteRef(entityRouteRef);
const { starredEntities, toggleStarredEntity } = useStarredEntities();
const [activeTab, setActiveTab] = React.useState(0);
// Grab starred entities from catalog to ensure they still exist and also retrieve display titles
const entities = useAsync(async () => {
@@ -83,7 +79,7 @@ export const Content = (props: {
if (starredEntities.size === 0)
return (
<Typography variant="body1">
{props.noStarredEntitiesMessage ||
{noStarredEntitiesMessage ||
'Click the star beside an entity name to add it to this list!'}
</Typography>
);
@@ -91,8 +87,8 @@ export const Content = (props: {
if (entities.loading) {
return <Progress />;
}
const groupedEntities: { [kind: string]: Entity[] } = {};
const groupedEntities: { [kind: string]: Entity[] } = {};
entities.value?.forEach(entity => {
const kind = entity.kind;
if (!groupedEntities[kind]) {
@@ -101,15 +97,46 @@ export const Content = (props: {
groupedEntities[kind].push(entity);
});
const entityEntries = Object.entries(groupedEntities);
const groupByKindEntries = Object.entries(groupedEntities);
return entities.error ? (
<ResponseErrorPanel error={entities.error} />
) : (
<div>
{entityEntries.map(([kind, entitiesByKind]) => (
<div key={kind}>
<Typography variant="h6">{kind}</Typography> {/* Kind as Title */}
{!groupByKind && (
<List>
{entities.value
?.sort((a, b) =>
(a.metadata.title ?? a.metadata.name).localeCompare(
b.metadata.title ?? b.metadata.name,
),
)
.map(entity => (
<StarredEntityListItem
key={stringifyEntityRef(entity)}
entity={entity}
onToggleStarredEntity={toggleStarredEntity}
/>
))}
</List>
)}
{groupByKind && (
<Tabs
value={activeTab}
onChange={(_, newValue) => setActiveTab(newValue)}
variant="scrollable"
scrollButtons="auto"
aria-label="entity-tabs"
>
{groupByKindEntries.map(([kind]) => (
<Tab key={kind} label={kind} />
))}
</Tabs>
)}
{groupByKindEntries.map(([kind, entitiesByKind], index) => (
<div key={kind} hidden={groupByKind && activeTab !== index}>
<List>
{entitiesByKind
?.sort((a, b) =>
@@ -118,24 +145,11 @@ export const Content = (props: {
),
)
.map(entity => (
<ListItem key={stringifyEntityRef(entity)}>
<Link to={catalogEntityRoute(entityRouteParams(entity))}>
<ListItemText
primary={entity.metadata.title ?? entity.metadata.name}
/>
</Link>
<ListItemSecondaryAction>
<Tooltip title="Remove from starred">
<IconButton
edge="end"
aria-label="unstar"
onClick={() => toggleStarredEntity(entity)}
>
<StarIcon style={{ color: '#f3ba37' }} />
</IconButton>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
<StarredEntityListItem
key={stringifyEntityRef(entity)}
entity={entity}
onToggleStarredEntity={toggleStarredEntity}
/>
))}
</List>
</div>
+2 -1
View File
@@ -26,6 +26,7 @@ import { createCardExtension } from '@backstage/plugin-home-react';
import { ToolkitContentProps, VisitedByTypeProps } from './homePageComponents';
import { rootRouteRef } from './routes';
import { VisitsStorageApi, visitsApiRef } from './api';
import { StarredEntitiesProps } from './homePageComponents/StarredEntities/Content';
/** @public */
export const homePlugin = createPlugin({
@@ -164,7 +165,7 @@ export const HomePageToolkit = homePlugin.provide(
* @public
*/
export const HomePageStarredEntities = homePlugin.provide(
createCardExtension({
createCardExtension<Partial<StarredEntitiesProps>>({
name: 'HomePageStarredEntities',
title: 'Your Starred Entities',
components: () => import('./homePageComponents/StarredEntities'),