Uh oh!
There was an error while loading. Please reload this page.
fix(fields): required file grid cells announce their state — FileCell gains the published error slot - #5561
Merged
Conversation
… gains the published error slot A required-but-empty `file` cell in GridField flagged only the visual ring and `title` on the `td`; nothing in the cell subtree carried `aria-invalid`, so assistive tech was told nothing (objectui#5431). #3318 / PR #5429 wired every other cell type; `file` was left out because `FileCell`'s prop set was closed. `FileCell` now takes the published `error?: string` slot — the same spelling `FieldWidgetPropsSchema` declares and `LookupField` / `FileField` already consume (#3222) — and puts `aria-invalid` on its own focusable picker button (#5223: never the wrapper). GridField's file branch passes the slot exactly as its lookup branch does. Public-surface widening: `FileCell` is exported from `@object-ui/fields`'s barrel, so the new optional prop is an API addition (declared minor in the changeset). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012u2pRjcqAYtoEjgr3wwhnK
Contributor
✅ 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-sales
marked this pull request as ready for review
August 21, 2026 12:46
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#5431
What
A required-but-empty
filecell inGridFieldflagged only the visual ring andtitleon thetd— no element in the cell subtree carriedaria-invalid, so assistive tech was told nothing. #3318 / PR #5429 wired every other cell type (text/number/currency/date/datetime/time, select, lookup);filewas left out becauseFileCell's prop set was closed.Per the dispatch ruling (option 1, triage-concurred):
FileCellgains the publishederrorslot mirroringLookupField, and the grid passes it.Public-surface widening (stated explicitly, per the contract-review dispatch)
FileCellis exported from@object-ui/fields's barrel (export * from './widgets/FileField.js'), so the new optionalerror?: stringprop is an API addition to a published package. It is the same validation slot@objectstack/spec/ui'sFieldWidgetPropsSchemadeclares (verified:error: z.ZodOptional<z.ZodString>in the installed spec typings) andFieldWidgetComponentPropsnames (objectui#3222) — no second spelling is minted. The changeset declaresminorand describes the addition.How
packages/fields/src/widgets/FileField.tsx—FileCelltakeserror?: string(doc comment states producer/consumer and distinguishes it from the component's own per-pick uploaderrors). The carrier is the focusable picker button (aria-invalid={!!error}), the same computation asFileField's dropzone andLookupField's trigger — never the wrapper ([finding] the aria-invalid registry sweep cannot tell a wrapper mark from a real delivery — a DELIVERING row passes even if the mark lands on a non-focusable element #5223).packages/fields/src/widgets/GridField.tsx— thefilebranch passeserror={invalid ?…is required: undefined}exactly as the lookup branch does; the "not marked — tracked, not silently accepted" comment is replaced.Tests
GridField.test.tsx: required-empty file cell → thearia-invalid="true"carrier inside the flagged cell is a BUTTON and thetdis not the carrier; optional-empty and required-filled cells announce nothing; ghost row stays unmarked.complex-widgets.test.tsx:FileCellunit pins —errorset → picker buttonaria-invalid="true"; slot empty →"false".origin/main, mutation confirmed on disk by anchored grep counts 2→1 for botharia-invalid={!!error}anderror={invalid): exactly the 3 positive pins went red (Tests 3 failed | 85 passed), the 2 negative guards stayed green as predicted; restore leg confirmed markers back (counts 2/2) and 88/88 green. No dist rebuild involved — both suites import the widgets by relative src path.widget-aria-invalid-registry-e2e.test.tsx) legitimately does not see this per-column-type gap (text/number grid config) and stays green before and after — not cited as evidence.Verification (all at head
818107d)pnpm exec vitest run packages/fields/ packages/plugin-detail/src/__tests__/RelatedList.filecolumns.test.tsx packages/plugin-form/src/deriveMasterDetail.test.ts packages/plugin-form/src/deriveMasterDetail.declaredSpelling.test.tsx --maxWorkers=2→Test Files 113 passed (113),Tests 1882 passed (1882)(includes the two FileCell-consumer suites outside the package)pnpm --filter @object-ui/fields type-check→ exit 0 (script echoed; deps built first viapnpm --workspace-concurrency=2 --filter '@object-ui/fields^...' build)pnpm exec turbo run lint --filter=@object-ui/fields --concurrency=2→0 errors(857 pre-existing warnings, packageno-explicit-anybaseline). Narrowed lint declared: root lint is per-packageturbo run lint; no per-package eslint config exists, root flat config has no type-aware (projectService/project) settings and no markdown plugin, so the fields package is the entire lint population this diff can move; changesets sit in no eslint population.node scripts/check-control-bytes.mjs,check-changeset-presence.mjs,check-changeset-no-major.mjs,check-changeset-fixed.mjs→ all ✅Note: the dispatch's gate list named objectstack-side gates (
check:slot-lookup,check:test-source-alias,check:type-source-resolution, plugin-teardown/docs-audit/ADR-0087 scripts) that do not exist in this repo; re-derived against objectui's actual gate set as above.Scope
File surface as dispatched: the two widget sources, their tests, and a changeset.
packages/fields/README.mdmentionsFileCellonly for upload mechanics (no prop list documented), so no doc file is touched; the changeset carries the API-addition record.Generated by Claude Code