diff --git a/src/components/charts/Chart.tsx b/src/components/charts/Chart.tsx index 6604a2d28..0285cf943 100644 --- a/src/components/charts/Chart.tsx +++ b/src/components/charts/Chart.tsx @@ -18,6 +18,8 @@ export interface ChartProps extends BoxProps { updateMode?: UpdateMode; animationDuration?: number; onTooltip?: (model: any) => void; + hiddenLabels?: Set; + onLegendClick?: (label: string, willBeHidden: boolean) => void; } export function Chart({ @@ -27,6 +29,8 @@ export function Chart({ updateMode, onTooltip, chartOptions, + hiddenLabels, + onLegendClick, ...props }: ChartProps) { const canvas = useRef(null); @@ -61,6 +65,15 @@ export function Chart({ }, [chartOptions]); const handleLegendClick = (item: LegendItem) => { + if (onLegendClick && type === 'bar') { + // Controlled mode: caller owns the hidden state. We report the click + // and let the parent push a new hiddenLabels set on the next render. + const { datasetIndex } = item; + const ds = chart.current.data.datasets[datasetIndex]; + onLegendClick(ds.label, !hiddenLabels?.has(ds.label)); + return; + } + if (type === 'bar') { const { datasetIndex } = item; const meta = chart.current.getDatasetMeta(datasetIndex); @@ -111,13 +124,28 @@ export function Chart({ }); } + // Re-apply caller-driven hidden flags after focusLabel handling so a + // dataset stays hidden across data changes (e.g. date-range switches) + // even though Chart.js regenerates dataset meta on every replace. + if (hiddenLabels) { + chart.current.data.datasets.forEach((ds: { hidden: boolean; label: any }) => { + if (hiddenLabels.has(ds.label)) { + ds.hidden = true; + } else if (!chartData.focusLabel) { + // Explicitly reset so un-hiding a label is always reflected, + // regardless of whether the focusLabel pass ran above. + ds.hidden = false; + } + }); + } + chart.current.options = options; chart.current.update(updateMode); setLegendItems(chart.current.legend.legendItems); } - }, [chartData, options, updateMode]); + }, [chartData, options, updateMode, hiddenLabels]); return ( diff --git a/src/components/metrics/EventsChart.tsx b/src/components/metrics/EventsChart.tsx index 473b8d20c..837ed2dae 100644 --- a/src/components/metrics/EventsChart.tsx +++ b/src/components/metrics/EventsChart.tsx @@ -27,6 +27,16 @@ export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps) const { locale, dateLocale } = useLocale(); const { data, isLoading, error } = useWebsiteEventsSeriesQuery(websiteId, { limit }); const [label, setLabel] = useState(focusLabel); + const [hiddenLabels, setHiddenLabels] = useState>(() => new Set()); + + const handleLegendClick = useCallback((legendLabel: string, willBeHidden: boolean) => { + setHiddenLabels(prev => { + const next = new Set(prev); + if (willBeHidden) next.add(legendLabel); + else next.delete(legendLabel); + return next; + }); + }, []); const chartData: any = useMemo(() => { if (!data) return; @@ -115,6 +125,8 @@ export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps) stacked={true} renderXLabel={renderXLabel} height="400px" + hiddenLabels={hiddenLabels} + onLegendClick={handleLegendClick} /> )}