Replacing SortableTable and a few MUI tables with new Table component (#722)

* Befind work of replacing tables with new Table component

* Removed test plugins and fixed test

* Fixed lint issue

* Removed toolbar from lighthouse audit page

* Moved column out of auditstables render function, no no interation with useInterval
This commit is contained in:
Sebastian Qvarfordt
2020-05-06 12:58:34 +02:00
committed by GitHub
parent 5b0e808204
commit 425cf93c08
12 changed files with 161 additions and 676 deletions
@@ -1,83 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* 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, { FC } from 'react';
import { Link, TableCell, TableRow } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import { TrendLine } from '@backstage/core';
import { Website } from '../../api';
import {
formatTime,
CATEGORIES,
CATEGORY_LABELS,
SparklinesDataByCategory,
} from '../../utils';
import AuditStatusIcon from '../AuditStatusIcon';
const useStyles = makeStyles(theme => ({
table: {
minWidth: 650,
},
status: {
textTransform: 'capitalize',
},
link: {
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
display: 'inline-block',
},
statusCell: { whiteSpace: 'nowrap' },
sparklinesCell: { minWidth: 120 },
}));
export const AuditRow: FC<{
website: Website;
categorySparkline: SparklinesDataByCategory;
}> = ({ website, categorySparkline }) => {
const classes = useStyles();
return (
<TableRow key={website.url}>
<TableCell>
<Link
className={classes.link}
href={`/lighthouse/audit/${website.lastAudit.id}`}
>
{website.url}
</Link>
</TableCell>
{CATEGORIES.map(category => (
<TableCell
key={`${website.url}|${category}`}
className={classes.sparklinesCell}
>
<TrendLine
title={`trendline for ${CATEGORY_LABELS[category]} category of ${website.url}`}
data={categorySparkline[category] || []}
/>
</TableCell>
))}
<TableCell className={classes.statusCell}>
<AuditStatusIcon audit={website.lastAudit} />{' '}
<span className={classes.status}>
{website.lastAudit.status.toLowerCase()}
</span>
</TableCell>
<TableCell>{formatTime(website.lastAudit.timeCreated)}</TableCell>
</TableRow>
);
};
export default AuditRow;
@@ -1,54 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* 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, { FC, useState } from 'react';
import { useInterval } from 'react-use';
import { Website, lighthouseApiRef } from '../../api';
import { useApi } from '@backstage/core';
import {
SparklinesDataByCategory,
buildSparklinesDataForItem,
} from '../../utils';
import { AuditRow } from './AuditRow';
export const LIMIT = 10;
export const Audit: FC<{
website: Website;
categorySparkline: SparklinesDataByCategory;
}> = ({ website, categorySparkline }) => {
const lighthouseApi = useApi(lighthouseApiRef);
const [websiteState, setWebsiteState] = useState(website);
const [sparklineState, setSparklineState] = useState(categorySparkline);
const runRefresh = async () => {
const response = await lighthouseApi.getWebsiteForAuditId(
websiteState.lastAudit.id,
);
const auditStatus = response.lastAudit.status;
if (auditStatus === 'COMPLETED' || auditStatus === 'FAILED') {
setSparklineState(buildSparklinesDataForItem(response));
setWebsiteState(response);
}
};
useInterval(
runRefresh,
websiteState?.lastAudit.status === 'RUNNING' ? 5000 : null,
);
return <AuditRow website={websiteState} categorySparkline={sparklineState} />;
};
export default Audit;
@@ -55,7 +55,7 @@ describe('AuditListTable', () => {
);
const link = rendered.queryByText('https://anchor.fm');
const website = websiteListResponse.items.find(
w => w.url === 'https://anchor.fm',
(w) => w.url === 'https://anchor.fm',
);
if (!website)
throw new Error('https://anchor.fm must be present in fixture');
@@ -71,7 +71,7 @@ describe('AuditListTable', () => {
wrapInThemedTestApp(auditList(websiteListResponse)),
);
const website = websiteListResponse.items.find(
w => w.url === 'https://anchor.fm',
(w) => w.url === 'https://anchor.fm',
);
if (!website)
throw new Error('https://anchor.fm must be present in fixture');
@@ -85,21 +85,22 @@ describe('AuditListTable', () => {
wrapInThemedTestApp(auditList(websiteListResponse)),
);
const completed = await rendered.findAllByText('completed');
const completed = await rendered.findAllByText('COMPLETED');
expect(completed).toHaveLength(
websiteListResponse.items.filter(w => w.lastAudit.status === 'COMPLETED')
.length,
websiteListResponse.items.filter(
(w) => w.lastAudit.status === 'COMPLETED',
).length,
);
const failed = await rendered.findAllByText('failed');
const failed = await rendered.findAllByText('FAILED');
expect(failed).toHaveLength(
websiteListResponse.items.filter(w => w.lastAudit.status === 'FAILED')
websiteListResponse.items.filter((w) => w.lastAudit.status === 'FAILED')
.length,
);
const running = await rendered.findAllByText('failed');
const running = await rendered.findAllByText('FAILED');
expect(running).toHaveLength(
websiteListResponse.items.filter(w => w.lastAudit.status === 'RUNNING')
websiteListResponse.items.filter((w) => w.lastAudit.status === 'RUNNING')
.length,
);
});
@@ -13,81 +13,111 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { FC, useMemo } from 'react';
import {
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
} from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import { Website } from '../../api';
import React, { FC, useState } from 'react';
import { Table, TableColumn, TrendLine, useApi } from '@backstage/core';
import { Website, lighthouseApiRef } from '../../api';
import { useInterval } from 'react-use';
import {
formatTime,
CATEGORIES,
CATEGORY_LABELS,
SparklinesDataByCategory,
buildSparklinesDataForItem,
} from '../../utils';
import Audit from '../Audit';
import { Link } from '@material-ui/core';
import AuditStatusIcon from '../AuditStatusIcon';
const useStyles = makeStyles(theme => ({
table: {
minWidth: 650,
const columns: TableColumn[] = [
{
title: 'Website URL',
field: 'websiteUrl',
},
status: {
textTransform: 'capitalize',
...CATEGORIES.map((category) => ({
title: CATEGORY_LABELS[category],
field: category,
})),
{
title: 'Last Report',
field: 'lastReport',
cellStyle: {
whiteSpace: 'nowrap',
},
},
link: {
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
display: 'inline-block',
{
title: 'Last Audit Triggered',
field: 'lastAuditTriggered',
cellStyle: {
minWidth: 120,
},
},
statusCell: { whiteSpace: 'nowrap' },
sparklinesCell: { minWidth: 120 },
}));
];
export const AuditListTable: FC<{ items: Website[] }> = ({ items }) => {
const classes = useStyles();
const categorySparklines: Record<string, SparklinesDataByCategory> = useMemo(
() =>
items.reduce(
(res, item) => ({
...res,
[item.url]: buildSparklinesDataForItem(item),
}),
{},
),
[items],
const [websiteState, setWebsiteState] = useState(items);
const lighthouseApi = useApi(lighthouseApiRef);
const runRefresh = (websites: Website[]) => {
websites.forEach(async (website) => {
const response = await lighthouseApi.getWebsiteForAuditId(
website.lastAudit.id,
);
const auditStatus = response.lastAudit.status;
if (auditStatus === 'COMPLETED' || auditStatus === 'FAILED') {
const newWebsiteData = websiteState.slice(0);
newWebsiteData[
newWebsiteData.findIndex((w) => w.url === response.url)
] = response;
setWebsiteState(newWebsiteData);
}
});
};
const runningWebsiteAudits = websiteState
? websiteState.filter((website) => website.lastAudit.status === 'RUNNING')
: [];
useInterval(
() => runRefresh(runningWebsiteAudits),
runningWebsiteAudits.length > 0 ? 5000 : null,
);
const data = websiteState.map((website) => {
const trendlineData = buildSparklinesDataForItem(website);
const trendlines: any = {};
CATEGORIES.forEach((category) => {
trendlines[category] = (
<TrendLine
title={`trendline for ${CATEGORY_LABELS[category]} category of ${website.url}`}
data={trendlineData[category] || []}
/>
);
});
return {
websiteUrl: (
<Link href={`/lighthouse/audit/${website.lastAudit.id}`}>
{website.url}
</Link>
),
...trendlines,
lastReport: (
<>
<AuditStatusIcon audit={website.lastAudit} />{' '}
<span>{website.lastAudit.status.toUpperCase()}</span>
</>
),
lastAuditTriggered: formatTime(website.lastAudit.timeCreated),
};
});
return (
<TableContainer>
<Table className={classes.table} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell>Website URL</TableCell>
{CATEGORIES.map(category => (
<TableCell key={`${category}-label`}>
{CATEGORY_LABELS[category]}
</TableCell>
))}
<TableCell>Last Report</TableCell>
<TableCell>Last Audit Triggered</TableCell>
</TableRow>
</TableHead>
<TableBody>
{items.map(website => (
<Audit
key={website.url}
website={website}
categorySparkline={categorySparklines[website.url]}
/>
))}
</TableBody>
</Table>
</TableContainer>
<Table
options={{
paging: false,
toolbar: false,
}}
columns={columns}
data={data}
/>
);
};