Browse Source

fix(host): a newer path edit supersedes a pending navigation

Editing the draft bumps the request sequence, so a slow lookup that settles
afterwards can neither clear the newer text nor repopulate the view with
the older path (ds-review-bot round 6).
creatixchu 2 months ago
parent
commit
36be9f26db

+ 8 - 1
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) {

+ 18 - 0
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<DirectoryListing>((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<HTMLInputElement>('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() })