fix(InputMenu/SelectMenu): re-highlight first item when items change#6538
Conversation
reka-ui only re-highlights the first item when the collection goes from empty to non-empty. With `create-item`, the create item is always registered so the count never drops to 0, leaving the highlight stale when async `items` load. This makes Enter create instead of selecting the first option (or fall through to TagsInput's internal add). Re-highlight the first item when `items` change while the menu is open, waiting an extra tick so freshly mounted items are registered in reka-ui's collection first. Resolves #5974 Co-authored-by: lorado <4480983+lorado@users.noreply.github.com>
📝 WalkthroughWalkthroughThis PR fixes stale highlight behavior in Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/runtime/components/InputMenu.vue`:
- Around line 539-550: The watcher that calls
comboboxRootRef.value?.highlightFirstItem?.() should special-case
props.createItemPosition === 'top' so it highlights the first non-create option
instead of the "Create" item; update the watcher (the async function watching ()
=> props.items) to, after await nextTick(), check props.createItemPosition and
if it's 'top' find the first index in props.items that is not the create entry
and call the combobox API to highlight that index (or fall back to
highlightFirstItem() when createItemPosition !== 'top' or no separate index API
exists); also remove any lingering comment/guard about autocomplete methods
since comboboxRootRef.value?.highlightFirstItem?.() is safe with
AutocompleteRoot in reka-ui 2.9.8.
In `@src/runtime/components/SelectMenu.vue`:
- Around line 497-508: The watch currently always calls
comboboxRootRef.value?.highlightFirstItem(), which re-highlights the persistent
create-item when createItemPosition === 'top'; update the watch to detect when
createItemPosition === 'top' and in that case highlight the first non-create
option instead of blindly calling highlightFirstItem() — e.g. use
comboboxRootRef to inspect the registered items, find the first item that is not
the create-item (or skip items with the create-item marker), and call the
combobox highlight method for that item (or a highlight-by-index helper) so
async-loaded options get the correct focus while keeping the existing behavior
for bottom placement.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 548dd77e-cdef-4ba4-9046-764fb2373c23
📒 Files selected for processing (4)
src/runtime/components/InputMenu.vuesrc/runtime/components/SelectMenu.vuetest/components/InputMenu.spec.tstest/components/SelectMenu.spec.ts
| // reka-ui only re-highlights the first item when the list goes from empty to non-empty. | ||
| // With `create-item`, the create item is always registered so the count never drops to 0, | ||
| // leaving the highlight stale when async `items` load. Re-highlight when items change while open. | ||
| // Wait an extra tick so freshly mounted items are registered in reka-ui's collection before highlighting. | ||
| watch(() => props.items, async () => { | ||
| if (!isOpen.value) { | ||
| return | ||
| } | ||
|
|
||
| await nextTick() | ||
| comboboxRootRef.value?.highlightFirstItem?.() | ||
| }, { flush: 'post' }) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does reka-ui 2.9.8 AutocompleteRoot expose a highlightFirstItem method like ComboboxRoot?
💡 Result:
Yes, in Reka UI version 2.9.8, the AutocompleteRoot component exposes a highlightFirstItem method, similar to the ComboboxRoot component [1]. Based on the Reka UI codebase, the AutocompleteRoot implementation includes a defineExpose block that specifically exposes several methods, including highlightFirstItem, highlightItem, and highlightSelected [1]. These methods are bound to the underlying primitive element's corresponding functions, allowing for imperative control over the highlighted item within the autocomplete component [1]. This pattern matches the implementation found in ComboboxRoot, which also provides these imperative methods for managing highlighted states [1][2][3].
Citations:
- 1: unovue/reka-ui@3439cfa7
- 2: fix(combobox): async options highlight unovue/reka-ui#1854
- 3: [Bug]: ComboboxRoot exposed
highlightFirstItem,highlightSelected,highlightItemare always undefined unovue/reka-ui#1687
Handle createItemPosition="top" and remove unnecessary autocomplete-method concern
highlightFirstItem()re-highlights the first entry in reka-ui’s collection; whencreateItemPosition === 'top', the “Create” item is registered first, so this watcher will re-highlight “Create” rather than the first loaded option. Please confirm the intended behavior for thetopconfiguration (or adjust the logic to target the first non-create option).- The optional call to
comboboxRootRef?.highlightFirstItem?.()is safe in both modes: in reka-ui 2.9.8,AutocompleteRootalso exposeshighlightFirstItem.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/runtime/components/InputMenu.vue` around lines 539 - 550, The watcher
that calls comboboxRootRef.value?.highlightFirstItem?.() should special-case
props.createItemPosition === 'top' so it highlights the first non-create option
instead of the "Create" item; update the watcher (the async function watching ()
=> props.items) to, after await nextTick(), check props.createItemPosition and
if it's 'top' find the first index in props.items that is not the create entry
and call the combobox API to highlight that index (or fall back to
highlightFirstItem() when createItemPosition !== 'top' or no separate index API
exists); also remove any lingering comment/guard about autocomplete methods
since comboboxRootRef.value?.highlightFirstItem?.() is safe with
AutocompleteRoot in reka-ui 2.9.8.
| // reka-ui only re-highlights the first item when the list goes from empty to non-empty. | ||
| // With `create-item`, the create item is always registered so the count never drops to 0, | ||
| // leaving the highlight stale when async `items` load. Re-highlight when items change while open. | ||
| // Wait an extra tick so freshly mounted items are registered in reka-ui's collection before highlighting. | ||
| watch(() => props.items, async () => { | ||
| if (!isOpen.value) { | ||
| return | ||
| } | ||
|
|
||
| await nextTick() | ||
| comboboxRootRef.value?.highlightFirstItem?.() | ||
| }, { flush: 'post' }) |
There was a problem hiding this comment.
Same createItem top-position edge case as InputMenu.
highlightFirstItem() targets the first registered collection item, so when createItemPosition === 'top' (lines 672, 687‑688) the create item is highlighted again instead of the first async-loaded option, leaving the stale highlight unresolved for that configuration. The default bottom placement behaves correctly. Flagging here so the limitation is tracked consistently across both components.
Also applies to: 590-590
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/runtime/components/SelectMenu.vue` around lines 497 - 508, The watch
currently always calls comboboxRootRef.value?.highlightFirstItem(), which
re-highlights the persistent create-item when createItemPosition === 'top';
update the watch to detect when createItemPosition === 'top' and in that case
highlight the first non-create option instead of blindly calling
highlightFirstItem() — e.g. use comboboxRootRef to inspect the registered items,
find the first item that is not the create-item (or skip items with the
create-item marker), and call the combobox highlight method for that item (or a
highlight-by-index helper) so async-loaded options get the correct focus while
keeping the existing behavior for bottom placement.
commit: |
🔗 Linked issue
Resolves #5974
❓ Type of change
📚 Description
reka-ui only re-highlights the first item when its collection goes from empty to non-empty (
ComboboxInput'sfilterStatewatch guards onoldValue.count === 0). Withcreate-item, the create item is always registered, so the count never drops to0and the highlight stays stale when asyncitemsload.This caused the issues reported in #5974:
TagsInput's internal add, producing a phantom tag and inconsistentonCreate.Fix: re-highlight the first item via reka-ui's exposed
highlightFirstItem()wheneveritemschange while the menu is open. We wait one extra tick first, since freshly mounted items only register in reka-ui's collection after their element ref is set (otherwise the empty→populated transition re-highlights the stale create item).No custom Enter handler is needed: once the correct item is highlighted, reka-ui's native Enter handling selects/creates it.
Added regression tests for both components covering the async-items +
create-itemflow.📝 Checklist