Skip to content

Combobox never reads options[].disabled: @object-ui/types declares it and form.zod.ts validates it, the component renders every option enabled #7687

Description

@claude

Found by the os-dev seat while implementing batch 3 of objectui#6349 (the ComboboxOption convergence). Out of that card's scope: it is a behaviour defect, and #6349 only moves which file declares the name. Grading and domain:* are the triage seat's.

The fact

options[].disabled on a combobox node is declared, validated, and never read.

  • Declared: packages/types/src/form.tsComboboxOption.disabled?: boolean ("Whether option is disabled").
  • Validated: packages/types/src/zod/form.zod.tsComboboxOptionSchema carries disabled: z.boolean().optional(), and packages/types/src/__tests__/disabled-twin-symmetry-7087.test.ts pins it as boolean on both faces.
  • Passed through: packages/components/src/renderers/form/combobox.tsx hands schema.options straight to the Combobox component.
  • Never read: packages/components/src/custom/combobox.tsx maps each option to a CommandItem with key, value, onSelect, the check icon and the label. option.disabled has zero read sites in the component (measured: grep -c 'option.disabled' is 0; the only disabled the component reads is its own whole-control prop, forwarded to the trigger button).

The underlying primitive would honour it: CommandItem (packages/components/src/ui/command.tsx) forwards props to cmdk's Command.Item, which takes a disabled prop, and the wrapper's className already styles data-[disabled=true] with reduced opacity and no pointer events.

Consequence

An authored option { value, label, disabled: true } passes safeValidateSchema, type-checks against the published ComboboxSchema, and renders as an ordinary selectable option. The key is a confident phantom — declared on the authorable surface with no read site behind it — the class the enforce-or-remove ledgers exist to close.

After #6349 batch 3 the ComboboxOption that @object-ui/components exports IS the @object-ui/types declaration (a re-export), so the member is now visible on the component's own face too. That PR deliberately changes no behaviour; this card is where the behaviour question lives.

Two remedies, neither local to this card

  1. Honour itCommandItem disabled={option.disabled} and refuse the select in onSelect for a disabled option; pin with a render test that a disabled option carries data-disabled and does not fire onValueChange. Check the sibling SelectOption.disabled read path in the select renderer for the house precedent before choosing the spelling.
  2. Retire it — remove disabled from ComboboxOption in types and from the zod mirror. This narrows a published surface (clause 2 content) and would need the twin-symmetry pin updated.

Reproduce

grep -n 'disabled' packages/components/src/custom/combobox.tsx
grep -n 'disabled' packages/types/src/zod/form.zod.ts | grep -i combobox

The first shows only the whole-control disabled; the second shows the option-level key the mirror accepts.

Related

  • objectui#6349 — batch 3 converges the ComboboxOption NAME; this card is about the MEMBER.
  • objectui#7087 — the disabled twin-symmetry ruling that keeps this member boolean on both faces.

Filed by the os-dev seat for objectui#6349 batch 3, session session_01KbJQ1y1J12nZxYzFWhP8Q3 (Claude Code).


Generated by Claude Code

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

    bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpm:queuepriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions