Skip to content

feat(core): add ComplexSelector - #4659

Draft
cixzhang wants to merge 5 commits into
mainfrom
feat/complex-selector
Draft

feat(core): add ComplexSelector#4659
cixzhang wants to merge 5 commits into
mainfrom
feat/complex-selector

Conversation

@cixzhang

@cixzhang cixzhang commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds ComplexSelector, a high-level selector shell for rich custom selection surfaces. It keeps the XDS-style single-component API while letting consumers compose their own accessible content from value, onChange, close, and state helpers.

What changed

  • Adds an accessible Field + button + dialog-popover selector shell with optimistic changeAction support and generated exports.
  • Simplifies the API to open composition: custom content owns its list/grid/tree/date/input semantics, while ComplexSelector owns trigger semantics, popover behavior, focus restore, and the async change flow.
  • Keeps the closed-trigger API narrow with triggerLabel only.
  • Updates Storybook examples:
    • Fruit/ripeness selector restyled toward a compact row + pill selector pattern, with useGridFocus preserving columns for keyboard users.
    • Project destination selector combining TextInput search with TreeList hierarchy.
    • Product category selector showing the same tree-search pattern in a form-like context.
  • Adds docs guidance to use Astryx focus hooks for custom content and evaluate against WCAG 2.2.
  • Adds a focused complex-selector vibe-test set covering grid, tree/search, date-time, and freeform-option selectors.

Risk

This is additive. The main design question is whether the open render-prop shape is enough guidance for agents to choose the right accessible structure inside the dialog.

WCAG 2.2 evaluation notes

  • 2.1.1 Keyboard / 2.1.2 No Keyboard Trap: trigger is a button; popover content uses focusable controls. Fruit grid uses useGridFocus; tree examples rely on TreeList keyboard behavior.
  • 2.4.3 Focus Order: focus enters the dialog content and returns to the trigger on close. Grid arrow movement preserves row/column expectations.
  • 2.4.7 Focus Visible / 2.4.11 Focus Not Obscured: custom fruit pills provide explicit :focus-visible outline; TreeList/TextInput/Button use component focus treatment inside the popover.
  • 2.5.8 Target Size: fruit selector pills are compact and adjacent, but each has spacing and visible focus. If this pattern becomes production-facing, confirm target sizing against the final density requirements.
  • 3.3.2 Labels or Instructions: ComplexSelector fields and search inputs have labels/descriptions; hidden search labels remain accessible.
  • 4.1.2 Name, Role, Value: fruit grid uses role="grid", row, rowheader, gridcell, and aria-selected; tree examples delegate hierarchy semantics to TreeList.
  • 1.4.3 Contrast / 1.4.11 Non-text Contrast: examples use semantic theme tokens for text, borders, focus outlines, and selected states rather than hardcoded colors.

Vibe test

Focused iteration: 3e411072 using internal/vibe-tests/test-sets/complex-selector.json.

  • Aggregate score: 94 overall
    • Correctness 93
    • Accessibility 100
    • Code quality 96
    • Efficiency 90
    • Maintainability 93
  • Typecheck: 3/4 generated outputs clean
    • cs-1 fruit/ripeness grid: clean
    • cs-2 destination tree/search picker: clean
    • cs-4 snack + Other text input: clean
    • cs-3 deadline picker: failed on branded ISODateString / ISOTimeString types, which looks like DateInput/TimeInput discoverability friction rather than a ComplexSelector issue.

Testing

  • pnpm --filter @astryxdesign/core typecheck
  • pnpm vitest run packages/core/src/ComplexSelector/ComplexSelector.test.tsx
  • pnpm exec eslint packages/core/src/ComplexSelector/ComplexSelector.tsx packages/core/src/ComplexSelector/ComplexSelector.test.tsx apps/storybook/stories/ComplexSelector.stories.tsx
  • pnpm sync:exports:check
  • pnpm --filter @astryxdesign/storybook typecheck (still blocked by existing workspace package-resolution errors; no ComplexSelector story errors after fixes)
  • pnpm -F @astryxdesign/vibe-tests interactive --test-set complex-selector --target astryx --label complex-selector-pr4659
  • npx tsx src/build-previews.ts --iterations 3e411072 --tsc-only from internal/vibe-tests
  • pnpm -F @astryxdesign/vibe-tests aggregate --iteration 3e411072

@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
astryx Ready Ready Preview Aug 3, 2026 2:46am

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Aug 2, 2026
@github-actions github-actions Bot added the needs:design-review Affects visuals — Design should review label Aug 2, 2026
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

🧪 Sandbox Preview

View Sandbox for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

New Components

ComplexSelector (@astryxdesign/core) · View in Storybook
Metric Value
Bundle Size (ESM) N/A
Bundle Size (CJS) 232B
Lines of Code 353
Source Files 2
Complexity Very High (31)
Exports ComplexSelector, type, type, type, type
Props Count N/A
Has Tests Yes
Has Stories Yes

Bundle Size Summary

Package Size (ESM) Size (CJS) Gzipped
@astryxdesign/core N/A 4.9KB 1.3KB

Accessibility Audit

Status: No accessibility violations detected.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

github-actions Bot added a commit that referenced this pull request Aug 2, 2026
Comment thread packages/core/src/ComplexSelector/ComplexSelector.tsx Outdated
Comment thread packages/core/src/ComplexSelector/ComplexSelector.tsx Outdated
Comment thread packages/core/src/ComplexSelector/ComplexSelector.tsx Outdated
Comment thread packages/core/src/ComplexSelector/ComplexSelector.tsx Outdated
Comment thread packages/core/src/ComplexSelector/ComplexSelector.tsx Outdated

@cixzhang cixzhang left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Some applications to consider that we can build vibe tests around.

  • date/time selectors, check on the current ones we have, should any of them have been complex selectors? If so, how could they have been accomplished through this framework?
  • tree list selectors with search, multiple pages, etc. This was common internally for complex structures that needed navigation to perform selection
  • fun grid-like selectors that reduce time to input multiple dimensions (ex. model + thinking level selection).
  • Additional options that require inputs. Ex. choose "apple, banana, etc" but also have an input that lets you add a freeform option.

We can design a vibe test:

  1. We should evaluate the right accessible design for each of these selectors.
  2. Determine some naive prompts that someone might request a UI like this
  3. See if the API can be discovered through Astryx and where the LLM runs into friction. Evaluate the results to see if it accomplished the user request and has the right accessible design we determined in step 1.

@cixzhang

cixzhang commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Noticed that when tapping the trigger while it's open it stays open since it ends up closing the popovers due to auto and then retriggering the trigger.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot. needs:design-review Affects visuals — Design should review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant