Skip to content

docs(docsite): add ComplexSelector showcase and examples - #4814

Open
cixzhang wants to merge 2 commits into
mainfrom
complexselector-docs
Open

docs(docsite): add ComplexSelector showcase and examples#4814
cixzhang wants to merge 2 commits into
mainfrom
complexselector-docs

Conversation

@cixzhang

@cixzhang cixzhang commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

What

ComplexSelector shipped without a docsite showcase or examples, so its
component page had an empty preview and no Examples section. This adds one
showcase block and three example blocks.

The docsite builds both surfaces from CLI template blocks — generate-data.mjs
copies blocks with isShowcase: true into showcaseRegistry and the rest into
exampleRegistry, keyed on exampleFor — so all four live under
packages/cli/assets/templates/blocks/components/ComplexSelector/ rather than
being wired into page code.

Block What it demonstrates
ComplexSelectorShowcase The shape Selector cannot express: a reporting-period picker with quick presets beside a range Calendar, edited as a draft and committed on Apply.
ComplexSelectorFolderTree TreeList as popup content, so the hierarchy keeps its own tree keyboard navigation instead of the shell reimplementing it.
ComplexSelectorStageFilter Multi-step content: a CheckboxList that stays open across several toggles and closes only when the apply button calls close().
ComplexSelectorAsyncAssign changeAction driving the optimistic value and the trigger's busy spinner, settling into a validation status.

An earlier revision used a visibility RadioList as the showcase. That is a
plain Selector in a popover and undersold the component, so it was replaced
with the date-range picker above.

The arrow-key flap

The reviewer example originally called close() from the radio group's
onChange. RadioList follows the APG radio pattern, where arrow keys move
and select
, so the first arrow committed a value and closed the popup;
usePopover.onHide then restored focus to the trigger, whose own ArrowDown
reopened it. The popup appeared to flap open/closed on every arrow press.

Worth recording what it was not: keystrokes are not leaking from the popup to
the trigger. The popover is not a descendant of the trigger (it renders in the
top layer), and a keydown dispatched on the radio with
{bubbles: true, composed: true} never reaches the trigger button under a
capture-phase listener. The fix is to not close on a change that arrow keys
emit, not to stop propagation.

Each one follows the component's own documented guidance: compose the popup
from the right accessible structure for the job, use the onChange helper the
render prop hands you, and call close() only when a selection should dismiss.

The one non-block change is a single line: ComplexSelector.tsx's SYNC:
block now references the new block directory. scripts/check-sync.js requires
that reference once a showcase directory exists, and fails the repo check
without it.

Test plan

Ran against a local docsite (pnpm generate && pnpm dev) at
/components/ComplexSelector, driving real Chromium.

Repo checks

  • pnpm check:repo — clean (this is what caught the missing SYNC: reference).
  • pnpm -F @astryxdesign/cli typecheck:template-docs — clean.
  • pnpm -F @astryxdesign/cli typecheck:strict — clean.
  • eslint on the new directory with --max-warnings=0 — clean.
  • prettier --check — clean.
  • apps/docsite: pnpm generate picks up all four (170 showcases, 483
    examples), and pnpm test passes 324/324.

Rendered behavior (each of the four selectors opened in turn, light and dark)

  • Popup content renders fully — no clipping, no scroll overflow, nothing off
    the viewport.
  • Esc closes the popup and returns focus to the trigger in all four,
    in both themes.
  • No console errors.
  • 375 × 812: no horizontal page overflow, and every field and popup stays
    inside the viewport. The showcase's two panes stack rather than squeeze
    (calendar top lands below the preset list), and the popup scrolls within the
    shell's min(480px, 100vh - 32px) cap — Apply is reachable and works.

Accessibility

Run against the Accessibility Checklist.
These are example blocks, not a new component, so the shell's own conformance
is ComplexSelector's; what is checked here is that the popup content each
example composes stays conformant.

  • Name — every trigger is named by its Field label; the popup content
    reuses the same label via isLabelHidden on the inner RadioList /
    CheckboxList rather than leaving the group unnamed.
  • Role — content is RadioList (radiogroup), TreeList (tree), and
    CheckboxList; no bespoke ARIA patterns are hand-rolled in the examples.
  • State — selection reaches AT: the selected preset carries
    aria-current="true" (verified live, and exactly one row at a time), and the
    async example exposes its outcome through status (icon + message), not
    color alone.
  • Keyboard — full keyboard operation via the composed components; verified
    Esc-to-close plus focus restore on all four, and that arrow keys
    no longer toggle any popup. Tab order in the showcase runs presets → month
    nav → calendar grid → Cancel → Apply.
  • Focus — restore-to-trigger verified in light and dark; the shell's
    usePopover owns the trap.
  • Announcements — no hand-wired aria-live; the busy state rides
    aria-busy on the trigger.
  • Target size — the effective targets are the full 36px rows. List and
    TreeList both render a visually smaller inner button (~20px), so this was
    checked functionally rather than by measurement: clicking a row 2px from its
    top edge, outside that inner button, does activate the row (the range
    changes, the tree selection moves). The same ~20px inner buttons appear on
    the stock List and TreeList component pages on main, so this is the
    shipped components' own markup, not something these blocks introduce.
  • axeaxe-core run scoped to each open popup, wcag2a/wcag2aa/
    wcag21a/wcag21aa/wcag22aa: 0 violations across all four, in both
    themes, including the rebuilt showcase.
  • Contrast — every distinct text/background pair in the popups measured;
    minimum observed 6.00:1 (dark) and 6.92:1 (light) against a 4.5:1
    bar. Re-measured for the new showcase: min 6.83:1 dark, 7.81:1 light.

Template rubric

Self-graded against the
Template Grading Rubric:
all four are 100/100 — zero raw HTML elements, zero raw SVG, zero custom CSS
declarations (no stylex.create, no style={{}}, no className), no
AppShell, single-pattern focus, complete .doc.mjs metadata with accurate
componentsUsed, no images, and 'use client' + default export +
self-contained imports + realistic mock data throughout. The showcase is 119
lines, over the rubric's 20–100 guidance for blocks; the extra lines are the
preset table and the draft-state content component, and splitting them would
hide the very composition the block exists to show.

Notes

  • Presets were originally This quarter / Year to date, which on the mocked
    date resolve to the same range and so both rendered as current. Replaced
    Year to date with Previous quarter, and the range label now derives its
    year(s) from the value instead of hardcoding 2026.

  • No changeset: docs/example blocks only, matching the precedent set by docs(docsite): add useContainerReveal example #4578.

  • Pre-existing and untouched by this PR: apps/docsite tsc --noEmit reports
    one error in the generated componentRegistry.ts for a TextArea derived
    var ("replaces" does not exist in type 'DerivedVar'). It reproduces without
    these blocks and is not part of the CI typecheck set.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Aug 7, 2026
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
astryx Ignored Ignored Aug 10, 2026 4:07pm

Request Review

@github-actions github-actions Bot added the needs:design-review Affects visuals — Design should review label Aug 7, 2026
@cixzhang
cixzhang force-pushed the complexselector-docs branch from 69af15d to 839d251 Compare August 7, 2026 23:23
@github-actions

github-actions Bot commented Aug 7, 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.

Modified Components

ComplexSelector (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Bundle Size (ESM) N/A N/A N/A
Lines of Code N/A 348 -
Complexity N/A Very High (31) -

Bundle Size Summary

Package Size (ESM) Size (CJS) Gzipped
@astryxdesign/core N/A 4.7KB 1.2KB

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 7, 2026
Adds the missing docsite showcase and example blocks for ComplexSelector.
The docsite builds both from CLI template blocks (blocks with isShowcase
feed showcaseRegistry, the rest feed exampleRegistry keyed on exampleFor),
so all four live under packages/cli/assets/templates/blocks/components/.

- Showcase: a project-visibility picker whose popup content is a RadioList.
- Folder Tree: a TreeList destination picker, so the hierarchy keeps its own
  tree keyboard navigation.
- Multi-select Filter: CheckboxList content that stays open across several
  toggles and closes only when the apply button calls close().
- Async Assignment: changeAction driving the optimistic value, busy spinner,
  and a settled validation status.

Also references the new block directory from the component's SYNC block,
which scripts/check-sync.js requires once a showcase dir exists.
…ow-key flap

The visibility picker was a RadioList in a popover, which a plain Selector
already does. Replaces it with a reporting-period picker: quick presets beside
a range Calendar, edited as a draft and committed on Apply. Two panes,
multi-step, explicit commit is the shape Selector cannot express.

Also drops close() from the reviewer example's radio onChange. RadioList
follows APG and commits on arrow navigation, so closing on change dismissed
the popup mid-traversal; focus then restored to the trigger, whose ArrowDown
reopened it, and the popup appeared to flap open/closed on every arrow key.
@cixzhang
cixzhang force-pushed the complexselector-docs branch from 839d251 to b82d884 Compare August 10, 2026 16:07
github-actions Bot added a commit that referenced this pull request Aug 10, 2026
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