Apply the chart cursor correction exactly once

uPlot writes what `cursor.move` returns back into the value it hands in next
time and calls it again on every redraw — and a chart sets its data on every
render. The correction was therefore applied repeatedly: the cursor walked
left while the pointer stood still, and the position taken at mousedown no
longer matched the one held at mouseup, which uPlot reads as a drag and
answers by swallowing the click. That is what stopped a chart tile being
selectable in the dashboard editor.

The refiner now returns its previous answer unchanged when handed it back, so
a redraw is a no-op. Lifted out of the config as `cursorRefiner` so the branch
can be checked without a browser; `cursor.check.ts` replays uPlot's own
sequences, including the press-is-not-a-drag one that regressed.

The Playwright hover test now also asserts the cursor holds its place across a
redraw, which is what the first version missed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZeGnqVsf5VHQqvz4HdUhN
This commit is contained in:
2026-08-23 09:13:03 +02:00
co-authored by Claude Opus 5
parent 2fcaf96a83
commit b9922e7a7d
3 changed files with 130 additions and 15 deletions
+15 -3
View File
@@ -445,10 +445,22 @@ test("a chart's cursor follows the pointer", async ({ page }) => {
const cursor = chart.locator(".u-cursor-x")
await expect(cursor).toBeVisible()
const line = (await cursor.boundingBox()) as { x: number }
const at = async () => ((await cursor.boundingBox()) as { x: number }).x
const line = await at()
expect(
Math.abs(line.x - x),
`the cursor is drawn at ${line.x.toFixed(1)}, the pointer is at ${x.toFixed(1)}`,
Math.abs(line - x),
`the cursor is drawn at ${line.toFixed(1)}, the pointer is at ${x.toFixed(1)}`,
).toBeLessThan(3)
// And it stays there. The chart sets its data on every render, which makes
// uPlot recompute the cursor from the position it already holds — so a
// correction applied twice would walk the line left while nothing moved.
await publish(page, w("level"), 55)
await page.waitForTimeout(1500)
const settled = await at()
expect(
Math.abs(settled - x),
`after a redraw the cursor is at ${settled.toFixed(1)}, the pointer at ${x.toFixed(1)}`,
).toBeLessThan(3)
})