Skip to content

finding(types): CollapsibleSchema.open is declared on both faces and read by no renderer — the collapsible renderer passes only defaultOpen #8236

Description

@os-justin

Found while fixing objectui#8197 (the collapsible.mdx content-slot row), which asked whoever picked it up to check the rest of that fence against the declaration. Measured on origin/main f57ca757b.

The reading

packages/types/src/disclosure.ts:119-122 declares:

  /**
   * Controlled open state
   */
  open?: boolean;

and the zod twin agrees — packages/types/src/zod/disclosure.zod.ts:55 is open: z.boolean().optional().describe('Controlled open state'), so an authored open validates.

packages/components/src/renderers/disclosure/collapsible.tsx:23 is:

<Collapsible defaultOpen={schema.defaultOpen} disabled={hostDisabled} className={className} {...props}>

schema.open appears nowhere. grep -rn 'schema\.open\b' packages/ returns hits only in plugin-form (ObjectForm.tsx, ModalForm.tsx, DrawerForm.tsx) and a designer test mock — none of them the collapsible renderer. The {...props} spread carries the renderer's props, not the schema, so it does not smuggle it in either.

open is authorable on both declaration faces, accepted by the validator, and inert. An author who reaches for controlled open state gets silence: no error, no effect.

Why this was filed and not folded into objectui#8197

That card's slot is the content row. This is a declaration-side question with more than one defensible answer, and picking one is not a docs edit:

  • Wire it — read schema.open in the renderer and pass it to Radix Collapsible's open. But controlled open without a matching onOpenChange is a Radix anti-pattern, and onOpenChange is a runtime slot the zod twin refuses by name (objectui#6124), so a JSON author has no way to supply the other half. This route probably ends at "controlled open is not authorable in SDUI".
  • Retire it — ADR-0049 enforce-or-remove: drop open from the interface and the twin, leaving defaultOpen as the only authorable open state.

The second looks right, but it is a contract decision, not a mechanical fix.

Deliberately not documented on content/docs/components/disclosure/collapsible.mdx: adding a row for an inert key is the same trap objectui#8197 removed. The page stays a list of keys the renderer reads until this card resolves.

Class

Same shape as objectui#6158 (RadioGroupSchema.orientation declared on both faces, read by nothing) and objectui#5667 (CalendarViewSchema declares 9 of 13 keys the renderer never reads). objectui#4631 is the standing card about nothing reconciling the three declared surfaces.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepm:queuepriority:p3

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions