mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 15:06:32 +00:00
fix(roadtrip): a freshly placed via lost its first drag
Placing one triggers a re-route, the re-route lands about a second later, and the render it causes rebuilt every via handle — which is exactly when somebody reaches for the dot they just made. On the GL renderer that rebuild replaces the DOM element outright, and a pointerdown belongs to the element it happened on, so the drag went to an element that no longer existed. The second attempt worked because by then nothing was pending, and an older via never showed it because nothing was re-routing. Two causes, both identity: the vias arrive as a fresh object on every reload even when the rows came back identical, and `onMoveVia` is a fresh closure on every render of the planner. Both were in the effect's dependencies. The list is now keyed by what a handle is built from — id and position — so it only changes when a via does, and the callbacks are read through a ref. A via that genuinely moved still gets a new list, which is what redraws it where it now is; the test pins both halves, because holding the element still must not mean holding it in the wrong place. Leaflet's markers had the same churn without the same symptom: a fresh `position` array called `setLatLng` on every marker on every render, and a fresh `eventHandlers` object unbound and rebound every listener. Same fix.
This commit is contained in:
@@ -1884,4 +1884,75 @@ describe('MapViewGL', () => {
|
||||
|
||||
expect(pin.style.transform).toContain('translate(140px, 60px)')
|
||||
})
|
||||
|
||||
// A via handle is a DOM element, and a pointerdown belongs to the element it happened
|
||||
// on. Rebuilding the handles on an unrelated render pulled that element out from under
|
||||
// the pointer, so a freshly placed via lost its first drag — the re-route it triggered
|
||||
// landed about a second later, which is exactly when somebody reaches for it.
|
||||
it('FE-COMP-MAPVIEWGL-VIA-001: a via handle survives a render it has nothing to do with', async () => {
|
||||
loadOnAttach()
|
||||
glCanvasContainer.replaceChildren()
|
||||
glMap.project.mockReturnValue({ x: 100, y: 80 })
|
||||
|
||||
const stored = { id: 5, day_id: 1, after_order_index: 0, sequence: 0, lat: 48.1, lng: 2.1 }
|
||||
const { rerender } = render(
|
||||
<MapViewGL
|
||||
places={[]}
|
||||
fitKey={1}
|
||||
glProvider="maplibre-gl"
|
||||
roadtripVias={{ 1: [stored] }}
|
||||
onMoveVia={() => {}}
|
||||
/>,
|
||||
)
|
||||
await flushFrames()
|
||||
|
||||
const layer = glCanvasContainer.firstElementChild as HTMLElement
|
||||
const handle = layer.firstElementChild
|
||||
expect(handle).toBeTruthy()
|
||||
|
||||
// The same via in a fresh object, with a fresh callback: what every render of the
|
||||
// planner hands down while the day re-routes.
|
||||
rerender(
|
||||
<MapViewGL
|
||||
places={[]}
|
||||
fitKey={1}
|
||||
glProvider="maplibre-gl"
|
||||
roadtripVias={{ 1: [{ ...stored }] }}
|
||||
onMoveVia={() => {}}
|
||||
/>,
|
||||
)
|
||||
await flushFrames()
|
||||
|
||||
expect(layer.firstElementChild).toBe(handle)
|
||||
})
|
||||
|
||||
it('FE-COMP-MAPVIEWGL-VIA-002: a via that actually moved is drawn again', async () => {
|
||||
loadOnAttach()
|
||||
glCanvasContainer.replaceChildren()
|
||||
glMap.project.mockReturnValue({ x: 100, y: 80 })
|
||||
|
||||
const stored = { id: 5, day_id: 1, after_order_index: 0, sequence: 0, lat: 48.1, lng: 2.1 }
|
||||
const { rerender } = render(
|
||||
<MapViewGL places={[]} fitKey={1} glProvider="maplibre-gl" roadtripVias={{ 1: [stored] }} onMoveVia={() => {}} />,
|
||||
)
|
||||
await flushFrames()
|
||||
|
||||
const layer = glCanvasContainer.firstElementChild as HTMLElement
|
||||
const handle = layer.firstElementChild
|
||||
|
||||
rerender(
|
||||
<MapViewGL
|
||||
places={[]}
|
||||
fitKey={1}
|
||||
glProvider="maplibre-gl"
|
||||
roadtripVias={{ 1: [{ ...stored, lat: 48.9 }] }}
|
||||
onMoveVia={() => {}}
|
||||
/>,
|
||||
)
|
||||
await flushFrames()
|
||||
|
||||
// The other half of the contract: holding the element still must not mean holding it
|
||||
// in the wrong place.
|
||||
expect(layer.firstElementChild).not.toBe(handle)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user