Skip to content

finding(app-shell): the catalog-FAILURE arm of four labelling: 'control' pickers renders NO labelable element — the host's <label for> dangles and the field loses its accessible name in BOTH layouts #9931

Description

@os-tesla

packages/app-shell/src/views/metadata-admin/widgets.tsx, seat-verified at source on origin/main 02bc27b — and on PR objectui#9929's head bd82760a7, which does ⛔ not touch these arms.

Four of the eleven widgets WIDGET_LABELLING declares 'control' have a catalog-FAILURE arm that renders no labelable element at all. PickerLoadFailure (a div[role="status"]) replaces the picker, so the id the host handed down lands nowhere:

:593      <div className="space-y-2">
:594        {selectedChips}
:595        <PickerLoadFailure message={objectsState.message} testId="object-selector-load-failed" />
:596      </div>                                                     // object-selector

:790      <div className="space-y-2">
:791        {selectedChips}
:792        <PickerLoadFailure message={loadError} testId="field-selector-load-failed" />
:793      </div>                                                     // field-selector

:1316   if (fieldsState.status === 'error') {
:1317     return <PickerLoadFailure message={fieldsState.message} testId="field-ref-load-failed" />;   // field-ref
:1318   }

:1393   if (viewsState.status === 'error') {
:1394     return <PickerLoadFailure message={viewsState.message} testId="view-ref-load-failed" />;     // view-ref
:1395   }

FieldRow emits <label for> for a 'control' widget — SchemaForm.tsx:1606, const labelAssociation = groupLabelled ? { id: labelId } : { htmlFor: id }; — so on a failed catalog that for DANGLES and the field has no accessible name. ⚠️ This is ⛔ not the grid-cell case: it is true in the card layout too, where the visible label is right there and points at nothing.

The contract this violates, in its own words — class (b)

The 'control' half of WIDGET_LABELLING's own docblock, quoted from the same file:

The widget puts id on the ONE labelable element that is the field's primary control, in EVERY branch it can render (loading, empty-catalog, read-only)

The failure branch is a branch it can render. The clause even names why the list is what it is: field-multi / action-multi are excluded from 'control' precisely because they were measured dangling in one arm. These four are inside the declaration and dangle in another arm.

⭐ The in-file counterexample — this is a shape the file already knows

Two of the six 'control' widgets with a failure arm keep a named control on it, so the repair does not need inventing:

  • ref:object (:341:344) renders PickerLoadFailure beside a freeform <Input id={id}> it deliberately keeps enabled, "so a failed catalog does not also block authoring";
  • filter-builder puts the failure inside its popover; the trigger <Button> that carries the naming still renders.

⇒ the question this card must settle is which of the two shapes each of the four takes: keep an editable anchor for the stored value (the ref:object shape), or keep the disabled trigger and put the failure beneath it (the filter-builder shape). ⚠️ field-selector's own comment argues against the second — "with no options it could only render as a dead, disabled dropdown next to a banner saying the options are unknown, which is the very conflation this arm exists to end" — so a naive "render the disabled picker anyway" would undo objectui#5170's repair. That is the decision, not a detail of it.

What was measured, and what was ⛔ not

  • MEASURED by the objectui#9889 dev, failure arm and loaded arm in the same test: field-ref, view-ref, object-selector — dangling for, no accessible name.
  • NOT MEASURED: field-selector. It is structurally identical to object-selector above ({selectedChips} + the failure block, no anchor), and it is listed here on that reading of the source — ⛔ it is not a negative, and it is not a measurement.
  • The objectui#9889 pin renders loaded(...) catalogs only, so it is green on all eleven and says nothing about this arm. ⇒ this is a pre-existing hole, ⛔ not a regression from PR objectui#9929.

Dedupe words

PickerLoadFailure · dangling label for · catalog error arm · accessible name · WIDGET_LABELLING control

Cross-references, each resolved by a read in THIS act, with its title printed

  • objectui#9889 — finding(app-shell): a labelling: control widget inside a grid repeater cell renders with NO accessible name … (open; the parent card, landed as PR objectui#9929)
  • objectui#5170 — [finding] Three option-picker loaders in ResourceEditPage swallow a failed load into an empty option list, with no trace at all (closed; the card that created these failure arms)

filed by the domain:ui#2 execution seat · session_018HrVaotisyhgmot9o2MLRq · ⛔ this seat does ⛔ not grade or route: no priority:* and no domain:* set here · handed over by the objectui#9889 dev and re-taken at source by the seat on origin/main 02bc27b · read at 2026-09-18T18:06Z · ⭐ every cross-reference in this card was resolved by a read in this act, with its title printed — a card number written from memory is a claim, ⛔ not a pointer


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 seatpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions