feat(plugins/adr): render subdirectories in list

Signed-off-by: Andy Ladjadj <andy.ladjadj@gmail.com>
This commit is contained in:
Andy Ladjadj
2023-03-14 17:54:20 +01:00
committed by Andy Ladjadj
parent 77aa3a4d47
commit f66fb122ff
3 changed files with 118 additions and 39 deletions
+1
View File
@@ -34,6 +34,7 @@
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.61",
"lodash": "^4.17.21",
"react-markdown": "^8.0.0",
"react-use": "^17.2.4",
"remark-gfm": "^3.0.1"
@@ -16,9 +16,14 @@
import React, { useEffect, useState } from 'react';
import { Link, useSearchParams } from 'react-router-dom';
import useAsync from 'react-use/lib/useAsync';
import groupBy from 'lodash/groupBy';
import {
Content,
ContentHeader,
InfoCard,
MissingAnnotationEmptyState,
Progress,
SupportButton,
@@ -35,27 +40,118 @@ import {
} from '@backstage/plugin-adr-common';
import { useEntity } from '@backstage/plugin-catalog-react';
import {
Box,
Chip,
Collapse,
Grid,
List,
ListItem,
ListItemIcon,
ListItemText,
makeStyles,
Theme,
Typography,
} from '@material-ui/core';
import ExpandLess from '@material-ui/icons/ExpandLess';
import ExpandMore from '@material-ui/icons/ExpandMore';
import FolderIcon from '@material-ui/icons/Folder';
import { adrApiRef, AdrFileInfo } from '../../api';
import { rootRouteRef } from '../../routes';
import { AdrContentDecorator, AdrReader } from '../AdrReader';
import { adrApiRef, AdrFileInfo } from '../../api';
import useAsync from 'react-use/lib/useAsync';
const useStyles = makeStyles((theme: Theme) => ({
adrMenu: {
backgroundColor: theme.palette.background.paper,
},
adrContainerTitle: {
color: theme.palette.grey[700],
marginBottom: theme.spacing(1),
},
adrChip: {
position: 'absolute',
right: 0,
},
}));
const AdrListContainer = (props: {
adrs: AdrFileInfo[];
selectedAdr: string;
title: string;
}) => {
const { adrs, selectedAdr, title } = props;
const classes = useStyles();
const rootLink = useRouteRef(rootRouteRef);
const [open, setOpen] = React.useState(true);
const getChipColor = (status: string) => {
switch (status) {
case 'accepted':
return 'primary';
case 'rejected' || 'deprecated':
return 'secondary';
default:
return 'default';
}
};
const handleClick = () => {
setOpen(!open);
};
return (
<>
{title && (
<ListItem
button
className={classes.adrContainerTitle}
onClick={handleClick}
>
<ListItemIcon>
<FolderIcon />
</ListItemIcon>
<ListItemText primary={title} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
)}
<Collapse in={open} timeout="auto" unmountOnExit>
<List dense>
{adrs.map((adr, idx) => (
<ListItem
button
component={Link}
key={idx}
selected={selectedAdr === adr.path}
to={`${rootLink()}?record=${adr.name}`}
>
<ListItemText
primary={adr.title ?? adr?.name.replace(/\.md$/, '')}
primaryTypographyProps={{
style: { whiteSpace: 'normal' },
}}
secondary={
<Box>
{adr.date}
{adr.status && (
<Chip
color={getChipColor(adr.status)}
label={adr.status}
size="small"
variant="outlined"
className={classes.adrChip}
/>
)}
</Box>
}
/>
</ListItem>
))}
</List>
</Collapse>
</>
);
};
/**
* Component for browsing ADRs on an entity page.
* @public
@@ -67,7 +163,6 @@ export const EntityAdrContent = (props: {
const { contentDecorators, filePathFilterFn } = props;
const classes = useStyles();
const { entity } = useEntity();
const rootLink = useRouteRef(rootRouteRef);
const [adrList, setAdrList] = useState<AdrFileInfo[]>([]);
const [searchParams, setSearchParams] = useSearchParams();
const scmIntegrations = useApi(scmIntegrationsApiRef);
@@ -82,6 +177,10 @@ export const EntityAdrContent = (props: {
const selectedAdr =
adrList.find(adr => adr.name === searchParams.get('record'))?.path ?? '';
const adrSubDirectoryFunc = (adr: AdrFileInfo) => {
return adr.path.split('/').slice(0, -1).join('/');
};
useEffect(() => {
if (adrList.length && !selectedAdr) {
searchParams.set('record', adrList[0].name);
@@ -105,16 +204,9 @@ export const EntityAdrContent = (props: {
setAdrList(adrs);
}, [filePathFilterFn, value]);
const getChipColor = (status: string) => {
switch (status) {
case 'accepted':
return 'primary';
case 'rejected' || 'deprecated':
return 'secondary';
default:
return 'default';
}
};
const adrListGrouped = Object.entries(
groupBy(adrList, adrSubDirectoryFunc),
).sort();
return (
<Content>
@@ -138,33 +230,18 @@ export const EntityAdrContent = (props: {
(adrList.length ? (
<Grid container direction="row">
<Grid item xs={3}>
<List className={classes.adrMenu} dense>
{adrList.map((adr, idx) => (
<ListItem
key={idx}
button
component={Link}
to={`${rootLink()}?record=${adr.name}`}
selected={selectedAdr === adr.path}
>
<ListItemText
primaryTypographyProps={{
style: { whiteSpace: 'normal' },
}}
primary={adr.title ?? adr?.name.replace(/\.md$/, '')}
secondary={adr.date}
<InfoCard>
<List className={classes.adrMenu} dense>
{adrListGrouped.map(([title, adrs], idx) => (
<AdrListContainer
adrs={adrs}
key={idx}
selectedAdr={selectedAdr}
title={title}
/>
{adr.status && (
<Chip
label={adr.status}
size="small"
variant="outlined"
color={getChipColor(adr.status)}
/>
)}
</ListItem>
))}
</List>
))}
</List>
</InfoCard>
</Grid>
<Grid item xs={9}>
<AdrReader adr={selectedAdr} decorators={contentDecorators} />
+1
View File
@@ -4316,6 +4316,7 @@ __metadata:
"@types/git-url-parse": ^9.0.0
"@types/node": "*"
cross-fetch: ^3.1.5
lodash: ^4.17.21
msw: ^1.0.0
react-markdown: ^8.0.0
react-use: ^17.2.4