@object-ui/fields 17.2.0 → 17.3.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 (142) hide show
  1. package/CHANGELOG.md +592 -0
  2. package/dist/__tests__/cell-truncation.test.d.ts +0 -0
  3. package/dist/__tests__/field-carrier-sdui.test.d.ts +0 -0
  4. package/dist/__tests__/widget-aria-invalid-e2e.test.d.ts +0 -0
  5. package/dist/__tests__/widget-aria-invalid-registry-e2e.test.d.ts +0 -0
  6. package/dist/__tests__/widget-dom-leak-e2e.test.d.ts +0 -0
  7. package/dist/index.cjs +5 -5
  8. package/dist/index.d.ts +4 -0
  9. package/dist/index.js +2881 -2566
  10. package/dist/widgets/AddressField.d.ts +1 -1
  11. package/dist/widgets/AddressField.uniqueIds.test.d.ts +0 -0
  12. package/dist/widgets/AvatarField.d.ts +1 -1
  13. package/dist/widgets/CharacterCount.d.ts +56 -0
  14. package/dist/widgets/CheckboxesField.d.ts +1 -1
  15. package/dist/widgets/CodeField.d.ts +1 -1
  16. package/dist/widgets/ColorField.d.ts +1 -1
  17. package/dist/widgets/CurrencyField.d.ts +1 -1
  18. package/dist/widgets/EmailField.d.ts +1 -1
  19. package/dist/widgets/FileField.d.ts +1 -1
  20. package/dist/widgets/FilterConditionField.d.ts +1 -1
  21. package/dist/widgets/FullscreenFieldEditor.d.ts +155 -0
  22. package/dist/widgets/GeolocationField.d.ts +1 -1
  23. package/dist/widgets/GeolocationField.uniqueIds.test.d.ts +0 -0
  24. package/dist/widgets/ImageField.d.ts +1 -1
  25. package/dist/widgets/LocationField.d.ts +1 -1
  26. package/dist/widgets/LookupField.d.ts +1 -1
  27. package/dist/widgets/LookupField.gateHintLabel.test.d.ts +8 -0
  28. package/dist/widgets/MultiSelectField.d.ts +1 -1
  29. package/dist/widgets/ObjectField.d.ts +1 -1
  30. package/dist/widgets/ObjectRefField.d.ts +1 -1
  31. package/dist/widgets/OptionsEmptyState.d.ts +45 -0
  32. package/dist/widgets/OptionsEmptyState.no-provider.test.d.ts +0 -0
  33. package/dist/widgets/OptionsEmptyState.test.d.ts +0 -0
  34. package/dist/widgets/PercentField.d.ts +1 -1
  35. package/dist/widgets/PhoneField.d.ts +1 -1
  36. package/dist/widgets/QRCodeField.d.ts +1 -1
  37. package/dist/widgets/RadioField.d.ts +1 -1
  38. package/dist/widgets/RatingField.d.ts +1 -1
  39. package/dist/widgets/RecipientPickerField.d.ts +1 -1
  40. package/dist/widgets/RecordPickerDialog.d.ts +17 -1
  41. package/dist/widgets/RecordPickerDialog.filterOptions.test.d.ts +8 -0
  42. package/dist/widgets/RecordPickerDialog.selectLabel.test.d.ts +8 -0
  43. package/dist/widgets/RichTextField.d.ts +19 -1
  44. package/dist/widgets/TagsField.d.ts +1 -1
  45. package/dist/widgets/TagsField.placeholder.no-provider.test.d.ts +0 -0
  46. package/dist/widgets/TagsField.placeholder.test.d.ts +0 -0
  47. package/dist/widgets/TextAreaField.d.ts +44 -5
  48. package/dist/widgets/UrlField.d.ts +1 -1
  49. package/dist/widgets/__tests__/FullscreenFieldEditor.i18n.test.d.ts +0 -0
  50. package/dist/widgets/__tests__/FullscreenFieldEditor.no-provider.test.d.ts +0 -0
  51. package/dist/widgets/__tests__/RichTextField.fullscreenDisabled.test.d.ts +0 -0
  52. package/dist/widgets/__tests__/RichTextField.mobileFullscreen.test.d.ts +0 -0
  53. package/dist/widgets/__tests__/TextAreaField.characterCount.announcements.test.d.ts +0 -0
  54. package/dist/widgets/__tests__/TextAreaField.characterCount.i18n.test.d.ts +0 -0
  55. package/dist/widgets/__tests__/TextAreaField.characterCount.no-provider.test.d.ts +0 -0
  56. package/dist/widgets/__tests__/TextAreaField.fullscreenCharacterCount.i18n.test.d.ts +0 -0
  57. package/dist/widgets/__tests__/TextAreaField.fullscreenCharacterCount.test.d.ts +0 -0
  58. package/dist/widgets/__tests__/TextAreaField.fullscreenDisabled.test.d.ts +0 -0
  59. package/dist/widgets/__tests__/TextAreaField.mobileFullscreen.test.d.ts +0 -0
  60. package/dist/widgets/toDomProps.d.ts +91 -0
  61. package/dist/widgets/types.d.ts +128 -33
  62. package/dist/withFieldCarrier.d.ts +56 -0
  63. package/package.json +13 -13
  64. package/src/__tests__/cell-truncation.test.tsx +96 -0
  65. package/src/__tests__/field-carrier-sdui.test.tsx +169 -0
  66. package/src/__tests__/spec-symbol-batch7.test.ts +29 -15
  67. package/src/__tests__/validation-feedback.test.tsx +22 -13
  68. package/src/__tests__/widget-aria-invalid-e2e.test.tsx +265 -0
  69. package/src/__tests__/widget-aria-invalid-registry-e2e.test.tsx +338 -0
  70. package/src/__tests__/widget-dom-leak-e2e.test.tsx +598 -0
  71. package/src/__tests__/widget-props-contract.test.tsx +22 -9
  72. package/src/index.tsx +74 -26
  73. package/src/widgets/AddressField.tsx +31 -12
  74. package/src/widgets/AddressField.uniqueIds.test.tsx +92 -0
  75. package/src/widgets/AvatarField.tsx +9 -2
  76. package/src/widgets/BooleanField.tsx +3 -3
  77. package/src/widgets/CharacterCount.tsx +263 -0
  78. package/src/widgets/CheckboxesField.tsx +31 -14
  79. package/src/widgets/CodeField.tsx +9 -2
  80. package/src/widgets/ColorField.tsx +11 -2
  81. package/src/widgets/CurrencyField.tsx +5 -4
  82. package/src/widgets/DateField.tsx +2 -2
  83. package/src/widgets/DateTimeField.tsx +2 -2
  84. package/src/widgets/EmailField.tsx +5 -5
  85. package/src/widgets/FileField.tsx +13 -3
  86. package/src/widgets/FilterConditionField.tsx +11 -0
  87. package/src/widgets/FormulaField.tsx +1 -1
  88. package/src/widgets/FullscreenFieldEditor.tsx +271 -0
  89. package/src/widgets/GeolocationField.tsx +21 -6
  90. package/src/widgets/GeolocationField.uniqueIds.test.tsx +88 -0
  91. package/src/widgets/GridField.tsx +1 -1
  92. package/src/widgets/ImageField.tsx +9 -2
  93. package/src/widgets/LocationField.tsx +9 -2
  94. package/src/widgets/LookupField.gateHintLabel.test.tsx +135 -0
  95. package/src/widgets/LookupField.tsx +41 -6
  96. package/src/widgets/MasterDetailField.tsx +1 -1
  97. package/src/widgets/MultiSelectField.tsx +30 -14
  98. package/src/widgets/NumberField.tsx +3 -3
  99. package/src/widgets/ObjectField.tsx +17 -8
  100. package/src/widgets/ObjectRefField.tsx +10 -0
  101. package/src/widgets/OptionsEmptyState.no-provider.test.tsx +88 -0
  102. package/src/widgets/OptionsEmptyState.test.tsx +169 -0
  103. package/src/widgets/OptionsEmptyState.tsx +83 -0
  104. package/src/widgets/PasswordField.tsx +3 -3
  105. package/src/widgets/PeoplePicker.tsx +7 -1
  106. package/src/widgets/PercentField.tsx +5 -4
  107. package/src/widgets/PhoneField.tsx +5 -5
  108. package/src/widgets/QRCodeField.tsx +9 -2
  109. package/src/widgets/RadioField.tsx +24 -14
  110. package/src/widgets/RatingField.tsx +18 -3
  111. package/src/widgets/RecipientPickerField.tsx +18 -0
  112. package/src/widgets/RecordPickerDialog.filterOptions.test.tsx +611 -0
  113. package/src/widgets/RecordPickerDialog.selectLabel.test.tsx +112 -0
  114. package/src/widgets/RecordPickerDialog.tsx +173 -8
  115. package/src/widgets/RichTextField.tsx +151 -24
  116. package/src/widgets/SelectField.tsx +48 -17
  117. package/src/widgets/SliderField.tsx +1 -1
  118. package/src/widgets/SummaryField.tsx +1 -1
  119. package/src/widgets/TagsField.placeholder.no-provider.test.tsx +68 -0
  120. package/src/widgets/TagsField.placeholder.test.tsx +95 -0
  121. package/src/widgets/TagsField.tsx +23 -2
  122. package/src/widgets/TextAreaField.tsx +171 -93
  123. package/src/widgets/TextField.tsx +3 -3
  124. package/src/widgets/TimeField.tsx +2 -2
  125. package/src/widgets/UrlField.tsx +5 -5
  126. package/src/widgets/UserField.tsx +1 -1
  127. package/src/widgets/VectorField.tsx +1 -1
  128. package/src/widgets/__tests__/FullscreenFieldEditor.i18n.test.tsx +248 -0
  129. package/src/widgets/__tests__/FullscreenFieldEditor.no-provider.test.tsx +116 -0
  130. package/src/widgets/__tests__/RichTextField.fullscreenDisabled.test.tsx +244 -0
  131. package/src/widgets/__tests__/RichTextField.mobileFullscreen.test.tsx +195 -0
  132. package/src/widgets/__tests__/TextAreaField.characterCount.announcements.test.tsx +417 -0
  133. package/src/widgets/__tests__/TextAreaField.characterCount.i18n.test.tsx +287 -0
  134. package/src/widgets/__tests__/TextAreaField.characterCount.no-provider.test.tsx +160 -0
  135. package/src/widgets/__tests__/TextAreaField.fullscreenCharacterCount.i18n.test.tsx +161 -0
  136. package/src/widgets/__tests__/TextAreaField.fullscreenCharacterCount.test.tsx +346 -0
  137. package/src/widgets/__tests__/TextAreaField.fullscreenDisabled.test.tsx +246 -0
  138. package/src/widgets/__tests__/TextAreaField.mobileFullscreen.test.tsx +175 -0
  139. package/src/widgets/toDomProps.ts +175 -0
  140. package/src/widgets/types.ts +135 -35
  141. package/src/widgets/useFieldTranslation.ts +60 -0
  142. package/src/withFieldCarrier.tsx +75 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,597 @@
1
1
  # @object-ui/fields
2
2
 
3
+ ## 17.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3889ffb: Console chrome i18n gaps (objectstack#5407).
8
+
9
+ - A dependency-gated lookup now names its controlling field by its **label**
10
+ instead of its raw API name. The sentence was localized but the interpolated
11
+ name was not, so every locale — English included — read `Select crm_account
12
+ first`. The form renderer passes a new `dependsOnLabels` widget prop (the
13
+ lookup-side counterpart of `emptyHint`, which it already resolves to labels
14
+ for the fixed-option widgets); a name the host does not cover still falls
15
+ back to itself.
16
+ - The page-header overflow trigger's `More actions` accessible name now reads
17
+ `detail.moreActions`, the same key `action:menu`'s own overflow trigger uses,
18
+ so the two cannot diverge per locale.
19
+ - The activity-feed reaction button's `Add reaction` accessible name is now a
20
+ bundle key (`detail.addReaction`, added to all ten packs).
21
+ - The "check the highlighted fields" toast joins field names with a per-locale
22
+ separator (`validation.formInvalidJoiner`) instead of a hardcoded `、`
23
+ (U+3001) — right for zh/ja by accident, wrong in English and every Latin
24
+ locale. Latin packs use `, `, CJK `、`, Arabic `، `.
25
+ - The Spanish `validation.required` / `validation.unique` templates gained
26
+ their own masculine head noun (`El campo {{field}} es obligatorio`) so the
27
+ adjective agrees for feminine field labels too — `Cuenta es obligatorio` was
28
+ ungrammatical.
29
+
30
+ - 19b8c9b: Field widgets no longer spread renderer-only props — or arbitrary keys from a
31
+ field config — onto the DOM element they render (objectui#3291).
32
+
33
+ **Behaviour change:** an unknown key written on a field configuration (or on an
34
+ SDUI `field:*` node) stops becoming an HTML attribute on the rendered control.
35
+ Nothing reads those attributes, but they were serialized into the DOM, into
36
+ snapshots, and into anything scraping rendered markup.
37
+
38
+ ## What was happening
39
+
40
+ Widgets forwarded their leftover props with a bare spread, so whatever a host
41
+ handed them became an attribute. Measured on a real form with a real widget:
42
+
43
+ ```
44
+ <input placeholder="PH-f" zzcanary="CANARY-STR" zzcanaryobj="[object Object]"
45
+ zzcanarynum="42" id="…" type="text" value="" name="f">
46
+ ```
47
+
48
+ `zzcanaryobj="[object Object]"` is an ordinary extra key on the field config
49
+ being `String()`-ed onto an attribute. React 19 does not warn about any of it:
50
+ an all-lowercase unknown attribute is passed through in complete silence, which
51
+ is why this went unnoticed.
52
+
53
+ Eleven widgets carried a line that looked like it prevented exactly this:
54
+
55
+ ```ts
56
+ const { inputType, ...domProps } = props as any; // "Filter out non-DOM props"
57
+ ```
58
+
59
+ `inputType` is already stripped by the form renderer before a widget sees it,
60
+ so that line filtered nothing — the comment actively misled. It is gone.
61
+
62
+ ## What changed
63
+ - New `toDomProps(props)`, exported from `@object-ui/fields`. It keeps only
64
+ what may legitimately become a DOM attribute and drops the rest.
65
+ - The 14 field widgets that spread onto a host element now go through it:
66
+ `text`, `textarea`, `number`, `boolean`, `date`, `datetime`, `time`, `email`,
67
+ `phone`, `url`, `password`, `currency`, `percent`, and `select`.
68
+
69
+ **It is a whitelist, not a list of keys to drop.** The largest leak source is
70
+ not any named renderer prop — it is the open tail of author-supplied keys. The
71
+ form renderer destructures a fixed set of known keys and forwards the rest
72
+ verbatim, and `SchemaRenderer` is wider still: it spreads the whole authored
73
+ node as props with no strip layer at all, so on that path a widget's own spread
74
+ is the only line of defence. A blacklist of today's renderer-only props would
75
+ pass every canary above and would not stop the next authored key either.
76
+
77
+ The forwarded set is the one `FieldWidgetComponentProps` already **declares**:
78
+ `id`, `name`, `autoFocus`, `tabIndex`, `onBlur`, `onFocus`, `onClick`,
79
+ `className`, `disabled`, plus `aria-*` and the `data-*` family. Until now that
80
+ was a type-level claim a widget could violate at runtime just by spreading;
81
+ `toDomProps` is its executable form.
82
+
83
+ Two compile-time assertions tie the helper to the declaration, and it is worth
84
+ being exact about which drift each one prevents:
85
+
86
+ - the contract's DOM pass-through block is now a named type
87
+ (`FieldWidgetDomProps`), and **both directions are compiler-bound**:
88
+ forwarding a key the contract does not declare fails to compile, and
89
+ declaring a DOM key the helper does not forward fails to compile too. The
90
+ second direction guards _declared but not delivered_ — a key that
91
+ type-checks, reads as supported, and silently never reaches the element. The
92
+ leak test structurally cannot see that class of bug: it looks for attributes
93
+ that arrive, not for ones that go missing.
94
+ - `className` and `disabled` are bound in the **forward direction only**. They
95
+ are DOM-legal and are forwarded, but they live in the controlled-input block
96
+ because widgets also interpret them, so they are deliberately outside
97
+ `FieldWidgetDomProps`.
98
+
99
+ An HTML global attribute the contract does not declare (`role`, say) is no
100
+ longer forwarded. It only ever arrived through the open spread. If a field node
101
+ should be able to author one, declare it on `FieldWidgetComponentProps` and add
102
+ it to the whitelist — the fix belongs at the contract, not in a wider spread.
103
+
104
+ ## Regression gate
105
+
106
+ A new contract test renders **every** registered field widget through **both**
107
+ hosts — the real form renderer and `SchemaRenderer` — and fails on any
108
+ attribute HTML does not define for that element. It walks real DOM attributes
109
+ rather than listening for React warnings (React 19 is silent for the exact case
110
+ that leaked), asserts a validation error genuinely rendered before scanning the
111
+ error variant, and calibrates its own judge against two fixtures: standard
112
+ markup that must produce zero findings, and planted fake attributes that must
113
+ all be found. A new widget type is covered automatically — the sweep is derived
114
+ from the widget registry, so adding one without covering it fails the test.
115
+
116
+ - 56409c2: Field widgets are finally told when their field fails validation, and the props
117
+ slot that carries it takes the name the published contract gives it
118
+ (objectui#3222).
119
+
120
+ **Breaking** for anyone implementing a field widget (see migration below). The
121
+ repo version policy keeps this a `minor` — objectui's major tracks
122
+ `@objectstack`'s — so read the bump as "breaking within objectui".
123
+
124
+ ## The a11y defect this fixes
125
+
126
+ `@objectstack/spec/ui`'s `FieldWidgetPropsSchema` — the published contract that
127
+ third-party and AI-authored field widgets are written against — has always
128
+ declared `error?: string`. `@object-ui/fields` declared its own slot as
129
+ `errorMessage`. That looked like a naming split; it was worse:
130
+
131
+ ```
132
+ producers of `errorMessage` anywhere in packages/ + apps/ : 0
133
+ reads of `errorMessage` in packages/fields/src : 15 (7 widgets)
134
+ reads of `props.error` : 0
135
+ ```
136
+
137
+ The slot was dead under BOTH spellings. No host ever passed it: the form
138
+ renderer showed validation text through its own `<FormMessage/>` and never
139
+ forwarded the prop. So `EmailField`, `CurrencyField`, `UrlField`,
140
+ `RichTextField`, `PercentField`, `TextAreaField` and `PhoneField` each computed
141
+ `aria-invalid={!!errorMessage}` from a value that was `undefined` forever —
142
+ **`aria-invalid` had never once been set, and a screen reader was never told
143
+ the field had failed validation.**
144
+
145
+ Worse than "never set": `<FormControl>` is a Radix `Slot` that hands its child a
146
+ CORRECT `aria-invalid`, but a widget's own attribute is written after the props
147
+ spread, so it wins. Those seven widgets were actively overwriting the right
148
+ answer with `false`.
149
+
150
+ FROM: `renderFieldComponent` received no validation state, and the widget props
151
+ type declared `errorMessage?: string`, which nothing produced.
152
+ TO: the form renderer passes react-hook-form's `fieldState.error?.message` down
153
+ as `error` when it renders a registered widget, and the props type declares
154
+ `error?: string`. Both ends of the contract are live for the first time; a
155
+ rename alone would only have swapped one dead key for another.
156
+
157
+ ## Migration for widget authors
158
+
159
+ ```diff
160
+ -export function MyField({ value, onChange, field, readonly, errorMessage }: FieldWidgetComponentProps< string >) {
161
+ - return <Input value={value} aria-invalid={!!errorMessage} />;
162
+ +export function MyField({ value, onChange, field, readonly, error }: FieldWidgetComponentProps< string >) {
163
+ + return <Input value={value} aria-invalid={!!error} />;
164
+ ```
165
+
166
+ No alias is kept. `errorMessage` was retained nowhere on purpose — a tolerant
167
+ second spelling is exactly the de-facto second contract AGENTS.md #0.1 forbids,
168
+ and it is what would let a missed call site go quiet again. Because
169
+ objectui#3221 had already removed the type's `[key: string]: any`, every missed
170
+ site is a compile error rather than a silent `any`, so the compiler — not grep
171
+ — validated this rename.
172
+
173
+ ## Responsibilities are split, not duplicated
174
+
175
+ The widget consumes `error` **only** to drive `aria-invalid` on the control it
176
+ renders (which only it can do — `aria-invalid` has to sit on the input element).
177
+ The message TEXT stays with `<FormMessage/>` in the form renderer. A widget that
178
+ also renders the text double-displays it, and the docs, the agent prompt and the
179
+ tests all now say so.
180
+
181
+ For the same reason `required` — also declared by the spec, also never delivered
182
+ — is deliberately NOT lowered into widget props: the required marker has exactly
183
+ one author, the renderer's `<FormLabel>`, and giving widgets the flag invites a
184
+ second asterisk. The a11y state a widget could legitimately carry is
185
+ `aria-required`, which needs no contract change at all (`AriaAttributes` is
186
+ already part of the type and widgets already forward it).
187
+
188
+ Builtin field types are unaffected: they render inside `<FormControl>`, whose
189
+ Slot already supplies `aria-invalid`, so `error` is stripped there rather than
190
+ leaking into the DOM as a stray attribute.
191
+
192
+ Docs updated to match: `content/docs/guide/plugin-development.md`,
193
+ `skills/objectui/guides/plugin-development.md` and
194
+ `.github/prompts/component.prompt.md` — the last of which additionally used the
195
+ spec's non-generic type alias as a generic (`FieldWidgetProps< number >`) and
196
+ destructured a `mode` prop that exists on neither type.
197
+
198
+ - 042e09d: **BREAKING (v17)** — field widgets receive their metadata on ONE key, `field`.
199
+ `schema` is removed from the widget contract (objectui#3233).
200
+
201
+ ## What changed
202
+
203
+ `schema` was a second carrier for what `field` already means. Two producers fed
204
+ it: `SchemaRenderer` passed the authored node as `schema`, and the form
205
+ renderer's `renderFieldComponent` passed `schema={props.field || props.schema ||
206
+ props}` _alongside_ `field`. The predictable result was ~30 widgets resolving
207
+ their config as `field || schema` — one concept, two spellings, a de-facto
208
+ second contract (AGENTS.md #0.1).
209
+
210
+ - `FieldWidgetComponentProps` no longer declares `schema`. Reading
211
+ `props.schema` is now a compile error, not a silent `any`.
212
+ - Both producers converged. The form renderer passes `field` only. The SDUI node
213
+ → `field` translation happens exactly once, in a new registration adapter
214
+ (`withFieldCarrier`), which every built-in field widget is registered through.
215
+ - All `field || schema` reads in `@object-ui/fields` are now plain `field` reads.
216
+
217
+ ## Migrating a widget you wrote
218
+
219
+ **Reading the metadata** — replace the fallback with the single key:
220
+
221
+ ```diff
222
+ -const config = field || (props as any).schema;
223
+ +const config = field;
224
+ ```
225
+
226
+ **Registering a widget** — if your widget can be rendered from a schema node
227
+ (anything `SchemaRenderer` dispatches, not just forms), wrap it once so it still
228
+ gets `field`:
229
+
230
+ ```diff
231
+ +import { withFieldCarrier } from '@object-ui/fields';
232
+ +
233
+ -ComponentRegistry.register('color', ColorField, { namespace: 'field' });
234
+ +ComponentRegistry.register('color', withFieldCarrier(ColorField), { namespace: 'field' });
235
+ ```
236
+
237
+ `withFieldCarrier` forwards the node **by reference** — nothing is copied,
238
+ narrowed or renamed — and consumes `schema` so it cannot reach the DOM through a
239
+ widget's `...props` spread.
240
+
241
+ A third-party widget that still reads `props.schema` and is **not** re-registered
242
+ through the adapter will read `undefined` in v17 and silently render an empty /
243
+ default state. That is the deliberate cost of a major boundary: one contract
244
+ beats N dialects, and a widget that picks the wrong spelling should fail at
245
+ compile time rather than work under one host and not another.
246
+
247
+ ## What did NOT change
248
+ - **Host metadata (SDUI JSON) is untouched.** No authored schema changes; this is
249
+ a change to how widgets are _written_, not to what apps declare.
250
+ - **`schema` is still the universal SDUI prop** every registered component
251
+ receives from `SchemaRenderer` (`element:*`, `page:*`, grids, reports). Only
252
+ the _field-widget_ contract retired it. In particular `renderFieldComponent`
253
+ still passes `schema` when a form field type resolves to a plain component
254
+ through the bare-name fallback (e.g. `type: 'text'` reaching the display text
255
+ widget) — that component's contract is the node, and dropping it there would
256
+ render `undefined.className`.
257
+
258
+ ## Payload equivalence
259
+
260
+ Every path that used to deliver a payload through `schema` now delivers the
261
+ identical object through `field`, and both halves are pinned by tests asserting
262
+ **object identity**, not shape:
263
+
264
+ - form path — `packages/components/src/renderers/form/__tests__/form-field-carrier.test.tsx`
265
+ - SDUI path — `packages/fields/src/__tests__/field-carrier-sdui.test.tsx`
266
+
267
+ - 30ae33a: `RichTextField` honours `mobile_fullscreen`, so `mobile.fullscreenLongText` is
268
+ finally true of rich text too (objectui#3301).
269
+
270
+ `ObjectFormSchema.mobile.fullscreenLongText` has always been documented as
271
+ "textarea/rich-text get an expand button", and `ObjectForm` has always stamped
272
+ `mobile_fullscreen` onto `field:markdown` / `field:html` fields to deliver it.
273
+ Both of those types resolve to `RichTextField`, and that widget never read the
274
+ flag: a producer with no consumer. Turning the setting on gave a phone user an
275
+ expand affordance on their textareas and nothing at all on their markdown or
276
+ HTML fields, with nothing anywhere reporting that half the feature was inert.
277
+
278
+ FROM: `RichTextField` ignored the flag entirely (`grep fullscreen` over that
279
+ file returned nothing). TO: it reads `field.mobile_fullscreen` — the same single
280
+ metadata carrier `TextAreaField` reads, and nowhere else — and renders the same
281
+ expand affordance and full-height editing dialog.
282
+
283
+ **The affordance now has one implementation, not two.** One form-level setting
284
+ should produce one behaviour, so the expand button, the dialog and the
285
+ draft/commit semantics moved into a shared `FullscreenFieldEditor` that both
286
+ widgets render; only the EDITOR is per-widget. A second hand-written copy of
287
+ that state machine would be the same defect this release fixes, with an extra
288
+ step — it drifts, and nothing reports the drift. The rich-text dialog hosts the
289
+ widget's real editing surface (same format indicator, same editor), not a bare
290
+ textarea, so whatever that editor grows into, both positions get it at once.
291
+
292
+ Behaviour is identical across the two widgets and unchanged for
293
+ `TextAreaField`: the dialog seeds its draft from the committed value at open
294
+ time, keeps typing local (a react-hook-form field is not marked dirty by an
295
+ edit the user may still cancel), commits once on "Done", and discards on
296
+ "Cancel". Test ids follow the existing convention per widget —
297
+ `richtext-fullscreen-toggle` / `-dialog` / `-input` / `-save` alongside the
298
+ `textarea-*` ones, since a single form can contain both.
299
+
300
+ There is deliberately no prop spelling of the flag and no `??` fallback chain in
301
+ either widget. The field metadata is the one carrier (objectui#3233), so a
302
+ misspelled or misplaced flag stays inert and visible rather than being quietly
303
+ caught by a tolerant consumer.
304
+
305
+ Also removes a dead type from the producer: `ObjectForm` stamped the flag on
306
+ `'string-multiline'`, a string that `grep -rn` finds exactly once across both
307
+ this repo and `objectstack` — that line itself. No producer emitted it, no
308
+ registry key matched it, no widget read it. The remaining four stamped types
309
+ (`textarea`, `field:textarea`, `field:markdown`, `field:html`) each have a real
310
+ reader.
311
+
312
+ ### Patch Changes
313
+
314
+ - b7165ce: AddressField / GeolocationField sub-inputs now derive their DOM ids from a `useId()` prefix + sub-field name (the RadioField / CheckboxesField `groupId` paradigm) instead of hardcoded literals ("street", "city", "state", "zipCode", "country", "latitude", "longitude"). Two address or geolocation fields in one form no longer produce duplicate DOM ids, and each sub-label's `htmlFor` resolves to and focuses its own field's input instead of the first match in the document (#3343).
315
+ - 7d08c3f: `TextAreaField` / `RichTextField` now honour `disabled` on their fullscreen editing path. `disabled` used to reach the inline control only: `showFullscreenButton` never consulted it, neither widget forwarded it to `FullscreenFieldEditor`, and that component did not declare the prop at all. A disabled long-text or rich-text field therefore sat correctly greyed out next to a live expand button whose dialog accepted any edit and wrote it straight back through `onCommit` — reproduced dynamically before the fix as toggle `disabled=false`, dialog input `disabled=false`, `onChange` called with "EDITED WHILE DISABLED". The state was easy to miss precisely because the visible control looked right.
316
+
317
+ `FullscreenFieldEditor` now declares `disabled`: the expand button stays (disabled means "not interactive, muted", unlike `readonly`, which suppresses the affordance entirely via each widget's read-only early return) but is disabled and refuses to open, the dialog's editor is disabled through a new third `children` argument, and "Done" is both disabled and gated before `onCommit`. The dialog locks on its own rather than trusting the button, because the form renderer folds `isSubmitting` into `disabled` — so a submit starting while the dialog was already open used to leave the field editable for the duration of the submit. Cancel and Esc stay live in every state. This is the registered-widget half of the same defect #3400 / #3401 fixed on the built-in `form.tsx` path, so both render paths now give the same metadata the same behaviour (#3402).
318
+
319
+ - 6fe485b: fix(fields): translate the registered path's fullscreen long-text dialog (objectui#3404)
320
+
321
+ `FullscreenFieldEditor` — the expand button and dialog that `TextAreaField`
322
+ (`field:textarea`) and `RichTextField` (`field:markdown` / `field:html`) render
323
+ when `ObjectFormSchema.mobile.fullscreenLongText` is on — shipped four English
324
+ literals: the toggle's accessible name (`Edit {label} fullscreen`), the title
325
+ fallback `Edit text`, `Cancel` and `Done`.
326
+
327
+ No translation was missing. `form.fullscreen.*` and `common.cancel` have shipped
328
+ in all ten locale packs since objectui#3272 translated the built-in branch; this
329
+ path simply never consumed them. The result was visible inside a SINGLE form: a
330
+ zh session saw 「取消 / 完成」 on a built-in-rendered long-text field and
331
+ `Cancel / Done` on a registered-widget one.
332
+
333
+ All four now consume those existing keys — **no new keys, no locale-pack
334
+ change**. The dialog also gained the sr-only `form.fullscreen.description` the
335
+ built-in branch already carries, so it has an accessible description
336
+ (`aria-describedby`) instead of none.
337
+
338
+ Copy resolves through `useFieldTranslation()` (`createSafeTranslation`), as the
339
+ built-in branch does, whose English defaults are byte-identical to the literals
340
+ they replace — so widgets rendered with no `I18nProvider` (standalone/embedded
341
+ hosts) render exactly what they did before rather than raw i18n keys.
342
+
343
+ - 825bbe3: The option widgets' "this list cannot be filled" message now has one source, and
344
+ it is translated (objectui#3231).
345
+
346
+ FROM: `SelectField`, `MultiSelectField`, `RadioField` and `CheckboxesField` each
347
+ carried their own copy of the empty/gated state, each destructured the declared
348
+ `emptyHint` prop into `_emptyHint` and dropped it, and each rendered a hardcoded
349
+ English literal (`'No options available'`, `` `Select ${…} first` ``) even in a
350
+ Chinese or Japanese session. TO: one shared `OptionsEmptyState` — the host's
351
+ `emptyHint` when it supplied one, otherwise a translated fallback
352
+ (`fields.options.empty` / `fields.options.selectFirst`, added to all ten locale
353
+ packs).
354
+
355
+ `emptyHint` was declared, produced by the form renderer and transported, then
356
+ lost three times over — so no registered widget could ever render it. All three
357
+ breaks are fixed, because closing only the last one delivers nothing:
358
+
359
+ - `isOptionField` compared the raw resolved type against `'select'` /`'radio'` /
360
+ `'multiselect'` / `'checkboxes'`. Object-derived forms emit
361
+ `mapFieldTypeToFormType`'s prefixed ids (`field:select`), which matched none of
362
+ them, so for every option field coming from an object schema — the normal case
363
+ in the console — the whole cascade block was skipped and no hint was computed
364
+ at all. It now normalizes the `field:` prefix, the same normalization
365
+ `stripRegisteredFieldProps` already applied a few lines below.
366
+ - `stripRegisteredFieldProps` then removed the `emptyHint` key from what was
367
+ left. It is now forwarded to the four cascade option types, alongside
368
+ `dependentValues`. This stays an allow-list rather than a blanket
369
+ pass-through: every other registered widget spreads its leftover props onto a
370
+ DOM node, where an unknown `emptyHint` attribute is a React warning.
371
+ - the widgets themselves discarded it. Keeping it out of the `...props` spread
372
+ was correct; not using it afterwards was not.
373
+
374
+ User-visible effect: a dependency-gated option list now prompts with the
375
+ controlling field's **label** ("Select Country first") instead of its raw
376
+ metadata name, in the session's language; an unconfigured list says so in the
377
+ session's language too. The gate sentence is one i18n key shared by the renderer
378
+ and the widget fallback, so the two sides cannot word it differently.
379
+
380
+ Untouched: the built-in (unregistered) `select` branch of the form renderer,
381
+ which already consumed `emptyHint`. That is a separate live path.
382
+
383
+ - 34d9169: The record picker's filter panel now sends the AUTHORED value of a picked `select` filter option instead of the control's stringified form. Radix `Select` speaks strings — options render as `String(opt.value)` and `onValueChange` returns that string — and the panel stored it as-is, so a filter option whose value is a number or a boolean queried `{ level: "1" }` against records storing `level: 1`. The user picked an option that plainly has records and got an empty list. This bit the `lookup_filters` auto-derivation in particular: `lookup_filters: [{ field: 'level', operator: 'in', value: [1, 2, 3] }]` derives options whose values keep the author's type (`LookupFilterDef.value` is `unknown`), so every non-string option in the picker was unfilterable. The control's string is now mapped back through `col.options` at the control boundary — the control speaks string, the payload keeps the authored type — reusing the `matchOptionValue` / `toControlValue` semantics `@object-ui/components` introduced for the standalone form's select (#3090), rather than coercing in `filterValuesToRecord`, which would have to guess whether `"1"` meant `1` or `"1"`. Options coming from the object schema are strings by spec and round-trip unchanged, and the `number` / `boolean` filter inputs (which already converted explicitly) are untouched — the `select` branch is lifted to their standard, resolving the three-way inconsistency (#3422).
384
+ - 5881a2c: The lookup "Browse all records" Record Picker's filter panel now offers the
385
+ options a `select` field declares in its schema (objectui#3336). `LookupField`
386
+ turns each typed picker column into a filter column, and those carried no
387
+ `options` — so the filter panel's dropdown opened EMPTY and the column could
388
+ not be filtered at all, even though the same column's table cells had rendered
389
+ the authored option labels since objectui#3333.
390
+
391
+ `RecordPickerDialog` now fills a `select` filter column's missing `options`
392
+ from `fieldsMeta` (the referenced object's schema `fields` map) through the
393
+ same resolver — and the same i18n option translation — the table cells use, so
394
+ the filter dropdown and the cells can never disagree about what an option is
395
+ called. Explicitly authored filter `options` still win (including the ones
396
+ auto-derived from an `in`/`notIn` `lookup_filters` entry), and a select field
397
+ whose schema declares no options keeps an empty dropdown: no options are
398
+ synthesised from the loaded page's raw stored values.
399
+
400
+ - 9bc3709: The lookup "Browse all records" Record Picker now formats its columns with
401
+ the same field metadata the list view uses (objectui#3333). Previously the
402
+ dialog handed cell renderers a bare `{ name, type }` descriptor, so a
403
+ `select` column had no `options` and fell back to title-casing the raw
404
+ stored value (`manufacturing` rendered as "Manufacturing" instead of the
405
+ authored option label, e.g. "03 制造") — while the same field displayed
406
+ correctly in the list view and on the record detail page.
407
+
408
+ `RecordPickerDialog` gains an optional `fieldsMeta` prop (the referenced
409
+ object's schema `fields` map). When provided, each column's field descriptor
410
+ is enriched from the schema — `options` (run through the shared i18n option
411
+ translation), `currency`, `scale`, `precision`, `format`, `reference_to`, … —
412
+ and columns authored as plain strings in `lookup_columns` inherit the schema
413
+ field's `type`, so they format identically to typed columns. `LookupField`
414
+ passes the referenced object's schema it already fetches for `titleFormat`.
415
+ Callers that don't pass `fieldsMeta` keep the previous behavior.
416
+
417
+ - 49f7449: `field:select` now announces its validation state to assistive tech: the
418
+ widget's DOM pass-through lands on the Radix `SelectTrigger` — the focusable
419
+ `<button role="combobox">` a user actually interacts with — instead of
420
+ `Select.Root`, which renders no DOM element of its own and silently dropped
421
+ every `aria-*` the form renderer delivered (objectui#3306).
422
+
423
+ Before this, a required select that failed validation showed the red message
424
+ while a screen reader was told nothing: `aria-invalid`, `aria-describedby`
425
+ (the link to the message text) and `aria-required` (objectui#3290's state
426
+ channel) all landed on Root and vanished. All three now reach the trigger,
427
+ and the widget computes `aria-invalid` from the published `error` slot after
428
+ the spread, the objectui#3222 discipline — a valid field explicitly says
429
+ `aria-invalid="false"` rather than staying mute.
430
+
431
+ Two keys deliberately stay on Root: `name` (Root forwards it to the hidden
432
+ native `<select>` that takes part in form submission) and `disabled` (Root is
433
+ the single authority that disables trigger, items and hidden select together).
434
+
435
+ Guarding the whole class forward, a new registry-wide sweep
436
+ (`widget-aria-invalid-registry-e2e.test.tsx`, the objectui#3291 leak-sweep
437
+ paradigm) renders every registered field widget through the real form, drives
438
+ a real validation failure, and asserts `aria-invalid="true"` appears inside
439
+ the field's row. The 29 widget types measured not to deliver yet are pinned in
440
+ a ratchet ledger (tracked in objectui#3318): fixing one turns its ledger row
441
+ red until the entry is removed, so the ledger only ever shrinks.
442
+
443
+ - c7ed4c3: `TagsField` no longer ships a hardcoded Chinese input placeholder
444
+ (objectui#3342, AGENTS.md Commandment #-1). The placeholder now resolves
445
+ through the pinned chain: the author-declared `field.placeholder` wins
446
+ (previously ignored by this widget); otherwise the widget's own copy arrives
447
+ via `useFieldTranslation()` under the new `fields.tags.placeholder` key, added
448
+ at full parity across all locale packs (Chinese lives in the zh pack, not in
449
+ code); with no `I18nProvider` mounted the English default from FIELD_DEFAULTS
450
+ renders — never a raw key.
451
+ - 2409e1d: `TextAreaField`'s character counter now announces itself in the session locale. The counter block — rendered only when the field declares `maxLength` — carried the accessible name `Character count: {n} of {max}` as an English literal, and the element is an `aria-live="polite"` region, so a zh/ja/ar session had that English sentence read out on every keystroke while sighted users saw only the language-independent `{n}/{max}` digits. Nothing was wrong on screen, which is why it survived: only screen reader users could perceive it (#3406).
452
+
453
+ Unlike #3404, no key existed to consume — none of the ten locale packs had any character-count string. `fields.textarea.characterCount` is new in all ten, interpolating `{{count}}` and `{{max}}` as one sentence rather than parts assembled in code, because `ja` and `ko` put the cap before the count ("of {{max}} characters, {{count}}"), an order no concatenation can produce. The English pack value and the `FIELD_DEFAULTS` fallback are byte-identical to the literal they replace, so an `en` session and a provider-less embed both render exactly what they did before.
454
+
455
+ Behaviour is unchanged: `aria-live="polite"` and the per-keystroke recompute are deliberately untouched here and tracked separately (#3408).
456
+
457
+ - 789fe3e: `TextAreaField`'s character counter no longer re-announces itself on every keystroke. Measured on `main` in a zh session with `maxLength: 500`, typing a 52-character sentence one character at a time produced 52 distinct screen-reader announcements totalling 979 spoken characters — roughly 19x the text being written, each one cutting off the reader's echo of the letter just typed. The counter element was simultaneously the visible `{n}/{max}` digits, the carrier of the translated sentence and the `aria-live` region itself, so "re-render" and "announce" were the same event; the field also had no `aria-describedby`, so focusing it said nothing about the cap at all (#3408).
458
+
459
+ It is now the three-node shape the GOV.UK Design System character-count component uses: the visible digits are `aria-hidden` and purely decorative; the counter sentence (`fields.textarea.characterCount`, unchanged in all ten packs) has moved onto the textarea's `aria-describedby`, so focus reads "Character count: 12 of 500" once and then stays quiet; and a separate visually-hidden `aria-live="polite"` region carries a new near-limit warning, `fields.textarea.charactersRemaining` (new in all ten packs), which stays silent until the value is inside the last 10% or last 20 characters of the cap — whichever the typist reaches first — and updates only after typing pauses for a second. The same 52-keystroke probe now produces zero announcements; a run that types all the way onto a 500-character cap produces five. Any `aria-describedby` the host already supplied (the form renderer's description and error-message ids) is appended to, never replaced.
460
+
461
+ No metadata change: the counter still renders exactly when the field declares `maxLength` (or the legacy `max_length`), and a widget rendered with no `I18nProvider` still shows the same English sentences.
462
+
463
+ - f789c3b: `TextAreaField`'s fullscreen edit dialog now gives screen reader users the character count it has always shown sighted ones. The dialog's footer counter was a bare `{n}/{max}` span: no accessible name, nothing `aria-live`, and nothing tying it to the dialog's textarea — so browse mode read "5 slash 500" if it happened to sweep the footer, and focusing the input said nothing about the cap at all. The inline surface of the same field has carried a proper three-node counter since #3408, so the fullscreen branch was at zero for the same field, the same cap and the same user (#3417). It is reachable on any phone form with `ObjectFormSchema.mobile.fullscreenLongText` on, for every long-text field that declares a limit.
464
+
465
+ The counting UI is now ONE shared `CharacterCount` component that both surfaces render, instead of two hand-written copies that could only drift. In the dialog it renders `aria-hidden` digits plus a visually-hidden description carrying `fields.textarea.characterCount`, wired to the dialog's textarea through `aria-describedby`, so focus reads "Character count: 12 of 500" once and the count follows the draft as it is edited. The description ids are per surface, because the dialog's draft and the committed value diverge as soon as the user types.
466
+
467
+ The dialog deliberately gets NO live region: it is a modal opened to write at length, the description already delivers the cap on focus, and the inline surface's `aria-live` region stays mounted behind the overlay. The inline surface is unchanged — same DOM, same threshold-gated debounced announcements, same ten locale packs and the same English fallbacks with no `I18nProvider` mounted. No new i18n keys and no metadata change: the counter still renders exactly when the field declares `maxLength` (or the legacy `max_length`).
468
+
469
+ - a321fa4: `TextAreaField`'s mobile fullscreen flag converges on its one real producer
470
+ (objectui#3232).
471
+
472
+ FROM: the widget resolved the "show the expand affordance" decision through a
473
+ four-way `??` chain — a `mobileFullscreen` (camelCase) prop, the field
474
+ metadata's `mobile_fullscreen`, a `mobile_fullscreen` prop, and
475
+ `schema.mobile_fullscreen`. TO: a single read of the field metadata's
476
+ `mobile_fullscreen`, resolved through the `field || schema` carrier pair every
477
+ widget in this package already uses.
478
+
479
+ No runtime behaviour changes, because three of those four reads were
480
+ permanently `undefined`:
481
+
482
+ - `mobileFullscreen` (camelCase) had **no producer anywhere in the repo** — the
483
+ only occurrences of that spelling were the widget's own read and the
484
+ destructure that kept it off the DOM spread. The doc comment nonetheless
485
+ claimed "the host form passes `mobileFullscreen`", so the contract it
486
+ described had never held.
487
+ - `mobile_fullscreen` as a **prop** cannot arrive: the form renderer's
488
+ `stripRegisteredFieldProps` explicitly removes `mobile_fullscreen` and
489
+ `fullscreen` from the props forwarded to registered field widgets.
490
+ - `schema.mobile_fullscreen` was the same object `field || schema` already
491
+ resolves, so it could only ever restate the metadata read.
492
+
493
+ What actually drives the affordance — and is now the only thing that does — is
494
+ the field metadata flag `ObjectForm` stamps onto long-text fields from
495
+ `ObjectFormSchema.mobile.fullscreenLongText`. That path is unchanged and is now
496
+ pinned by tests (button, dialog, and the committed edit), so the cleanup cannot
497
+ have silently removed the working behaviour.
498
+
499
+ Also untouched: the built-in (unregistered) `textarea` branch of the form
500
+ renderer, which reads `mobile_fullscreen || fullscreen` off the form-field
501
+ props and renders its own `FullscreenTextarea`. That is a separate live path.
502
+
503
+ Why this is worth a changeset rather than a silent tidy-up: reads that nobody
504
+ writes are not free. They document a contract that does not exist — the next
505
+ author follows the comment, passes the prop, and is ignored without a word —
506
+ and a `??` chain that accepts four spellings and rejects none is exactly where
507
+ a misspelled key hides. With one source, a wrong spelling has no read path left
508
+ to absorb it. Per AGENTS.md #0.1 and Prime Directive #12, divergence like this
509
+ converges at the producer, not by accumulating tolerance at the consumer. No
510
+ host-override prop was invented in its place: inventing a key with no producer
511
+ is the same mistake in the other direction.
512
+
513
+ - 8d8094a: 20 more registered field widgets now announce a failed validation to assistive
514
+ tech: `multiselect`, `radio`, `checkboxes`, `tags`, `lookup`, `master_detail`,
515
+ `user`, `owner`, `file`, `image`, `location`, `object`, `color`, `rating`,
516
+ `code`, `avatar`, `address`, `geolocation`, `qrcode` and `object-ref` carry
517
+ `aria-invalid="true"` on their real focusable control after a validation
518
+ failure, where before the red message rendered while a screen reader was told
519
+ nothing (objectui#3318, the registry-wide gap objectui#3306's sweep measured).
520
+
521
+ Each widget follows the objectui#3222/#3306 pattern: the `toDomProps(props)`
522
+ whitelist spread goes onto the control the user actually focuses — the input,
523
+ the lookup trigger button, the radiogroup (`role="radiogroup"` is the
524
+ ARIA-designated carrier for a set of radios), every chip/checkbox/star of the
525
+ composite option widgets, the upload dropzone/button — followed by an explicit
526
+ `aria-invalid={!!error}` computed from the published `error` slot. Wrapper
527
+ `<div>`s never carry the state, and `name` is withheld from non-form-control
528
+ elements (the objectui#3291 leak class).
529
+
530
+ `Combobox` (`@object-ui/components`) now accepts standard button attributes
531
+ and forwards them to its focusable `role="combobox"` trigger, giving
532
+ combobox-based widgets an element to deliver `aria-invalid` /
533
+ `aria-describedby` to — the same seam objectui#3306 opened on
534
+ `SelectTrigger`.
535
+
536
+ Nine types remain on the objectui#3318 ratchet ledger with their blockers
537
+ documented there (`formula`/`summary`/`auto_number`/`vector` render no
538
+ focusable control; `grid`, `slider`, `signature` need component-level design;
539
+ `filter-condition`/`recipient-picker` deliver in their editable states but
540
+ render a dependency-gate hint with no control in a fresh form).
541
+
542
+ - Updated dependencies [18cd432]
543
+ - Updated dependencies [532cf8b]
544
+ - Updated dependencies [680080a]
545
+ - Updated dependencies [a7651e6]
546
+ - Updated dependencies [d915c47]
547
+ - Updated dependencies [b71fc92]
548
+ - Updated dependencies [65516ba]
549
+ - Updated dependencies [94c5b7c]
550
+ - Updated dependencies [ca0fa8f]
551
+ - Updated dependencies [34595eb]
552
+ - Updated dependencies [3889ffb]
553
+ - Updated dependencies [5781fb1]
554
+ - Updated dependencies [7e2406a]
555
+ - Updated dependencies [9e9e9a9]
556
+ - Updated dependencies [56409c2]
557
+ - Updated dependencies [042e09d]
558
+ - Updated dependencies [9cbcbf4]
559
+ - Updated dependencies [85c4c9c]
560
+ - Updated dependencies [fd54c3e]
561
+ - Updated dependencies [4eeb932]
562
+ - Updated dependencies [5c856ec]
563
+ - Updated dependencies [23018cc]
564
+ - Updated dependencies [53811d1]
565
+ - Updated dependencies [68b6a28]
566
+ - Updated dependencies [0554e88]
567
+ - Updated dependencies [d915c47]
568
+ - Updated dependencies [f44d872]
569
+ - Updated dependencies [28b2e65]
570
+ - Updated dependencies [509104a]
571
+ - Updated dependencies [825bbe3]
572
+ - Updated dependencies [6195841]
573
+ - Updated dependencies [5dd0127]
574
+ - Updated dependencies [06632e9]
575
+ - Updated dependencies [a415684]
576
+ - Updated dependencies [a4cff5b]
577
+ - Updated dependencies [175bd79]
578
+ - Updated dependencies [5af2852]
579
+ - Updated dependencies [f833d3a]
580
+ - Updated dependencies [a6ec93d]
581
+ - Updated dependencies [2a9513d]
582
+ - Updated dependencies [71be406]
583
+ - Updated dependencies [d22ae31]
584
+ - Updated dependencies [c7ed4c3]
585
+ - Updated dependencies [2409e1d]
586
+ - Updated dependencies [789fe3e]
587
+ - Updated dependencies [8d8094a]
588
+ - @object-ui/core@17.3.0
589
+ - @object-ui/components@17.3.0
590
+ - @object-ui/types@17.3.0
591
+ - @object-ui/i18n@17.3.0
592
+ - @object-ui/react@17.3.0
593
+ - @object-ui/providers@17.3.0
594
+
3
595
  ## 17.2.0
4
596
 
5
597
  ### Minor Changes
File without changes
File without changes
File without changes