@object-ui/fields 15.0.0 → 16.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +219 -0
  2. package/README.md +45 -7
  3. package/dist/index.cjs +5 -5
  4. package/dist/index.d.ts +21 -0
  5. package/dist/index.js +1798 -1721
  6. package/dist/widgets/CheckboxesField.cascade.test.d.ts +0 -0
  7. package/dist/widgets/CheckboxesField.d.ts +8 -1
  8. package/dist/widgets/FileField.d.ts +1 -1
  9. package/dist/widgets/ImageField.d.ts +1 -1
  10. package/dist/widgets/MultiSelectField.cascade.test.d.ts +0 -0
  11. package/dist/widgets/MultiSelectField.d.ts +10 -2
  12. package/dist/widgets/RadioField.cascade.test.d.ts +0 -0
  13. package/dist/widgets/RadioField.d.ts +8 -1
  14. package/dist/widgets/SelectField.d.ts +13 -9
  15. package/dist/widgets/deriveLookupColumns.d.ts +1 -0
  16. package/dist/widgets/types.d.ts +6 -0
  17. package/dist/widgets/useCascadingOptions.d.ts +18 -0
  18. package/dist/widgets/useUploadingSignal.d.ts +9 -0
  19. package/dist/widgets/useUploadingSignal.test.d.ts +1 -0
  20. package/package.json +9 -9
  21. package/src/field-type-coverage.test.ts +3 -1
  22. package/src/index.tsx +47 -6
  23. package/src/standard-widgets.test.tsx +44 -0
  24. package/src/widgets/CapabilityMultiSelectField.test.tsx +26 -1
  25. package/src/widgets/CapabilityMultiSelectField.tsx +35 -10
  26. package/src/widgets/CheckboxesField.cascade.test.tsx +143 -0
  27. package/src/widgets/CheckboxesField.tsx +70 -7
  28. package/src/widgets/FileField.tsx +9 -1
  29. package/src/widgets/ImageField.tsx +45 -42
  30. package/src/widgets/MultiSelectField.cascade.test.tsx +143 -0
  31. package/src/widgets/MultiSelectField.tsx +72 -8
  32. package/src/widgets/RadioField.cascade.test.tsx +141 -0
  33. package/src/widgets/RadioField.tsx +64 -7
  34. package/src/widgets/SelectField.tsx +33 -31
  35. package/src/widgets/TextAreaField.tsx +4 -2
  36. package/src/widgets/deriveLookupColumns.test.ts +13 -0
  37. package/src/widgets/deriveLookupColumns.ts +5 -15
  38. package/src/widgets/types.ts +6 -0
  39. package/src/widgets/useCascadingOptions.ts +46 -0
  40. package/src/widgets/useFieldTranslation.ts +13 -0
  41. package/src/widgets/useUploadingSignal.test.tsx +62 -0
  42. package/src/widgets/useUploadingSignal.ts +20 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,224 @@
1
1
  # @object-ui/fields
2
2
 
3
+ ## 16.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1c8935a: feat(app-shell): render ActionParamDialog params through the shared form field-widget renderer (ADR-0059, #2700)
8
+
9
+ `ActionParamDialog` no longer hand-rolls a per-type ternary chain (select /
10
+ lookup / textarea / number / boolean, everything else → text input). Every
11
+ declared action param now renders through the same `fieldWidgetMap` the object
12
+ form uses, so a param of ANY form-supported field type — `file`, `image`,
13
+ `richtext`, `markdown`, `color`, `address`, `code`, `date`, … — gets its real
14
+ widget, lazily loaded behind `Suspense`. Subsumes the single `file` branch ask
15
+ in #2698: `type: 'file'` params render the real `FileField` upload control via
16
+ the ambient `UploadProvider`, honoring `multiple`/`accept`/`maxSize`.
17
+
18
+ - `@object-ui/fields`: new exports `resolveFormWidgetType(type)` (widget-key
19
+ resolution incl. spec aliases, text fallback) and `getLazyFieldWidget(type)`
20
+ (per-type-cached `React.lazy` over the form's own widget loaders).
21
+ - `@object-ui/core`: `ActionParamDef` gains `accept`/`maxSize`; `multiple` is
22
+ now general widget config (was lookup-only).
23
+ - `@object-ui/app-shell`: new pure `paramToField()` adapter (param → field
24
+ shape) with a drift test pinning param support ⊇ form support (`FORM_FIELD_TYPES`),
25
+ mirroring the FieldEditWidget parity guard; `resolveActionParams()` inherits
26
+ `multiple`/`accept`/`maxSize` from the referenced field for every type.
27
+ `required` validation, `visible` CEL gating, helpText, error styling, and
28
+ value shapes for previously-supported types are unchanged.
29
+
30
+ - ef14f69: feat(fields): CheckboxesField per-option `visibleWhen` cascading + `dependsOn` gating (completes the option-widget parity set)
31
+
32
+ `checkboxes` was the last static-option widget still rendering `config.options`
33
+ raw — with no per-option `visibleWhen` filtering, `dependsOn` gating, or cascade
34
+ clear. It now matches `MultiSelectField` (its multi-value sibling), completing
35
+ the ADR-0058 parity across `select` / `multiselect` / `radio` / `checkboxes`.
36
+
37
+ - **`@object-ui/fields`**: `CheckboxesField` routes through the shared
38
+ `useCascadingOptions` hook — offered boxes narrow against the live record +
39
+ `current_user`, the control gates behind a "select the parent first" hint
40
+ while a `dependsOn` field is empty, and selections no longer offered are
41
+ pruned per-element from the array. Adds `checkboxes-empty-*` /
42
+ `checkboxes-option-*` testids.
43
+ - **`@object-ui/components`**: adds `checkboxes` to the form renderer's option
44
+ field sets (`CASCADE_OPTION_FIELD_TYPES`, the cross-field cascade-clear
45
+ effect, and the option pre-filter) so a `checkboxes` field is threaded
46
+ `dependentValues` and gated identically to the other option widgets.
47
+ - Tests: `CheckboxesField.cascade.test.tsx` mirrors `MultiSelectField.cascade.test.tsx`.
48
+
49
+ - 14cb729: feat(fields): MultiSelectField per-option `visibleWhen` cascading + `dependsOn` gating (parity with single select, #2715)
50
+
51
+ The multi-value chip picker now implements the same ADR-0058 option
52
+ resolution as the single `SelectField`, closing the gap #2709 opened when a
53
+ `select` + `multiple` (and the `multiselect` type) started delegating to it.
54
+
55
+ - Extracted `useCascadingOptions` — the shared hook that resolves per-option
56
+ `visibleWhen` filtering, `dependsOn` gating, and the live `dependentValues` +
57
+ predicate-scope wiring — and routed both `SingleSelectField` and
58
+ `MultiSelectField` through it (no duplicated resolver).
59
+ - `MultiSelectField` narrows its offered chips against the live record +
60
+ `current_user`, gates behind a "select the parent first" hint while a
61
+ `dependsOn` field is empty, and surfaces a legible empty state instead of a
62
+ bare chip row.
63
+ - Cascade-clear: when the offered set changes (parent changed / predicate
64
+ flipped) the widget prunes only the now-invalid selections, keeping the
65
+ still-offered ones — the array analogue of the single select's clear.
66
+ - Tests: `MultiSelectField.cascade.test.tsx` mirrors `SelectField.cascade.test.tsx`
67
+ (gating, per-element cascade clear, role/context gating).
68
+
69
+ - 1629313: feat(fields): RadioField per-option `visibleWhen` cascading + `dependsOn` gating; single-source the option resolver
70
+
71
+ Brings `RadioField` to parity with `SelectField` / `MultiSelectField` for ADR-0058
72
+ cascading & role-gated options, and collapses the three copies of the
73
+ gate-then-filter logic onto one shared resolver.
74
+
75
+ - **`@object-ui/core`**: new pure `resolveCascadingOptions(rawOptions, record, dependsOn, scope)`
76
+ → `{ options, gated, dependsOnFields }` — the single source of truth for
77
+ `dependsOn` gating + per-option `visibleWhen` filtering.
78
+ - **`@object-ui/fields`**: `RadioField` now narrows its offered radios against
79
+ the live record + `current_user`, gates behind a "select the parent first"
80
+ hint while a `dependsOn` field is empty, and clears a value no longer offered
81
+ (scalar cascade clear). The `useCascadingOptions` hook is refactored to a thin
82
+ React wrapper over `resolveCascadingOptions`.
83
+ - **`@object-ui/components`**: the form renderer's inline option pre-filter and
84
+ cross-field cascade-clear effect now call `resolveCascadingOptions` instead of
85
+ re-deriving gating/filtering, so they can't drift from the widgets (no
86
+ behavior change).
87
+ - Tests: `RadioField.cascade.test.tsx` mirrors the select cascade tests; core
88
+ gains `resolveCascadingOptions` unit coverage.
89
+
90
+ - eee4ded: feat(fields): render `select` + `multiple` through the multi-value chip picker; restore fields/core lint gates
91
+
92
+ - **Multi-value select** — a `select` field/param declared `multiple: true`
93
+ now renders the multi-value chip picker (the `multiselect` widget) and stores
94
+ a `string[]`, instead of collapsing to a single-value dropdown that could
95
+ hold only one value. The delegation lives inside `SelectField`, so the object
96
+ form, the inline grid editor, and the app-shell `ActionParamDialog` all
97
+ inherit it from the one `select` widget with no per-surface drift. Single
98
+ selects keep the cascading dropdown (multi + per-option `visibleWhen`
99
+ cascading is not a combination in use today).
100
+ - **`autonumber` mapping is unchanged** here; this change is orthogonal.
101
+ - **Lint gates restored** — fixed the pre-existing baseline lint errors that
102
+ had left the `@object-ui/fields` and `@object-ui/core` package lints red (so
103
+ the gate could not catch new violations): `react-hooks/rules-of-hooks` in
104
+ `ImageField` / `TextAreaField` / `index.tsx` (hooks hoisted above early
105
+ returns; the `useFieldTranslate` hook no longer wrapped in try/catch), plus
106
+ `no-useless-assignment` / `no-useless-escape` / `no-control-regex` /
107
+ `prefer-const` / `preserve-caught-error` in the core evaluator and utils. No
108
+ behavior change from the lint fixes.
109
+
110
+ ### Patch Changes
111
+
112
+ - 0318118: fix(app-shell): block ActionParamDialog submit while a file/image param is uploading; map spec `autonumber` (ADR-0059 follow-ups)
113
+
114
+ Two follow-ups to the shared-field-widget param rendering (ADR-0059):
115
+
116
+ - **Upload-in-progress guard.** A `file`/`image` param's value only becomes its
117
+ fileId once the presigned upload settles, so confirming mid-upload sent an
118
+ empty/stale value. `FileField`/`ImageField` now surface their upload state via
119
+ an optional `onUploadingChange` prop (shared `useUploadingSignal` hook,
120
+ ignored by other widgets); `ActionParamDialog` wires it for `file`/`image`
121
+ params and disables Confirm (label → "Uploading…", new `actionDialog.uploading`
122
+ i18n key across all locales) plus blocks submit while any upload is in flight.
123
+ - **`autonumber` spelling.** `mapFieldTypeToFormType` now maps the spec
124
+ `FieldType` spelling `autonumber` (in addition to the widget-map key
125
+ `auto_number`) to the AutoNumber widget, so a spec-typed `autonumber`
126
+ field/param no longer falls through to the plain text input — fixes the object
127
+ form path as well as action params.
128
+
129
+ - aefcf39: feat(action-params): serialize file/image action params to storage id(s); retire the approvals composer
130
+
131
+ Declared action params of `type: 'file'`/`'image'` now POST the portable API
132
+ contract — the storage id(s) — instead of the upload widget's rich object:
133
+
134
+ - `FileField` surfaces the id it already receives from the upload adapter
135
+ (`meta.fileId`) as `file_id` on each emitted file object (additive; the
136
+ record file-field value shape is unchanged).
137
+ - `ActionParamDialog` maps upload-param values to their `file_id`(s) at submit
138
+ (`serializeParamValues`, pure + exported): single → string, `multiple` →
139
+ `string[]`. The api handler already forwards param values untouched, so an
140
+ action with a `file` param POSTs `attachments: string[]`.
141
+
142
+ This lets the approvals inbox retire its last hand-wired UI — the approve/reject
143
+ composer with its bespoke attachment upload — so the drawer renders every
144
+ decision through `DeclaredActionsBar` with the declared `attachments` file param
145
+ (framework side declares it; see the paired framework change). `DeclaredActionsBar`'s
146
+ `exclude` prop stays as a general capability.
147
+
148
+ - 3b2e4d9: fix(list): route remaining system-field groupings through the shared classifier
149
+
150
+ Follow-up to the `owner_id` default-column fix: consolidate the display-oriented
151
+ system-field exclusions onto the shared `isSystemManagedField` /
152
+ `SYSTEM_MANAGED_FIELD_NAMES` (from `@object-ui/types`) so the framework-injected
153
+ `owner_id` is treated consistently across the grid, record picker, and detail
154
+ drawer.
155
+
156
+ - `ObjectGrid` record-detail drawer: the business-fields vs. muted meta-section
157
+ split now uses the shared classifier, so `owner_id` (and other injected system
158
+ fields) land in the meta section instead of the business body.
159
+ - `deriveLookupColumns` (record picker): drops its local name set for the shared
160
+ classifier — now flag-aware (`field.system`), not just name-based.
161
+ - `RecordDetailDrawer`: its default `systemFields` set is derived from the shared
162
+ `SYSTEM_MANAGED_FIELD_NAMES`; the `systemFields` prop override is preserved.
163
+
164
+ `deriveRelatedLists`' narrow "audit FK on every object" set and plugin-detail's
165
+ inline-edit "never editable" set are intentionally left distinct — different
166
+ semantics (the latter deliberately keeps `owner_id` editable).
167
+
168
+ - Updated dependencies [0318118]
169
+ - Updated dependencies [1c8935a]
170
+ - Updated dependencies [af1b0db]
171
+ - Updated dependencies [8b8b744]
172
+ - Updated dependencies [7cf4051]
173
+ - Updated dependencies [803558e]
174
+ - Updated dependencies [2e7d7f0]
175
+ - Updated dependencies [ef14f69]
176
+ - Updated dependencies [94d4876]
177
+ - Updated dependencies [1100a8b]
178
+ - Updated dependencies [7abe4cd]
179
+ - Updated dependencies [69fa5d1]
180
+ - Updated dependencies [549c67d]
181
+ - Updated dependencies [ebe6494]
182
+ - Updated dependencies [2b17339]
183
+ - Updated dependencies [31b77d4]
184
+ - Updated dependencies [6d4fbe6]
185
+ - Updated dependencies [0a3710b]
186
+ - Updated dependencies [f80aaf2]
187
+ - Updated dependencies [62b9ab5]
188
+ - Updated dependencies [1629313]
189
+ - Updated dependencies [29c6040]
190
+ - Updated dependencies [faebac3]
191
+ - Updated dependencies [2331ac9]
192
+ - Updated dependencies [199fa83]
193
+ - Updated dependencies [eee4ded]
194
+ - @object-ui/i18n@16.1.0
195
+ - @object-ui/core@16.1.0
196
+ - @object-ui/types@16.1.0
197
+ - @object-ui/react@16.1.0
198
+ - @object-ui/components@16.1.0
199
+ - @object-ui/providers@16.1.0
200
+
201
+ ## 16.0.0
202
+
203
+ ### Patch Changes
204
+
205
+ - Updated dependencies [d3e19ed]
206
+ - Updated dependencies [59d4fa9]
207
+ - Updated dependencies [4c7c47f]
208
+ - Updated dependencies [210806a]
209
+ - Updated dependencies [b4ef588]
210
+ - Updated dependencies [ca0f5f0]
211
+ - Updated dependencies [5534535]
212
+ - Updated dependencies [9b8f978]
213
+ - Updated dependencies [195a651]
214
+ - Updated dependencies [33b4995]
215
+ - @object-ui/react@16.0.0
216
+ - @object-ui/components@16.0.0
217
+ - @object-ui/types@16.0.0
218
+ - @object-ui/i18n@16.0.0
219
+ - @object-ui/core@16.0.0
220
+ - @object-ui/providers@16.0.0
221
+
3
222
  ## 15.0.0
4
223
 
5
224
  ### Patch Changes
package/README.md CHANGED
@@ -55,6 +55,23 @@ Supported types out of the box:
55
55
  - **Media**: `file`, `image`
56
56
  - **System**: `formula`, `summary`, `auto_number`
57
57
 
58
+ ### Rendering form field widgets outside the form
59
+
60
+ The full widget surface is exported for consumers that render field widgets
61
+ outside a record form (ADR-0059):
62
+
63
+ - `FORM_FIELD_TYPES` — the frozen list of every type the form can render.
64
+ - `resolveFormWidgetType(type)` — resolves any field-type spelling to its
65
+ widget key (spec aliases like `toggle`/`json`/`secret` included; unknown
66
+ types fall back to `text`, mirroring the form).
67
+ - `getLazyFieldWidget(type)` — the widget wrapped in `React.lazy` (cached per
68
+ type; render inside `<Suspense>`), sharing the same loaders `registerField`
69
+ uses so nothing is bundled eagerly.
70
+
71
+ The app-shell `ActionParamDialog` uses these to render declared action params
72
+ through the exact same widgets as the object form — with a drift test pinning
73
+ param support ⊇ form support.
74
+
58
75
  ### File uploads in line-item grids
59
76
 
60
77
  `GridField` (the master-detail line-items grid) supports `type: 'file'` columns:
@@ -66,15 +83,36 @@ or photo per row without opening the row form (objectui#2360). Columns accept
66
83
  exported as `FileCell`). Auto-derived subform columns map `file`/`image`/
67
84
  `avatar` fields to file columns instead of dropping them.
68
85
 
86
+ ### Multi-value selects
87
+
88
+ A `select` field declared `multiple: true` selects zero-or-more values (spec
89
+ allows `multiple` on `select`). `SelectField` delegates to the multi-value chip
90
+ picker (the same widget the `multiselect` type uses) and stores a `string[]`.
91
+ Delegating inside `SelectField` — rather than at a type-resolution layer — means
92
+ every surface that renders the `select` widget (the object form, the inline grid
93
+ editor, and the app-shell `ActionParamDialog`) gets multi-select identically,
94
+ with no per-surface drift. Both single- and multi-value selects resolve
95
+ per-option `visibleWhen` cascading and `dependsOn` gating through the same
96
+ [`useCascadingOptions`](./src/widgets/useCascadingOptions.ts) hook, so the
97
+ offered chips narrow (and now-invalid selections are pruned) exactly as the
98
+ single dropdown does.
99
+
69
100
  ### Cascading & role-gated select options
70
101
 
71
- `select` options support a per-option `visibleWhen` CEL predicate (offered only
72
- when TRUE, evaluated against the live record + `current_user`) and a field-level
73
- `dependsOn`. Together they drive dependent selects (country → province → city)
74
- and role-gated options with no bespoke matrix — the same primitives dependent
75
- lookups use. While a `dependsOn` parent is empty the control is gated; a parent
76
- change re-filters the list and clears a now-invalid value. Client-side hiding is
77
- UX only — gate authorization-sensitive values on the server too. See
102
+ `select`, `multiselect`, `radio`, and `checkboxes` options support a per-option
103
+ `visibleWhen` CEL predicate (offered only when TRUE, evaluated against the live
104
+ record + `current_user`) and a field-level `dependsOn`. Together they drive
105
+ dependent selects (country → province → city) and role-gated options with no
106
+ bespoke matrix — the same primitives dependent lookups use. All four widgets
107
+ resolve this through the shared [`useCascadingOptions`](./src/widgets/useCascadingOptions.ts)
108
+ hook, which wraps the pure `resolveCascadingOptions` helper in `@object-ui/core`
109
+ (also used by the form renderer's inline pre-filter, so gating and filtering
110
+ never drift). While a `dependsOn` parent is empty the control is gated; a parent
111
+ change re-filters the list and clears a now-invalid value (scalar `select` /
112
+ `radio` drop the value; multi-value `multiselect` / `checkboxes` prune just the
113
+ offered-out entries).
114
+ Client-side hiding is UX only — gate authorization-sensitive values on the
115
+ server too. See
78
116
  [`content/docs/fields/select.mdx`](../../content/docs/fields/select.mdx).
79
117
 
80
118
  <!-- release-metadata:v3.3.0 -->