Skip to content

feat(InputMenu): add autocomplete prop#6026

Merged
benjamincanac merged 9 commits intov4from
feat/4859
Mar 5, 2026
Merged

feat(InputMenu): add autocomplete prop#6026
benjamincanac merged 9 commits intov4from
feat/4859

Conversation

@benjamincanac
Copy link
Member

@benjamincanac benjamincanac commented Feb 11, 2026

🔗 Linked issue

Resolves #4859, follows-up #6147

❓ Type of change

  • 📖 Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Integrate Reka UI's new Autocomplete component into the InputMenu component.

When the autocomplete prop is true, the InputMenu uses Autocomplete.Root and Autocomplete.Input instead of their Combobox counterparts. The modelValue becomes the input text (string) instead of a selected item, turning the component into a free-form text input with optional suggestions.

  • multiple, by, resetSearchTermOnSelect and resetModelValueOnClear are not applicable in autocomplete mode.
  • resetSearchTermOnBlur defaults to false in autocomplete mode.
  • Typing updates both the filter and the root's modelValue.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 11, 2026

npm i https://pkg.pr.new/@nuxt/ui@6026

commit: 48540ad

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 4, 2026

📝 Walkthrough

Walkthrough

Adds an autocomplete?: boolean prop to InputMenu and switches internal rendering to use dynamic Autocomplete or Combobox components based on that prop. Refactors prop forwarding, initialization, input/update/blur behavior, and templates to support free-form string model behavior when autocomplete is enabled. Adds a new Vue example demonstrating autocomplete, updates documentation with an Autocomplete section and navigation entry, and extends unit tests to cover autocomplete interactions and emitted events.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(InputMenu): add autocomplete prop' clearly and concisely describes the main change: adding a new autocomplete prop to the InputMenu component.
Linked Issues check ✅ Passed The implementation fully addresses the requirements from issue #4859: free-form text input where modelValue reflects the typed text, ability to accept arbitrary typed values, and proper input behavior instead of SelectMenu behavior.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the autocomplete feature: a new component example, documentation updates, InputMenu component refactoring, and corresponding tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description clearly relates to the changeset, explaining the new autocomplete feature integration and its behavior.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/4859

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/runtime/components/InputMenu.vue (1)

599-613: ⚠️ Potential issue | 🟠 Major

Disable the multiple structural path when autocomplete is enabled.

Autocomplete mode is documented as free-form text, but template structure still enables tags/multiple rendering. That creates an incompatible mode mix.

🩹 Proposed fix
-    :as-child="!!multiple"
+    :as-child="!!multiple && !autocomplete"
-      <TagsInputRoot
-        v-if="multiple"
+      <TagsInputRoot
+        v-if="multiple && !autocomplete"

Also applies to: 638-649

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/components/InputMenu.vue` around lines 599 - 613, The template
allows the multiple/tags structural path even when autocomplete mode is enabled,
causing an incompatible mix; update the conditional rendering and related props
to ensure the tags/multiple UI is disabled whenever autocomplete is true (e.g.,
change v-if="multiple" to v-if="multiple && !autocomplete" for TagsInputRoot and
any other multiple-specific blocks, and adjust :as-child="!!multiple" to
:as-child="multiple && !autocomplete" or equivalent), and apply the same change
to the other occurrence referenced (lines ~638-649) so all multiple-specific
markup is gated by multiple && !autocomplete.
🧹 Nitpick comments (1)
test/components/InputMenu.spec.ts (1)

167-179: Add regression tests for autocomplete-specific constraints.

The new tests cover emits, but they don’t lock behavior for the high-risk cases introduced here (e.g., blur retention/defaults and interaction with multiple). Adding those would prevent regressions in the new mode.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/components/InputMenu.spec.ts` around lines 167 - 179, Add regression
tests to lock autocomplete-specific behaviors: extend the InputMenu spec by
adding tests that (1) verify blur retention/defaults when autocomplete is true
by mounting InputMenu with autocomplete: true, setting a value on the
AutocompleteRoot, triggering a blur event and asserting whether the emitted
'update:modelValue' and 'change' events match the intended retention or fallback
behavior, and (2) verify interaction with the multiple prop by mounting
InputMenu with autocomplete: true and multiple: true, selecting multiple values
via AutocompleteRoot (setValue/clicks) and asserting emitted 'update:modelValue'
contains an array of selections and that blur/confirm behavior matches the
component's contract; reference the existing tests 'update:modelValue event with
autocomplete' and 'change event with autocomplete' to place these new cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/content/docs/2.components/input-menu.md`:
- Line 269: The caution line about props when `autocomplete` is `true`
incorrectly lists `displayValue` as a UInputMenu prop; update the sentence for
UInputMenu to either remove `displayValue` from the list or append a brief
clarification that `displayValue` is an internal/option-level field (not a
public UInputMenu prop) so users aren’t misled; locate the text mentioning
`autocomplete` and the list (`multiple`, `by`, `displayValue`,
`resetSearchTermOnSelect`, `resetModelValueOnClear`) and edit it to reflect the
correct public API for UInputMenu.

---

Outside diff comments:
In `@src/runtime/components/InputMenu.vue`:
- Around line 599-613: The template allows the multiple/tags structural path
even when autocomplete mode is enabled, causing an incompatible mix; update the
conditional rendering and related props to ensure the tags/multiple UI is
disabled whenever autocomplete is true (e.g., change v-if="multiple" to
v-if="multiple && !autocomplete" for TagsInputRoot and any other
multiple-specific blocks, and adjust :as-child="!!multiple" to
:as-child="multiple && !autocomplete" or equivalent), and apply the same change
to the other occurrence referenced (lines ~638-649) so all multiple-specific
markup is gated by multiple && !autocomplete.

---

Nitpick comments:
In `@test/components/InputMenu.spec.ts`:
- Around line 167-179: Add regression tests to lock autocomplete-specific
behaviors: extend the InputMenu spec by adding tests that (1) verify blur
retention/defaults when autocomplete is true by mounting InputMenu with
autocomplete: true, setting a value on the AutocompleteRoot, triggering a blur
event and asserting whether the emitted 'update:modelValue' and 'change' events
match the intended retention or fallback behavior, and (2) verify interaction
with the multiple prop by mounting InputMenu with autocomplete: true and
multiple: true, selecting multiple values via AutocompleteRoot (setValue/clicks)
and asserting emitted 'update:modelValue' contains an array of selections and
that blur/confirm behavior matches the component's contract; reference the
existing tests 'update:modelValue event with autocomplete' and 'change event
with autocomplete' to place these new cases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3803ce52-278b-4cab-aaa4-fe6e54b72782

📥 Commits

Reviewing files that changed from the base of the PR and between 355c99b and deef266.

⛔ Files ignored due to path filters (2)
  • test/components/__snapshots__/InputMenu-vue.spec.ts.snap is excluded by !**/*.snap
  • test/components/__snapshots__/InputMenu.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • docs/app/components/content/examples/input-menu/InputMenuAutocompleteExample.vue
  • docs/content/docs/2.components/input-menu.md
  • src/runtime/components/InputMenu.vue
  • test/components/InputMenu.spec.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/content/docs/2.components/input-menu.md`:
- Around line 258-266: The linter flags a setext-style heading because the YAML
frontmatter marker '---' in the ::component-example block is being parsed as an
underline; fix by ensuring the heading "### Autocomplete :badge{label=\"Soon\"
class=\"align-text-top\"}" is followed by a blank line before the
::component-example block (or alternatively replace the leading '---'
frontmatter markers inside the ::component-example with fenced YAML like
```yaml) so the section uses ATX heading style and MD003 is resolved.

In `@src/runtime/components/InputMenu.vue`:
- Around line 385-387: The autocomplete searchTerm is only initialized from
props.modelValue and ignores props.defaultValue, causing empty initial filtering
for uncontrolled usage; update the initialization in the block guarded by
props.autocomplete so that searchTerm.value is set from props.modelValue if
present, otherwise from props.defaultValue, and finally fall back to an empty
string (i.e., use props.modelValue ?? props.defaultValue ?? ''), ensuring this
change affects the existing searchTerm and props.autocomplete logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bb6c8d49-47d5-4954-b4cb-6ff1f95835b2

📥 Commits

Reviewing files that changed from the base of the PR and between deef266 and 828653b.

📒 Files selected for processing (2)
  • docs/content/docs/2.components/input-menu.md
  • src/runtime/components/InputMenu.vue

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
test/components/InputMenu.spec.ts (1)

168-180: Add a controlled-prop regression test for autocomplete mode.

These tests validate emit paths, but not the case where parent updates modelValue directly. Adding that case would guard against input/filter desync regressions in controlled usage.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/components/InputMenu.spec.ts` around lines 168 - 180, Add a regression
test in InputMenu.spec.ts that covers controlled-prop behavior in autocomplete
mode: mount InputMenu with autocomplete: true and items, find the
AutocompleteRoot, simulate user input (setValue) then simulate the parent
updating the prop by calling wrapper.setProps({ modelValue: 'Option 1' }) (or
the appropriate value shape used elsewhere) and assert that the component's
input/filter state stays in sync (e.g., the AutocompleteRoot value or displayed
suggestions reflect the new modelValue) and that no unexpected emits occur;
reference the existing test patterns using mount(InputMenu...), findComponent({
name: 'AutocompleteRoot' }), and wrapper.emitted() to mirror style and
assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/runtime/components/InputMenu.vue`:
- Around line 159-164: Prop `autocomplete` allows string-based runtime updates
but `modelValue`/`defaultValue`/`update:modelValue` are still typed as the
selection-model generics; change typings to use a unified InputMenuModel alias
that can represent either the selection model or string when autocomplete is
true. Update the component props/types: replace direct generic types for
`modelValue` and `defaultValue` with InputMenuModel<...>, update the
`InputMenuEmits['update:modelValue']` type to the same InputMenuModel alias, and
change the type of `highlight.value` to InputMenuModel<...> as well so all
places accept the string form when `autocomplete` is true. Ensure references to
`autocomplete` remain intact and adjust any related utility types or type guards
to discriminate the string model when `autocomplete` is true.
- Around line 383-390: The mounted-only initialization of searchTerm inside
onMounted/nextTick causes it to drift when the parent updates props.modelValue;
add a reactive watcher (e.g., watch or watchEffect) that observes
props.modelValue (and optionally props.defaultValue) and, when
props.autocomplete is true, sets searchTerm.value = String(props.modelValue ??
props.defaultValue ?? '') and when false resets searchTerm.value = ''. Update
the existing onMounted block to remain for initial setup but implement the
watcher to keep searchTerm in sync; reference the onMounted/nextTick
initialization, props.autocomplete, props.modelValue, props.defaultValue, and
the searchTerm ref when adding the watch.

---

Nitpick comments:
In `@test/components/InputMenu.spec.ts`:
- Around line 168-180: Add a regression test in InputMenu.spec.ts that covers
controlled-prop behavior in autocomplete mode: mount InputMenu with
autocomplete: true and items, find the AutocompleteRoot, simulate user input
(setValue) then simulate the parent updating the prop by calling
wrapper.setProps({ modelValue: 'Option 1' }) (or the appropriate value shape
used elsewhere) and assert that the component's input/filter state stays in sync
(e.g., the AutocompleteRoot value or displayed suggestions reflect the new
modelValue) and that no unexpected emits occur; reference the existing test
patterns using mount(InputMenu...), findComponent({ name: 'AutocompleteRoot' }),
and wrapper.emitted() to mirror style and assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2be4c7e3-0988-43aa-a1a2-fb7b4f14a29d

📥 Commits

Reviewing files that changed from the base of the PR and between 828653b and 9e23e5d.

⛔ Files ignored due to path filters (2)
  • test/components/__snapshots__/InputMenu-vue.spec.ts.snap is excluded by !**/*.snap
  • test/components/__snapshots__/InputMenu.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (2)
  • src/runtime/components/InputMenu.vue
  • test/components/InputMenu.spec.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
src/runtime/components/InputMenu.vue (2)

278-280: ⚠️ Potential issue | 🟠 Major

Root prop forwarding is still mode-locked and leaks blur-reset behavior into autocomplete.

Line 279 picks the forwarding branch once at setup. If autocomplete changes later, forwarded props won’t switch. Also, autocomplete forwarding still includes resetSearchTermOnBlur (defaulted to true at Line 263), which contradicts the autocomplete behavior contract.

♻️ Proposed fix
 const rootPropsPick = reactivePick(props, 'as', 'modelValue', 'defaultValue', 'open', 'defaultOpen', 'required', 'multiple', 'resetSearchTermOnBlur', 'resetSearchTermOnSelect', 'resetModelValueOnClear', 'highlightOnHover', 'openOnClick', 'openOnFocus', 'by')
-const rootProps = useForwardPropsEmits(props.autocomplete ? reactiveOmit(rootPropsPick, 'multiple', 'resetSearchTermOnSelect', 'resetModelValueOnClear', 'by') : rootPropsPick, emits)
+const comboboxRootProps = useForwardPropsEmits(rootPropsPick, emits)
+const autocompleteRootProps = useForwardPropsEmits(
+  reactiveOmit(rootPropsPick, 'multiple', 'resetSearchTermOnBlur', 'resetSearchTermOnSelect', 'resetModelValueOnClear', 'by'),
+  emits
+)
+const rootProps = computed(() => props.autocomplete ? autocompleteRootProps : comboboxRootProps)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/components/InputMenu.vue` around lines 278 - 280, rootPropsPick
is created once at setup using props.autocomplete, so later changes to
props.autocomplete won't switch which props are forwarded and autocomplete still
receives resetSearchTermOnBlur; make the forwarding reactive to
props.autocomplete by deriving rootPropsPick and rootProps from a computed that
watches props.autocomplete (use computed(() => ...)) and inside use
reactivePick/reactiveOmit based on the current props.autocomplete value, then
pass that computed result into useForwardPropsEmits so forwarded props update
when props.autocomplete toggles and ensure resetSearchTermOnBlur is omitted for
the autocomplete branch (i.e., when computed sees autocomplete true, omit
'multiple','resetSearchTermOnSelect','resetModelValueOnClear','resetSearchTermOnBlur','by'
as appropriate).

159-164: ⚠️ Potential issue | 🟠 Major

Autocomplete’s string model contract is not reflected in public TS types.

Runtime now treats modelValue as free-form text in autocomplete mode, but defaultValue / modelValue / update:modelValue / highlight.value remain typed as selection-model generics. This makes valid autocomplete usage type-unsafe for consumers.

♻️ Typing direction
+type InputMenuModel<
+  T extends ArrayOrNested<InputMenuItem>,
+  VK extends GetItemKeys<T> | undefined,
+  M extends boolean,
+  Mod extends Omit<ModelModifiers, 'lazy'>,
+  C extends boolean | object,
+  A extends boolean
+> = A extends true
+  ? string
+  : ApplyModifiers<GetModelValue<T, VK, M, ExcludeItem>, Mod> | IsClearUsed<M, C>

Then thread A extends boolean = false through InputMenuProps, InputMenuEmits, and slot payload types so autocomplete mode is strongly typed as string.

#!/bin/bash
# Read-only verification: confirm autocomplete is added but model typing is not discriminated by it.
rg -n -A2 -B2 "autocomplete\\?:|defaultValue\\?:|modelValue\\?:|'update:modelValue'|'highlight':" src/runtime/components/InputMenu.vue

Expected: autocomplete?: boolean exists, while model-related types still use GetModelValue-based generics without an autocomplete discriminator.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/components/InputMenu.vue` around lines 159 - 164, Prop types
currently always use the selection-model generics, so when autocomplete is true
the model types remain incorrect; update the component to thread a boolean
generic (e.g., A extends boolean = false) through InputMenuProps, InputMenuEmits
and the slot payload types and make GetModelValue conditional on A so that when
A is true defaultValue, modelValue, the 'update:modelValue' emit payload, and
highlight.value are typed as string instead of the selection generic; adjust all
references in InputMenu.vue (InputMenuProps, InputMenuEmits, slot payload types
and any usages of GetModelValue/highlight) to use the new A discriminator.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/runtime/components/InputMenu.vue`:
- Around line 278-280: rootPropsPick is created once at setup using
props.autocomplete, so later changes to props.autocomplete won't switch which
props are forwarded and autocomplete still receives resetSearchTermOnBlur; make
the forwarding reactive to props.autocomplete by deriving rootPropsPick and
rootProps from a computed that watches props.autocomplete (use computed(() =>
...)) and inside use reactivePick/reactiveOmit based on the current
props.autocomplete value, then pass that computed result into
useForwardPropsEmits so forwarded props update when props.autocomplete toggles
and ensure resetSearchTermOnBlur is omitted for the autocomplete branch (i.e.,
when computed sees autocomplete true, omit
'multiple','resetSearchTermOnSelect','resetModelValueOnClear','resetSearchTermOnBlur','by'
as appropriate).
- Around line 159-164: Prop types currently always use the selection-model
generics, so when autocomplete is true the model types remain incorrect; update
the component to thread a boolean generic (e.g., A extends boolean = false)
through InputMenuProps, InputMenuEmits and the slot payload types and make
GetModelValue conditional on A so that when A is true defaultValue, modelValue,
the 'update:modelValue' emit payload, and highlight.value are typed as string
instead of the selection generic; adjust all references in InputMenu.vue
(InputMenuProps, InputMenuEmits, slot payload types and any usages of
GetModelValue/highlight) to use the new A discriminator.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ba2547f1-0685-4b4c-8d2c-0b7263e7537b

📥 Commits

Reviewing files that changed from the base of the PR and between 9e23e5d and 8123730.

📒 Files selected for processing (2)
  • src/runtime/components/InputMenu.vue
  • test/components/InputMenu.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/components/InputMenu.spec.ts

@benjamincanac benjamincanac merged commit ee8a248 into v4 Mar 5, 2026
17 checks passed
@benjamincanac benjamincanac deleted the feat/4859 branch March 5, 2026 11:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v4 #4488

Projects

None yet

Development

Successfully merging this pull request may close these issues.

InputMenu behaves like a SelectMenu instead of a true input – needs proper input behavior

1 participant