docs(docsite): add ComplexSelector showcase and examples - #4814
Open
cixzhang wants to merge 2 commits into
Open
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
github-actions
Bot
requested review from
cvkxx,
ernestt,
kentonquatman and
rubyycheung
August 7, 2026 23:21
cixzhang
force-pushed
the
complexselector-docs
branch
from
August 7, 2026 23:23
69af15d to
839d251
Compare
Contributor
PR Analysis Report📚 Storybook PreviewView Storybook for this PR 🧪 Sandbox PreviewView Sandbox for this PR Modified ComponentsComplexSelector (@astryxdesign/core) · View in Storybook
Bundle Size Summary
Accessibility AuditStatus: No accessibility violations detected. Generated by PR Enrichment workflow | Storybook | Sandbox | View full report |
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
force-pushed
the
complexselector-docs
branch
from
August 10, 2026 16:07
839d251 to
b82d884
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
ComplexSelectorshipped without a docsite showcase or examples, so itscomponent 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.mjscopies blocks with
isShowcase: trueintoshowcaseRegistryand the rest intoexampleRegistry, keyed onexampleFor— so all four live underpackages/cli/assets/templates/blocks/components/ComplexSelector/rather thanbeing wired into page code.
ComplexSelectorShowcaseSelectorcannot express: a reporting-period picker with quick presets beside a rangeCalendar, edited as a draft and committed on Apply.ComplexSelectorFolderTreeTreeListas popup content, so the hierarchy keeps its own tree keyboard navigation instead of the shell reimplementing it.ComplexSelectorStageFilterCheckboxListthat stays open across several toggles and closes only when the apply button callsclose().ComplexSelectorAsyncAssignchangeActiondriving the optimistic value and the trigger's busy spinner, settling into a validation status.An earlier revision used a visibility
RadioListas the showcase. That is aplain
Selectorin a popover and undersold the component, so it was replacedwith the date-range picker above.
The arrow-key flap
The reviewer example originally called
close()from the radio group'sonChange.RadioListfollows the APG radio pattern, where arrow keys moveand select, so the first arrow committed a value and closed the popup;
usePopover.onHidethen restored focus to the trigger, whose ownArrowDownreopened 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
keydowndispatched on the radio with{bubbles: true, composed: true}never reaches the trigger button under acapture-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
onChangehelper therender prop hands you, and call
close()only when a selection should dismiss.The one non-block change is a single line:
ComplexSelector.tsx'sSYNC:block now references the new block directory.
scripts/check-sync.jsrequiresthat 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 missingSYNC:reference).pnpm -F @astryxdesign/cli typecheck:template-docs— clean.pnpm -F @astryxdesign/cli typecheck:strict— clean.eslinton the new directory with--max-warnings=0— clean.prettier --check— clean.apps/docsite:pnpm generatepicks up all four (170showcases,483examples), and
pnpm testpasses 324/324.Rendered behavior (each of the four selectors opened in turn, light and dark)
the viewport.
in both themes.
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 —Applyis 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 eachexample composes stays conformant.
Fieldlabel; the popup contentreuses the same label via
isLabelHiddenon the innerRadioList/CheckboxListrather than leaving the group unnamed.RadioList(radiogroup),TreeList(tree), andCheckboxList; no bespoke ARIA patterns are hand-rolled in the examples.aria-current="true"(verified live, and exactly one row at a time), and theasync example exposes its outcome through
status(icon + message), notcolor alone.
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.
usePopoverowns the trap.aria-live; the busy state ridesaria-busyon the trigger.ListandTreeListboth render a visually smaller inner button (~20px), so this waschecked 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
ListandTreeListcomponent pages onmain, so this is theshipped components' own markup, not something these blocks introduce.
axe-corerun scoped to each open popup,wcag2a/wcag2aa/wcag21a/wcag21aa/wcag22aa: 0 violations across all four, in boththemes, including the rebuilt showcase.
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, nostyle={{}}, noclassName), noAppShell, single-pattern focus, complete.doc.mjsmetadata with accuratecomponentsUsed, 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 mockeddate resolve to the same range and so both rendered as current. Replaced
Year to datewithPrevious quarter, and the range label now derives itsyear(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/docsitetsc --noEmitreportsone error in the generated
componentRegistry.tsfor aTextAreaderivedvar (
"replaces" does not exist in type 'DerivedVar'). It reproduces withoutthese blocks and is not part of the CI typecheck set.