Skip to content

[fields] a required file column in the grid widget flags its cell visually but never announces it — FileCell's closed prop set has no aria-invalid path #5431

Description

@os-support-ai

Filed unassigned while implementing #3318 (PR #5429). ⛔ Not claimed. Out of that card's scope, and deliberately not fixed there because the correct shape is a contract decision rather than a mechanical edit.

The gap

#3318 moved the grid widget's per-cell aria-invalid off the non-focusable td and onto each cell's own control, so a required-but-empty cell now announces itself to assistive tech. That was wired for the cell controls GridField renders directly:

  • text / number / currency / date / datetime / time — the Input
  • select — the SelectTrigger
  • lookup — LookupField, via its published error slot (it already sets aria-invalid on its own focusable trigger)

file columns were left out. A required file cell still gets the red ring and the title, but no element in the cell carries aria-invalid. Same defect class as the card that was just closed, one cell type short of complete.

Why it was not fixed in PR #5429

FileCell (packages/fields/src/widgets/FileField.tsx) takes a closed prop setvalue, onChange, disabled, multiple, accept, maxSize, aria-label, data-cell — with no validity slot. Delivering the state means changing that component's contract, and there is a real choice in how:

  1. Give FileCell an error slot mirroring LookupField, so the grid passes the published slot and the component decides its own carrier. Consistent with how the lookup cell was wired, and with the error slot on FieldWidgetPropsSchema.
  2. Give it a narrower boolean invalid prop — smaller, but a second spelling for a state the codebase already names error.

(1) looks right on the contract-first reading (AGENTS.md #0.1 — one spelling, fixed at the producer), but FileCell is a cell-level helper rather than a full field widget, so it is worth a moment's thought rather than an assumption. That is why this is a card and not a drive-by edit.

Reproducing

Configure a grid column with type: 'file' and required: true, leave the cell empty on a real row, and inspect the cell: the td gets bg-destructive/5 ring-destructive/50 and data-testid="line-items-invalid-{row}-{name}", and nothing in the subtree has aria-invalid="true".

Note on the registry sweep

The widget-aria-invalid-registry-e2e.test.tsx sweep does not catch this and is not expected to: it configures the grid with text/number columns, so the row it renders delivers correctly and grid sits legitimately in the positive guard. This is a per-column-type gap inside a widget that passes, which is a level of detail that sweep does not reach.

Related


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

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

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions