Skip to content

Commit 8ebfbce

Browse files
committed
fix: clear hover for non-existent steps
1 parent 8ed8fd6 commit 8ebfbce

2 files changed

Lines changed: 52 additions & 14 deletions

File tree

src/components/ChartContainer.jsx

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,27 @@ const ChartWrapper = ({ data, options, chartId, onRegisterChart, onSyncHover })
103103
);
104104
};
105105

106+
export const syncHoverToCharts = (charts, step) => {
107+
charts.forEach((chart) => {
108+
if (!chart) return;
109+
if (step === null) {
110+
chart.setActiveElements([]);
111+
chart.tooltip.setActiveElements([]);
112+
chart.update('none');
113+
} else {
114+
const activeElements = getActiveElementsAtStep(chart.data.datasets, step);
115+
chart.setActiveElements(activeElements);
116+
if (activeElements.length > 0) {
117+
const xPixel = chart.scales.x.getPixelForValue(step);
118+
chart.tooltip.setActiveElements(activeElements, { x: xPixel, y: 0 });
119+
} else {
120+
chart.tooltip.setActiveElements([]);
121+
}
122+
chart.update('none');
123+
}
124+
});
125+
};
126+
106127
export default function ChartContainer({
107128
files,
108129
metrics = [],
@@ -121,19 +142,7 @@ export default function ChartContainer({
121142
}, []);
122143

123144
const syncHoverToAllCharts = useCallback((step) => {
124-
chartRefs.current.forEach((chart) => {
125-
if (!chart) return;
126-
if (step === null) {
127-
chart.setActiveElements([]);
128-
chart.tooltip.setActiveElements([]);
129-
chart.update('none');
130-
} else {
131-
const activeElements = getActiveElementsAtStep(chart.data.datasets, step);
132-
chart.setActiveElements(activeElements);
133-
chart.tooltip.setActiveElements(activeElements, { x: 0, y: 0 });
134-
chart.update('none');
135-
}
136-
});
145+
syncHoverToCharts(chartRefs.current, step);
137146
}, []);
138147

139148
const parsedData = useMemo(() => {

src/components/__tests__/ChartContainer.test.jsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ vi.mock('chart.js', () => {
2626

2727
vi.mock('chartjs-plugin-zoom', () => ({ default: {} }));
2828

29-
import ChartContainer, { getComparisonData, getActiveElementsAtStep } from '../ChartContainer.jsx';
29+
import ChartContainer, { getComparisonData, getActiveElementsAtStep, syncHoverToCharts } from '../ChartContainer.jsx';
3030

3131
const sampleFile = {
3232
name: 'test.log',
@@ -126,4 +126,33 @@ describe('ChartContainer', () => {
126126
const result = getActiveElementsAtStep(datasets, 0);
127127
expect(result).toEqual([]);
128128
});
129+
130+
it('clears highlights when step is absent', () => {
131+
const chart = {
132+
setActiveElements: vi.fn(),
133+
tooltip: { setActiveElements: vi.fn() },
134+
update: vi.fn(),
135+
data: { datasets: [{ data: [{ x: 210, y: 1 }] }] }
136+
};
137+
const charts = new Map([["a", chart]]);
138+
syncHoverToCharts(charts, 0);
139+
expect(chart.setActiveElements).toHaveBeenCalledWith([]);
140+
expect(chart.tooltip.setActiveElements).toHaveBeenCalledWith([]);
141+
expect(chart.update).toHaveBeenCalledWith('none');
142+
});
143+
144+
it('positions tooltip at matching step', () => {
145+
const chart = {
146+
setActiveElements: vi.fn(),
147+
tooltip: { setActiveElements: vi.fn() },
148+
update: vi.fn(),
149+
data: { datasets: [{ data: [{ x: 210, y: 1 }] }] },
150+
scales: { x: { getPixelForValue: vi.fn().mockReturnValue(123) } }
151+
};
152+
const charts = new Map([["a", chart]]);
153+
syncHoverToCharts(charts, 210);
154+
expect(chart.setActiveElements).toHaveBeenCalledWith([{ datasetIndex: 0, index: 0 }]);
155+
expect(chart.tooltip.setActiveElements).toHaveBeenCalledWith([{ datasetIndex: 0, index: 0 }], { x: 123, y: 0 });
156+
expect(chart.update).toHaveBeenCalledWith('none');
157+
});
129158
});

0 commit comments

Comments
 (0)