Skip to content

feat: add MultiSelect composition#677

Draft
isaquebock wants to merge 1 commit into
devfrom
feat/multi-select
Draft

feat: add MultiSelect composition#677
isaquebock wants to merge 1 commit into
devfrom
feat/multi-select

Conversation

@isaquebock

Copy link
Copy Markdown
Contributor

Summary

  • New inputs/multi-select composition aligned with Figma 3899:29362 (options panel) and reusing the InputText / Select trigger visuals.
  • Root MultiSelect plus four sub-components: MultiSelectTrigger, MultiSelectContent, MultiSelectGroup, MultiSelectOption.
  • Multi-only API — no multiple prop, modelValue: unknown[] defaulting to [], displayValue: (value: unknown[]) => string. The option indicator is the existing webkit Checkbox; semantics ride on role="option" + aria-selected. Content is role="listbox" with aria-multiselectable="true". Enter / Space toggle without closing the panel; Escape and Tab close it.
  • Popover rendered with native primitives only — <Teleport> to body plus getBoundingClientRect + fixed positioning + the popup-scale-in/out semantic animations. No @floating-ui runtime.
  • #search and #footer slots on <MultiSelectContent> are optional and ungated by props (presence drives rendering).
  • Styling lives inline on each root class with data-* variants, per .claude/rules/styling.md.
  • Storybook: Default, Sizes, WithGroups, WithSearchAndFooter, WithOptionExtras, LongList, Filled, Invalid, Disabled — every story binds args so controls drive the component.
  • Five new webkit exports: ./multi-select, ./multi-select-trigger, ./multi-select-content, ./multi-select-group, ./multi-select-option.

Pending follow-ups (tracked, not in this PR)

  • var(--bg-surface-raised) is used on MultiSelectContent matching the existing SelectContent precedent but is not yet catalogued in .claude/docs/DESIGN.md. Decide whether to catalog the token or migrate both panels to var(--bg-surface) + var(--shadow-xs).
  • No multi-select.figma.ts yet — the select family has no Code Connect precedent. Adopt Code Connect for Select first, then mirror.

Test plan

  • pnpm webkit:lint && pnpm webkit:type-check && pnpm webkit:type-coverage
  • pnpm --filter storybook build
  • Storybook → Components / Inputs / MultiSelect: each story renders; controls panel drives the component.
  • Keyboard: open with Space / Enter / ArrowDown, navigate with Arrow* / Home / End, toggle with Enter / Space without closing, close with Escape.
  • Open the panel and inspect: <div role="listbox" aria-multiselectable="true">; selected option has aria-selected="true" and an internal <Checkbox aria-hidden="true">.
  • Resize / scroll the page with the panel open — anchored position updates.

New `inputs/multi-select` composition aligned with Figma `3899:29362`:

- root `MultiSelect` plus four sub-components — `MultiSelectTrigger`,
  `MultiSelectContent`, `MultiSelectGroup`, `MultiSelectOption`
- multi-only API: no `multiple` prop, `modelValue: unknown[]` defaulting
  to `[]`, `displayValue: (value: unknown[]) => string`
- option indicator is the existing `Checkbox` component (decorative,
  `aria-hidden`, `tabindex=-1`); semantics live on `role=option` +
  `aria-selected`
- panel is `role=listbox` with `aria-multiselectable=true`; Enter / Space
  toggles without closing, Escape and Tab close
- popover rendered via native primitives only — `<Teleport>` to body plus
  `getBoundingClientRect` + fixed positioning + `popup-scale-in/out`
  semantic animations (no `floating-ui` runtime)
- `#search` and `#footer` slots on `<MultiSelectContent>` are optional
  and ungated by props
- styling sits inline on each root `class` with `data-*` variants, in
  line with `.claude/rules/styling.md`
- Storybook: Default, Sizes, WithGroups, WithSearchAndFooter,
  WithOptionExtras, LongList, Filled, Invalid, Disabled — every story
  binds `args` so controls drive the component
- five new webkit exports: `./multi-select`, `./multi-select-trigger`,
  `./multi-select-content`, `./multi-select-group`, `./multi-select-option`
@isaquebock isaquebock requested a review from a team as a code owner June 26, 2026 00:12
@isaquebock isaquebock marked this pull request as draft June 26, 2026 00:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant