Skip to content

feat(createCombobox): commit free-text values in non-strict mode#383

Draft
johnleider wants to merge 3 commits into
masterfrom
worktree-combobox-freetext
Draft

feat(createCombobox): commit free-text values in non-strict mode#383
johnleider wants to merge 3 commits into
masterfrom
worktree-combobox-freetext

Conversation

@johnleider

Copy link
Copy Markdown
Member

Summary

createCombobox exposed a strict option whose only real effect was the aria-autocomplete attribute. The documented "reverts query to selected value on close" behaviour was unconditional (driven by pristine + display, fired on every close()), so strict was effectively cosmetic — and the default non-strict combobox silently discarded any typed text that didn't match a registered option. A non-strict / free-solo combobox is supposed to accept arbitrary values.

This PR makes non-strict mean what it says: typed text that matches no option is committed as a value.

What changed

  • commit() action on the combobox context. On confirm it selects an exact value match if one exists; otherwise, when !strict, it mints a registry ticket for the typed text, selects it, and emits it through v-model. When strict, unmatched text is discarded.
  • ComboboxControl wires commit() to Enter (when nothing is highlighted) and Tab (accept highlight, else commit). Escape still cancels/reverts — which is why commit lives in its own action rather than in close() (Escape and click-outside both route through close()).
  • Ad-hoc value hygiene: minted free-text tickets are kept out of virtual-focus navigation (they have no rendered option / DOM node), pruned when superseded in single-select, and cleared on clear().
  • Docs/JSDoc corrected: the strict prop, module remarks, Strict Mode prose, the aria-autocomplete note, and the keyboard table previously described the unconditional revert as if it were strict-gated.

Design note

This is the "register-on-commit" approach (vs. a parallel free-value channel) — it composes with the existing ticket/useProxyModel machinery instead of bolting a second value path onto every selection consumer. Single-select is the primary path; multiple-select (free-text tags) works but is the lighter-tested surface — removing a tag via direct deselect leaves an orphaned-but-deselected ticket until clear().

Tests

No automated tests are included in this commit, per the repo convention of not adding tests unless requested. The existing 119 combobox tests pass unchanged (the new behaviour is keyed on Enter/Tab; the prior "strict mode" tests exercise close() and are unaffected). The commit() path — free-text emit, strict discard, exact-match dedup, single-select pruning — is currently uncovered; happy to add a focused describe('free-text commit') block if you want it in this PR.

A non-strict combobox (the default) now accepts typed text that matches no
registered option. Confirming with Enter or Tab mints a ticket for the typed
value, selects it, and emits it through v-model; strict keeps the constrained
behaviour and discards unmatched text on confirm.

Adds a commit() action to the combobox context, wired to Enter (no highlight)
and Tab in ComboboxControl. Free-text values are kept out of keyboard
navigation and pruned when superseded (single-select) or cleared. Corrects the
strict prop and docs, which previously described the unconditional
revert-on-close as if it were strict-gated.
@johnleider johnleider added this to the v1.0.0 milestone Jun 25, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jun 25, 2026

Copy link
Copy Markdown

Open in StackBlitz

commit: a6d1b4a

Tab routes to commit()/select(), neither of which closes the menu in
multiple-select mode, and useClickOutside only dismisses on pointer
events — so tabbing out left the listbox floating over the next focused
control. Close explicitly on Tab (idempotent for single-select, which
already closes via commit/select).
@johnleider johnleider marked this pull request as draft July 1, 2026 15:22
# Conflicts:
#	apps/docs/src/pages/components/forms/combobox.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant