fix(places): coordinates pasted on the phone survive the next search pick

On the phone place form, pasting a "lat, lng" pair into the latitude field
filled both coordinates, but a later search pick whose place came without a
position emptied them again whenever an earlier pick had filled them. On the
desktop the pasted position stays.

Cause: the phone's paste handler wrote lat and lng with setForm directly, so
autoFilledRef still listed them as written by the previous pick, and
mergeResult clears such fields when the new pick says nothing about them. The
desktop goes through handleChange, which marks a field as typed by hand.

Fix: the phone's paste handler goes through handleChange for both halves, the
same as the desktop dialog.

Red test: tests/unit/mobile/trip/MPlaceEditSheet.test.tsx FE-MOB-PLEDIT-047
failed on the unfixed code with "expect(element).toHaveValue(35.7101)
Expected the element to have value: 35.7101 Received: (empty)".

No existing assertions changed.
This commit is contained in:
Maurice
2026-10-09 17:38:48 +02:00
parent 0bdf0fc345
commit cee496c469
2 changed files with 33 additions and 2 deletions
@@ -165,7 +165,10 @@ export default function MPlaceEditSheet({ planner, onOpenExpense }: MPlaceEditSh
const pair = parseCoordinatePair(e.clipboardData.getData('text'))
if (pair) {
e.preventDefault()
setForm(prev => ({ ...prev, lat: pair[0], lng: pair[1] }))
// Through handleChange, as on the desktop: a pasted position is the user's,
// so the next search pick must not clear it.
handleChange('lat', pair[0])
handleChange('lng', pair[1])
}
}
@@ -11,7 +11,7 @@ import { server } from '../../../helpers/msw/server'
import { resetAllStores, seedStore } from '../../../helpers/store'
import { fireEvent, render, screen, waitFor } from '../../../helpers/render'
// FE-MOB-PLEDIT-001 to FE-MOB-PLEDIT-046, plus the 009b, 025b and 029b variants
// FE-MOB-PLEDIT-001 to FE-MOB-PLEDIT-047, plus the 009b, 025b and 029b variants
// planner.t echoes the key, so every label/placeholder is asserted as its key.
const CATEGORIES = [
@@ -189,6 +189,34 @@ describe('MPlaceEditSheet', () => {
expect(screen.getByPlaceholderText('places.formLng')).toHaveValue('139.6917')
})
it('FE-MOB-PLEDIT-047: a pasted "lat, lng" pair counts as typed, so a later pick without a position keeps it', async () => {
let searches = 0
server.use(
http.post('/api/maps/search', () => {
searches += 1
return HttpResponse.json(searches === 1
? { source: 'osm', places: [{ name: 'Ueno Koen', address: 'Taito', lat: 35.7, lng: 139.7 }] }
: { source: 'osm', places: [{ name: 'Ameyoko', address: 'Ueno' }] })
}),
)
setup()
const search = screen.getByPlaceholderText('places.mapsSearchPlaceholder')
fireEvent.change(search, { target: { value: 'ueno koen' } })
fireEvent.click(screen.getByRole('button', { name: 'common.search' }))
fireEvent.click(await screen.findByText('Ueno Koen'))
const lat = screen.getByPlaceholderText('places.formLat')
fireEvent.paste(lat, { clipboardData: { getData: () => '35.7101, 139.7745' } })
fireEvent.change(search, { target: { value: 'ameyoko' } })
fireEvent.click(screen.getByRole('button', { name: 'common.search' }))
fireEvent.click(await screen.findByText('Ameyoko'))
expect(nameField()).toHaveValue('Ameyoko')
expect(lat).toHaveValue('35.7101')
expect(screen.getByPlaceholderText('places.formLng')).toHaveValue('139.7745')
})
it('FE-MOB-PLEDIT-013: an unparseable paste is left to the browser', () => {
setup()
const lat = screen.getByPlaceholderText('places.formLat')