@object-ui/fields 17.1.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 (171) hide show
  1. package/CHANGELOG.md +786 -0
  2. package/dist/FieldEditWidget.d.ts +2 -2
  3. package/dist/__tests__/cell-truncation.test.d.ts +0 -0
  4. package/dist/__tests__/field-carrier-sdui.test.d.ts +0 -0
  5. package/dist/__tests__/spec-symbol-batch7.test.d.ts +8 -0
  6. package/dist/__tests__/widget-aria-invalid-e2e.test.d.ts +0 -0
  7. package/dist/__tests__/widget-aria-invalid-registry-e2e.test.d.ts +0 -0
  8. package/dist/__tests__/widget-dom-leak-e2e.test.d.ts +0 -0
  9. package/dist/__tests__/widget-props-contract.test.d.ts +8 -0
  10. package/dist/index.cjs +5 -5
  11. package/dist/index.d.ts +4 -0
  12. package/dist/index.js +2882 -2569
  13. package/dist/widgets/AddressField.d.ts +2 -2
  14. package/dist/widgets/AddressField.uniqueIds.test.d.ts +0 -0
  15. package/dist/widgets/AutoNumberField.d.ts +2 -2
  16. package/dist/widgets/AvatarField.d.ts +2 -2
  17. package/dist/widgets/BooleanField.d.ts +2 -2
  18. package/dist/widgets/CapabilityMultiSelectField.d.ts +2 -2
  19. package/dist/widgets/CharacterCount.d.ts +56 -0
  20. package/dist/widgets/CheckboxesField.d.ts +2 -2
  21. package/dist/widgets/CodeField.d.ts +2 -2
  22. package/dist/widgets/ColorField.d.ts +2 -2
  23. package/dist/widgets/CurrencyField.d.ts +2 -2
  24. package/dist/widgets/DateField.d.ts +2 -2
  25. package/dist/widgets/DateTimeField.d.ts +2 -2
  26. package/dist/widgets/EmailField.d.ts +2 -2
  27. package/dist/widgets/FileField.d.ts +2 -2
  28. package/dist/widgets/FilterConditionField.d.ts +2 -2
  29. package/dist/widgets/FormulaField.d.ts +2 -2
  30. package/dist/widgets/FullscreenFieldEditor.d.ts +155 -0
  31. package/dist/widgets/GeolocationField.d.ts +2 -2
  32. package/dist/widgets/GeolocationField.uniqueIds.test.d.ts +0 -0
  33. package/dist/widgets/GridField.d.ts +2 -2
  34. package/dist/widgets/ImageField.d.ts +2 -2
  35. package/dist/widgets/LocationField.d.ts +2 -2
  36. package/dist/widgets/LookupField.d.ts +2 -2
  37. package/dist/widgets/LookupField.gateHintLabel.test.d.ts +8 -0
  38. package/dist/widgets/MasterDetailField.d.ts +2 -2
  39. package/dist/widgets/MultiSelectField.d.ts +2 -2
  40. package/dist/widgets/NumberField.d.ts +2 -2
  41. package/dist/widgets/ObjectField.d.ts +2 -2
  42. package/dist/widgets/ObjectRefField.d.ts +2 -2
  43. package/dist/widgets/OptionsEmptyState.d.ts +45 -0
  44. package/dist/widgets/OptionsEmptyState.no-provider.test.d.ts +0 -0
  45. package/dist/widgets/OptionsEmptyState.test.d.ts +0 -0
  46. package/dist/widgets/PasswordField.d.ts +2 -2
  47. package/dist/widgets/PercentField.d.ts +2 -2
  48. package/dist/widgets/PhoneField.d.ts +2 -2
  49. package/dist/widgets/QRCodeField.d.ts +2 -2
  50. package/dist/widgets/RadioField.d.ts +2 -2
  51. package/dist/widgets/RatingField.d.ts +2 -2
  52. package/dist/widgets/RecipientPickerField.d.ts +2 -2
  53. package/dist/widgets/RecordPickerDialog.d.ts +17 -1
  54. package/dist/widgets/RecordPickerDialog.filterOptions.test.d.ts +8 -0
  55. package/dist/widgets/RecordPickerDialog.selectLabel.test.d.ts +8 -0
  56. package/dist/widgets/RichTextField.d.ts +20 -2
  57. package/dist/widgets/SelectField.d.ts +2 -2
  58. package/dist/widgets/SignatureField.d.ts +2 -2
  59. package/dist/widgets/SliderField.d.ts +2 -2
  60. package/dist/widgets/SummaryField.d.ts +2 -2
  61. package/dist/widgets/TagsField.d.ts +2 -2
  62. package/dist/widgets/TagsField.placeholder.no-provider.test.d.ts +0 -0
  63. package/dist/widgets/TagsField.placeholder.test.d.ts +0 -0
  64. package/dist/widgets/TextAreaField.d.ts +45 -6
  65. package/dist/widgets/TextField.d.ts +2 -2
  66. package/dist/widgets/TimeField.d.ts +2 -2
  67. package/dist/widgets/UrlField.d.ts +2 -2
  68. package/dist/widgets/UserField.d.ts +2 -2
  69. package/dist/widgets/VectorField.d.ts +2 -2
  70. package/dist/widgets/__tests__/FullscreenFieldEditor.i18n.test.d.ts +0 -0
  71. package/dist/widgets/__tests__/FullscreenFieldEditor.no-provider.test.d.ts +0 -0
  72. package/dist/widgets/__tests__/RichTextField.fullscreenDisabled.test.d.ts +0 -0
  73. package/dist/widgets/__tests__/RichTextField.mobileFullscreen.test.d.ts +0 -0
  74. package/dist/widgets/__tests__/TextAreaField.characterCount.announcements.test.d.ts +0 -0
  75. package/dist/widgets/__tests__/TextAreaField.characterCount.i18n.test.d.ts +0 -0
  76. package/dist/widgets/__tests__/TextAreaField.characterCount.no-provider.test.d.ts +0 -0
  77. package/dist/widgets/__tests__/TextAreaField.fullscreenCharacterCount.i18n.test.d.ts +0 -0
  78. package/dist/widgets/__tests__/TextAreaField.fullscreenCharacterCount.test.d.ts +0 -0
  79. package/dist/widgets/__tests__/TextAreaField.fullscreenDisabled.test.d.ts +0 -0
  80. package/dist/widgets/__tests__/TextAreaField.mobileFullscreen.test.d.ts +0 -0
  81. package/dist/widgets/file-value.d.ts +20 -33
  82. package/dist/widgets/toDomProps.d.ts +91 -0
  83. package/dist/widgets/types.d.ts +215 -4
  84. package/dist/withFieldCarrier.d.ts +56 -0
  85. package/package.json +13 -13
  86. package/src/FieldEditWidget.tsx +6 -4
  87. package/src/__tests__/cell-truncation.test.tsx +96 -0
  88. package/src/__tests__/field-carrier-sdui.test.tsx +169 -0
  89. package/src/__tests__/spec-symbol-batch7.test.ts +126 -0
  90. package/src/__tests__/validation-feedback.test.tsx +22 -13
  91. package/src/__tests__/widget-aria-invalid-e2e.test.tsx +265 -0
  92. package/src/__tests__/widget-aria-invalid-registry-e2e.test.tsx +338 -0
  93. package/src/__tests__/widget-dom-leak-e2e.test.tsx +598 -0
  94. package/src/__tests__/widget-props-contract.test.tsx +142 -0
  95. package/src/complex-widgets.test.tsx +3 -3
  96. package/src/datetime-widgets.test.tsx +2 -2
  97. package/src/index.tsx +77 -27
  98. package/src/widgets/AddressField.tsx +32 -13
  99. package/src/widgets/AddressField.uniqueIds.test.tsx +92 -0
  100. package/src/widgets/AutoNumberField.tsx +2 -2
  101. package/src/widgets/AvatarField.tsx +10 -3
  102. package/src/widgets/BooleanField.tsx +5 -5
  103. package/src/widgets/CapabilityMultiSelectField.tsx +4 -4
  104. package/src/widgets/CharacterCount.tsx +263 -0
  105. package/src/widgets/CheckboxesField.tsx +35 -18
  106. package/src/widgets/CodeField.tsx +10 -3
  107. package/src/widgets/ColorField.tsx +12 -3
  108. package/src/widgets/CurrencyField.tsx +6 -5
  109. package/src/widgets/DateField.tsx +4 -4
  110. package/src/widgets/DateTimeField.tsx +4 -4
  111. package/src/widgets/EmailField.tsx +6 -6
  112. package/src/widgets/FileField.tsx +14 -4
  113. package/src/widgets/FilterConditionField.tsx +14 -3
  114. package/src/widgets/FormulaField.tsx +3 -3
  115. package/src/widgets/FullscreenFieldEditor.tsx +271 -0
  116. package/src/widgets/GeolocationField.tsx +22 -7
  117. package/src/widgets/GeolocationField.uniqueIds.test.tsx +88 -0
  118. package/src/widgets/GridField.tsx +4 -4
  119. package/src/widgets/ImageField.tsx +10 -3
  120. package/src/widgets/LocationField.tsx +10 -3
  121. package/src/widgets/LookupField.gateHintLabel.test.tsx +135 -0
  122. package/src/widgets/LookupField.tsx +50 -15
  123. package/src/widgets/MasterDetailField.tsx +3 -3
  124. package/src/widgets/MultiSelectField.tsx +34 -18
  125. package/src/widgets/NumberField.tsx +5 -5
  126. package/src/widgets/ObjectField.tsx +18 -9
  127. package/src/widgets/ObjectRefField.tsx +14 -4
  128. package/src/widgets/OptionsEmptyState.no-provider.test.tsx +88 -0
  129. package/src/widgets/OptionsEmptyState.test.tsx +169 -0
  130. package/src/widgets/OptionsEmptyState.tsx +83 -0
  131. package/src/widgets/PasswordField.tsx +5 -5
  132. package/src/widgets/PeoplePicker.tsx +7 -1
  133. package/src/widgets/PercentField.tsx +6 -5
  134. package/src/widgets/PhoneField.tsx +6 -6
  135. package/src/widgets/QRCodeField.tsx +10 -3
  136. package/src/widgets/RadioField.tsx +28 -18
  137. package/src/widgets/RatingField.tsx +19 -4
  138. package/src/widgets/RecipientPickerField.tsx +22 -4
  139. package/src/widgets/RecordPickerDialog.filterOptions.test.tsx +611 -0
  140. package/src/widgets/RecordPickerDialog.selectLabel.test.tsx +112 -0
  141. package/src/widgets/RecordPickerDialog.tsx +173 -8
  142. package/src/widgets/RichTextField.tsx +152 -25
  143. package/src/widgets/SelectField.tsx +53 -22
  144. package/src/widgets/SignatureField.tsx +2 -2
  145. package/src/widgets/SliderField.tsx +3 -3
  146. package/src/widgets/SummaryField.tsx +3 -3
  147. package/src/widgets/TagsField.placeholder.no-provider.test.tsx +68 -0
  148. package/src/widgets/TagsField.placeholder.test.tsx +95 -0
  149. package/src/widgets/TagsField.tsx +25 -4
  150. package/src/widgets/TextAreaField.tsx +172 -94
  151. package/src/widgets/TextField.tsx +5 -5
  152. package/src/widgets/TimeField.tsx +4 -4
  153. package/src/widgets/UrlField.tsx +6 -6
  154. package/src/widgets/UserField.tsx +3 -3
  155. package/src/widgets/VectorField.tsx +3 -3
  156. package/src/widgets/__tests__/FullscreenFieldEditor.i18n.test.tsx +248 -0
  157. package/src/widgets/__tests__/FullscreenFieldEditor.no-provider.test.tsx +116 -0
  158. package/src/widgets/__tests__/RichTextField.fullscreenDisabled.test.tsx +244 -0
  159. package/src/widgets/__tests__/RichTextField.mobileFullscreen.test.tsx +195 -0
  160. package/src/widgets/__tests__/TextAreaField.characterCount.announcements.test.tsx +417 -0
  161. package/src/widgets/__tests__/TextAreaField.characterCount.i18n.test.tsx +287 -0
  162. package/src/widgets/__tests__/TextAreaField.characterCount.no-provider.test.tsx +160 -0
  163. package/src/widgets/__tests__/TextAreaField.fullscreenCharacterCount.i18n.test.tsx +161 -0
  164. package/src/widgets/__tests__/TextAreaField.fullscreenCharacterCount.test.tsx +346 -0
  165. package/src/widgets/__tests__/TextAreaField.fullscreenDisabled.test.tsx +246 -0
  166. package/src/widgets/__tests__/TextAreaField.mobileFullscreen.test.tsx +175 -0
  167. package/src/widgets/file-value.ts +21 -5
  168. package/src/widgets/toDomProps.ts +175 -0
  169. package/src/widgets/types.ts +230 -7
  170. package/src/widgets/useFieldTranslation.ts +60 -0
  171. package/src/withFieldCarrier.tsx +75 -0
@@ -39,6 +39,12 @@ const FIELD_DEFAULTS: Record<string, string> = {
39
39
  'lookup.nextPage': 'Next page',
40
40
  'lookup.jumpToPage': 'Jump to page',
41
41
  'lookup.retry': 'Retry',
42
+ // objectui#3231 — the empty / dependency-gated state of the fixed-option
43
+ // widgets (select, multiselect, radio, checkboxes). Only used when the host
44
+ // supplies no `emptyHint`; the gate sentence shares its key with the form
45
+ // renderer so both cannot drift apart in a locale.
46
+ 'fields.options.empty': 'No options available',
47
+ 'fields.options.selectFirst': 'Select {{fields}} first',
42
48
  // objectstack#3821 — sharing-rule authoring widgets (object-ref /
43
49
  // recipient-picker / filter-condition). The recipient placeholder is keyed
44
50
  // PER TYPE rather than interpolating the enum value into an English
@@ -69,6 +75,60 @@ const FIELD_DEFAULTS: Record<string, string> = {
69
75
  'fields.filterCondition.jsonOnly': 'This criteria can only be edited as JSON',
70
76
  'fields.filterCondition.editAsJson': 'Edit as JSON',
71
77
  'fields.filterCondition.useVisualBuilder': 'Use visual builder',
78
+ // objectui#3342 — the tags widget's input hint. Used only when the field
79
+ // author declared no `placeholder` of their own (author declaration wins).
80
+ 'fields.tags.placeholder': 'Type and press Enter to add…',
81
+ // objectui#3406 — the accessible name of `TextAreaField`'s character
82
+ // counter, rendered only when the field declares `maxLength`. The visible
83
+ // `{n}/{max}` is digits; this sentence is what a screen reader speaks, and
84
+ // the element is `aria-live`, so before the key existed a non-English
85
+ // session heard English on every keystroke. Byte-identical to the literal
86
+ // it replaces, so a no-provider render is unchanged.
87
+ 'fields.textarea.characterCount': 'Character count: {{count}} of {{max}}',
88
+ // objectui#3408 — the NEAR-LIMIT warning, spoken by the counter's status
89
+ // region after the typist pauses and only once they are inside the last 10%
90
+ // (or last 20 characters) of the cap. The sentence above became the field's
91
+ // `aria-describedby` description in the same change; this one is the only
92
+ // thing that is ever announced mid-typing, which is why it says what is
93
+ // LEFT rather than repeating the whole count.
94
+ //
95
+ // Colon form, not "You have {{count}} characters remaining": `count` makes
96
+ // i18next attempt plural lookup (`_one` / `_few` / …) before the base key,
97
+ // and these packs declare the base key only — the same base-key fallback
98
+ // `characterCount` already relies on. A sentence whose grammar does not
99
+ // depend on the number stays correct at 1 without ten plural entries per
100
+ // pack, and matches the sibling key's shape.
101
+ 'fields.textarea.charactersRemaining': 'Characters remaining: {{count}}',
102
+ // objectui#3404 — the shared fullscreen long-text dialog
103
+ // (`FullscreenFieldEditor`, reached from `TextAreaField` and
104
+ // `RichTextField` when `mobile_fullscreen` is set).
105
+ //
106
+ // Deliberately the SAME `form.fullscreen.*` / `common.cancel` keys the
107
+ // built-in branch consumes (`FullscreenTextarea` in
108
+ // `components/src/renderers/form/form.tsx`, objectui#3272) rather than
109
+ // `fields.fullscreen.*` twins. One form-level setting
110
+ // (`ObjectFormSchema.mobile.fullscreenLongText`) projects onto BOTH render
111
+ // paths inside a single form, so a second copy of this copy is guaranteed
112
+ // drift — the shape #3231/#3263 had to undo — and would mean editing ten
113
+ // locale packs to say what they already say. `common.cancel` is already
114
+ // declared above and is reused here for exactly that reason.
115
+ //
116
+ // The values are byte-identical to the English literals they replaced, so
117
+ // a widget rendered with no I18nProvider (standalone/embedded hosts, and
118
+ // every bare-render test in this package) shows what it always did instead
119
+ // of a raw key. Measured before the change: with no provider mounted, bare
120
+ // `useObjectTranslation().t` returns the key itself
121
+ // (`common.cancel` → "common.cancel"), which is why this goes through
122
+ // `createSafeTranslation` like the built-in branch does.
123
+ 'form.fullscreen.title': 'Edit text',
124
+ 'form.fullscreen.description': 'Edit the full text value, then save or cancel your changes.',
125
+ 'form.fullscreen.done': 'Done',
126
+ // Kept as ONE interpolated sentence with a translated generic noun standing
127
+ // in for a missing label — the literal's own shape
128
+ // (`Edit ${label ?? 'text'} fullscreen`). A second, label-less sentence key
129
+ // would need ten new pack entries to say the same thing.
130
+ 'form.fullscreen.toggle': 'Edit {{label}} fullscreen',
131
+ 'form.fullscreen.textFallback': 'text',
72
132
  // objectui#2600 B5 — capability picker scope group headers.
73
133
  'capability.group.platform': 'Platform',
74
134
  'capability.group.org': 'Organization',
@@ -0,0 +1,75 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import React from 'react';
10
+
11
+ /**
12
+ * Adapt `SchemaRenderer`'s SDUI carrier onto the field-widget contract —
13
+ * objectui#3233, the producer-side half of retiring `schema` from
14
+ * {@link import('./widgets/types').FieldWidgetComponentProps}.
15
+ *
16
+ * ## Why this exists
17
+ *
18
+ * Two kinds of host render a registered field widget, and they name the
19
+ * metadata differently *for good reason*:
20
+ *
21
+ * - the **form renderer** (`renderFieldComponent` in
22
+ * `@object-ui/components`), the inline-edit hosts (`FieldEditWidget`) and
23
+ * the action dialogs pass **`field`** — the field metadata they already
24
+ * hold;
25
+ * - **`SchemaRenderer`** (`@object-ui/react`) passes **`schema`** — the
26
+ * authored node. That key is the UNIVERSAL SDUI contract *every* registered
27
+ * component receives (`element:*`, `page:*`, `object-grid`,
28
+ * `ReportRenderer` …). It is not being retired and must not be: it is what
29
+ * the renderer means by "the node you were rendered from".
30
+ *
31
+ * What WAS wrong is that both keys reached the widget, so ~30 widgets resolved
32
+ * their config as `field || schema` — one concept under two spellings, a second
33
+ * de-facto contract on the widget side (AGENTS.md #0.1) and precisely the shape
34
+ * that lets an AI-written widget pick one spelling and appear to work under one
35
+ * host while reading `undefined` under the other.
36
+ *
37
+ * The translation belongs at the seam, exactly once: here. Downstream `field`
38
+ * is the only carrier, and a widget reading `props.schema` no longer compiles.
39
+ *
40
+ * ## Guarantee
41
+ *
42
+ * Payload-preserving by construction: the node is forwarded **by reference**,
43
+ * so a widget's `field` is the very object it used to read through `schema`.
44
+ * `field` wins when a host supplies both (the form path always does, and it
45
+ * holds the resolved metadata rather than the raw node).
46
+ *
47
+ * `schema` is *consumed* here rather than forwarded — with the key gone from
48
+ * the contract, passing it on would only reach the DOM as a stray attribute
49
+ * through a widget's `...props` spread.
50
+ *
51
+ * ## For widget authors outside this repo
52
+ *
53
+ * Registering a widget raw means `SchemaRenderer` hands it `schema` and no
54
+ * `field`. Wrap it once at registration and the widget only ever implements
55
+ * `FieldWidgetComponentProps`:
56
+ *
57
+ * ```tsx
58
+ * import { withFieldCarrier } from '@object-ui/fields';
59
+ *
60
+ * ComponentRegistry.register('color', withFieldCarrier(ColorPickerField), {
61
+ * namespace: 'field',
62
+ * });
63
+ * ```
64
+ */
65
+ export function withFieldCarrier<P extends object>(
66
+ FieldWidget: React.ComponentType<P>,
67
+ ): React.FC<Record<string, any>> {
68
+ const FieldCarrierAdapter: React.FC<Record<string, any>> = ({ schema, field, ...props }) => (
69
+ <FieldWidget {...(props as unknown as P)} field={field ?? schema} />
70
+ );
71
+ FieldCarrierAdapter.displayName = `FieldCarrier(${
72
+ (FieldWidget as any).displayName || (FieldWidget as any).name || 'Component'
73
+ })`;
74
+ return FieldCarrierAdapter;
75
+ }