From 4f72d17faf966d2283a156b2acd36e8f89ac9abe Mon Sep 17 00:00:00 2001 From: Yan <75355375+yancat160@users.noreply.github.com> Date: Fri, 8 May 2026 08:38:15 -0400 Subject: [PATCH] keep hidden events hidden across date-range changes Clicking a legend item on the events chart toggled hidden via chart.current.getDatasetMeta(idx).hidden, which lives on Chart.js's per-dataset meta object. Each time chartData changed (date-range switch, refetch, focusLabel update) the second useEffect in Chart.tsx replaces datasets wholesale, Chart.js regenerates the meta, and the hidden flags vanish, so previously-toggled-off events came back on their own. Lift the hidden state into React. EventsChart owns a Set of hidden labels and passes it down via a new optional hiddenLabels prop on Chart, plus an onLegendClick callback for controlled toggling. Chart re-applies hidden after the existing focusLabel pass so the set survives every data refresh, and falls back to the original meta-based behaviour when no callback is provided so other charts (website overview, revenue) keep their existing semantics. Verified with the seeded Demo SaaS data: hide signup_started in the Last 24 hours view, switch to Last 7 days then Last 30 days, the label stays greyed in the legend and absent from the bars; clicking again restores it. State is component-scoped, so it intentionally resets on reload or navigation away from the events page. --- src/components/charts/Chart.tsx | 26 +++++++++++++++++++++++++- src/components/metrics/EventsChart.tsx | 12 ++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/components/charts/Chart.tsx b/src/components/charts/Chart.tsx index b6ae9d794..6fe167690 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, !ds.hidden); + return; + } + if (type === 'bar') { const { datasetIndex } = item; const meta = chart.current.getDatasetMeta(datasetIndex); @@ -111,13 +124,24 @@ 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; + } + }); + } + 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 db23dcc47..9fe9d449b 100644 --- a/src/components/metrics/EventsChart.tsx +++ b/src/components/metrics/EventsChart.tsx @@ -26,6 +26,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; @@ -87,6 +97,8 @@ export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps) stacked={true} renderXLabel={renderXLabel} height="400px" + hiddenLabels={hiddenLabels} + onLegendClick={handleLegendClick} /> )}