diff --git a/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx b/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx index 88844d4120..b91ac5311d 100644 --- a/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx +++ b/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx @@ -309,7 +309,14 @@ export function DirectoryBrowser({ open, listDirectory, createDirectory, onOpen, aria-label={t('browser.editPath')} autoFocus disabled={parentInert} - onChange={(event) => { setPathDraft(event.target.value) }} + onChange={(event) => { + // Editing the draft supersedes any in-flight navigation: + // its completion must neither clear the newer text nor + // repopulate the view with the older path. + requestSeq.current += 1 + setLoading(false) + setPathDraft(event.target.value) + }} {...compositionGuard} onKeyDown={(event) => { if (event.key === 'Enter' && !composingRef.current) { diff --git a/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx b/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx index 360b03ac6a..2fae7adbda 100644 --- a/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx +++ b/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx @@ -337,6 +337,24 @@ describe('DirectoryBrowser', () => { expect(columns()).toHaveLength(2) }) + it('keeps a newer path edit when an older slow navigation settles', async () => { + const pending: ((listing: DirectoryListing) => void)[] = [] + const b = mount() + await waitFor(() => { expect(screen.getByRole('listitem')).toBeTruthy() }) + fireEvent.click(screen.getByRole('button', { name: 'browser.editPath' })) + const input = screen.getByLabelText('browser.editPath') + b.listDirectory.mockImplementation(() => + new Promise((settle) => { pending.push(settle) })) + fireEvent.change(input, { target: { value: DOCS } }) + fireEvent.keyDown(input, { key: 'Enter' }) + // The user keeps typing while the lookup hangs; the older completion must + // neither clear this newer draft nor swap the view to the older path. + fireEvent.change(input, { target: { value: `${DOCS}/har` } }) + await act(async () => { pending.shift()!(listingFor(DOCS)) }) + expect(screen.getByLabelText('browser.editPath').value).toBe(`${DOCS}/har`) + expect(screen.queryByText('harness')).toBeNull() + }) + it('ignores dismissal while adoption is busy', async () => { const b = mount({ busy: true }) await waitFor(() => { expect(screen.getByRole('dialog')).toBeTruthy() })