@object-ui/fields 16.1.0 → 17.2.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 (129) hide show
  1. package/CHANGELOG.md +626 -0
  2. package/dist/FieldEditWidget.d.ts +9 -2
  3. package/dist/__tests__/spec-symbol-batch7.test.d.ts +8 -0
  4. package/dist/__tests__/widget-props-contract.test.d.ts +8 -0
  5. package/dist/field-type-alias.d.ts +16 -0
  6. package/dist/index.cjs +5 -5
  7. package/dist/index.d.ts +14 -11
  8. package/dist/index.js +2426 -2100
  9. package/dist/widgets/AddressField.d.ts +2 -2
  10. package/dist/widgets/AutoNumberField.d.ts +2 -2
  11. package/dist/widgets/AvatarField.d.ts +2 -2
  12. package/dist/widgets/BooleanField.d.ts +2 -2
  13. package/dist/widgets/CapabilityMultiSelectField.d.ts +2 -2
  14. package/dist/widgets/CheckboxesField.d.ts +2 -2
  15. package/dist/widgets/CodeField.d.ts +2 -2
  16. package/dist/widgets/ColorField.d.ts +2 -2
  17. package/dist/widgets/CurrencyField.d.ts +2 -2
  18. package/dist/widgets/DateField.d.ts +2 -2
  19. package/dist/widgets/DateTimeField.d.ts +2 -2
  20. package/dist/widgets/EmailField.d.ts +2 -2
  21. package/dist/widgets/FileField.d.ts +2 -2
  22. package/dist/widgets/FilterConditionField.d.ts +34 -2
  23. package/dist/widgets/FormulaField.d.ts +2 -2
  24. package/dist/widgets/GeolocationField.d.ts +2 -2
  25. package/dist/widgets/GridField.d.ts +2 -2
  26. package/dist/widgets/ImageField.d.ts +2 -2
  27. package/dist/widgets/ImageLightbox.d.ts +30 -0
  28. package/dist/widgets/ImageLightbox.test.d.ts +1 -0
  29. package/dist/widgets/LocationField.d.ts +2 -2
  30. package/dist/widgets/LookupField.d.ts +2 -2
  31. package/dist/widgets/LookupField.hydration.test.d.ts +1 -0
  32. package/dist/widgets/LookupField.idField.test.d.ts +1 -0
  33. package/dist/widgets/MasterDetailField.d.ts +2 -2
  34. package/dist/widgets/MultiSelectField.d.ts +2 -2
  35. package/dist/widgets/NumberField.d.ts +2 -2
  36. package/dist/widgets/ObjectField.d.ts +2 -2
  37. package/dist/widgets/ObjectRefField.d.ts +2 -2
  38. package/dist/widgets/PasswordField.d.ts +2 -2
  39. package/dist/widgets/PercentField.d.ts +2 -2
  40. package/dist/widgets/PhoneField.d.ts +2 -2
  41. package/dist/widgets/QRCodeField.d.ts +2 -2
  42. package/dist/widgets/RadioField.d.ts +2 -2
  43. package/dist/widgets/RatingField.d.ts +2 -2
  44. package/dist/widgets/RecipientPickerField.d.ts +2 -2
  45. package/dist/widgets/RecipientPickerField.test.d.ts +0 -0
  46. package/dist/widgets/RichTextField.d.ts +2 -2
  47. package/dist/widgets/SelectField.d.ts +2 -2
  48. package/dist/widgets/SignatureField.d.ts +2 -2
  49. package/dist/widgets/SliderField.d.ts +2 -2
  50. package/dist/widgets/SummaryField.d.ts +2 -2
  51. package/dist/widgets/TagsField.d.ts +2 -2
  52. package/dist/widgets/TextAreaField.d.ts +2 -2
  53. package/dist/widgets/TextField.d.ts +2 -2
  54. package/dist/widgets/TimeField.d.ts +2 -2
  55. package/dist/widgets/UrlField.d.ts +2 -2
  56. package/dist/widgets/UserField.d.ts +2 -2
  57. package/dist/widgets/VectorField.d.ts +2 -2
  58. package/dist/widgets/__tests__/FilterConditionField.matchAll.test.d.ts +8 -0
  59. package/dist/widgets/__tests__/FilterConditionField.operators.test.d.ts +8 -0
  60. package/dist/widgets/file-value.d.ts +99 -0
  61. package/dist/widgets/file-value.test.d.ts +1 -0
  62. package/dist/widgets/types.d.ts +119 -3
  63. package/dist/widgets/useFieldTranslation.d.ts +2 -0
  64. package/package.json +9 -8
  65. package/src/FieldEditWidget.test.ts +74 -2
  66. package/src/FieldEditWidget.tsx +50 -8
  67. package/src/__tests__/spec-symbol-batch7.test.ts +112 -0
  68. package/src/__tests__/widget-props-contract.test.tsx +129 -0
  69. package/src/complex-widgets.test.tsx +3 -3
  70. package/src/datetime-widgets.test.tsx +2 -2
  71. package/src/field-type-alias.ts +100 -0
  72. package/src/index.tsx +97 -134
  73. package/src/widgets/AddressField.tsx +2 -2
  74. package/src/widgets/AutoNumberField.tsx +2 -2
  75. package/src/widgets/AvatarField.tsx +2 -2
  76. package/src/widgets/BooleanField.tsx +2 -2
  77. package/src/widgets/CapabilityMultiSelectField.tsx +4 -4
  78. package/src/widgets/CheckboxesField.tsx +12 -9
  79. package/src/widgets/CodeField.tsx +2 -2
  80. package/src/widgets/ColorField.tsx +2 -2
  81. package/src/widgets/CurrencyField.tsx +2 -2
  82. package/src/widgets/DateField.tsx +2 -2
  83. package/src/widgets/DateTimeField.tsx +2 -2
  84. package/src/widgets/EmailField.tsx +2 -2
  85. package/src/widgets/FileField.tsx +90 -59
  86. package/src/widgets/FilterConditionField.tsx +112 -12
  87. package/src/widgets/FormulaField.tsx +2 -2
  88. package/src/widgets/GeolocationField.tsx +2 -2
  89. package/src/widgets/GridField.tsx +3 -3
  90. package/src/widgets/ImageField.tsx +73 -35
  91. package/src/widgets/ImageLightbox.test.tsx +52 -0
  92. package/src/widgets/ImageLightbox.tsx +108 -0
  93. package/src/widgets/LocationField.tsx +2 -2
  94. package/src/widgets/LookupField.hydration.test.tsx +136 -0
  95. package/src/widgets/LookupField.idField.test.tsx +61 -0
  96. package/src/widgets/LookupField.tsx +116 -19
  97. package/src/widgets/MasterDetailField.tsx +2 -2
  98. package/src/widgets/MultiSelectField.tsx +10 -7
  99. package/src/widgets/NumberField.tsx +2 -2
  100. package/src/widgets/ObjectField.tsx +2 -2
  101. package/src/widgets/ObjectRefField.tsx +13 -8
  102. package/src/widgets/PasswordField.tsx +2 -2
  103. package/src/widgets/PeoplePicker.test.tsx +20 -0
  104. package/src/widgets/PeoplePicker.tsx +15 -3
  105. package/src/widgets/PercentField.tsx +2 -2
  106. package/src/widgets/PhoneField.tsx +2 -2
  107. package/src/widgets/QRCodeField.tsx +2 -2
  108. package/src/widgets/RadioField.tsx +10 -7
  109. package/src/widgets/RatingField.tsx +2 -2
  110. package/src/widgets/RecipientPickerField.test.tsx +157 -0
  111. package/src/widgets/RecipientPickerField.tsx +40 -20
  112. package/src/widgets/RichTextField.tsx +2 -2
  113. package/src/widgets/SelectField.tsx +5 -5
  114. package/src/widgets/SignatureField.tsx +2 -2
  115. package/src/widgets/SliderField.tsx +2 -2
  116. package/src/widgets/SummaryField.tsx +2 -2
  117. package/src/widgets/TagsField.tsx +3 -3
  118. package/src/widgets/TextAreaField.tsx +6 -3
  119. package/src/widgets/TextField.tsx +2 -2
  120. package/src/widgets/TimeField.tsx +2 -2
  121. package/src/widgets/UrlField.tsx +2 -2
  122. package/src/widgets/UserField.tsx +2 -2
  123. package/src/widgets/VectorField.tsx +2 -2
  124. package/src/widgets/__tests__/FilterConditionField.matchAll.test.ts +66 -0
  125. package/src/widgets/__tests__/FilterConditionField.operators.test.ts +142 -0
  126. package/src/widgets/file-value.test.ts +230 -0
  127. package/src/widgets/file-value.ts +246 -0
  128. package/src/widgets/types.ts +129 -6
  129. package/src/widgets/useFieldTranslation.ts +30 -0
@@ -1,10 +1,57 @@
1
- import { FieldMetadata } from '@object-ui/types';
1
+ import type { AriaAttributes, FocusEventHandler, MouseEventHandler } from 'react';
2
+ import type { DependsOnInput, FieldMetadata } from '@object-ui/types';
3
+
4
+ /**
5
+ * Props every field widget in this package receives at RUNTIME.
6
+ *
7
+ * Named `FieldWidgetComponentProps`, not `FieldWidgetProps` (objectui#3161,
8
+ * objectstack#4115 ledger batch 7), adopting the name `@object-ui/app-shell`
9
+ * settled on for the same split in objectui#3169: `@objectstack/spec/ui` owns
10
+ * `FieldWidgetProps` for the DECLARED widget-plugin contract — a zod object
11
+ * (`FieldWidgetPropsSchema`) a plugin manifest is validated against, with
12
+ * `field` narrowed to `{ name?, label?, type: <the FieldType enum> }` and
13
+ * `readonly` / `required` carrying `.default()`s. This is the React interface
14
+ * the widgets in this directory actually implement, against
15
+ * `@object-ui/types`'s much richer `FieldMetadata`.
16
+ *
17
+ * The two are NOT interchangeable, and the divergence is not cosmetic: the
18
+ * spec's contract names the error slot `error`, this one names it
19
+ * `errorMessage`, so a widget written to the spec's declared props renders no
20
+ * validation message here. That divergence is tracked in objectui#3222 and is
21
+ * deliberately NOT resolved here — but it is now *visible*: reading
22
+ * `props.error` or `props.required` off this type is a compile error rather
23
+ * than a silent `any`.
24
+ *
25
+ * ## Why there is no `[key: string]: any` (objectui#3221)
26
+ *
27
+ * This type used to end in an index signature, "load-bearing" for the widgets'
28
+ * `...props` spreads. It was also why every drift above was invisible: **a type
29
+ * that claims to have every key can never be reported as missing one** (the
30
+ * objectstack#4075 mechanism). `props.required` and `props.error` were legal
31
+ * reads typed `any` and always `undefined` at runtime; a misspelled prop
32
+ * (`readOnly` for `readonly`, `onchange` for `onChange`) compiled; and any
33
+ * structural/parity comparison against the type was useless *in principle*,
34
+ * which is why the batch-7 symbol guard was the only detector that could see
35
+ * the collision at all.
36
+ *
37
+ * It is replaced by the CLOSED set below: the controlled-input contract, the
38
+ * host plumbing the form renderer genuinely forwards, and a DOM pass-through
39
+ * allowance. `data-*` is an open family by design (it is open in HTML too) and
40
+ * is expressed as a template-literal index signature, which — unlike
41
+ * `[key: string]` — leaves `keyof` finite, so an undeclared prop still fails.
42
+ *
43
+ * Adding a key here is a contract change: say who produces it and who reads it.
44
+ */
45
+ export type FieldWidgetComponentProps<T = any> = {
46
+ /* ── The controlled-input contract every widget implements ─────────────── */
2
47
 
3
- export type FieldWidgetProps<T = any> = {
4
48
  value: T;
5
49
  onChange: (val: T) => void;
6
- // Use a looser type for field to avoid complex circular dependencies for now
7
- field: FieldMetadata;
50
+ /**
51
+ * The field's metadata. Deliberately the looser `@object-ui/types` shape
52
+ * rather than the spec's, to avoid a circular dependency for now.
53
+ */
54
+ field: FieldMetadata;
8
55
  readonly?: boolean;
9
56
  disabled?: boolean;
10
57
  className?: string;
@@ -15,5 +62,81 @@ export type FieldWidgetProps<T = any> = {
15
62
  * widgets ignore it.
16
63
  */
17
64
  onUploadingChange?: (uploading: boolean) => void;
18
- [key: string]: any;
19
- }
65
+
66
+ /* ── Host plumbing: what a rendering host actually forwards ─────────────── */
67
+
68
+ /**
69
+ * The raw authored node the widget was rendered from. Two hosts supply it:
70
+ * `SchemaRenderer` (`<Component schema={schema} {...schema} />`) and the
71
+ * form renderer's `renderFieldComponent` (`schema={props.field || ...}`),
72
+ * which is why ~25 widgets read `field || schema` for their config.
73
+ *
74
+ * This is a second carrier for what `field` already means, i.e. a de-facto
75
+ * second contract (AGENTS.md #0.1) — declared here so it is at least
76
+ * visible; converging the two is tracked separately.
77
+ */
78
+ schema?: FieldMetadata | Record<string, unknown>;
79
+ /**
80
+ * DataSource for widgets that query records (lookup / user / object-ref /
81
+ * recipient-picker / grid). Injected by the form renderer for the field
82
+ * types that need it, and passed directly by inline-edit hosts. Option
83
+ * widgets destructure it purely to keep it off their DOM spread.
84
+ *
85
+ * Left structural (`unknown`): `@object-ui/fields` must not depend on a
86
+ * concrete adapter — every consumer narrows it itself.
87
+ */
88
+ dataSource?: unknown;
89
+ /**
90
+ * Live sibling-field values driving cascading / role-gated options and
91
+ * dependent lookups (ADR-0058, #2215/#2284). The form renderer passes the
92
+ * in-progress record; widgets fall back to `SchemaRendererContext`.
93
+ */
94
+ dependentValues?: Record<string, unknown>;
95
+ /**
96
+ * Controlling field(s) that gate this field's option list. Normally declared
97
+ * on the field metadata; accepted as a prop so a host can drive the gate for
98
+ * a field it synthesised. Field metadata wins when both are present.
99
+ */
100
+ dependsOn?: DependsOnInput;
101
+ /**
102
+ * Hint shown when a dependency-gated option list is still waiting on its
103
+ * controlling field. Forwarded by the form renderer; the option widgets in
104
+ * this package currently discard it and render their own message instead.
105
+ */
106
+ emptyHint?: string;
107
+ /**
108
+ * Render as a single-line, borderless control for a grid cell (the inline
109
+ * editor and the line-item grid set it) instead of the full form layout.
110
+ */
111
+ compact?: boolean;
112
+ /**
113
+ * Receive the FULL selected record rather than just its id, so a host can
114
+ * auto-fill sibling fields from it (a line-item grid copying a product's
115
+ * price). When provided, the host owns the resulting value change.
116
+ */
117
+ onSelectRecord?: (record: Record<string, any>) => void;
118
+ /**
119
+ * Offer a "create new" affordance in a record picker, carrying whatever the
120
+ * user had typed. Also declarable on the field metadata.
121
+ */
122
+ onCreateNew?: (searchQuery: string) => void;
123
+
124
+ /* ── DOM pass-through: what `...props` may legitimately reach an input ──── */
125
+
126
+ id?: string;
127
+ /** react-hook-form's field name, spread in by the form renderer. */
128
+ name?: string;
129
+ autoFocus?: boolean;
130
+ tabIndex?: number;
131
+ onBlur?: FocusEventHandler<HTMLElement>;
132
+ onFocus?: FocusEventHandler<HTMLElement>;
133
+ onClick?: MouseEventHandler<HTMLElement>;
134
+ } & AriaAttributes & {
135
+ /**
136
+ * Arbitrary `data-*` attributes (test ids, analytics hooks). Open by design,
137
+ * but a template-literal key — `keyof` stays finite, so this does NOT
138
+ * reintroduce the index signature objectui#3221 removed: `props.required`
139
+ * is still an error, `props['data-testid']` is still fine.
140
+ */
141
+ [dataAttribute: `data-${string}`]: string | number | boolean | undefined;
142
+ };
@@ -39,6 +39,36 @@ 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
+ // objectstack#3821 — sharing-rule authoring widgets (object-ref /
43
+ // recipient-picker / filter-condition). The recipient placeholder is keyed
44
+ // PER TYPE rather than interpolating the enum value into an English
45
+ // sentence, which no locale could translate.
46
+ 'fields.objectRef.loading': 'Loading objects…',
47
+ 'fields.objectRef.placeholder': 'Select an object',
48
+ 'fields.objectRef.search': 'Search objects…',
49
+ 'fields.objectRef.empty': 'No objects found',
50
+ 'fields.recipient.selectTypeFirst': 'Select a recipient type first.',
51
+ 'fields.recipient.loading': 'Loading…',
52
+ 'fields.recipient.search': 'Search…',
53
+ 'fields.recipient.empty': 'No matches',
54
+ 'fields.recipient.select': 'Select a recipient',
55
+ 'fields.recipient.selectUser': 'Select a user',
56
+ 'fields.recipient.selectTeam': 'Select a team',
57
+ 'fields.recipient.selectBusinessUnit': 'Select a business unit',
58
+ 'fields.recipient.selectPosition': 'Select a position',
59
+ 'fields.recipient.selectUnitAndSubordinates': 'Select a business unit',
60
+ 'fields.filterCondition.selectObjectFirst': 'Select an object first.',
61
+ // objectstack#3896 — this used to be 'All records'. An empty criteria never
62
+ // meant "share everything"; it meant the predicate was missing, and the
63
+ // sharing evaluator failed open on it. Such a rule is now refused on save
64
+ // and shares nothing, so say that rather than advertise the old bug.
65
+ 'fields.filterCondition.noCriteria': 'No criteria — this rule shares nothing',
66
+ 'fields.filterCondition.criteriaRequired':
67
+ 'Add at least one condition. A rule with no criteria would share every record, so it cannot be saved.',
68
+ 'fields.filterCondition.invalidJson': 'Invalid JSON — the rule will match no records until fixed.',
69
+ 'fields.filterCondition.jsonOnly': 'This criteria can only be edited as JSON',
70
+ 'fields.filterCondition.editAsJson': 'Edit as JSON',
71
+ 'fields.filterCondition.useVisualBuilder': 'Use visual builder',
42
72
  // objectui#2600 B5 — capability picker scope group headers.
43
73
  'capability.group.platform': 'Platform',
44
74
  'capability.group.org': 'Organization',