diff --git a/.changeset/11550-fields-tolerated-teaching-dropped.md b/.changeset/11550-fields-tolerated-teaching-dropped.md new file mode 100644 index 0000000000..c5856d12fd --- /dev/null +++ b/.changeset/11550-fields-tolerated-teaching-dropped.md @@ -0,0 +1,19 @@ +--- +'@object-ui/plugin-form': patch +--- + +The top-level `fields` input descriptions of `object-form`, `view:form`, +`embeddable-form` and `object-master-detail-form`, and the `console.warn` a +top-level `fields` member that resolves to no field name draws, no longer say +that a `{ name }` object member "is tolerated" (objectui#11550). Each still says +that the members are bare field names, and the warning still names the right +spelling: a bare field-name string, or an entry in `sections[].fields`. + +Write top-level `fields` members as bare field-name strings, for example +`"fields": ["name", "email"]`. A per-field override (`colSpan`, a label) goes on +a `sections[].fields` entry instead. + +Behaviour is unchanged. A `{ name }` member already stored in a form view still +reads at render, and the member that resolves to no name is still skipped with +the same warning; only the wording that taught `{ name }` as an authoring +spelling is gone. diff --git a/packages/plugin-form/src/index.tsx b/packages/plugin-form/src/index.tsx index ef2d497e8b..7ee8161365 100644 --- a/packages/plugin-form/src/index.tsx +++ b/packages/plugin-form/src/index.tsx @@ -229,7 +229,7 @@ ComponentRegistry.register('object-form', ObjectFormRenderer, { category: 'plugin', inputs: [ { name: 'objectName', type: 'string', required: true }, - { name: 'fields', type: 'array', description: 'Bare field names to show, in order (each looked up in the object schema; `{ name }` is tolerated). NOT the same vocabulary as `sections[].fields`, which also accepts the spec `FormFieldSchema` object (identity key `field`, e.g. `{ field: "note", colSpan: 2 }`) — that shape resolves to no name HERE and is silently skipped (SimpleObjectForm in ObjectForm.tsx; buildFlatFields in flatFields.ts for the drawer/modal presentations).' }, + { name: 'fields', type: 'array', description: 'Bare field names to show, in order (each looked up in the object schema). NOT the same vocabulary as `sections[].fields`, which also accepts the spec `FormFieldSchema` object (identity key `field`, e.g. `{ field: "note", colSpan: 2 }`) — that shape resolves to no name HERE and is silently skipped (SimpleObjectForm in ObjectForm.tsx; buildFlatFields in flatFields.ts for the drawer/modal presentations).' }, { name: 'mode', type: 'enum', enum: ['create', 'edit', 'view'] }, { name: 'formType', type: 'enum', enum: ['simple', 'tabbed', 'wizard', 'split', 'drawer', 'modal'] }, { name: 'sections', type: 'array' }, @@ -338,7 +338,7 @@ ComponentRegistry.register('form', ObjectFormRenderer, { category: 'view', inputs: [ { name: 'objectName', type: 'string', required: true }, - { name: 'fields', type: 'array', description: 'Bare field names to show, in order (each looked up in the object schema; `{ name }` is tolerated). NOT the same vocabulary as `sections[].fields`, which also accepts the spec `FormFieldSchema` object (identity key `field`, e.g. `{ field: "note", colSpan: 2 }`) — that shape resolves to no name HERE and is silently skipped (this renders through the same `ObjectFormRenderer` / `SimpleObjectForm` as `object-form` above — see its `fields` description).' }, + { name: 'fields', type: 'array', description: 'Bare field names to show, in order (each looked up in the object schema). NOT the same vocabulary as `sections[].fields`, which also accepts the spec `FormFieldSchema` object (identity key `field`, e.g. `{ field: "note", colSpan: 2 }`) — that shape resolves to no name HERE and is silently skipped (this renders through the same `ObjectFormRenderer` / `SimpleObjectForm` as `object-form` above — see its `fields` description).' }, { name: 'mode', type: 'enum', enum: ['create', 'edit', 'view'] }, ] }); @@ -400,7 +400,7 @@ ComponentRegistry.register('embeddable-form', EmbeddableFormRenderer, { { name: 'objectName', type: 'string', required: true }, { name: 'title', type: 'string' }, { name: 'description', type: 'string' }, - { name: 'fields', type: 'array', description: 'Bare field names to show, in order (each looked up in the object schema; `{ name }` is tolerated). NOT the same vocabulary as `sections[].fields`, which also accepts the spec `FormFieldSchema` object (identity key `field`, e.g. `{ field: "note", colSpan: 2 }`) — that shape resolves to no name HERE and is silently skipped (`EmbeddableForm` passes this array straight through to `` with no `sections`, so it renders through the same `SimpleObjectForm` as `object-form` above — see its `fields` description).' }, + { name: 'fields', type: 'array', description: 'Bare field names to show, in order (each looked up in the object schema). NOT the same vocabulary as `sections[].fields`, which also accepts the spec `FormFieldSchema` object (identity key `field`, e.g. `{ field: "note", colSpan: 2 }`) — that shape resolves to no name HERE and is silently skipped (`EmbeddableForm` passes this array straight through to `` with no `sections`, so it renders through the same `SimpleObjectForm` as `object-form` above — see its `fields` description).' }, { name: 'allowMultiple', type: 'boolean' }, ] }); @@ -491,7 +491,7 @@ ComponentRegistry.register('object-master-detail-form', MasterDetailFormRenderer // Declaring them would mint choices an authoring UI offers and this block // cannot honour. { name: 'formType', type: 'enum', enum: ['simple', 'tabbed'], description: 'How the PARENT half of the form is presented. The detail grids below it are unaffected.' }, - { name: 'fields', type: 'array', description: 'Which parent fields to show, in order — and it is NOT ignored when `sections` is given: the two INTERSECT. The parent field pool is built from this key first and every section then resolves its own members against that pool, so a section member this key does not list is dropped from the rendered form, and a section that loses EVERY member that way disappears with its heading. Each such drop is reported once via `console.warn` (objectui#9884); it is not repaired, because this key bounds what the form DRAWS and edits, not what Save writes: on a create, the submitted set is the drawn fields plus any parent value seeded through `initialValues` (or its alternate spelling `initialData`), drawn or not. A seed for an undeclared, server-owned, computed or read-only field is still stripped, as on any save. Author one or the other, or list every section member here too. Members are bare field names (`{ name }` tolerated); NOT the spec `FormFieldSchema` object `sections[].fields` accepts (identity key `field`) — that shape resolves to no name here and is silently skipped (the parent form renders through the same `ObjectForm` / `SimpleObjectForm` as `object-form` — see its `fields` description).' }, + { name: 'fields', type: 'array', description: 'Which parent fields to show, in order — and it is NOT ignored when `sections` is given: the two INTERSECT. The parent field pool is built from this key first and every section then resolves its own members against that pool, so a section member this key does not list is dropped from the rendered form, and a section that loses EVERY member that way disappears with its heading. Each such drop is reported once via `console.warn` (objectui#9884); it is not repaired, because this key bounds what the form DRAWS and edits, not what Save writes: on a create, the submitted set is the drawn fields plus any parent value seeded through `initialValues` (or its alternate spelling `initialData`), drawn or not. A seed for an undeclared, server-owned, computed or read-only field is still stripped, as on any save. Author one or the other, or list every section member here too. Members are bare field names; NOT the spec `FormFieldSchema` object `sections[].fields` accepts (identity key `field`) — that shape resolves to no name here and is silently skipped (the parent form renders through the same `ObjectForm` / `SimpleObjectForm` as `object-form` — see its `fields` description).' }, // The three labels are the spec's `I18nLabel` (`ComponentPropsMap // ['object-master-detail-form']`), and `MasterDetailForm` resolves a map // with `pickLocalized` against the active UI language (objectui#10935). So diff --git a/packages/plugin-form/src/sectionFields.ts b/packages/plugin-form/src/sectionFields.ts index a352be47ee..1202250513 100644 --- a/packages/plugin-form/src/sectionFields.ts +++ b/packages/plugin-form/src/sectionFields.ts @@ -186,9 +186,11 @@ function warnOnMixedVocabulary(fd: Record, objectName: string): voi * `field`, normalized by `normalizeSectionField` above. TOP-LEVEL `fields` — * `SimpleObjectForm`'s `fieldsToShow` loop in `ObjectForm.tsx`, and * `buildFlatFields` below in `flatFields.ts` for the drawer/modal - * presentations — does NOT: it reads only bare field-name strings (`{ name }` - * tolerated). The exact same `{ field: 'x', ... }` object `normalizeSectionField` - * treats as canonical resolves to no `name` at the top level, and both read + * presentations — does NOT: it takes only bare field-name strings. (A STORED + * `{ name }` entry still reads, but it is no authoring spelling and neither + * this warning nor the registrations teach it: objectui#11550.) The exact same + * `{ field: 'x', ... }` object `normalizeSectionField` treats as canonical + * resolves to no `name` at the top level, and both read * sites used to drop it in total silence — no throw, no warning, no * empty-state. This is the same voice and the same once-per-occurrence * discipline as `warnOnMixedVocabulary`, for the sibling mistake where a @@ -209,7 +211,7 @@ export function warnUnresolvedTopLevelField(entry: unknown, objectName: string): warnedUnresolvedTopLevelField.add(key); console.warn( `[object-ui] top-level \`fields\` entry ${shape} resolved to no field name and was skipped. ` + - `Top-level \`fields\` takes bare field-name strings (\`{ name }\` is tolerated) — it is NOT the ` + + `Top-level \`fields\` takes bare field-name strings — it is NOT the ` + `same vocabulary as \`sections[].fields\`, which also accepts the spec \`FormFieldSchema\` object ` + `(identity key \`field\`, e.g. \`{ field: 'note', colSpan: 2 }\`). That shape has no \`name\` here ` + `and is silently dropped; use a bare field-name string, or move the entry into a ` +