Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .changeset/12130-form-field-channel-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'@object-ui/types': minor
---

A form field declares the rest of the field metadata its widget reads off the `fields[]` entry, on both faces, so the strict authoring face stops telling authors to remove keys that work (objectui#12130).

A hand-authored `form` hands each field widget the `fields[]` entry itself as its metadata carrier. objectui#11070 graded that channel from a source read and missed keys on it. This change measures it at runtime through the real `SchemaRenderer`: a census records every key of the entry a reader asks for by name, and each key it found read and undeclared is rendered with and without, to show the widget acting on it. The census is a test (`form-field-channel-census-12130.test.tsx` in `examples/schema-catalog/test/`), so a key a widget starts reading without a declaration turns it red.

- **Declared, by reference to `@objectstack/spec`'s `FieldSchema`:** `language` (the `code` widget), `maxSize` (`file`, `image`), and the lookup picker's `allowCreate`, `descriptionField`, `displayField`, `lookupColumns`, `lookupFilters` and `lookupPageSize` (`lookup`, `master_detail`, and for `displayField` / `lookupFilters` the `user` widget too). Each is the spec member itself, value checks included.
- **Declared, by reference to the field metadata types:** `idField` (`LookupFieldMetadata`), `picker` and `subtitle` (`UserFieldMetadata`), read by the `lookup` and `user` widgets.
- **Declared as the widget reads them, since no type declared them:** `capture` (`'environment' | 'user' | false`, the `file` widget's camera input), `crop` (`boolean`, `false` turns off the `image` widget's cropping) and `defaultName` (`string`, the name an empty `avatar` draws its initials from).
- **TypeScript.** `FormField` gains each member, typed as above. Each used to fall to the index signature's `any`, so a typed literal with a value of the wrong type is now a compile error.
- **zod (`@object-ui/types/zod`).** WIDENS on the strict authoring face (`StrictAnyComponentSchema`): each key used to be refused with `unrecognized_keys`, and now parses. NARROWS on the tolerant face (`safeValidateSchema`), where each key used to be stripped unjudged: a value of the wrong type (a `maxSize` that is not a positive whole number, a `lookupFilters` operator the spec does not list, a `picker` other than `search` / `default`) is now refused, and the values are now kept. The face is flat: each key is accepted on every field type, and only the widgets named above read it.
- **Not declared: `defaultValue` and `defaultChecked` on a form field.** No reader asks for either, and neither becomes the field's value on any type the effects pins measure, with or without `@object-ui/fields` registered: a checkbox stays unchecked and submits `false`, and the submitted payload carries no value. The strict face keeps refusing both by name. A form's initial values are `FormSchema.defaultValues`, keyed by field name. `content/docs/components/form/form.mdx` no longer lists a field-level `defaultValue`, and the `login-form` example in this package drops its `defaultChecked: false`.
- **Still refused, by name:** the snake_case second spellings the `lookup` widget still reads before the spec's keys above (`allow_create`, `lookup_columns`, `lookup_page_size`, and `reference_field` for `displayField`). Still undeclared, and listed as open in the census: `step`, which the `number` and `slider` widgets read but `ObjectForm` writes as `'any'`, a value the spec's numeric `step` refuses; `avatarField` / `avatar_field`; and `className`.

BREAKING (`@object-ui/types`), for a document whose form field carries one of the declared keys with a value of the wrong type: it no longer validates on the tolerant face. Fix the value. (The bump is `minor` by this repo's release model: objectui's major follows the `@objectstack` family major, and its own breaking changes ship as `minor` with the breaking semantics stated here.)

**Clause-②: yes (widening)**: each declared key widens the strict face's accept set, and its value check narrows the tolerant face.
5 changes: 1 addition & 4 deletions content/docs/components/form/form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,12 @@ interface FormField {

// Options (for select, radio-group)
options?: Array<{ value: string; label: string }>;

// Default value
defaultValue?: any;
}

interface FormSchema {
type: 'form';
fields: FormField[]; // Form fields
defaultValues?: Record<string, any>; // Initial values, keyed by field name
defaultValues?: Record<string, any>; // Initial values, keyed by field name (a field has no default key of its own)
submitLabel?: string; // Submit button text (default 'Submit')

// Events
Expand Down
Loading
Loading