@wordpress/dataviews 9.0.1-next.6f42e1382.0 → 9.0.1-next.a730c9c8c.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 (175) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/README.md +106 -11
  3. package/build/components/dataviews-filters/input-widget.js +48 -4
  4. package/build/components/dataviews-filters/input-widget.js.map +1 -1
  5. package/build/dataform-controls/array.js +9 -7
  6. package/build/dataform-controls/array.js.map +1 -1
  7. package/build/dataform-controls/checkbox.js +31 -20
  8. package/build/dataform-controls/checkbox.js.map +1 -1
  9. package/build/dataform-controls/color.js +29 -24
  10. package/build/dataform-controls/color.js.map +1 -1
  11. package/build/dataform-controls/date.js +32 -24
  12. package/build/dataform-controls/date.js.map +1 -1
  13. package/build/dataform-controls/datetime.js +133 -19
  14. package/build/dataform-controls/datetime.js.map +1 -1
  15. package/build/dataform-controls/email.js +7 -1
  16. package/build/dataform-controls/email.js.map +1 -1
  17. package/build/dataform-controls/index.js +23 -0
  18. package/build/dataform-controls/index.js.map +1 -1
  19. package/build/dataform-controls/integer.js +47 -34
  20. package/build/dataform-controls/integer.js.map +1 -1
  21. package/build/dataform-controls/radio.js +42 -9
  22. package/build/dataform-controls/radio.js.map +1 -1
  23. package/build/dataform-controls/relative-date-control.js +6 -10
  24. package/build/dataform-controls/relative-date-control.js.map +1 -1
  25. package/build/dataform-controls/select.js +41 -10
  26. package/build/dataform-controls/select.js.map +1 -1
  27. package/build/dataform-controls/telephone.js +7 -1
  28. package/build/dataform-controls/telephone.js.map +1 -1
  29. package/build/dataform-controls/text.js +14 -2
  30. package/build/dataform-controls/text.js.map +1 -1
  31. package/build/dataform-controls/textarea.js +33 -20
  32. package/build/dataform-controls/textarea.js.map +1 -1
  33. package/build/dataform-controls/toggle-group.js +36 -6
  34. package/build/dataform-controls/toggle-group.js.map +1 -1
  35. package/build/dataform-controls/toggle.js +33 -22
  36. package/build/dataform-controls/toggle.js.map +1 -1
  37. package/build/dataform-controls/url.js +7 -1
  38. package/build/dataform-controls/url.js.map +1 -1
  39. package/build/dataform-controls/utils/validated-input.js +34 -32
  40. package/build/dataform-controls/utils/validated-input.js.map +1 -1
  41. package/build/dataforms-layouts/panel/dropdown.js +10 -14
  42. package/build/dataforms-layouts/panel/dropdown.js.map +1 -1
  43. package/build/dataforms-layouts/panel/index.js +24 -11
  44. package/build/dataforms-layouts/panel/index.js.map +1 -1
  45. package/build/dataforms-layouts/panel/modal.js +22 -27
  46. package/build/dataforms-layouts/panel/modal.js.map +1 -1
  47. package/build/dataforms-layouts/panel/summary-button.js +67 -0
  48. package/build/dataforms-layouts/panel/summary-button.js.map +1 -0
  49. package/build/normalize-fields.js +17 -0
  50. package/build/normalize-fields.js.map +1 -1
  51. package/build/types.js.map +1 -1
  52. package/build-module/components/dataviews-filters/input-widget.js +48 -4
  53. package/build-module/components/dataviews-filters/input-widget.js.map +1 -1
  54. package/build-module/dataform-controls/array.js +9 -7
  55. package/build-module/dataform-controls/array.js.map +1 -1
  56. package/build-module/dataform-controls/checkbox.js +31 -21
  57. package/build-module/dataform-controls/checkbox.js.map +1 -1
  58. package/build-module/dataform-controls/color.js +28 -24
  59. package/build-module/dataform-controls/color.js.map +1 -1
  60. package/build-module/dataform-controls/date.js +32 -24
  61. package/build-module/dataform-controls/date.js.map +1 -1
  62. package/build-module/dataform-controls/datetime.js +135 -21
  63. package/build-module/dataform-controls/datetime.js.map +1 -1
  64. package/build-module/dataform-controls/email.js +7 -1
  65. package/build-module/dataform-controls/email.js.map +1 -1
  66. package/build-module/dataform-controls/index.js +23 -0
  67. package/build-module/dataform-controls/index.js.map +1 -1
  68. package/build-module/dataform-controls/integer.js +46 -34
  69. package/build-module/dataform-controls/integer.js.map +1 -1
  70. package/build-module/dataform-controls/radio.js +44 -11
  71. package/build-module/dataform-controls/radio.js.map +1 -1
  72. package/build-module/dataform-controls/relative-date-control.js +6 -10
  73. package/build-module/dataform-controls/relative-date-control.js.map +1 -1
  74. package/build-module/dataform-controls/select.js +43 -12
  75. package/build-module/dataform-controls/select.js.map +1 -1
  76. package/build-module/dataform-controls/telephone.js +7 -1
  77. package/build-module/dataform-controls/telephone.js.map +1 -1
  78. package/build-module/dataform-controls/text.js +14 -2
  79. package/build-module/dataform-controls/text.js.map +1 -1
  80. package/build-module/dataform-controls/textarea.js +32 -20
  81. package/build-module/dataform-controls/textarea.js.map +1 -1
  82. package/build-module/dataform-controls/toggle-group.js +38 -8
  83. package/build-module/dataform-controls/toggle-group.js.map +1 -1
  84. package/build-module/dataform-controls/toggle.js +33 -23
  85. package/build-module/dataform-controls/toggle.js.map +1 -1
  86. package/build-module/dataform-controls/url.js +7 -1
  87. package/build-module/dataform-controls/url.js.map +1 -1
  88. package/build-module/dataform-controls/utils/validated-input.js +34 -33
  89. package/build-module/dataform-controls/utils/validated-input.js.map +1 -1
  90. package/build-module/dataforms-layouts/panel/dropdown.js +10 -15
  91. package/build-module/dataforms-layouts/panel/dropdown.js.map +1 -1
  92. package/build-module/dataforms-layouts/panel/index.js +24 -11
  93. package/build-module/dataforms-layouts/panel/index.js.map +1 -1
  94. package/build-module/dataforms-layouts/panel/modal.js +22 -28
  95. package/build-module/dataforms-layouts/panel/modal.js.map +1 -1
  96. package/build-module/dataforms-layouts/panel/summary-button.js +60 -0
  97. package/build-module/dataforms-layouts/panel/summary-button.js.map +1 -0
  98. package/build-module/normalize-fields.js +15 -0
  99. package/build-module/normalize-fields.js.map +1 -1
  100. package/build-module/types.js.map +1 -1
  101. package/build-types/components/dataform/stories/index.story.d.ts +3 -0
  102. package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
  103. package/build-types/components/dataviews/stories/fixtures.d.ts +4 -2
  104. package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
  105. package/build-types/components/dataviews-filters/input-widget.d.ts.map +1 -1
  106. package/build-types/dataform-controls/array.d.ts.map +1 -1
  107. package/build-types/dataform-controls/checkbox.d.ts.map +1 -1
  108. package/build-types/dataform-controls/color.d.ts.map +1 -1
  109. package/build-types/dataform-controls/date.d.ts.map +1 -1
  110. package/build-types/dataform-controls/datetime.d.ts.map +1 -1
  111. package/build-types/dataform-controls/email.d.ts.map +1 -1
  112. package/build-types/dataform-controls/index.d.ts +1 -1
  113. package/build-types/dataform-controls/index.d.ts.map +1 -1
  114. package/build-types/dataform-controls/integer.d.ts.map +1 -1
  115. package/build-types/dataform-controls/radio.d.ts.map +1 -1
  116. package/build-types/dataform-controls/relative-date-control.d.ts +6 -5
  117. package/build-types/dataform-controls/relative-date-control.d.ts.map +1 -1
  118. package/build-types/dataform-controls/select.d.ts.map +1 -1
  119. package/build-types/dataform-controls/telephone.d.ts.map +1 -1
  120. package/build-types/dataform-controls/text.d.ts +1 -1
  121. package/build-types/dataform-controls/text.d.ts.map +1 -1
  122. package/build-types/dataform-controls/textarea.d.ts +1 -1
  123. package/build-types/dataform-controls/textarea.d.ts.map +1 -1
  124. package/build-types/dataform-controls/toggle-group.d.ts.map +1 -1
  125. package/build-types/dataform-controls/toggle.d.ts.map +1 -1
  126. package/build-types/dataform-controls/url.d.ts.map +1 -1
  127. package/build-types/dataform-controls/utils/validated-input.d.ts +4 -4
  128. package/build-types/dataform-controls/utils/validated-input.d.ts.map +1 -1
  129. package/build-types/dataforms-layouts/panel/dropdown.d.ts +2 -1
  130. package/build-types/dataforms-layouts/panel/dropdown.d.ts.map +1 -1
  131. package/build-types/dataforms-layouts/panel/index.d.ts.map +1 -1
  132. package/build-types/dataforms-layouts/panel/modal.d.ts +2 -1
  133. package/build-types/dataforms-layouts/panel/modal.d.ts.map +1 -1
  134. package/build-types/dataforms-layouts/panel/summary-button.d.ts +15 -0
  135. package/build-types/dataforms-layouts/panel/summary-button.d.ts.map +1 -0
  136. package/build-types/field-types/stories/index.story.d.ts.map +1 -1
  137. package/build-types/normalize-fields.d.ts +3 -0
  138. package/build-types/normalize-fields.d.ts.map +1 -1
  139. package/build-types/types.d.ts +66 -3
  140. package/build-types/types.d.ts.map +1 -1
  141. package/build-wp/index.js +1468 -1422
  142. package/package.json +16 -15
  143. package/src/components/dataform/stories/index.story.tsx +436 -7
  144. package/src/components/dataviews/stories/fixtures.tsx +99 -41
  145. package/src/components/dataviews/stories/index.story.tsx +1 -1
  146. package/src/components/dataviews-filters/input-widget.tsx +44 -5
  147. package/src/components/dataviews-picker/stories/index.story.tsx +1 -1
  148. package/src/dataform-controls/array.tsx +4 -6
  149. package/src/dataform-controls/checkbox.tsx +41 -24
  150. package/src/dataform-controls/color.tsx +33 -24
  151. package/src/dataform-controls/date.tsx +47 -21
  152. package/src/dataform-controls/datetime.tsx +171 -23
  153. package/src/dataform-controls/email.tsx +9 -1
  154. package/src/dataform-controls/index.tsx +26 -0
  155. package/src/dataform-controls/integer.tsx +82 -49
  156. package/src/dataform-controls/radio.tsx +53 -11
  157. package/src/dataform-controls/relative-date-control.tsx +11 -10
  158. package/src/dataform-controls/select.tsx +53 -10
  159. package/src/dataform-controls/telephone.tsx +9 -1
  160. package/src/dataform-controls/text.tsx +18 -1
  161. package/src/dataform-controls/textarea.tsx +38 -24
  162. package/src/dataform-controls/toggle-group.tsx +50 -10
  163. package/src/dataform-controls/toggle.tsx +41 -24
  164. package/src/dataform-controls/url.tsx +9 -1
  165. package/src/dataform-controls/utils/validated-input.tsx +50 -50
  166. package/src/dataforms-layouts/panel/dropdown.tsx +12 -23
  167. package/src/dataforms-layouts/panel/index.tsx +39 -16
  168. package/src/dataforms-layouts/panel/modal.tsx +24 -30
  169. package/src/dataforms-layouts/panel/summary-button.tsx +92 -0
  170. package/src/field-types/stories/index.story.tsx +89 -1
  171. package/src/normalize-fields.ts +18 -0
  172. package/src/test/filter-and-sort-data-view.js +148 -138
  173. package/src/test/normalize-fields.ts +114 -0
  174. package/src/types.ts +73 -3
  175. package/tsconfig.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/dataviews",
3
- "version": "9.0.1-next.6f42e1382.0",
3
+ "version": "9.0.1-next.a730c9c8c.0",
4
4
  "description": "DataViews is a component that provides an API to render datasets using different types of layouts (table, grid, list, etc.).",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -46,22 +46,23 @@
46
46
  "dependencies": {
47
47
  "@ariakit/react": "^0.4.15",
48
48
  "@babel/runtime": "7.25.7",
49
- "@wordpress/base-styles": "^6.6.1-next.6f42e1382.0",
50
- "@wordpress/components": "^30.3.2-next.6f42e1382.0",
51
- "@wordpress/compose": "^7.30.1-next.6f42e1382.0",
52
- "@wordpress/data": "^10.30.1-next.6f42e1382.0",
53
- "@wordpress/date": "^5.30.1-next.6f42e1382.0",
54
- "@wordpress/element": "^6.30.1-next.6f42e1382.0",
55
- "@wordpress/i18n": "^6.3.1-next.6f42e1382.0",
56
- "@wordpress/icons": "^10.30.1-next.6f42e1382.0",
57
- "@wordpress/keycodes": "^4.30.1-next.6f42e1382.0",
58
- "@wordpress/primitives": "^4.30.1-next.6f42e1382.0",
59
- "@wordpress/private-apis": "^1.30.1-next.6f42e1382.0",
60
- "@wordpress/url": "^4.30.1-next.6f42e1382.0",
61
- "@wordpress/warning": "^3.30.1-next.6f42e1382.0",
49
+ "@wordpress/base-styles": "^6.6.1-next.a730c9c8c.0",
50
+ "@wordpress/components": "^30.3.2-next.a730c9c8c.0",
51
+ "@wordpress/compose": "^7.30.1-next.a730c9c8c.0",
52
+ "@wordpress/data": "^10.30.1-next.a730c9c8c.0",
53
+ "@wordpress/date": "^5.30.1-next.a730c9c8c.0",
54
+ "@wordpress/element": "^6.30.1-next.a730c9c8c.0",
55
+ "@wordpress/i18n": "^6.3.1-next.a730c9c8c.0",
56
+ "@wordpress/icons": "^10.30.1-next.a730c9c8c.0",
57
+ "@wordpress/keycodes": "^4.30.1-next.a730c9c8c.0",
58
+ "@wordpress/primitives": "^4.30.1-next.a730c9c8c.0",
59
+ "@wordpress/private-apis": "^1.30.1-next.a730c9c8c.0",
60
+ "@wordpress/url": "^4.30.1-next.a730c9c8c.0",
61
+ "@wordpress/warning": "^3.30.1-next.a730c9c8c.0",
62
62
  "clsx": "^2.1.1",
63
63
  "colord": "^2.7.0",
64
64
  "date-fns": "^4.1.0",
65
+ "deepmerge": "4.3.1",
65
66
  "fast-deep-equal": "^3.1.3",
66
67
  "remove-accents": "^0.5.0"
67
68
  },
@@ -75,5 +76,5 @@
75
76
  "scripts": {
76
77
  "build:wp": "node build"
77
78
  },
78
- "gitHead": "8806899f598577a3c90a55d9aa79fbc372fe1e75"
79
+ "gitHead": "85a580bd2c55f811c8969b42dbb10209d19d514e"
79
80
  }
@@ -1,3 +1,8 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import deepMerge from 'deepmerge';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
@@ -41,6 +46,14 @@ type SamplePost = {
41
46
  address1?: string;
42
47
  address2?: string;
43
48
  city?: string;
49
+ comment_status?: string;
50
+ ping_status?: boolean;
51
+ longDescription?: string;
52
+ origin?: string;
53
+ destination?: string;
54
+ flight_status?: string;
55
+ gate?: string;
56
+ seat?: string;
44
57
  };
45
58
 
46
59
  const fields: Field< SamplePost >[] = [
@@ -79,6 +92,9 @@ const fields: Field< SamplePost >[] = [
79
92
  { value: 3, label: 'Alice' },
80
93
  { value: 4, label: 'Bob' },
81
94
  ],
95
+ setValue: ( { value } ) => ( {
96
+ author: Number( value ),
97
+ } ),
82
98
  },
83
99
  {
84
100
  id: 'reviewer',
@@ -174,6 +190,80 @@ const fields: Field< SamplePost >[] = [
174
190
  type: 'text',
175
191
  Edit: 'textarea',
176
192
  },
193
+ {
194
+ id: 'longDescription',
195
+ label: 'Long Description',
196
+ type: 'text',
197
+ Edit: {
198
+ control: 'textarea',
199
+ rows: 5,
200
+ },
201
+ },
202
+ {
203
+ id: 'comment_status',
204
+ label: 'Comment Status',
205
+ type: 'text',
206
+ Edit: 'radio',
207
+ elements: [
208
+ { value: 'open', label: 'Allow comments' },
209
+ { value: 'closed', label: 'Comments closed' },
210
+ ],
211
+ },
212
+ {
213
+ id: 'ping_status',
214
+ label: 'Allow Pings/Trackbacks',
215
+ type: 'boolean',
216
+ },
217
+ {
218
+ id: 'discussion',
219
+ label: 'Discussion',
220
+ type: 'text',
221
+ render: ( { item } ) => {
222
+ const commentLabel =
223
+ item.comment_status === 'open'
224
+ ? 'Allow comments'
225
+ : 'Comments closed';
226
+ const pingLabel = item.ping_status
227
+ ? 'Pings enabled'
228
+ : 'Pings disabled';
229
+ return (
230
+ <span>
231
+ { commentLabel }, { pingLabel }
232
+ </span>
233
+ );
234
+ },
235
+ },
236
+ {
237
+ id: 'origin',
238
+ label: 'Origin',
239
+ type: 'text',
240
+ },
241
+ {
242
+ id: 'destination',
243
+ label: 'Destination',
244
+ type: 'text',
245
+ },
246
+ {
247
+ id: 'flight_status',
248
+ label: 'Flight Status',
249
+ type: 'text',
250
+ Edit: 'radio',
251
+ elements: [
252
+ { value: 'on-time', label: 'On Time' },
253
+ { value: 'delayed', label: 'Delayed' },
254
+ { value: 'cancelled', label: 'Cancelled' },
255
+ ],
256
+ },
257
+ {
258
+ id: 'gate',
259
+ label: 'Gate',
260
+ type: 'text',
261
+ },
262
+ {
263
+ id: 'seat',
264
+ label: 'Seat',
265
+ type: 'text',
266
+ },
177
267
  ];
178
268
 
179
269
  const LayoutRegularComponent = ( {
@@ -220,6 +310,7 @@ const LayoutRegularComponent = ( {
220
310
  'dimensions',
221
311
  'tags',
222
312
  'description',
313
+ 'longDescription',
223
314
  ],
224
315
  } ),
225
316
  [ labelPosition ]
@@ -308,6 +399,13 @@ const LayoutPanelComponent = ( {
308
399
  address1: '123 Main St',
309
400
  address2: 'Apt 4B',
310
401
  city: 'New York',
402
+ comment_status: 'open',
403
+ ping_status: true,
404
+ origin: 'New York (JFK)',
405
+ destination: 'Los Angeles (LAX)',
406
+ flight_status: 'on-time',
407
+ gate: 'A12',
408
+ seat: '14F',
311
409
  } );
312
410
 
313
411
  const form: Form = useMemo( () => {
@@ -329,11 +427,34 @@ const LayoutPanelComponent = ( {
329
427
  'filesize',
330
428
  'dimensions',
331
429
  'tags',
430
+ {
431
+ id: 'discussion',
432
+ label: 'Discussion',
433
+ children: [ 'comment_status', 'ping_status' ],
434
+ summary: 'discussion',
435
+ },
332
436
  {
333
437
  id: 'address1',
334
438
  label: 'Combined Address',
335
439
  children: [ 'address1', 'address2', 'city' ],
336
440
  },
441
+ {
442
+ id: 'flight_info',
443
+ label: 'Flight Information',
444
+ children: [
445
+ 'origin',
446
+ 'destination',
447
+ 'flight_status',
448
+ 'gate',
449
+ ],
450
+ summary: [ 'origin', 'destination', 'flight_status' ],
451
+ },
452
+ {
453
+ id: 'passenger_details',
454
+ label: 'Passenger Details',
455
+ children: [ 'author', 'seat' ],
456
+ summary: [ 'author', 'seat' ],
457
+ },
337
458
  ],
338
459
  };
339
460
  }, [ labelPosition, openAs ] );
@@ -359,15 +480,13 @@ function CustomEditControl< Item >( {
359
480
  onChange,
360
481
  hideLabelFromVision,
361
482
  }: DataFormControlProps< Item > ) {
362
- const { id, label, placeholder, description } = field;
363
- const value = field.getValue( { item: data } );
483
+ const { label, placeholder, description, getValue, setValue } = field;
484
+ const value = getValue( { item: data } );
364
485
 
365
486
  const onChangeControl = useCallback(
366
487
  ( newValue: string ) =>
367
- onChange( {
368
- [ id ]: newValue,
369
- } ),
370
- [ id, onChange ]
488
+ onChange( setValue( { item: data, value: newValue } ) ),
489
+ [ data, onChange, setValue ]
371
490
  );
372
491
 
373
492
  return (
@@ -396,6 +515,8 @@ const ValidationComponent = ( {
396
515
  } ) => {
397
516
  type ValidatedItem = {
398
517
  text: string;
518
+ select?: string;
519
+ textWithRadio?: string;
399
520
  textarea: string;
400
521
  email: string;
401
522
  telephone: string;
@@ -405,10 +526,14 @@ const ValidationComponent = ( {
405
526
  boolean: boolean;
406
527
  customEdit: string;
407
528
  password: string;
529
+ toggle?: boolean;
530
+ toggleGroup?: string;
408
531
  };
409
532
 
410
533
  const [ post, setPost ] = useState< ValidatedItem >( {
411
534
  text: 'Can have letters and spaces',
535
+ select: undefined,
536
+ textWithRadio: undefined,
412
537
  textarea: 'Can have letters and spaces',
413
538
  email: 'hi@example.com',
414
539
  telephone: '+306978241796',
@@ -418,6 +543,8 @@ const ValidationComponent = ( {
418
543
  boolean: true,
419
544
  customEdit: 'custom control',
420
545
  password: 'secretpassword123',
546
+ toggle: undefined,
547
+ toggleGroup: undefined,
421
548
  } );
422
549
 
423
550
  const customTextRule = ( value: ValidatedItem ) => {
@@ -427,6 +554,22 @@ const ValidationComponent = ( {
427
554
 
428
555
  return null;
429
556
  };
557
+
558
+ const customSelectRule = ( value: ValidatedItem ) => {
559
+ if ( value.select !== 'option1' ) {
560
+ return 'Value must be Option 1.';
561
+ }
562
+ return null;
563
+ };
564
+
565
+ const customTextRadioRule = ( value: ValidatedItem ) => {
566
+ if ( value.textWithRadio !== 'item1' ) {
567
+ return 'Value must be Item 1.';
568
+ }
569
+
570
+ return null;
571
+ };
572
+
430
573
  const customTextareaRule = ( value: ValidatedItem ) => {
431
574
  if ( ! /^[a-zA-Z ]+$/.test( value.textarea ) ) {
432
575
  return 'Value must only contain letters and spaces.';
@@ -469,6 +612,27 @@ const ValidationComponent = ( {
469
612
 
470
613
  return null;
471
614
  };
615
+ const customBooleanRule = ( value: ValidatedItem ) => {
616
+ if ( value.boolean !== true ) {
617
+ return 'Boolean must be active.';
618
+ }
619
+
620
+ return null;
621
+ };
622
+ const customToggleRule = ( value: ValidatedItem ) => {
623
+ if ( value.toggle !== true ) {
624
+ return 'Toggle must be checked.';
625
+ }
626
+
627
+ return null;
628
+ };
629
+ const customToggleGroupRule = ( value: ValidatedItem ) => {
630
+ if ( value.toggleGroup !== 'option1' ) {
631
+ return 'Value must be Option 1.';
632
+ }
633
+
634
+ return null;
635
+ };
472
636
 
473
637
  const customPasswordRule = ( value: ValidatedItem ) => {
474
638
  if ( value.password.length < 8 ) {
@@ -500,6 +664,33 @@ const ValidationComponent = ( {
500
664
  custom: maybeCustomRule( customTextRule ),
501
665
  },
502
666
  },
667
+ {
668
+ id: 'select',
669
+ type: 'text',
670
+ label: 'Select',
671
+ elements: [
672
+ { value: 'option1', label: 'Option 1' },
673
+ { value: 'option2', label: 'Option 2' },
674
+ ],
675
+ isValid: {
676
+ required,
677
+ custom: maybeCustomRule( customSelectRule ),
678
+ },
679
+ },
680
+ {
681
+ id: 'textWithRadio',
682
+ type: 'text',
683
+ Edit: 'radio',
684
+ label: 'Text with radio',
685
+ elements: [
686
+ { value: 'item1', label: 'Item 1' },
687
+ { value: 'item2', label: 'Item 2' },
688
+ ],
689
+ isValid: {
690
+ required,
691
+ custom: maybeCustomRule( customTextRadioRule ),
692
+ },
693
+ },
503
694
  {
504
695
  id: 'textarea',
505
696
  type: 'text',
@@ -561,6 +752,7 @@ const ValidationComponent = ( {
561
752
  label: 'Boolean',
562
753
  isValid: {
563
754
  required,
755
+ custom: maybeCustomRule( customBooleanRule ),
564
756
  },
565
757
  },
566
758
  {
@@ -580,12 +772,39 @@ const ValidationComponent = ( {
580
772
  custom: maybeCustomRule( customPasswordRule ),
581
773
  },
582
774
  },
775
+ {
776
+ id: 'toggle',
777
+ type: 'boolean',
778
+ label: 'Toggle',
779
+ Edit: 'toggle',
780
+ isValid: {
781
+ required,
782
+ custom: maybeCustomRule( customToggleRule ),
783
+ },
784
+ },
785
+ {
786
+ id: 'toggleGroup',
787
+ type: 'text',
788
+ label: 'Toggle Group',
789
+ Edit: 'toggleGroup',
790
+ elements: [
791
+ { value: 'option1', label: 'Option 1' },
792
+ { value: 'option2', label: 'Option 2' },
793
+ { value: 'option3', label: 'Option 3' },
794
+ ],
795
+ isValid: {
796
+ required,
797
+ custom: maybeCustomRule( customToggleGroupRule ),
798
+ },
799
+ },
583
800
  ];
584
801
 
585
802
  const form = {
586
803
  layout: { type },
587
804
  fields: [
588
805
  'text',
806
+ 'select',
807
+ 'textWithRadio',
589
808
  'textarea',
590
809
  'email',
591
810
  'telephone',
@@ -593,8 +812,10 @@ const ValidationComponent = ( {
593
812
  'color',
594
813
  'integer',
595
814
  'boolean',
596
- 'customEdit',
815
+ 'toggle',
816
+ 'toggleGroup',
597
817
  'password',
818
+ 'customEdit',
598
819
  ],
599
820
  };
600
821
 
@@ -1261,3 +1482,211 @@ export const Validation = {
1261
1482
  export const Visibility = {
1262
1483
  render: VisibilityComponent,
1263
1484
  };
1485
+
1486
+ const DataAdapterComponent = () => {
1487
+ type DataAdapterItem = {
1488
+ user: {
1489
+ profile: {
1490
+ name: string;
1491
+ email: string;
1492
+ };
1493
+ preferences: {
1494
+ notifications: boolean;
1495
+ };
1496
+ };
1497
+ revenue: {
1498
+ total: number;
1499
+ units: number;
1500
+ pricePerUnit: number;
1501
+ };
1502
+ };
1503
+
1504
+ const [ data, setData ] = useState< DataAdapterItem >( {
1505
+ user: {
1506
+ profile: {
1507
+ name: 'John Doe',
1508
+ email: 'john@example.com',
1509
+ },
1510
+ preferences: {
1511
+ notifications: true,
1512
+ },
1513
+ },
1514
+ revenue: {
1515
+ total: 30,
1516
+ units: 10,
1517
+ pricePerUnit: 3,
1518
+ },
1519
+ } );
1520
+
1521
+ const nestedFields: Field< DataAdapterItem >[] = [
1522
+ // Examples of autogenerated getValue/setValue methods
1523
+ // for nested data based on the field id.
1524
+ {
1525
+ id: 'user.profile.name',
1526
+ label: 'User Name',
1527
+ type: 'text',
1528
+ },
1529
+ {
1530
+ id: 'user.profile.email',
1531
+ label: 'User Email',
1532
+ type: 'email',
1533
+ },
1534
+ // Example of adapting a data value to a control value
1535
+ // by providing getValue/setValue methods.
1536
+ {
1537
+ id: 'user.preferences.notifications',
1538
+ label: 'Notifications',
1539
+ type: 'boolean',
1540
+ Edit: 'radio',
1541
+ elements: [
1542
+ { label: 'Enabled', value: 'enabled' },
1543
+ { label: 'Disabled', value: 'disabled' },
1544
+ ],
1545
+ getValue: ( { item } ) =>
1546
+ item.user.preferences.notifications === true
1547
+ ? 'enabled'
1548
+ : 'disabled',
1549
+ setValue: ( { value } ) => ( {
1550
+ user: {
1551
+ preferences: { notifications: value === 'enabled' },
1552
+ },
1553
+ } ),
1554
+ },
1555
+ // Example of deriving data by leveraging setValue method.
1556
+ {
1557
+ id: 'revenue.total',
1558
+ label: 'Total Revenue',
1559
+ type: 'integer',
1560
+ readOnly: true,
1561
+ },
1562
+ {
1563
+ id: 'revenue.pricePerUnit',
1564
+ label: 'Price Per Unit',
1565
+ type: 'integer',
1566
+ setValue: ( { item, value } ) => ( {
1567
+ revenue: {
1568
+ total: value * item.revenue.units,
1569
+ pricePerUnit: value,
1570
+ },
1571
+ } ),
1572
+ },
1573
+ {
1574
+ id: 'revenue.units',
1575
+ label: 'Units',
1576
+ type: 'integer',
1577
+ setValue: ( { item, value } ) => ( {
1578
+ revenue: {
1579
+ total: item.revenue.pricePerUnit * value,
1580
+ units: value,
1581
+ },
1582
+ } ),
1583
+ },
1584
+ ];
1585
+
1586
+ const handleChange = useCallback( ( edits: any ) => {
1587
+ // Edits will respect the shape of the data
1588
+ // because fields provide the proper information
1589
+ // (via field.id or via field.setValue).
1590
+ setData( ( prev ) => deepMerge( prev, edits ) );
1591
+ }, [] );
1592
+
1593
+ return (
1594
+ <>
1595
+ <h1>Data adapter</h1>
1596
+ <p>
1597
+ This story is best looked at with the code on the side. It aims
1598
+ to highlight how DataForm can wrangle data in scenarios such as
1599
+ nested data, bridge data to/from UI controls, and derived data.
1600
+ </p>
1601
+ <p>
1602
+ <b>Current data snapshot:</b>
1603
+ </p>
1604
+ <pre>{ JSON.stringify( data, null, 2 ) }</pre>
1605
+ <h2>Nested data</h2>
1606
+ <p>
1607
+ The first example demonstrates how to signal nested data via{ ' ' }
1608
+ <code>field.id</code>.
1609
+ </p>
1610
+ <p>
1611
+ By using <code>{ `{ id: 'user.profile.name' }` }</code> as field
1612
+ id, when users edit the name, the edits will come in this shape:
1613
+ <code>{ `{ user: { profile: { name: 'John Doe' } } }` }</code>
1614
+ </p>
1615
+ <DataForm< DataAdapterItem >
1616
+ data={ data }
1617
+ fields={ nestedFields }
1618
+ form={ {
1619
+ layout: {
1620
+ type: 'panel',
1621
+ labelPosition: 'top',
1622
+ openAs: 'modal',
1623
+ },
1624
+ fields: [
1625
+ {
1626
+ id: 'userProfile',
1627
+ label: 'User Profile',
1628
+ children: [
1629
+ 'user.profile.name',
1630
+ 'user.profile.email',
1631
+ ],
1632
+ },
1633
+ ],
1634
+ } }
1635
+ onChange={ handleChange }
1636
+ />
1637
+ <h2>Adapt data and UI control</h2>
1638
+ <p>
1639
+ Sometimes, we need to adapt the data type to and from the UI
1640
+ control response. This example demonstrates how to adapt a
1641
+ boolean to a text string (Enabled/Disabled).
1642
+ </p>
1643
+ <DataForm< DataAdapterItem >
1644
+ data={ data }
1645
+ fields={ nestedFields }
1646
+ form={ {
1647
+ layout: {
1648
+ type: 'panel',
1649
+ labelPosition: 'top',
1650
+ openAs: 'modal',
1651
+ },
1652
+ fields: [ 'user.preferences.notifications' ],
1653
+ } }
1654
+ onChange={ handleChange }
1655
+ />
1656
+ <h2>Derived data</h2>
1657
+ <p>
1658
+ Last, but not least, this example showcases how to work with
1659
+ derived data by providing a custom setValue function. Note how,
1660
+ changing UNITS or PRICE PER UNIT, updates the TOTAL value as
1661
+ well.
1662
+ </p>
1663
+ <DataForm< DataAdapterItem >
1664
+ data={ data }
1665
+ fields={ nestedFields }
1666
+ form={ {
1667
+ layout: {
1668
+ type: 'panel',
1669
+ labelPosition: 'top',
1670
+ openAs: 'modal',
1671
+ },
1672
+ fields: [
1673
+ {
1674
+ id: 'revenue',
1675
+ label: 'Revenue',
1676
+ children: [
1677
+ 'revenue.pricePerUnit',
1678
+ 'revenue.units',
1679
+ 'revenue.total',
1680
+ ],
1681
+ },
1682
+ ],
1683
+ } }
1684
+ onChange={ handleChange }
1685
+ />
1686
+ </>
1687
+ );
1688
+ };
1689
+
1690
+ export const DataAdapter = {
1691
+ render: DataAdapterComponent,
1692
+ };