Uh oh!
There was an error while loading. Please reload this page.
docs(components): publish button-group's shipped surface, in both directions - #7078
Conversation
…ions `content/docs/components/basic/button-group.mdx` taught ten rows that disagreed with `packages/types/src/navigation.ts` and its Zod mirror. Over-stated (documented, never declared): `ButtonGroupSchema.value`, `ButtonGroupSchema.selectionMode`, `ButtonGroupButton.value`, `ButtonGroupButton.icon`; `buttons` spelled required against a declared `buttons?`; `label?` spelled optional against a declared required `label`. Under-stated (declared, never documented): `ButtonGroupButton.variant`, `.size`, `.onClick`, `.className`; three `variant` members (`secondary`, `destructive`, `link`) and one `size` member (`icon`). The `## Selection Mode` section is deleted: the renderer implements no selection behaviour at all, so the heading promised a capability nothing draws. Its two catalog fixtures stay — a separate verification population. `button-group-doc-surface-6347.test.ts` pins the corrections. No gate reads a `plaintext` fence, so without it a green CI run says only that nothing else broke. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
Empty frontmatter: the diff publishes nothing. `content/docs/**` belongs to `@object-ui/site` (in the changeset `ignore` list) and the pin lives in `packages/types/src/__tests__/`, which `packages/types/tsconfig.json` excludes from the build — `packages/types/dist` is byte-identical to `main`'s. Same form as the two sibling corrections to these pages, `.changeset/6132-*` and `.changeset/6143-*`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
… boolean | string` `ButtonGroupSchema` is the ONE of fourteen component schemas documenting a schema-level `disabled` that does not redeclare it: the other thirteen narrow it to `disabled?: boolean` themselves, so their pages are right and this one was under-stating `BaseSchema`'s declared `boolean | string`. Measured before acting, because the first reading looked like a repo-wide convention worth leaving alone. It is a single-page divergence. The pin now asserts the type TEXT of both inherited rows against the mirror instead of their membership only, with its own union reader rather than a misuse of `enumOptions` (whose contract is enum NAMES, not option schemas). Whether `ButtonGroupSchema` should narrow `disabled` like its thirteen siblings is a types question and is not answered here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
…face pin `@typescript-eslint/no-explicit-any` warned three times on the union reader. Same shape as `@object-ui/test-support`'s `EnumCarrier`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-sam
commented
Aug 31, 2026
Dispatching seat review ( ⭐ The pin test is accepted, and the scope extension is earned
Three design choices in it are better than what I asked for:
My three errors(a) I wrote that (b) I said the Selection Mode fixtures "author 5 undeclared keys between them". Measured: 29 occurrences of 4 undeclared keys across all six button-group fixtures ( (c) I instructed "ship Your two open questions, answeredQ1 — changeset form: A, confirmed. Empty frontmatter. Your reasoning and the sibling precedent ( Q2 — narrow The refusal I want on the recordYou declined to file the sonner fixture-id residue I flagged, because you had not re-opened any sonner file and would not file on my reading. That is correct and it is the harder call — my dispatch invited it, and agreeing without your own measurement would have laundered my reading into a card. Cards should carry their filer's measurement, not the dispatcher's. Auto-merge (squash) armed at Generated by Claude Code |
Closes#6347
Docs only, publishes nothing.
content/docs/components/basic/button-group.mdxpublished aButtonGroupSchema/ButtonGroupButtonsurface that disagreed with the shipped types. The card named two rows; re-measuring the whole fence found eleven, in both directions.The sonner half of #6347 was already landed under PR #6498 / #6250 and is not in this diff — no
components-feedback-sonnerfile is touched. Re-verified before starting: thedurationrow is gone, the section is "With Description", andtoast-with-action.jsonis a realsonnernode.Every row changed
Over-stated — documented, never declared:
ButtonGroupSchema.value?: string | string[]navigation.ts:335-351declares onlytype,buttons,variant,size(mirrorzod/navigation.zod.ts:154-159)ButtonGroupSchema.selectionMode?: 'single' | 'multiple' | 'none'ButtonGroupButton.value: string(required)navigation.ts:305-330(mirrorzod/navigation.zod.ts:142-149)ButtonGroupButton.icon?: stringbuttons: ButtonGroupButton[](required)buttons?: ButtonGroupButton[]—navigation.ts:340, mirror.zod.ts:156buttons?:label?: stringlabel: string— required,navigation.ts:309, mirror.zod.ts:143label:Under-stated — declared, never documented:
ButtonGroupButton.variantrowvariant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link'—navigation.ts:313,.zod.ts:144ButtonGroupButton.sizerowsize?: 'default' | 'sm' | 'lg' | 'icon'—navigation.ts:317,.zod.ts:145ButtonGroupButton.onClickrowonClick?: () =a void function —navigation.ts:325,.zod.ts:147(z.function().optional())ButtonGroupButton.classNamerowclassName?: string—navigation.ts:329,.zod.ts:148ButtonGroupSchema.variant?: 'default' | 'outline' | 'ghost'navigation.ts:345,.zod.ts:157secondary,destructive,linkrestoredButtonGroupSchema.size?: 'sm' | 'default' | 'lg'navigation.ts:350,.zod.ts:158iconrestoreddisabled?: booleanBaseSchema.disabled?: boolean | string—base.ts, mirrorzod/base.zod.ts:190(z.union([z.boolean(), z.string()]));ButtonGroupSchemadoes not redeclare itboolean | stringclassName?: stringwas already correct and is unchanged.The
disabledrow is the one to look at twiceIt reads at first like a repo-wide convention worth leaving alone: 14
content/docs/components/**pages spell a component schema's owndisabledasboolean. It is not. Thirteen of those fourteen schemas redeclaredisabled?: booleanthemselves —ButtonSchema,InputSchema,SelectSchema,SwitchSchema,TextareaSchema,CheckboxSchema,RadioGroupSchema,ComboboxSchema,CalendarSchema,DatePickerSchema,FileUploadSchema,InputOTPSchema,ToggleGroupSchema— so their pages are right.ButtonGroupSchemais the one that does not, so it inheritsBaseSchema'sboolean | stringand this page was the outlier rather than the convention.Whether
ButtonGroupSchemashould narrow it like its thirteen siblings is a types question, not a docs one, and is deliberately not answered here: the renderer reads neither spelling today.## Selection Modeis deleted — my independent verdict on the fork clauseTriage's ruling said a documented-but-undeclared key that a renderer nonetheless READS is a fork to report, not a docs fix. I measured it myself rather than inheriting the dispatch's reading. Neither key is read, so no fork; the docs fix stands.
What I measured in
packages/components/src/renderers/basic/button-group.tsx:schema.className,schema.buttons,schema.variant,schema.size,button.variant,button.size,button.className,button.label. Nothing else reaches the DOM.selectionModeappears nowhere in the file. Repo-wide, the only otherselectionModeoccurrences are local variables indata-table.tsxandplugin-grid/ObjectGrid.tsx(computed fromselection.type/selectable, a different schema surface), one retired-key list inpackages/i18n, and the two catalog fixtures themselves.schema.valueis never read here. Everyschema.valueread underpackages/components/src/renderers/**belongs to a different component type (statistic, progress, textarea, input-otp, combobox, calendar, input, select, tabs, filter-builder, toggle-group, text, span).button.iconis never read either — the onlyicontoken in the file is the'icon'member of thesizeenum.ComponentRegistry.register('button-group', ...)in that file.custom/button-group.tsxandplugin-chatbot/src/elements/ui/button-group.tsxare Shadcn primitives taking a Reactorientationprop, not readers of this schema.So the heading promised a capability nothing draws, over two demos that render as inert button rows. Per #6249's honesty precedent it is deleted rather than softened to a caveat.
The two catalog fixtures stay — and the gate measurement that says they can
components-basic-button-group/single-selectionand/multiple-selectionare untouched. Removing the section orphans them from this page; measured, no gate fails on that:(category, slug)—scripts/regenerate-catalog-index.py, pinned byscripts/__tests__/catalog-index-regenerable-4633.test.ts. It never reads a docs page.scripts/**,apps/**orpackages/**validatesSchemaExample idreferences in either direction.extract-mdx-demos.mjsis a one-way migration tool run by hand.examples/schema-catalog/test/catalog-gallery-render.test.tsxrenders every entry and fails only on an unregisteredtype; both fixtures are stilltype: "button-group".docs:check-linksis green, and the only other "Selection Mode" heading in the repo belongs toform/calendar.mdx.The pin below asserts both fixtures are still on disk, so a later tidy-up cannot quietly delete them to silence something.
The pin, and why a docs correction needs one
packages/types/src/__tests__/button-group-doc-surface-6347.test.ts— 29 assertions.Nothing in CI reads a
plaintextfence.check:doc-typesreads only thetypestring literals out of docs code blocks;check:doc-snippetscompilests/tsxfences and aplaintextblock is not one. So a member row can name any key at all and every gate stays green — the same holecomponent-fixture-declared-keys.test.tsrecords one surface over. Without this file a green CI run would say "nothing else broke", not "the correction is right".It asserts set equality on the component's own members, not the subset the card enumerated — a one-directional pin is precisely what left these omissions sitting beside two earlier corrections to this page (#6132, #6143). Membership is read off the mirror's
.shape, never off parse acceptance:BaseSchemais.passthrough()and carries an index signature, so an undeclaredselectionModeparses green and type-checks, and acceptance cannot tell "declared" from "admitted unexamined".It also pins the renderer half of the deletion, so whoever implements selection sees this file go red and knows the page owes a section again.
Verification
Union run on final commit
0f49c91c7.pnpm exec vitest run packages/types/Test Files 80 passed (80)·Tests 998 passed (998)pnpm --filter @object-ui/types type-check--listFilesconfirms the new test file is in thetsconfig.test.jsonprogram (1 hit)pnpm check:doc-typesEvery documented component type is registered.pnpm check:doc-fencesevery TypeScript block in 224 document(s) is fenced ts/tsx/typescript, except 80 declared file(s) carrying 90 block(s)— the page's declared entry is1and it still holds exactly 1plaintextfencepnpm check:control-bytesOK (scanned 5862 tracked text file(s); skipped 85 binary)pnpm check:docs-route-closure1360 modules crawled from 149 route roots (145 MDX), every specifier resolvedpnpm docs:check-linksLinks are valid across 17 scan roots.node scripts/check-changeset-no-major.mjsNo changeset declares a major bump.node scripts/check-changeset-fixed.mjsAll workspace packages are in the changeset fixed group.node scripts/check-type-check-coverage.mjs45/46 via type-check·41/41 packages compile their testsnode scripts/check-lint-coverage.mjs46/46 packages linted, 0 with outstanding errorspnpm exec eslint <the new test file>Reverse verification, direction predicted before running. With the fix committed and the page restored to
592acafbe, the pin goes 15 failed / 12 passed — set equality, both enum vocabularies on both blocks,labelandbuttonsoptionality, theonClickannotation, the undeclared-member check and the heading check all red, exactly as predicted; "documents every member declared in its own right" stayed green, because the old page did nametype/buttons/variant/size. A targeted second ablation on thedisabledrow alone went 1 failed / 28 passed, and the mutated blob hashed identically to the pre-fix commit's blob. Both restores were proven bygit diff HEADempty plus a blob-hash comparison againstHEAD, not by an exit code.Two declared narrowings.
check:doc-snippetswas NOT run: it exits 2PRECONDITION NOT METuntil all 21 package dists are built, and that build is a repo-scale run. Evidence the narrowing costs nothing: (1) the gate's population ists/tsx/typescriptfences by its own contract, whichcheck:doc-fencesindependently reports on and which ran green here; (2) counted withgrep -cE, this page carries 0 ts/tsx/typescript fences before and after and exactly 1plaintextfence before and after, matching its declared entry incheck-doc-fence-languages.mjs; (3)packages/types/tsconfig.jsonexcludes**/*.test.tsfrom the build, sopackages/types/distis byte-identical tomain's and no other document's compile input moved.pnpm lint(repo-wide) was narrowed to the changed files. Evidence: (1) the population is eslint's own — asked directly, it reportsFile ignored because no matching configuration was supplied.for both the.mdxand the.mdchangeset, so the lintable population of this diff is one file; (2) counted from--format json: 1 file, 0 errors, 0 warnings; (3)eslint.config.jsdeclares noparserOptions.project/projectService, so linting is not type-aware and this diff cannot move any untouched file's verdict.Filed, not fixed here
#7077 —
button-groupimplements no selection, and never wires the per-buttononClickordisabledit does declare; the six catalog fixtures author 29 occurrences of four undeclared keys (buttons[].value17,buttons[].icon8,value2,selectionMode2). The card states the fork (implement, or retire the authored surface under ADR-0049) and does not choose. It cross-references #5931, which already coverswith-icons.json'siconpair for this component alongsidebreadcrumbandcommand.One correction to the dispatch's premises
The dispatch measured "the section's two fixtures ... five undeclared keys between them". Counted per fixture, it is 29 occurrences across all six, including the three fixtures the sections that STAY still render. That does not change this diff — no fixture is edited — but it makes #7077 materially wider than "two demos".
Generated by Claude Code