Skip to content

Let an optional SearchableSelect be cleared again - #29

Merged
StanBarrows merged 1 commit into
mainfrom
fix/searchable-select-clearable
Aug 11, 2026
Merged

Let an optional SearchableSelect be cleared again#29
StanBarrows merged 1 commit into
mainfrom
fix/searchable-select-clearable

Conversation

@StanBarrows

Copy link
Copy Markdown
Contributor

Why

SearchableSelect picks from a closed set and only ever emits option.value, so once anything was picked there was no gesture that returned to "nothing chosen" — a trap for nullable fields. flows.codebar hit it on the data-source form: the store-dialog field is nullable all the way down, but a selected dialog could never be unselected. Its sibling controls both already had an exit (native Select renders a selectable placeholder option; Combobox is erasable free text); this closes the gap for the third sibling.

What

  • New clearable prop (+ clear-label for the ✕'s accessible name, default "Clear selection"). While a selection is visible, an ✕ appears between the label and the chevron; pressing it clears, keeps the menu closed, and hands focus back to the trigger (the ✕ it was on has just unmounted).
  • Clearing emits '', not null — the native Select's placeholder payload. Emitting null would widen the emit type to T | null for every consumer, forcing null-handling onto call sites whose select is not clearable and can never receive it. For T = string sites, T | '' collapses to string: existing handlers type-check unchanged.
  • The ✕ is a sibling of the trigger, not a child — the trigger is itself a <button>, and nested buttons are invalid HTML that browsers "repair" by splitting apart. Absolutely positioned into the trigger's right end at full control height; only rendered while a selected option's label is actually on screen.
  • Version bumped to 1.21.0. (Heads-up while releasing: tag v1.20.0 ships a manifest saying 1.19.1 — the tagged-without-bump failure mode verify-version documents for v1.17.0, so the next tag must be cut from a merged bump.)

Testing

  • New Clearable story pins the loop: clear → model '', placeholder restored, menu closed, focus on trigger, ✕ unmounted; re-pick → ✕ back.
  • Full suite: 248 Playwright tests pass; typecheck, lint, build clean.
  • Consumed from flows.codebar (dist synced locally): its new browser test picks the auto-selected store dialog, clears it via the ✕, saves, and asserts docuware_store_dialog_id is stored as null.

🤖 Generated with Claude Code

SearchableSelect picks from a closed set and only ever emits option.value,
so once anything was picked there was no gesture that returned to "nothing
chosen" — a trap for nullable fields like the consuming app's store-dialog
select. A new `clearable` prop shows an ✕ (accessible name via
`clear-label`) that emits '' — the native Select's placeholder payload, so
non-clearable call sites' types stay untouched — and hands focus back to
the trigger. Pinned by the Clearable story.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@StanBarrows
StanBarrows merged commit d0e183d into main Aug 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant