1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-03-18 00:19:49 +01:00

refactor: port metrics list to react-table (#1035)

* refactor: port metrics list to react-table

* refactor: hide columns on small screens

* refactor: use disableSortBy instead of canSort

* refactor: fix text contrast

* refactor: fix metrics section ids
This commit is contained in:
olav 2022-05-31 10:26:30 +02:00 committed by GitHub
parent 570e9f88be
commit 36196ad6d5
10 changed files with 134 additions and 83 deletions

View File

@ -39,7 +39,7 @@ export const SortableTableHeader: VFC<ISortableTableHeaderProps> = ({
? content
: undefined
}
isSortable={column.canSort}
isSortable={Boolean(column.canSort)}
isSorted={column.isSorted}
isDescending={column.isSortedDesc}
maxWidth={column.maxWidth}

View File

@ -61,7 +61,7 @@ export const EnvironmentTable = () => {
{
columns: COLUMNS as any,
data: environments,
autoResetGlobalFilter: false,
disableSortBy: true,
},
useGlobalFilter
);
@ -127,15 +127,13 @@ export const EnvironmentTable = () => {
const COLUMNS = [
{
id: 'Icon',
canSort: false,
width: '1%',
Cell: () => <IconCell icon={<CloudCircle color="disabled" />} />,
disableGlobalFilter: true,
},
{
Header: 'Name',
accessor: 'name',
width: '100%',
canSort: false,
Cell: ({ row: { original } }: any) => (
<EnvironmentNameCell environment={original} />
),
@ -144,7 +142,7 @@ const COLUMNS = [
Header: 'Actions',
id: 'Actions',
align: 'center',
canSort: false,
width: '1%',
Cell: ({ row: { original } }: any) => (
<EnvironmentActionCell environment={original} />
),

View File

@ -16,16 +16,17 @@ import { useLocationSettings } from 'hooks/useLocationSettings';
import 'chartjs-adapter-date-fns';
import { createChartData } from './createChartData';
import { createChartOptions } from './createChartOptions';
import { FEATURE_METRICS_STATS_ID } from '../FeatureMetricsStats/FeatureMetricsStats';
interface IFeatureMetricsChartProps {
metrics: IFeatureMetricsRaw[];
hoursBack: number;
statsSectionId: string;
}
export const FeatureMetricsChart = ({
metrics,
hoursBack,
statsSectionId,
}: IFeatureMetricsChartProps) => {
const { locationSettings } = useLocationSettings();
@ -49,7 +50,7 @@ export const FeatureMetricsChart = ({
options={options}
data={data}
aria-label="A feature metrics line chart, with three lines: all requests, positive requests, and negative requests."
aria-describedby={FEATURE_METRICS_STATS_ID}
aria-describedby={statsSectionId}
/>
</div>
);

View File

@ -6,7 +6,7 @@ export const useStyles = makeStyles()(theme => ({
marginBottom: '.5rem',
fontSize: theme.fontSizes.smallerBody,
fontWeight: theme.fontWeight.thin,
color: theme.palette.grey[600],
color: theme.palette.grey[800],
},
list: {
display: 'flex',

View File

@ -5,6 +5,7 @@ import { FeatureMetricsChart } from '../FeatureMetricsChart/FeatureMetricsChart'
import { FeatureMetricsEmpty } from '../FeatureMetricsEmpty/FeatureMetricsEmpty';
import { Box } from '@mui/material';
import theme from 'themes/theme';
import { useId } from 'hooks/useId';
interface IFeatureMetricsContentProps {
metrics: IFeatureMetricsRaw[];
@ -15,6 +16,9 @@ export const FeatureMetricsContent = ({
metrics,
hoursBack,
}: IFeatureMetricsContentProps) => {
const statsSectionId = useId();
const tableSectionId = useId();
if (metrics.length === 0) {
return (
<Box mt={6}>
@ -31,16 +35,25 @@ export const FeatureMetricsContent = ({
mt={3}
borderColor={theme.palette.grey[200]}
>
<FeatureMetricsChart metrics={metrics} hoursBack={hoursBack} />
<FeatureMetricsChart
metrics={metrics}
hoursBack={hoursBack}
statsSectionId={statsSectionId}
/>
</Box>
<Box mt={4}>
<FeatureMetricsStatsRaw
metrics={metrics}
hoursBack={hoursBack}
statsSectionId={statsSectionId}
tableSectionId={tableSectionId}
/>
</Box>
<Box mt={4}>
<FeatureMetricsTable metrics={metrics} />
<FeatureMetricsTable
metrics={metrics}
tableSectionId={tableSectionId}
/>
</Box>
</>
);

View File

@ -27,6 +27,6 @@ export const useStyles = makeStyles()(theme => ({
borderTopStyle: 'solid',
borderTopColor: theme.palette.grey[300],
fontSize: theme.fontSizes.smallerBody,
color: theme.palette.grey[700],
color: theme.palette.grey[800],
},
}));

View File

@ -2,18 +2,20 @@ import { calculatePercentage } from 'utils/calculatePercentage';
import { useStyles } from './FeatureMetricsStats.styles';
import { Grid } from '@mui/material';
interface IFeatureMetricsStatsProps {
export interface IFeatureMetricsStatsProps {
totalYes: number;
totalNo: number;
hoursBack: number;
statsSectionId?: string;
tableSectionId?: string;
}
export const FEATURE_METRICS_STATS_ID = 'feature-metrics-stats-id';
export const FeatureMetricsStats = ({
totalYes,
totalNo,
hoursBack,
statsSectionId,
tableSectionId,
}: IFeatureMetricsStatsProps) => {
const { classes: styles } = useStyles();
@ -24,7 +26,8 @@ export const FeatureMetricsStats = ({
<Grid
container
spacing={2}
id={FEATURE_METRICS_STATS_ID}
id={statsSectionId}
aria-describedby={tableSectionId}
aria-label="Feature metrics summary"
component="section"
>

View File

@ -1,15 +1,18 @@
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
import { useMemo } from 'react';
import { FeatureMetricsStats } from './FeatureMetricsStats';
import {
FeatureMetricsStats,
IFeatureMetricsStatsProps,
} from './FeatureMetricsStats';
interface IFeatureMetricsStatsRawProps {
interface IFeatureMetricsStatsRawProps
extends Omit<IFeatureMetricsStatsProps, 'totalYes' | 'totalNo'> {
metrics: IFeatureMetricsRaw[];
hoursBack: number;
}
export const FeatureMetricsStatsRaw = ({
metrics,
hoursBack,
...rest
}: IFeatureMetricsStatsRawProps) => {
const totalYes = useMemo(() => {
return metrics.reduce((acc, m) => acc + m.yes, 0);
@ -20,10 +23,6 @@ export const FeatureMetricsStatsRaw = ({
}, [metrics]);
return (
<FeatureMetricsStats
totalYes={totalYes}
totalNo={totalNo}
hoursBack={hoursBack}
/>
<FeatureMetricsStats {...rest} totalYes={totalYes} totalNo={totalNo} />
);
};

View File

@ -1,71 +1,108 @@
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
import {
Table,
TableBody,
TableCell,
TableHead,
TableRow,
useMediaQuery,
useTheme,
} from '@mui/material';
import { useLocationSettings } from 'hooks/useLocationSettings';
import { useMemo } from 'react';
import { formatDateYMDHMS } from 'utils/formatDate';
export const FEATURE_METRICS_TABLE_ID = 'feature-metrics-table-id';
import { TableBody, TableRow, useMediaQuery } from '@mui/material';
import { DateCell } from 'component/common/Table/cells/DateCell/DateCell';
import { useTable, useGlobalFilter, useSortBy } from 'react-table';
import { SortableTableHeader, TableCell, Table } from 'component/common/Table';
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
import { Assessment } from '@mui/icons-material';
import { useMemo, useEffect } from 'react';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import theme from 'themes/theme';
interface IFeatureMetricsTableProps {
metrics: IFeatureMetricsRaw[];
tableSectionId?: string;
}
export const FeatureMetricsTable = ({ metrics }: IFeatureMetricsTableProps) => {
const theme = useTheme();
const smallScreen = useMediaQuery(theme.breakpoints.down('md'));
const { locationSettings } = useLocationSettings();
export const FeatureMetricsTable = ({
metrics,
tableSectionId,
}: IFeatureMetricsTableProps) => {
const isMediumScreen = useMediaQuery(theme.breakpoints.down('md'));
const sortedMetrics = useMemo(() => {
return [...metrics].sort((metricA, metricB) => {
return metricB.timestamp.localeCompare(metricA.timestamp);
});
}, [metrics]);
const initialState = useMemo(
() => ({ sortBy: [{ id: 'timestamp', desc: true }] }),
[]
);
if (sortedMetrics.length === 0) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
setHiddenColumns,
} = useTable(
{
initialState,
columns: COLUMNS as any,
data: metrics as any,
disableSortRemove: true,
defaultColumn: { Cell: TextCell },
},
useGlobalFilter,
useSortBy
);
useEffect(() => {
if (isMediumScreen) {
setHiddenColumns(['appName', 'environment']);
} else {
setHiddenColumns([]);
}
}, [setHiddenColumns, isMediumScreen]);
if (metrics.length === 0) {
return null;
}
return (
<Table id={FEATURE_METRICS_TABLE_ID} aria-label="Feature metrics table">
<TableHead>
<TableRow>
<TableCell>Time</TableCell>
<TableCell hidden={smallScreen}>Application</TableCell>
<TableCell hidden={smallScreen}>Environment</TableCell>
<TableCell align="right">Requested</TableCell>
<TableCell align="right">Exposed</TableCell>
</TableRow>
</TableHead>
<TableBody>
{sortedMetrics.map(metric => (
<TableRow key={metric.timestamp}>
<TableCell>
{formatDateYMDHMS(
metric.timestamp,
locationSettings.locale
)}
</TableCell>
<TableCell hidden={smallScreen}>
{metric.appName}
</TableCell>
<TableCell hidden={smallScreen}>
{metric.environment}
</TableCell>
<TableCell align="right">
{metric.yes + metric.no}
</TableCell>
<TableCell align="right">{metric.yes}</TableCell>
</TableRow>
))}
<Table {...getTableProps()} rowHeight="standard" id={tableSectionId}>
<SortableTableHeader headerGroups={headerGroups} />
<TableBody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
<TableRow hover {...row.getRowProps()}>
{row.cells.map(cell => (
<TableCell {...cell.getCellProps()}>
{cell.render('Cell')}
</TableCell>
))}
</TableRow>
);
})}
</TableBody>
</Table>
);
};
const COLUMNS = [
{
id: 'Icon',
width: '1%',
disableSortBy: true,
Cell: () => <IconCell icon={<Assessment color="disabled" />} />,
},
{
Header: 'Time',
accessor: 'timestamp',
Cell: (props: any) => <DateCell value={props.row.original.timestamp} />,
},
{
Header: 'Application',
accessor: 'appName',
},
{
Header: 'Environment',
accessor: 'environment',
},
{
Header: 'Requested',
accessor: (original: any) => original.yes + original.no,
},
{
Header: 'Exposed',
accessor: 'yes',
},
];

View File

@ -159,9 +159,9 @@ const COLUMNS = [
{
id: 'Icon',
width: '1%',
canSort: false,
Cell: () => <IconCell icon={<DonutLarge color="disabled" />} />,
disableGlobalFilter: true,
disableSortBy: true,
Cell: () => <IconCell icon={<DonutLarge color="disabled" />} />,
},
{
Header: 'Name',
@ -187,7 +187,7 @@ const COLUMNS = [
id: 'Actions',
align: 'center',
width: '1%',
canSort: false,
disableSortBy: true,
disableGlobalFilter: true,
Cell: ({ row: { original } }: any) => (
<SegmentActionCell segment={original} />