@object-ui/fields 6.2.3 → 7.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 (97) hide show
  1. package/CHANGELOG.md +203 -0
  2. package/dist/complex-widgets.test.d.ts +1 -0
  3. package/dist/currency.d.ts +13 -0
  4. package/dist/datetime-widgets.test.d.ts +1 -0
  5. package/dist/field-type-coverage.test.d.ts +1 -0
  6. package/dist/index.cjs +4 -3
  7. package/dist/index.d.ts +300 -2
  8. package/dist/index.js +2460 -1274
  9. package/dist/standard-widgets.test.d.ts +0 -0
  10. package/dist/{packages/fields/src/widgets → widgets}/AddressField.d.ts +2 -1
  11. package/dist/{packages/fields/src/widgets → widgets}/AutoNumberField.d.ts +2 -1
  12. package/dist/{packages/fields/src/widgets → widgets}/AvatarField.d.ts +2 -1
  13. package/dist/{packages/fields/src/widgets → widgets}/BooleanField.d.ts +2 -1
  14. package/dist/widgets/CheckboxesField.d.ts +8 -0
  15. package/dist/{packages/fields/src/widgets → widgets}/CodeField.d.ts +2 -1
  16. package/dist/{packages/fields/src/widgets → widgets}/ColorField.d.ts +2 -1
  17. package/dist/{packages/fields/src/widgets → widgets}/CurrencyField.d.ts +2 -1
  18. package/dist/{packages/fields/src/widgets → widgets}/DateField.d.ts +2 -1
  19. package/dist/{packages/fields/src/widgets → widgets}/DateTimeField.d.ts +2 -1
  20. package/dist/{packages/fields/src/widgets → widgets}/EmailField.d.ts +2 -1
  21. package/dist/{packages/fields/src/widgets → widgets}/FileField.d.ts +2 -1
  22. package/dist/{packages/fields/src/widgets → widgets}/FormulaField.d.ts +2 -1
  23. package/dist/{packages/fields/src/widgets → widgets}/GeolocationField.d.ts +2 -1
  24. package/dist/widgets/GridField.d.ts +123 -0
  25. package/dist/widgets/GridField.test.d.ts +1 -0
  26. package/dist/{packages/fields/src/widgets → widgets}/ImageField.d.ts +2 -1
  27. package/dist/{packages/fields/src/widgets → widgets}/LocationField.d.ts +2 -1
  28. package/dist/{packages/fields/src/widgets → widgets}/LookupField.d.ts +2 -1
  29. package/dist/widgets/MarkdownContent.d.ts +9 -0
  30. package/dist/{packages/fields/src/widgets → widgets}/MasterDetailField.d.ts +2 -1
  31. package/dist/widgets/MultiSelectField.d.ts +9 -0
  32. package/dist/{packages/fields/src/widgets → widgets}/NumberField.d.ts +2 -1
  33. package/dist/{packages/fields/src/widgets → widgets}/ObjectField.d.ts +2 -1
  34. package/dist/{packages/fields/src/widgets → widgets}/PasswordField.d.ts +2 -1
  35. package/dist/{packages/fields/src/widgets → widgets}/PercentField.d.ts +2 -1
  36. package/dist/{packages/fields/src/widgets → widgets}/PhoneField.d.ts +2 -1
  37. package/dist/{packages/fields/src/widgets → widgets}/QRCodeField.d.ts +2 -1
  38. package/dist/widgets/RadioField.d.ts +8 -0
  39. package/dist/{packages/fields/src/widgets → widgets}/RatingField.d.ts +2 -1
  40. package/dist/{packages/fields/src/widgets → widgets}/RecordPickerDialog.d.ts +8 -2
  41. package/dist/{packages/fields/src/widgets → widgets}/RichTextField.d.ts +2 -1
  42. package/dist/{packages/fields/src/widgets → widgets}/SelectField.d.ts +2 -1
  43. package/dist/{packages/fields/src/widgets → widgets}/SignatureField.d.ts +2 -1
  44. package/dist/{packages/fields/src/widgets → widgets}/SliderField.d.ts +2 -1
  45. package/dist/{packages/fields/src/widgets → widgets}/SummaryField.d.ts +2 -1
  46. package/dist/widgets/TagsField.d.ts +8 -0
  47. package/dist/{packages/fields/src/widgets → widgets}/TextAreaField.d.ts +2 -1
  48. package/dist/{packages/fields/src/widgets → widgets}/TextField.d.ts +2 -1
  49. package/dist/{packages/fields/src/widgets → widgets}/TimeField.d.ts +2 -1
  50. package/dist/{packages/fields/src/widgets → widgets}/UrlField.d.ts +2 -1
  51. package/dist/{packages/fields/src/widgets → widgets}/UserField.d.ts +2 -1
  52. package/dist/{packages/fields/src/widgets → widgets}/VectorField.d.ts +2 -1
  53. package/dist/widgets/deriveLookupColumns.d.ts +36 -0
  54. package/dist/widgets/deriveLookupColumns.test.d.ts +1 -0
  55. package/dist/widgets/recentLookups.d.ts +13 -0
  56. package/dist/widgets/recentLookups.test.d.ts +1 -0
  57. package/dist/{packages/fields/src/widgets → widgets}/types.d.ts +1 -1
  58. package/package.json +14 -11
  59. package/src/__tests__/registry-collision.test.ts +91 -0
  60. package/src/__tests__/resolveFieldCurrency.test.ts +20 -0
  61. package/src/coerce-safe-value.test.ts +34 -0
  62. package/src/complex-widgets.test.tsx +58 -7
  63. package/src/currency.ts +14 -0
  64. package/src/field-type-coverage.test.ts +65 -0
  65. package/src/index.tsx +372 -57
  66. package/src/widgets/CheckboxesField.tsx +53 -0
  67. package/src/widgets/CurrencyField.tsx +5 -1
  68. package/src/widgets/GridField.test.tsx +309 -0
  69. package/src/widgets/GridField.tsx +907 -33
  70. package/src/widgets/LookupField.tsx +258 -107
  71. package/src/widgets/MarkdownContent.tsx +19 -0
  72. package/src/widgets/MultiSelectField.tsx +59 -0
  73. package/src/widgets/PercentField.tsx +19 -7
  74. package/src/widgets/RadioField.tsx +41 -0
  75. package/src/widgets/RecordPickerDialog.tsx +22 -18
  76. package/src/widgets/SelectField.tsx +23 -2
  77. package/src/widgets/TagsField.tsx +67 -0
  78. package/src/widgets/deriveLookupColumns.test.ts +74 -0
  79. package/src/widgets/deriveLookupColumns.ts +104 -0
  80. package/src/widgets/recentLookups.test.ts +45 -0
  81. package/src/widgets/recentLookups.ts +47 -0
  82. package/src/widgets/useFieldTranslation.ts +30 -0
  83. package/dist/packages/fields/src/fields-edge-cases.stories.d.ts +0 -45
  84. package/dist/packages/fields/src/fields.stories.d.ts +0 -56
  85. package/dist/packages/fields/src/index.d.ts +0 -286
  86. package/dist/packages/fields/src/record-picker-dialog.stories.d.ts +0 -25
  87. package/dist/packages/fields/src/widgets/GridField.d.ts +0 -7
  88. package/src/fields-edge-cases.stories.tsx +0 -407
  89. package/src/fields.stories.tsx +0 -566
  90. package/src/record-picker-dialog.stories.tsx +0 -160
  91. /package/dist/{packages/fields/src/__tests__/validation-feedback.test.d.ts → __tests__/registry-collision.test.d.ts} +0 -0
  92. /package/dist/{packages/fields/src/complex-widgets.test.d.ts → __tests__/resolveFieldCurrency.test.d.ts} +0 -0
  93. /package/dist/{packages/fields/src/standard-widgets.test.d.ts → __tests__/validation-feedback.test.d.ts} +0 -0
  94. /package/dist/{packages/fields/src/datetime-widgets.test.d.ts → coerce-safe-value.test.d.ts} +0 -0
  95. /package/dist/{packages/fields/src/widgets → widgets}/ImageCropperDialog.d.ts +0 -0
  96. /package/dist/{packages/fields/src/widgets → widgets}/_cell-renderer-bridge.d.ts +0 -0
  97. /package/dist/{packages/fields/src/widgets → widgets}/useFieldTranslation.d.ts +0 -0
@@ -1,407 +0,0 @@
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 type { Meta, StoryObj } from '@storybook/react';
10
- import React, { useState } from 'react';
11
- import {
12
- TextField,
13
- NumberField,
14
- SelectField,
15
- CurrencyField,
16
- EmailField,
17
- TextAreaField,
18
- BooleanField,
19
- UrlField,
20
- } from './index';
21
- import { FieldMetadata } from '@object-ui/types';
22
-
23
- /**
24
- * **Field Widgets – Edge Cases** from `@object-ui/fields`.
25
- *
26
- * Stories that exercise boundary conditions: empty values, overflow text,
27
- * RTL content, emoji-only input, error states, disabled state, and readonly
28
- * variants across multiple field types.
29
- */
30
- const meta = {
31
- title: 'Fields/Edge Cases',
32
- parameters: {
33
- layout: 'centered',
34
- },
35
- tags: ['autodocs'],
36
- argTypes: {
37
- readonly: { control: 'boolean', description: 'Toggle readonly mode' },
38
- disabled: { control: 'boolean', description: 'Toggle disabled mode' },
39
- },
40
- } satisfies Meta;
41
-
42
- export default meta;
43
- type Story = StoryObj<any>;
44
-
45
- // Wrapper to handle state since these are controlled components
46
- const FieldWrapper = ({
47
- Component,
48
- field,
49
- initialValue,
50
- readonly = false,
51
- disabled = false,
52
- errorMessage,
53
- }: {
54
- Component: any;
55
- field: Partial<FieldMetadata>;
56
- initialValue?: any;
57
- readonly?: boolean;
58
- disabled?: boolean;
59
- errorMessage?: string;
60
- }) => {
61
- const [value, setValue] = useState(initialValue);
62
-
63
- const fullField = {
64
- name: 'test_field',
65
- label: 'Test Field',
66
- type: 'text',
67
- ...field,
68
- } as FieldMetadata;
69
-
70
- return (
71
- <div className="w-[350px] space-y-2 p-4 border rounded-lg shadow-sm bg-white">
72
- <div className="flex justify-between items-center mb-2">
73
- <label className="text-sm font-semibold text-gray-700">{fullField.label}</label>
74
- {readonly && (
75
- <span className="text-[10px] bg-slate-100 px-2 py-0.5 rounded text-slate-500 font-medium">
76
- Read Only
77
- </span>
78
- )}
79
- {disabled && (
80
- <span className="text-[10px] bg-slate-100 px-2 py-0.5 rounded text-slate-500 font-medium">
81
- Disabled
82
- </span>
83
- )}
84
- </div>
85
-
86
- <Component
87
- value={value}
88
- onChange={(val: any) => {
89
- console.log('onChange', val);
90
- setValue(val);
91
- }}
92
- field={fullField}
93
- readonly={readonly}
94
- disabled={disabled}
95
- errorMessage={errorMessage}
96
- />
97
-
98
- {errorMessage && (
99
- <div className="text-xs text-destructive mt-1">{errorMessage}</div>
100
- )}
101
-
102
- {!readonly && (
103
- <div className="text-xs text-muted-foreground mt-4 pt-2 border-t font-mono overflow-hidden text-ellipsis">
104
- Value: {JSON.stringify(value)}
105
- </div>
106
- )}
107
- </div>
108
- );
109
- };
110
-
111
- // ---------------------------------------------------------------------------
112
- // 1. Empty / Null / Undefined Values
113
- // ---------------------------------------------------------------------------
114
-
115
- export const EmptyText: Story = {
116
- name: 'Empty – Text (undefined)',
117
- render: () => (
118
- <FieldWrapper
119
- Component={TextField}
120
- field={{ label: 'Empty Text', type: 'text', placeholder: 'Type here…' }}
121
- initialValue={undefined}
122
- />
123
- ),
124
- };
125
-
126
- export const NullNumber: Story = {
127
- name: 'Empty – Number (null)',
128
- render: () => (
129
- <FieldWrapper
130
- Component={NumberField}
131
- field={{ label: 'Null Number', type: 'number', precision: 2 }}
132
- initialValue={null}
133
- />
134
- ),
135
- };
136
-
137
- export const EmptySelect: Story = {
138
- name: 'Empty – Select (empty string)',
139
- render: () => (
140
- <FieldWrapper
141
- Component={SelectField}
142
- field={{
143
- label: 'No Selection',
144
- type: 'select',
145
- options: [
146
- { label: 'Option A', value: 'a' },
147
- { label: 'Option B', value: 'b' },
148
- ],
149
- }}
150
- initialValue=""
151
- />
152
- ),
153
- };
154
-
155
- // ---------------------------------------------------------------------------
156
- // 2. Very Long Strings (overflow handling)
157
- // ---------------------------------------------------------------------------
158
-
159
- const LONG_STRING =
160
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '.repeat(20).trim();
161
-
162
- export const LongText: Story = {
163
- name: 'Overflow – Long Text',
164
- render: () => (
165
- <FieldWrapper
166
- Component={TextField}
167
- field={{ label: 'Long Value', type: 'text' }}
168
- initialValue={LONG_STRING}
169
- />
170
- ),
171
- };
172
-
173
- export const LongTextArea: Story = {
174
- name: 'Overflow – Long TextArea',
175
- render: () => (
176
- <FieldWrapper
177
- Component={TextAreaField}
178
- field={{ label: 'Long TextArea', type: 'textarea', rows: 4 }}
179
- initialValue={LONG_STRING}
180
- />
181
- ),
182
- };
183
-
184
- export const LongUrl: Story = {
185
- name: 'Overflow – Long URL',
186
- render: () => (
187
- <FieldWrapper
188
- Component={UrlField}
189
- field={{ label: 'Long URL', type: 'url' }}
190
- initialValue={`https://example.com/${'segment/'.repeat(50)}`}
191
- />
192
- ),
193
- };
194
-
195
- // ---------------------------------------------------------------------------
196
- // 3. RTL Text (Arabic)
197
- // ---------------------------------------------------------------------------
198
-
199
- export const RTLText: Story = {
200
- name: 'RTL – Arabic Text',
201
- render: () => (
202
- <FieldWrapper
203
- Component={TextField}
204
- field={{ label: 'Arabic Input', type: 'text' }}
205
- initialValue="مرحبا بالعالم — هذا نص عربي طويل لاختبار الاتجاه"
206
- />
207
- ),
208
- };
209
-
210
- export const RTLTextArea: Story = {
211
- name: 'RTL – Arabic TextArea',
212
- render: () => (
213
- <FieldWrapper
214
- Component={TextAreaField}
215
- field={{ label: 'Arabic TextArea', type: 'textarea', rows: 3 }}
216
- initialValue="هذا نص عربي يمتد على عدة أسطر.\nالسطر الثاني هنا.\nالسطر الثالث."
217
- />
218
- ),
219
- };
220
-
221
- // ---------------------------------------------------------------------------
222
- // 4. Emoji-Only Values
223
- // ---------------------------------------------------------------------------
224
-
225
- export const EmojiText: Story = {
226
- name: 'Emoji – Text Field',
227
- render: () => (
228
- <FieldWrapper
229
- Component={TextField}
230
- field={{ label: 'Emoji Only', type: 'text' }}
231
- initialValue="🚀🔥✨💯🎉🌍"
232
- />
233
- ),
234
- };
235
-
236
- export const EmojiEmail: Story = {
237
- name: 'Emoji – Email Field (invalid)',
238
- render: () => (
239
- <FieldWrapper
240
- Component={EmailField}
241
- field={{ label: 'Emoji Email', type: 'email' }}
242
- initialValue="🚀@emoji.com"
243
- />
244
- ),
245
- };
246
-
247
- // ---------------------------------------------------------------------------
248
- // 5. Error State (with errorMessage prop)
249
- // ---------------------------------------------------------------------------
250
-
251
- export const ErrorText: Story = {
252
- name: 'Error – Text Field',
253
- render: () => (
254
- <FieldWrapper
255
- Component={TextField}
256
- field={{ label: 'Name', type: 'text', placeholder: 'Required field' }}
257
- initialValue=""
258
- errorMessage="This field is required."
259
- />
260
- ),
261
- };
262
-
263
- export const ErrorNumber: Story = {
264
- name: 'Error – Number Field',
265
- render: () => (
266
- <FieldWrapper
267
- Component={NumberField}
268
- field={{ label: 'Age', type: 'number', precision: 0 }}
269
- initialValue={-5}
270
- errorMessage="Value must be a positive number."
271
- />
272
- ),
273
- };
274
-
275
- export const ErrorSelect: Story = {
276
- name: 'Error – Select Field',
277
- render: () => (
278
- <FieldWrapper
279
- Component={SelectField}
280
- field={{
281
- label: 'Priority',
282
- type: 'select',
283
- options: [
284
- { label: 'Low', value: 'low' },
285
- { label: 'Medium', value: 'medium' },
286
- { label: 'High', value: 'high' },
287
- ],
288
- }}
289
- initialValue=""
290
- errorMessage="Please select a priority."
291
- />
292
- ),
293
- };
294
-
295
- // ---------------------------------------------------------------------------
296
- // 6. Disabled State
297
- // ---------------------------------------------------------------------------
298
-
299
- export const DisabledText: Story = {
300
- name: 'Disabled – Text Field',
301
- render: () => (
302
- <FieldWrapper
303
- Component={TextField}
304
- field={{ label: 'Locked Name', type: 'text' }}
305
- initialValue="Cannot change this"
306
- disabled={true}
307
- />
308
- ),
309
- };
310
-
311
- export const DisabledNumber: Story = {
312
- name: 'Disabled – Number Field',
313
- render: () => (
314
- <FieldWrapper
315
- Component={NumberField}
316
- field={{ label: 'Locked Qty', type: 'number', precision: 0 }}
317
- initialValue={100}
318
- disabled={true}
319
- />
320
- ),
321
- };
322
-
323
- export const DisabledSelect: Story = {
324
- name: 'Disabled – Select Field',
325
- render: () => (
326
- <FieldWrapper
327
- Component={SelectField}
328
- field={{
329
- label: 'Locked Status',
330
- type: 'select',
331
- options: [
332
- { label: 'Active', value: 'active' },
333
- { label: 'Inactive', value: 'inactive' },
334
- ],
335
- }}
336
- initialValue="active"
337
- disabled={true}
338
- />
339
- ),
340
- };
341
-
342
- export const DisabledBoolean: Story = {
343
- name: 'Disabled – Boolean Field',
344
- render: () => (
345
- <FieldWrapper
346
- Component={BooleanField}
347
- field={{ label: 'Locked Toggle', type: 'boolean' }}
348
- initialValue={true}
349
- disabled={true}
350
- />
351
- ),
352
- };
353
-
354
- // ---------------------------------------------------------------------------
355
- // 7. Readonly State with Various Field Types
356
- // ---------------------------------------------------------------------------
357
-
358
- export const ReadonlyGallery: Story = {
359
- name: 'Readonly – Multiple Fields',
360
- render: () => (
361
- <div className="flex flex-col gap-4">
362
- <FieldWrapper
363
- Component={TextField}
364
- field={{ label: 'Read Only Text', type: 'text' }}
365
- initialValue="Immutable value"
366
- readonly={true}
367
- />
368
- <FieldWrapper
369
- Component={NumberField}
370
- field={{ label: 'Read Only Number', type: 'number', precision: 2 }}
371
- initialValue={3.14}
372
- readonly={true}
373
- />
374
- <FieldWrapper
375
- Component={CurrencyField}
376
- field={{ label: 'Read Only Currency', type: 'currency', currency: 'USD', precision: 2 }}
377
- initialValue={9999.99}
378
- readonly={true}
379
- />
380
- <FieldWrapper
381
- Component={SelectField}
382
- field={{
383
- label: 'Read Only Select',
384
- type: 'select',
385
- options: [
386
- { label: 'Active', value: 'active' },
387
- { label: 'Archived', value: 'archived' },
388
- ],
389
- }}
390
- initialValue="active"
391
- readonly={true}
392
- />
393
- <FieldWrapper
394
- Component={BooleanField}
395
- field={{ label: 'Read Only Boolean', type: 'boolean' }}
396
- initialValue={false}
397
- readonly={true}
398
- />
399
- <FieldWrapper
400
- Component={EmailField}
401
- field={{ label: 'Read Only Email', type: 'email' }}
402
- initialValue="admin@example.com"
403
- readonly={true}
404
- />
405
- </div>
406
- ),
407
- };