Skip to content

Commit 7c2f13a

Browse files
committed
fix: prevent duplicate hover entries
1 parent c104dbe commit 7c2f13a

2 files changed

Lines changed: 27 additions & 17 deletions

File tree

src/components/ChartContainer.jsx

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ ChartJS.register(
2626
zoomPlugin
2727
);
2828

29-
const ChartWrapper = ({ data, options, chartId, onRegisterChart, onSyncHover }) => {
29+
const ChartWrapper = ({ data, options, chartId, onRegisterChart, onSyncHover, syncRef }) => {
3030
const chartRef = useRef(null);
3131

3232
const handleChartRef = useCallback((ref) => {
@@ -38,17 +38,18 @@ const ChartWrapper = ({ data, options, chartId, onRegisterChart, onSyncHover })
3838

3939
const enhancedOptions = {
4040
...options,
41-
onHover: (event, activeElements) => {
42-
if (activeElements.length > 0 && chartRef.current) {
43-
const { datasetIndex, index } = activeElements[0];
44-
const dataset = chartRef.current.data.datasets[datasetIndex];
45-
const point = dataset.data[index];
46-
const step = point.x;
47-
onSyncHover(step, chartId);
48-
} else {
49-
onSyncHover(null, chartId);
50-
}
51-
},
41+
onHover: (event, activeElements) => {
42+
if (syncRef?.current) return;
43+
if (activeElements.length > 0 && chartRef.current) {
44+
const { datasetIndex, index } = activeElements[0];
45+
const dataset = chartRef.current.data.datasets[datasetIndex];
46+
const point = dataset.data[index];
47+
const step = point.x;
48+
onSyncHover(step, chartId);
49+
} else {
50+
onSyncHover(null, chartId);
51+
}
52+
},
5253
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
5354
};
5455

@@ -74,17 +75,20 @@ export default function ChartContainer({
7475
onMaxStepChange
7576
}) {
7677
const chartRefs = useRef(new Map());
78+
const syncLockRef = useRef(false);
7779
const registerChart = useCallback((id, inst) => {
7880
chartRefs.current.set(id, inst);
7981
}, []);
8082

8183
const syncHoverToAllCharts = useCallback((step, sourceId) => {
84+
if (syncLockRef.current) return;
85+
syncLockRef.current = true;
8286
chartRefs.current.forEach((chart, id) => {
8387
if (!chart) return;
8488
if (step === null) {
8589
chart.setActiveElements([]);
86-
chart.tooltip.setActiveElements([]);
87-
chart.update('none');
90+
chart.tooltip.setActiveElements([], { x: 0, y: 0 });
91+
chart.draw();
8892
} else if (id !== sourceId) {
8993
const activeElements = [];
9094
chart.data.datasets.forEach((dataset, datasetIndex) => {
@@ -93,11 +97,13 @@ export default function ChartContainer({
9397
activeElements.push({ datasetIndex, index: idx });
9498
}
9599
});
100+
const pos = { x: chart.scales.x.getPixelForValue(step), y: 0 };
96101
chart.setActiveElements(activeElements);
97-
chart.tooltip.setActiveElements(activeElements, { x: 0, y: 0 });
98-
chart.update('none');
102+
chart.tooltip.setActiveElements(activeElements, pos);
103+
chart.draw();
99104
}
100105
});
106+
syncLockRef.current = false;
101107
}, []);
102108

103109
const parsedData = useMemo(() => {
@@ -548,6 +554,7 @@ export default function ChartContainer({
548554
chartId={`metric-comp-${idx}`}
549555
onRegisterChart={registerChart}
550556
onSyncHover={syncHoverToAllCharts}
557+
syncRef={syncLockRef}
551558
data={compData}
552559
options={compOptions}
553560
/>
@@ -562,6 +569,7 @@ export default function ChartContainer({
562569
chartId={`metric-${idx}`}
563570
onRegisterChart={registerChart}
564571
onSyncHover={syncHoverToAllCharts}
572+
syncRef={syncLockRef}
565573
data={createChartData(dataArray)}
566574
options={options}
567575
/>

src/components/__tests__/ChartContainer.test.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ vi.mock('react-chartjs-2', async () => {
3333
data: props.data,
3434
setActiveElements: vi.fn(),
3535
tooltip: { setActiveElements: vi.fn() },
36-
update: vi.fn(),
36+
draw: vi.fn(),
37+
scales: { x: { getPixelForValue: vi.fn(() => 0) } },
3738
};
3839
charts.push(chart);
3940
if (typeof ref === 'function') ref(chart);
@@ -110,6 +111,7 @@ describe('ChartContainer', () => {
110111
const hover = __lineProps[0].options.onHover;
111112
hover({}, [{ index: 0, datasetIndex: 0 }]);
112113
expect(__charts[1].setActiveElements).toHaveBeenCalled();
114+
expect(__charts[1].draw).toHaveBeenCalled();
113115
});
114116

115117
it('parses metrics, applies range and triggers callbacks', () => {

0 commit comments

Comments
 (0)