Skip to content

[fields] grid columns have two incompatible key spellings: declared type says name, GridField reads field — spec-compliant grid metadata renders empty cells #3951

Description

@yinlianghui

Found while implementing #3910 (converting the docs field catalog to form hosting). Out of that card's scope — filed rather than fixed, because picking the surviving spelling is a contract decision with a live blast radius.

The divergence

A grid field's columns are declared by GridColumnDefinition in packages/types/src/field-types.ts:677, which is also the declared type of GridFieldMetadata.columns (field-types.ts:651):

export interface GridColumnDefinition {
  /** Column field name */
  name: string;
  label?: string;
  type: string;
  // ...
}

But the widget that renders those columns, packages/fields/src/widgets/GridField.tsx:41, declares its own local interface and reads a different key:

export interface GridColumn {
  field: string;
  // ...
}

Every read in GridField is c.fieldkey={c.field} (:558, :590, :620), blank[c.field] (:399), applyCell(rowIdx, col.field, …) (:449), columns.findIndex((c) => c.field === totalField) (:528), extraShown.has(c.field) (:361). There is no normalization from name.

Consequence

Metadata authored against the declared type renders a grid whose cells are all empty, plus a React key warning (because key={c.field} is undefined for every column).

Measured by rendering GridField directly — no form, no docs wrapper, identical rows, only the column key spelling differs:

name-spelled  (GridColumnDefinition / catalog): key-warnings=1  rows=[["1","IN[]","IN[]","IN[]"],["2","IN[]","IN[]","IN[]"],["3","IN[]","IN[]","IN[]"]]
field-spelled (GridField GridColumn)          : key-warnings=0  rows=[["1","IN[Widget A]","IN[2]","IN[29.99]"],["2","IN[Widget B]","IN[1]","IN[49.99]"],["3","IN[]","IN[]","IN[]"]]

(IN[x] = the cell input's value; row 3 is the ghost/new row.)

Which side each producer is on:

  • nameGridColumnDefinition (the declared type), the three fields-grid catalog examples, and the prose on content/docs/fields/grid.mdx ("name: string; // Column field name").
  • fieldGridField itself, and the master-detail derivation in packages/plugin-form/src/deriveMasterDetail.ts (hydrateColumns reads fields[col.field] at :261).

So real app paths that go through deriveMasterDetail work, and anything authored from the published type or the documentation does not. That is the worst direction for this repo's second axis: an AI author reading GridColumnDefinition or the grid docs page produces a grid that silently renders nothing in every cell.

Visible today

The three demos on /docs/fields/grid render tables with the correct row COUNT and every cell empty (browser-verified during #3910). This predates #3910 — the old docs-demo wrapper passed the same name-spelled columns through, so the cells were empty there too. #3910 deliberately did not re-spell the examples to field: that would fossilize into the documentation a key the declared type does not have, which is the exact failure class #3798 ruling B set out to remove.

Two resolutions, and why this needs a maintainer

  1. Make GridField read the declared name (contract-first, AGENTS.md #0.1 — the consumer is the divergent side). Cost: must migrate deriveMasterDetail/hydrateColumns and any app metadata already passing field, or accept both during a deprecation window, which is itself the tolerant-alias smell.
  2. Change the declared type to field and fix GridColumnDefinition, the catalog examples and the grid docs prose. Cost: breaks any metadata authored against the currently published type.

Option 1 matches the stated contract and the documentation; option 2 matches what the live code actually does. Either way the fix must land on ONE side — a renderer-side col.field ?? col.name alias is the thing AGENTS.md #0.1 forbids, and would leave two dialects in the catalog forever.

Related family (types-vs-spec drift, not the same key): #2231, #2890.

Reproduce

pnpm exec vitest run packages/fields/src/widgets/  # existing tests all pass — nothing covers this

Render GridField with value set to two row objects and columns spelled name, and observe empty cells plus the key warning; re-spell to field and both go away.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions