fix(roadtrip): keep day-ending labels draggable after icon changes

This commit is contained in:
Maurice
2026-09-11 23:30:38 +02:00
parent fccc55732b
commit dd6ad31ab9
3 changed files with 28 additions and 3 deletions
@@ -2,8 +2,9 @@ import { expect, it, vi } from 'vitest'
import { bindDayBoundaryDrag } from './dayBoundaryDrag'
import type { RoadtripStop } from '../Roadtrip/useRoadtripRoutes'
function setup() {
function setup(withHandle = false) {
const el = document.createElement('span')
if (withHandle) el.innerHTML = '<span data-night-pause="route">1</span>'
document.body.append(el)
const save = vi.fn(async () => true), setPosition = vi.fn(), unlock = vi.fn()
const stop = (assignmentId: number, lng: number) => ({ assignmentId, lat: 0, lng }) as RoadtripStop
@@ -14,6 +15,29 @@ function setup() {
return { el, save, setPosition, unlock, dispose, pointer }
}
it('keeps dragging and keyboard controls after Leaflet replaces the label contents', async () => {
const s = setup(true)
for (const kind of ['route', 'place', 'route']) {
const label = s.el.firstElementChild!
s.pointer(label, 'pointerdown', 500)
s.pointer(document, 'pointermove', 990)
s.pointer(document, 'pointerup', 990)
await Promise.resolve()
s.el.innerHTML = `<span data-night-pause="${kind}">1</span>`
}
expect(s.save).toHaveBeenCalledTimes(3)
expect(s.unlock).toHaveBeenCalledTimes(3)
s.el.firstElementChild!.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', bubbles: true }))
expect(s.save).toHaveBeenLastCalledWith(1, null)
await Promise.resolve()
s.dispose()
s.pointer(s.el.firstElementChild!, 'pointerdown', 500)
s.pointer(document, 'pointermove', 990)
s.pointer(document, 'pointerup', 990)
expect(s.save).toHaveBeenCalledTimes(4)
s.el.remove()
})
it('saves the snapped visit once on release and restores map dragging', async () => {
const s = setup()
s.pointer(s.el, 'pointerdown', 500)
+2 -1
View File
@@ -16,7 +16,8 @@ interface DragMap {
export function bindDayBoundaryDrag(el: HTMLElement, via: RouteVia, controls: DayBoundaryControls, map: DragMap) {
const pause = via.nightPause!
const handle = el.querySelector<HTMLElement>('[data-night-pause]') ?? el
// Leaflet replaces the label contents when its icon changes, but keeps this root.
const handle = el
handle.style.cursor = 'grab'
handle.style.touchAction = 'none'
handle.tabIndex = 0
@@ -13,5 +13,5 @@ export function nightPauseMarker(via: RouteVia): string {
const stem = pause.atPlace
? 'left:20px;top:-7px;width:10px;height:1px;transform:rotate(-35deg);transform-origin:left center;'
: 'left:-.5px;top:-14px;width:1px;height:14px;'
return `<span aria-hidden="true" style="position:absolute;${stem}background:var(--text-muted);pointer-events:none;"></span><span data-night-pause="${pause.atPlace ? 'place' : 'route'}" style="position:absolute;${position}display:inline-flex;align-items:center;gap:5px;height:26px;padding:0 8px;box-sizing:border-box;border:1px solid var(--border-primary);border-radius:9px;background:var(--bg-card);color:var(--text-primary);box-shadow:var(--shadow-sm);font-family:var(--font-system);font-size:calc(11px * var(--fs-scale-caption,1));font-weight:600;line-height:1;white-space:nowrap;cursor:pointer;">${moon}<span>${escapeHtml(String(pause.day))}</span></span>`
return `<span aria-hidden="true" style="position:absolute;${stem}background:var(--text-muted);pointer-events:none;"></span><span data-night-pause="${pause.atPlace ? 'place' : 'route'}" style="position:absolute;${position}display:inline-flex;align-items:center;gap:5px;height:26px;padding:0 8px;box-sizing:border-box;border:1px solid var(--border-primary);border-radius:9px;background:var(--bg-card);color:var(--text-primary);box-shadow:var(--shadow-sm);font-family:var(--font-system);font-size:calc(11px * var(--fs-scale-caption,1));font-weight:600;line-height:1;white-space:nowrap;cursor:inherit;">${moon}<span>${escapeHtml(String(pause.day))}</span></span>`
}