mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
refactor: LineChart component (#6072)
Initial version of a reusable trend chart, with a tooltip and vertical highlight
This commit is contained in:
parent
f298d7d511
commit
e6ccd83739
@ -77,7 +77,7 @@ export const ExecutiveDashboard: VFC = () => {
|
||||
<Box sx={(theme) => ({ paddingBottom: theme.spacing(4) })}>
|
||||
<PageHeader
|
||||
titleElement={
|
||||
<Typography variant='h1' component='h2'>
|
||||
<Typography variant='h1' component='span'>
|
||||
Dashboard
|
||||
</Typography>
|
||||
}
|
||||
|
@ -0,0 +1,3 @@
|
||||
import { lazy } from 'react';
|
||||
|
||||
export const LineChart = lazy(() => import('./LineChartComponent'));
|
@ -1,15 +1,15 @@
|
||||
import { useMemo, useState, type VFC } from 'react';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
TimeScale,
|
||||
Chart,
|
||||
type ChartData,
|
||||
type ScatterDataPoint,
|
||||
} from 'chart.js';
|
||||
import { Line } from 'react-chartjs-2';
|
||||
import 'chartjs-adapter-date-fns';
|
||||
@ -18,7 +18,6 @@ import {
|
||||
useLocationSettings,
|
||||
type ILocationSettings,
|
||||
} from 'hooks/useLocationSettings';
|
||||
import { ExecutiveSummarySchema } from 'openapi';
|
||||
import { ChartTooltip, TooltipState } from './ChartTooltip/ChartTooltip';
|
||||
|
||||
const createOptions = (
|
||||
@ -34,7 +33,6 @@ const createOptions = (
|
||||
labels: {
|
||||
boxWidth: 12,
|
||||
padding: 30,
|
||||
// usePointStyle: true,
|
||||
generateLabels: (chart: Chart) => {
|
||||
const datasets = chart.data.datasets;
|
||||
const {
|
||||
@ -122,8 +120,8 @@ const createOptions = (
|
||||
unit: 'month',
|
||||
},
|
||||
grid: {
|
||||
color: theme.palette.divider,
|
||||
borderColor: theme.palette.divider,
|
||||
color: 'transparent',
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
ticks: {
|
||||
color: theme.palette.text.secondary,
|
||||
@ -132,47 +130,41 @@ const createOptions = (
|
||||
},
|
||||
}) as const;
|
||||
|
||||
interface IUsersChartComponentProps {
|
||||
userTrends: ExecutiveSummarySchema['userTrends'];
|
||||
}
|
||||
// Vertical line on the hovered chart, filled with gradient. Highlights a section of a chart when you hover over datapoints
|
||||
const customHighlightPlugin = {
|
||||
id: 'customLine',
|
||||
afterDraw: (chart: Chart) => {
|
||||
const width = 26;
|
||||
if (chart.tooltip?.opacity && chart.tooltip.x) {
|
||||
const x = chart.tooltip.caretX;
|
||||
const yAxis = chart.scales.y;
|
||||
const ctx = chart.ctx;
|
||||
ctx.save();
|
||||
const gradient = ctx.createLinearGradient(
|
||||
x,
|
||||
yAxis.top,
|
||||
x,
|
||||
yAxis.bottom,
|
||||
);
|
||||
gradient.addColorStop(0, 'rgba(129, 122, 254, 0)');
|
||||
gradient.addColorStop(1, 'rgba(129, 122, 254, 0.12)');
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(
|
||||
x - width / 2,
|
||||
yAxis.top,
|
||||
width,
|
||||
yAxis.bottom - yAxis.top,
|
||||
);
|
||||
ctx.restore();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const createData = (
|
||||
theme: Theme,
|
||||
userTrends: ExecutiveSummarySchema['userTrends'],
|
||||
) => ({
|
||||
labels: userTrends.map((item) => item.date),
|
||||
datasets: [
|
||||
{
|
||||
label: 'Total users',
|
||||
data: userTrends.map((item) => item.total),
|
||||
borderColor: theme.palette.primary.light,
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
label: 'Active users',
|
||||
data: userTrends.map((item) => item.active),
|
||||
borderColor: theme.palette.success.border,
|
||||
backgroundColor: theme.palette.success.border,
|
||||
},
|
||||
{
|
||||
label: 'Inactive users',
|
||||
data: userTrends.map((item) => item.inactive),
|
||||
borderColor: theme.palette.warning.border,
|
||||
backgroundColor: theme.palette.warning.border,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const UsersChartComponent: VFC<IUsersChartComponentProps> = ({
|
||||
userTrends,
|
||||
}) => {
|
||||
const LineChartComponent: VFC<{
|
||||
data: ChartData<'line', (number | ScatterDataPoint | null)[], unknown>;
|
||||
}> = ({ data }) => {
|
||||
const theme = useTheme();
|
||||
const { locationSettings } = useLocationSettings();
|
||||
const data = useMemo(
|
||||
() => createData(theme, userTrends),
|
||||
[theme, userTrends],
|
||||
);
|
||||
|
||||
const [tooltip, setTooltip] = useState<null | TooltipState>(null);
|
||||
const options = useMemo(
|
||||
@ -182,21 +174,25 @@ const UsersChartComponent: VFC<IUsersChartComponentProps> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Line options={options} data={data} />
|
||||
<Line
|
||||
options={options}
|
||||
data={data}
|
||||
plugins={[customHighlightPlugin]}
|
||||
/>
|
||||
<ChartTooltip tooltip={tooltip} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ChartJS.register(
|
||||
Chart.register(
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
TimeScale,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
);
|
||||
|
||||
export default UsersChartComponent;
|
||||
// for lazy-loading
|
||||
export default LineChartComponent;
|
@ -1,3 +1,42 @@
|
||||
import { lazy } from 'react';
|
||||
import { useMemo, type VFC } from 'react';
|
||||
import 'chartjs-adapter-date-fns';
|
||||
import { useTheme } from '@mui/material';
|
||||
import { ExecutiveSummarySchema } from 'openapi';
|
||||
import { LineChart } from '../LineChart/LineChart';
|
||||
|
||||
export const UsersChart = lazy(() => import('./UsersChartComponent'));
|
||||
interface IUsersChartProps {
|
||||
userTrends: ExecutiveSummarySchema['userTrends'];
|
||||
}
|
||||
|
||||
export const UsersChart: VFC<IUsersChartProps> = ({ userTrends }) => {
|
||||
const theme = useTheme();
|
||||
const data = useMemo(
|
||||
() => ({
|
||||
labels: userTrends.map((item) => item.date),
|
||||
datasets: [
|
||||
{
|
||||
label: 'Total users',
|
||||
data: userTrends.map((item) => item.total),
|
||||
borderColor: theme.palette.primary.light,
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
label: 'Active users',
|
||||
data: userTrends.map((item) => item.active),
|
||||
borderColor: theme.palette.success.border,
|
||||
backgroundColor: theme.palette.success.border,
|
||||
},
|
||||
{
|
||||
label: 'Inactive users',
|
||||
data: userTrends.map((item) => item.inactive),
|
||||
borderColor: theme.palette.warning.border,
|
||||
backgroundColor: theme.palette.warning.border,
|
||||
},
|
||||
],
|
||||
}),
|
||||
[theme, userTrends],
|
||||
);
|
||||
|
||||
return <LineChart data={data} />;
|
||||
};
|
||||
|
@ -1,23 +1,27 @@
|
||||
import { FC, ReactNode } from 'react';
|
||||
import { Paper, Typography } from '@mui/material';
|
||||
import { Paper, Typography, styled } from '@mui/material';
|
||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
const StyledPaper = styled(Paper)(({ theme }) => ({
|
||||
padding: theme.spacing(3),
|
||||
borderRadius: `${theme.shape.borderRadiusLarge}px`,
|
||||
minWidth: 0, // bugfix, see: https://github.com/chartjs/Chart.js/issues/4156#issuecomment-295180128
|
||||
position: 'relative',
|
||||
}));
|
||||
|
||||
export const Widget: FC<{
|
||||
title: ReactNode;
|
||||
order?: number;
|
||||
span?: number;
|
||||
tooltip?: ReactNode;
|
||||
}> = ({ title, order, children, span = 1, tooltip }) => (
|
||||
<Paper
|
||||
<StyledPaper
|
||||
elevation={0}
|
||||
sx={(theme) => ({
|
||||
padding: 3,
|
||||
borderRadius: `${theme.shape.borderRadiusLarge}px`,
|
||||
sx={{
|
||||
order,
|
||||
gridColumn: `span ${span}`,
|
||||
minWidth: 0, // bugfix, see: https://github.com/chartjs/Chart.js/issues/4156#issuecomment-295180128
|
||||
})}
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant='h3'
|
||||
@ -35,5 +39,5 @@ export const Widget: FC<{
|
||||
/>
|
||||
</Typography>
|
||||
{children}
|
||||
</Paper>
|
||||
</StyledPaper>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user