diff --git a/client/src/components/Roadtrip/RoadtripStopPopup.test.tsx b/client/src/components/Roadtrip/RoadtripStopPopup.test.tsx
index 8ba54af35..f9764950e 100644
--- a/client/src/components/Roadtrip/RoadtripStopPopup.test.tsx
+++ b/client/src/components/Roadtrip/RoadtripStopPopup.test.tsx
@@ -373,6 +373,27 @@ describe('editing a roadtrip stop', () => {
fireEvent.click(screen.getByRole('button', { name: 'Save' }))
await waitFor(() => expect(onSaveNight).toHaveBeenCalledWith({ endDayId: 5, checkIn: '16:00', checkOut: '10:00' }))
})
+
+ it('FE-ROADTRIP-STOPPOPUP-034: a new draft handed to the open popup shows the answers of that stop, not the previous one', async () => {
+ const onSave = vi.fn()
+ const onSaveNight = vi.fn()
+ const nights = { days: [{ id: 4, number: 1, date: null }, { id: 5, number: 2, date: null }], defaultEndDayId: 5 }
+ const hotelEdit = draft({ poi: poi({ category: 'hotel', name: 'Hotel Adlon' }), overnight: nights, editing: { placeId: 7, stopType: 'hotel', dwellMinutes: 30, accommodationId: 8, checkIn: '16:00', checkOut: '10:00' } })
+ const fuelEdit = draft({ editing: { placeId: 9, stopType: 'charging', dwellMinutes: 20 } })
+ const ui = (d: RoadtripStopDraft) =>
+
+ const { rerender } = render(ui(hotelEdit))
+ expect(screen.getByRole('textbox', { name: 'Check-in' })).toHaveValue('16:00')
+
+ rerender(ui(fuelEdit))
+ expect(screen.getByRole('button', { name: /Charging/ })).toHaveAttribute('aria-pressed', 'true')
+ fireEvent.click(screen.getByRole('button', { name: 'Save' }))
+ await waitFor(() => expect(onSave).toHaveBeenCalledWith({ stopType: 'charging', dwellMinutes: 20 }))
+
+ rerender(ui(draft({ poi: poi({ category: 'hotel', name: 'Hotel Bristol' }), arrivalTime: '15:45', overnight: nights })))
+ expect(screen.getByRole('button', { name: 'Overnight' })).toHaveAttribute('aria-pressed', 'true')
+ expect(screen.getByRole('textbox', { name: 'Check-in' })).toHaveValue('15:45')
+ })
})
diff --git a/client/src/components/Roadtrip/RoadtripStopPopup.tsx b/client/src/components/Roadtrip/RoadtripStopPopup.tsx
index 5da159da7..b6f9ee065 100644
--- a/client/src/components/Roadtrip/RoadtripStopPopup.tsx
+++ b/client/src/components/Roadtrip/RoadtripStopPopup.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react'
+import React, { useEffect, useState } from 'react'
import { ParkingSquare, Hourglass, AlertTriangle, BedDouble } from 'lucide-react'
import Modal from '../shared/Modal'
import CustomTimePicker from '../shared/CustomTimePicker'
@@ -69,6 +69,12 @@ interface RoadtripStopPopupProps {
* which neither column shows. This asks the two questions that matter (what kind of stop,
* how long) and says where it will land, with a way out to the full form for the rest.
*/
+const nightByDefault = (draft: RoadtripStopDraft | null): boolean =>
+ draft?.editing ? !!draft.editing.accommodationId : draft?.poi.category === 'hotel'
+
+const checkInByDefault = (draft: RoadtripStopDraft | null): string =>
+ draft?.editing?.checkIn || draft?.arrivalTime || ''
+
export default function RoadtripStopPopup({
draft, duplicateName, onClose, onSave, onSaveNight, onMoreDetails,
}: RoadtripStopPopupProps): React.ReactElement | null {
@@ -80,12 +86,20 @@ export default function RoadtripStopPopup({
// /trips//www.hotel.de instead of leaving the app. The helper adds the
// scheme for a bare host and drops anything that is not http(s).
const websiteHref = safeExternalHref(draft?.poi.website)
- const { stopType, dwell, setDwell, saving, setSaving, kind, pickKind } = useStopDraftForm(draft)
+ const { stopType, dwell, setDwell, saving, setSaving, kind, pickKind, seed } = useStopDraftForm(draft)
// A hotel is a night by default and a campsite a pause, which is what each already
// means everywhere else — but both offer the other, because a campsite is somewhere
// people sleep and a hotel is somewhere people stop for lunch.
- const [night, setNight] = useState(draft?.editing ? !!draft.editing.accommodationId : draft?.poi.category === 'hotel')
- const [checkIn, setCheckIn] = useState(draft?.editing?.checkIn || draft?.arrivalTime || '')
+ const [night, setNight] = useState(() => nightByDefault(draft))
+ const [checkIn, setCheckIn] = useState(() => checkInByDefault(draft))
+
+ // The popup stays mounted while the draft is swapped for another one, so every
+ // answer is seeded again from the new draft, the way the phone sheet does it.
+ useEffect(() => {
+ seed(draft)
+ setNight(nightByDefault(draft))
+ setCheckIn(checkInByDefault(draft))
+ }, [draft, seed])
if (!draft) return null
const overnight = draft.overnight
diff --git a/client/src/components/Roadtrip/useStopDraftForm.ts b/client/src/components/Roadtrip/useStopDraftForm.ts
index 6328e6e6a..4458fedc7 100644
--- a/client/src/components/Roadtrip/useStopDraftForm.ts
+++ b/client/src/components/Roadtrip/useStopDraftForm.ts
@@ -24,8 +24,8 @@ export function stopDraftDefaults(draft: RoadtripStopDraft | null | undefined):
/**
* The two questions a stop found along the drive answers before it goes onto the trip,
* what kind it is and how long it takes, behind the desktop stop popup and the phone's
- * draft sheet. `initialDraft` gives the first answers; the phone seeds them again for
- * every draft it is handed.
+ * draft sheet. `initialDraft` gives the first answers; both seed them again with
+ * `seed` for every draft they are handed while they stay open.
*/
export function useStopDraftForm(initialDraft: RoadtripStopDraft | null) {
const [stopType, setStopType] = useState(() => stopDraftDefaults(initialDraft).stopType);