Bladeren bron

fix(tui): preserve current selection across list updates (#39774)

Kit Langton 2 weken geleden
bovenliggende
commit
865f512a44
2 gewijzigde bestanden met toevoegingen van 21 en 3 verwijderingen
  1. 2 2
      packages/tui/src/ui/dialog-select.tsx
  2. 19 1
      packages/tui/test/cli/tui/dialog-select.test.tsx

+ 2 - 2
packages/tui/src/ui/dialog-select.tsx

@@ -240,7 +240,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
     on(
       () => props.options,
       () => {
-        if (!props.preserveSelection) {
+        if (!props.preserveSelection && props.current === undefined) {
           const count = flat().length
           if (count === 0) return
           const next = reconcileSelection(store.selected, count)
@@ -276,7 +276,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
           setStore("selected", index)
           selection = option
           if (!moved) return
-          if (!props.preserveSelection || store.filter.length > 0) return
+          if ((!props.preserveSelection && props.current === undefined) || store.filter.length > 0) return
           scrollAfterLayout(false, option.value)
           return
         }

+ 19 - 1
packages/tui/test/cli/tui/dialog-select.test.tsx

@@ -79,7 +79,7 @@ async function renderSelect(
   return app
 }
 
-async function mountSelect(root: string, initial: DialogSelectOption<string>[]) {
+async function mountSelect(root: string, initial: DialogSelectOption<string>[], current?: string) {
   const state = path.join(root, "state")
   await mkdir(state, { recursive: true })
   const config = createTuiResolvedConfig()
@@ -114,6 +114,7 @@ async function mountSelect(root: string, initial: DialogSelectOption<string>[])
           <DialogSelect
             title="Mutable options"
             options={options()}
+            current={current}
             onMove={(option) => moved.push(option.value)}
             onSelect={(option) => selected.push(option.value)}
           />
@@ -309,3 +310,20 @@ test("keeps the cursor index while options are temporarily empty", async () => {
     select.app.renderer.destroy()
   }
 })
+
+test("keeps the current option selected when options reorder", async () => {
+  await using tmp = await tmpdir()
+  const options = ["first", "current", "third"].map((value) => ({ title: value, value }))
+  const select = await mountSelect(tmp.path, options, "current")
+
+  try {
+    select.replaceOptions([options[1], options[2], options[0]])
+    await select.app.waitForFrame((frame) => frame.indexOf("current") < frame.indexOf("third"))
+    select.app.mockInput.pressEnter()
+    await select.app.waitFor(() => select.selected.length === 1)
+
+    expect(select.selected).toEqual(["current"])
+  } finally {
+    select.app.renderer.destroy()
+  }
+})