mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
Feat/dashboard chart tooltip (#6038)
Initial version of new chart tooltip
This commit is contained in:
parent
4a025a4b4b
commit
61c6583e24
@ -9,7 +9,8 @@ import { FlagStats } from './FlagStats/FlagStats';
|
||||
|
||||
const StyledGrid = styled(Box)(({ theme }) => ({
|
||||
display: 'grid',
|
||||
gridTemplateColumns: `repeat(auto-fill, minmax(600px, 1fr))`,
|
||||
gridTemplateColumns: `300px 1fr`,
|
||||
// TODO: responsive grid size
|
||||
gridAutoRows: '1fr',
|
||||
gap: theme.spacing(2),
|
||||
}));
|
||||
|
@ -0,0 +1,89 @@
|
||||
import { Paper, styled, Typography } from '@mui/material';
|
||||
import { VFC } from 'react';
|
||||
|
||||
export type TooltipState = {
|
||||
caretX: number;
|
||||
caretY: number;
|
||||
title: string;
|
||||
align: 'left' | 'right';
|
||||
body: {
|
||||
title: string;
|
||||
color: string;
|
||||
value: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
interface IChartTooltipProps {
|
||||
tooltip: TooltipState | null;
|
||||
}
|
||||
|
||||
const StyledList = styled('ul')(({ theme }) => ({
|
||||
listStyle: 'none',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
}));
|
||||
|
||||
const StyledItem = styled('li')(({ theme }) => ({
|
||||
marginBottom: theme.spacing(0.5),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}));
|
||||
|
||||
const StyledLabelIcon = styled('span')(({ theme }) => ({
|
||||
display: 'inline-block',
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
marginRight: theme.spacing(1),
|
||||
}));
|
||||
|
||||
export const ChartTooltip: VFC<IChartTooltipProps> = ({ tooltip }) => (
|
||||
<Paper
|
||||
elevation={3}
|
||||
sx={(theme) => ({
|
||||
top: tooltip?.caretY,
|
||||
left:
|
||||
tooltip?.align === 'left'
|
||||
? tooltip?.caretX + 40
|
||||
: (tooltip?.caretX || 0) - 220,
|
||||
position: 'absolute',
|
||||
display: tooltip ? 'block' : 'none',
|
||||
width: 220,
|
||||
padding: theme.spacing(1.5, 2),
|
||||
pointerEvents: 'none',
|
||||
})}
|
||||
>
|
||||
{
|
||||
<Typography
|
||||
variant='body2'
|
||||
sx={(theme) => ({
|
||||
marginBottom: theme.spacing(1),
|
||||
color: theme.palette.text.secondary,
|
||||
})}
|
||||
>
|
||||
{tooltip?.title}
|
||||
</Typography>
|
||||
}
|
||||
<StyledList>
|
||||
{tooltip?.body.map((item) => (
|
||||
<StyledItem key={item.title}>
|
||||
<StyledLabelIcon
|
||||
sx={{
|
||||
backgroundColor: item.color,
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
</StyledLabelIcon>
|
||||
<Typography
|
||||
variant='body2'
|
||||
sx={{
|
||||
display: 'inline-block',
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</Typography>
|
||||
</StyledItem>
|
||||
))}
|
||||
</StyledList>
|
||||
</Paper>
|
||||
);
|
@ -1,4 +1,4 @@
|
||||
import { useMemo, type VFC } from 'react';
|
||||
import { useMemo, useState, type VFC } from 'react';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
@ -9,34 +9,88 @@ import {
|
||||
Tooltip,
|
||||
Legend,
|
||||
TimeScale,
|
||||
Chart,
|
||||
} from 'chart.js';
|
||||
import { Line } from 'react-chartjs-2';
|
||||
import 'chartjs-adapter-date-fns';
|
||||
import { Paper, Theme, useTheme } from '@mui/material';
|
||||
import { Paper, Theme, Typography, useTheme } from '@mui/material';
|
||||
import {
|
||||
useLocationSettings,
|
||||
type ILocationSettings,
|
||||
} from 'hooks/useLocationSettings';
|
||||
import { formatDateYMD } from 'utils/formatDate';
|
||||
import { ExecutiveSummarySchema } from 'openapi';
|
||||
import { ChartTooltip, TooltipState } from './ChartTooltip/ChartTooltip';
|
||||
|
||||
const createOptions = (theme: Theme, locationSettings: ILocationSettings) =>
|
||||
const createOptions = (
|
||||
theme: Theme,
|
||||
locationSettings: ILocationSettings,
|
||||
setTooltip: React.Dispatch<React.SetStateAction<TooltipState | null>>,
|
||||
) =>
|
||||
({
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
boxWidth: 12,
|
||||
padding: 30,
|
||||
// usePointStyle: true,
|
||||
generateLabels: (chart: Chart) => {
|
||||
const datasets = chart.data.datasets;
|
||||
const {
|
||||
labels: {
|
||||
usePointStyle,
|
||||
pointStyle,
|
||||
textAlign,
|
||||
color,
|
||||
},
|
||||
} = chart?.legend?.options || {
|
||||
labels: {},
|
||||
};
|
||||
return (chart as any)
|
||||
._getSortedDatasetMetas()
|
||||
.map((meta: any) => {
|
||||
const style = meta.controller.getStyle(
|
||||
usePointStyle ? 0 : undefined,
|
||||
);
|
||||
return {
|
||||
text: datasets[meta.index].label,
|
||||
fillStyle: style.backgroundColor,
|
||||
fontColor: color,
|
||||
hidden: !meta.visible,
|
||||
lineWidth: 0,
|
||||
borderRadius: 6,
|
||||
strokeStyle: style.borderColor,
|
||||
pointStyle: pointStyle || style.pointStyle,
|
||||
textAlign: textAlign || style.textAlign,
|
||||
datasetIndex: meta.index,
|
||||
};
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
title: (tooltipItems: any) => {
|
||||
const item = tooltipItems?.[0];
|
||||
const date =
|
||||
item?.chart?.data?.labels?.[item.dataIndex];
|
||||
return date
|
||||
? formatDateYMD(date, locationSettings.locale)
|
||||
: '';
|
||||
},
|
||||
enabled: false,
|
||||
external: (context: any) => {
|
||||
const tooltipModel = context.tooltip;
|
||||
if (tooltipModel.opacity === 0) {
|
||||
setTooltip(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const tooltip = context.tooltip;
|
||||
setTooltip({
|
||||
caretX: tooltip?.caretX,
|
||||
caretY: tooltip?.caretY,
|
||||
title: tooltip?.title?.join(' ') || '',
|
||||
align: tooltip?.xAlign || 'left',
|
||||
body:
|
||||
tooltip?.body?.map((item: any, index: number) => ({
|
||||
title: item?.lines?.join(' '),
|
||||
color: tooltip?.labelColors?.[index]
|
||||
?.borderColor,
|
||||
})) || [],
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -45,9 +99,16 @@ const createOptions = (theme: Theme, locationSettings: ILocationSettings) =>
|
||||
intersect: false,
|
||||
axis: 'x',
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0,
|
||||
},
|
||||
},
|
||||
// cubicInterpolationMode: 'monotone',
|
||||
color: theme.palette.text.secondary,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
type: 'linear',
|
||||
grid: {
|
||||
color: theme.palette.divider,
|
||||
@ -84,23 +145,21 @@ const createData = (
|
||||
{
|
||||
label: 'Total users',
|
||||
data: userTrends.map((item) => item.total),
|
||||
borderColor: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
label: 'Inactive users',
|
||||
data: userTrends.map((item) => item.inactive),
|
||||
borderColor: theme.palette.error.main,
|
||||
backgroundColor: theme.palette.error.main,
|
||||
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.main,
|
||||
backgroundColor: theme.palette.success.main,
|
||||
fill: true,
|
||||
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,
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -115,11 +174,29 @@ const UsersChartComponent: VFC<IUsersChartComponentProps> = ({
|
||||
[theme, userTrends],
|
||||
);
|
||||
|
||||
const options = createOptions(theme, locationSettings);
|
||||
const [tooltip, setTooltip] = useState<null | TooltipState>(null);
|
||||
const options = useMemo(
|
||||
() => createOptions(theme, locationSettings, setTooltip),
|
||||
[theme, locationSettings],
|
||||
);
|
||||
|
||||
return (
|
||||
<Paper sx={(theme) => ({ padding: theme.spacing(4) })}>
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={(theme) => ({
|
||||
padding: theme.spacing(3),
|
||||
position: 'relative',
|
||||
})}
|
||||
>
|
||||
<Typography
|
||||
variant='h3'
|
||||
sx={(theme) => ({ marginBottom: theme.spacing(3) })}
|
||||
>
|
||||
Users
|
||||
</Typography>
|
||||
<Line options={options} data={data} />
|
||||
|
||||
<ChartTooltip tooltip={tooltip} />
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user