@wordpress/dataviews 5.0.0 → 6.0.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 (250) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/README.md +55 -27
  3. package/build/components/dataviews/index.js +13 -4
  4. package/build/components/dataviews/index.js.map +1 -1
  5. package/build/components/dataviews-context/index.js +3 -1
  6. package/build/components/dataviews-context/index.js.map +1 -1
  7. package/build/components/dataviews-filters/filter.js +15 -8
  8. package/build/components/dataviews-filters/filter.js.map +1 -1
  9. package/build/components/dataviews-filters/index.js +16 -5
  10. package/build/components/dataviews-filters/index.js.map +1 -1
  11. package/build/components/dataviews-filters/input-widget.js +7 -1
  12. package/build/components/dataviews-filters/input-widget.js.map +1 -1
  13. package/build/components/dataviews-filters/reset-filters.js +2 -2
  14. package/build/components/dataviews-filters/reset-filters.js.map +1 -1
  15. package/build/components/dataviews-layout/index.js +5 -2
  16. package/build/components/dataviews-layout/index.js.map +1 -1
  17. package/build/components/dataviews-view-config/index.js +4 -3
  18. package/build/components/dataviews-view-config/index.js.map +1 -1
  19. package/build/dataform-controls/boolean.js +15 -1
  20. package/build/dataform-controls/boolean.js.map +1 -1
  21. package/build/dataform-controls/date.js +385 -0
  22. package/build/dataform-controls/date.js.map +1 -0
  23. package/build/dataform-controls/datetime.js +5 -84
  24. package/build/dataform-controls/datetime.js.map +1 -1
  25. package/build/dataform-controls/email.js +15 -1
  26. package/build/dataform-controls/email.js.map +1 -1
  27. package/build/dataform-controls/index.js +2 -0
  28. package/build/dataform-controls/index.js.map +1 -1
  29. package/build/dataform-controls/integer.js +23 -4
  30. package/build/dataform-controls/integer.js.map +1 -1
  31. package/build/dataform-controls/relative-date-control.js +109 -0
  32. package/build/dataform-controls/relative-date-control.js.map +1 -0
  33. package/build/dataform-controls/select.js +12 -5
  34. package/build/dataform-controls/select.js.map +1 -1
  35. package/build/dataform-controls/text.js +15 -1
  36. package/build/dataform-controls/text.js.map +1 -1
  37. package/build/dataviews-layouts/grid/index.js +40 -23
  38. package/build/dataviews-layouts/grid/index.js.map +1 -1
  39. package/build/dataviews-layouts/grid/preview-size-picker.js +39 -85
  40. package/build/dataviews-layouts/grid/preview-size-picker.js.map +1 -1
  41. package/build/dataviews-layouts/list/index.js +7 -3
  42. package/build/dataviews-layouts/list/index.js.map +1 -1
  43. package/build/dataviews-layouts/table/column-primary.js +18 -3
  44. package/build/dataviews-layouts/table/column-primary.js.map +1 -1
  45. package/build/dataviews-layouts/table/index.js +57 -5
  46. package/build/dataviews-layouts/table/index.js.map +1 -1
  47. package/build/field-types/array.js +27 -18
  48. package/build/field-types/array.js.map +1 -1
  49. package/build/field-types/boolean.js +11 -7
  50. package/build/field-types/boolean.js.map +1 -1
  51. package/build/field-types/date.js +21 -12
  52. package/build/field-types/date.js.map +1 -1
  53. package/build/field-types/datetime.js +19 -10
  54. package/build/field-types/datetime.js.map +1 -1
  55. package/build/field-types/email.js +22 -18
  56. package/build/field-types/email.js.map +1 -1
  57. package/build/field-types/index.js +16 -6
  58. package/build/field-types/index.js.map +1 -1
  59. package/build/field-types/integer.js +22 -17
  60. package/build/field-types/integer.js.map +1 -1
  61. package/build/field-types/media.js +19 -10
  62. package/build/field-types/media.js.map +1 -1
  63. package/build/field-types/text.js +19 -10
  64. package/build/field-types/text.js.map +1 -1
  65. package/build/filter-and-sort-data-view.js +6 -4
  66. package/build/filter-and-sort-data-view.js.map +1 -1
  67. package/build/normalize-fields.js +4 -5
  68. package/build/normalize-fields.js.map +1 -1
  69. package/build/types.js.map +1 -1
  70. package/build/validation.js +15 -2
  71. package/build/validation.js.map +1 -1
  72. package/build-module/components/dataviews/index.js +15 -6
  73. package/build-module/components/dataviews/index.js.map +1 -1
  74. package/build-module/components/dataviews-context/index.js +3 -1
  75. package/build-module/components/dataviews-context/index.js.map +1 -1
  76. package/build-module/components/dataviews-filters/filter.js +15 -8
  77. package/build-module/components/dataviews-filters/filter.js.map +1 -1
  78. package/build-module/components/dataviews-filters/index.js +16 -5
  79. package/build-module/components/dataviews-filters/index.js.map +1 -1
  80. package/build-module/components/dataviews-filters/input-widget.js +7 -1
  81. package/build-module/components/dataviews-filters/input-widget.js.map +1 -1
  82. package/build-module/components/dataviews-filters/reset-filters.js +2 -2
  83. package/build-module/components/dataviews-filters/reset-filters.js.map +1 -1
  84. package/build-module/components/dataviews-layout/index.js +5 -2
  85. package/build-module/components/dataviews-layout/index.js.map +1 -1
  86. package/build-module/components/dataviews-view-config/index.js +4 -3
  87. package/build-module/components/dataviews-view-config/index.js.map +1 -1
  88. package/build-module/dataform-controls/boolean.js +17 -2
  89. package/build-module/dataform-controls/boolean.js.map +1 -1
  90. package/build-module/dataform-controls/date.js +376 -0
  91. package/build-module/dataform-controls/date.js.map +1 -0
  92. package/build-module/dataform-controls/datetime.js +3 -84
  93. package/build-module/dataform-controls/datetime.js.map +1 -1
  94. package/build-module/dataform-controls/email.js +17 -2
  95. package/build-module/dataform-controls/email.js.map +1 -1
  96. package/build-module/dataform-controls/index.js +2 -0
  97. package/build-module/dataform-controls/index.js.map +1 -1
  98. package/build-module/dataform-controls/integer.js +24 -5
  99. package/build-module/dataform-controls/integer.js.map +1 -1
  100. package/build-module/dataform-controls/relative-date-control.js +100 -0
  101. package/build-module/dataform-controls/relative-date-control.js.map +1 -0
  102. package/build-module/dataform-controls/select.js +12 -5
  103. package/build-module/dataform-controls/select.js.map +1 -1
  104. package/build-module/dataform-controls/text.js +17 -2
  105. package/build-module/dataform-controls/text.js.map +1 -1
  106. package/build-module/dataviews-layouts/grid/index.js +41 -24
  107. package/build-module/dataviews-layouts/grid/index.js.map +1 -1
  108. package/build-module/dataviews-layouts/grid/preview-size-picker.js +40 -85
  109. package/build-module/dataviews-layouts/grid/preview-size-picker.js.map +1 -1
  110. package/build-module/dataviews-layouts/list/index.js +7 -3
  111. package/build-module/dataviews-layouts/list/index.js.map +1 -1
  112. package/build-module/dataviews-layouts/table/column-primary.js +18 -3
  113. package/build-module/dataviews-layouts/table/column-primary.js.map +1 -1
  114. package/build-module/dataviews-layouts/table/index.js +58 -6
  115. package/build-module/dataviews-layouts/table/index.js.map +1 -1
  116. package/build-module/field-types/array.js +27 -18
  117. package/build-module/field-types/array.js.map +1 -1
  118. package/build-module/field-types/boolean.js +11 -7
  119. package/build-module/field-types/boolean.js.map +1 -1
  120. package/build-module/field-types/date.js +21 -12
  121. package/build-module/field-types/date.js.map +1 -1
  122. package/build-module/field-types/datetime.js +19 -10
  123. package/build-module/field-types/datetime.js.map +1 -1
  124. package/build-module/field-types/email.js +22 -18
  125. package/build-module/field-types/email.js.map +1 -1
  126. package/build-module/field-types/index.js +16 -6
  127. package/build-module/field-types/index.js.map +1 -1
  128. package/build-module/field-types/integer.js +22 -17
  129. package/build-module/field-types/integer.js.map +1 -1
  130. package/build-module/field-types/media.js +19 -10
  131. package/build-module/field-types/media.js.map +1 -1
  132. package/build-module/field-types/text.js +19 -10
  133. package/build-module/field-types/text.js.map +1 -1
  134. package/build-module/filter-and-sort-data-view.js +6 -4
  135. package/build-module/filter-and-sort-data-view.js.map +1 -1
  136. package/build-module/normalize-fields.js +4 -5
  137. package/build-module/normalize-fields.js.map +1 -1
  138. package/build-module/types.js.map +1 -1
  139. package/build-module/validation.js +15 -2
  140. package/build-module/validation.js.map +1 -1
  141. package/build-style/style-rtl.css +78 -43
  142. package/build-style/style.css +78 -43
  143. package/build-types/components/dataform/stories/index.story.d.ts +21 -0
  144. package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
  145. package/build-types/components/dataviews/index.d.ts +3 -2
  146. package/build-types/components/dataviews/index.d.ts.map +1 -1
  147. package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
  148. package/build-types/components/dataviews/stories/index.story.d.ts +16 -3
  149. package/build-types/components/dataviews/stories/index.story.d.ts.map +1 -1
  150. package/build-types/components/dataviews-context/index.d.ts +4 -2
  151. package/build-types/components/dataviews-context/index.d.ts.map +1 -1
  152. package/build-types/components/dataviews-filters/filter.d.ts.map +1 -1
  153. package/build-types/components/dataviews-filters/index.d.ts.map +1 -1
  154. package/build-types/components/dataviews-filters/input-widget.d.ts.map +1 -1
  155. package/build-types/components/dataviews-filters/reset-filters.d.ts.map +1 -1
  156. package/build-types/components/dataviews-layout/index.d.ts.map +1 -1
  157. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  158. package/build-types/constants.d.ts +2 -2
  159. package/build-types/dataform-controls/boolean.d.ts.map +1 -1
  160. package/build-types/dataform-controls/date.d.ts +3 -0
  161. package/build-types/dataform-controls/date.d.ts.map +1 -0
  162. package/build-types/dataform-controls/datetime.d.ts.map +1 -1
  163. package/build-types/dataform-controls/email.d.ts.map +1 -1
  164. package/build-types/dataform-controls/index.d.ts.map +1 -1
  165. package/build-types/dataform-controls/integer.d.ts.map +1 -1
  166. package/build-types/dataform-controls/relative-date-control.d.ts +46 -0
  167. package/build-types/dataform-controls/relative-date-control.d.ts.map +1 -0
  168. package/build-types/dataform-controls/select.d.ts.map +1 -1
  169. package/build-types/dataform-controls/text.d.ts.map +1 -1
  170. package/build-types/dataviews-layouts/grid/index.d.ts +1 -1
  171. package/build-types/dataviews-layouts/grid/index.d.ts.map +1 -1
  172. package/build-types/dataviews-layouts/grid/preview-size-picker.d.ts +0 -1
  173. package/build-types/dataviews-layouts/grid/preview-size-picker.d.ts.map +1 -1
  174. package/build-types/dataviews-layouts/index.d.ts +3 -3
  175. package/build-types/dataviews-layouts/list/index.d.ts.map +1 -1
  176. package/build-types/dataviews-layouts/table/column-primary.d.ts.map +1 -1
  177. package/build-types/dataviews-layouts/table/index.d.ts +1 -1
  178. package/build-types/dataviews-layouts/table/index.d.ts.map +1 -1
  179. package/build-types/field-types/array.d.ts.map +1 -1
  180. package/build-types/field-types/boolean.d.ts +5 -4
  181. package/build-types/field-types/boolean.d.ts.map +1 -1
  182. package/build-types/field-types/date.d.ts +9 -5
  183. package/build-types/field-types/date.d.ts.map +1 -1
  184. package/build-types/field-types/datetime.d.ts +4 -3
  185. package/build-types/field-types/datetime.d.ts.map +1 -1
  186. package/build-types/field-types/email.d.ts +4 -3
  187. package/build-types/field-types/email.d.ts.map +1 -1
  188. package/build-types/field-types/index.d.ts.map +1 -1
  189. package/build-types/field-types/integer.d.ts +4 -3
  190. package/build-types/field-types/integer.d.ts.map +1 -1
  191. package/build-types/field-types/media.d.ts +4 -3
  192. package/build-types/field-types/media.d.ts.map +1 -1
  193. package/build-types/field-types/text.d.ts +4 -3
  194. package/build-types/field-types/text.d.ts.map +1 -1
  195. package/build-types/filter-and-sort-data-view.d.ts.map +1 -1
  196. package/build-types/normalize-fields.d.ts.map +1 -1
  197. package/build-types/types.d.ts +20 -7
  198. package/build-types/types.d.ts.map +1 -1
  199. package/build-types/validation.d.ts.map +1 -1
  200. package/build-wp/index.js +1561 -670
  201. package/package.json +15 -14
  202. package/src/components/dataform/stories/index.story.tsx +229 -2
  203. package/src/components/dataviews/index.tsx +30 -10
  204. package/src/components/dataviews/stories/fixtures.tsx +3 -1
  205. package/src/components/dataviews/stories/index.story.tsx +49 -29
  206. package/src/components/dataviews/stories/style.css +6 -0
  207. package/src/components/dataviews-context/index.ts +8 -2
  208. package/src/components/dataviews-filters/filter.tsx +17 -7
  209. package/src/components/dataviews-filters/index.tsx +17 -2
  210. package/src/components/dataviews-filters/input-widget.tsx +7 -1
  211. package/src/components/dataviews-filters/reset-filters.tsx +4 -2
  212. package/src/components/dataviews-filters/style.scss +8 -2
  213. package/src/components/dataviews-layout/index.tsx +3 -0
  214. package/src/components/dataviews-view-config/index.tsx +5 -3
  215. package/src/dataform-controls/boolean.tsx +19 -2
  216. package/src/dataform-controls/date.tsx +499 -0
  217. package/src/dataform-controls/datetime.tsx +5 -91
  218. package/src/dataform-controls/email.tsx +19 -2
  219. package/src/dataform-controls/index.tsx +2 -0
  220. package/src/dataform-controls/integer.tsx +30 -4
  221. package/src/dataform-controls/relative-date-control.tsx +106 -0
  222. package/src/dataform-controls/select.tsx +23 -13
  223. package/src/dataform-controls/style.scss +19 -2
  224. package/src/dataform-controls/text.tsx +19 -2
  225. package/src/dataviews-layouts/grid/index.tsx +46 -24
  226. package/src/dataviews-layouts/grid/preview-size-picker.tsx +48 -73
  227. package/src/dataviews-layouts/grid/style.scss +15 -28
  228. package/src/dataviews-layouts/list/index.tsx +7 -4
  229. package/src/dataviews-layouts/list/style.scss +3 -3
  230. package/src/dataviews-layouts/table/column-primary.tsx +29 -5
  231. package/src/dataviews-layouts/table/index.tsx +134 -42
  232. package/src/dataviews-layouts/table/style.scss +45 -1
  233. package/src/field-types/array.tsx +33 -21
  234. package/src/field-types/boolean.tsx +15 -9
  235. package/src/field-types/date.ts +51 -15
  236. package/src/field-types/datetime.tsx +19 -13
  237. package/src/field-types/email.tsx +26 -21
  238. package/src/field-types/index.tsx +18 -8
  239. package/src/field-types/integer.tsx +26 -22
  240. package/src/field-types/media.tsx +19 -13
  241. package/src/field-types/text.tsx +19 -13
  242. package/src/filter-and-sort-data-view.ts +11 -4
  243. package/src/normalize-fields.ts +4 -8
  244. package/src/test/dataviews.tsx +129 -0
  245. package/src/test/filter-and-sort-data-view.js +52 -2
  246. package/src/test/validation.ts +4 -15
  247. package/src/types.ts +28 -8
  248. package/src/validation.ts +30 -1
  249. package/tsconfig.json +1 -0
  250. package/tsconfig.tsbuildinfo +1 -1
@@ -285,6 +285,71 @@ describe( 'DataViews component', () => {
285
285
  await user.click( titleField );
286
286
  expect( onClickItemCallback ).toHaveBeenCalledWith( data[ 0 ] );
287
287
  } );
288
+
289
+ it( 'accepts click for single selection', async () => {
290
+ render(
291
+ <DataViewWrapper
292
+ view={ {
293
+ ...DEFAULT_VIEW,
294
+ fields: [ 'author' ],
295
+ titleField: 'title',
296
+ } }
297
+ // A bulk action is required for the dataview to be multi-selectable.
298
+ actions={ actions }
299
+ />
300
+ );
301
+ const firstItemElement = screen.getByText( data[ 0 ].title );
302
+ const thirdItemElement = screen.getByText( data[ 2 ].title );
303
+ const user = userEvent.setup();
304
+ await user.click( firstItemElement );
305
+
306
+ // First item should be selected.
307
+ expect(
308
+ screen.getByRole( 'checkbox', { name: data[ 0 ].title } )
309
+ ).toBeChecked();
310
+ await user.click( thirdItemElement );
311
+
312
+ // Third item should be selected. First item was deselected.
313
+ expect(
314
+ screen.getByRole( 'checkbox', { name: data[ 2 ].title } )
315
+ ).toBeChecked();
316
+ } );
317
+
318
+ it( 'accepts ctrl/cmd key and click for non-consecutive multi-selection', async () => {
319
+ render(
320
+ <DataViewWrapper
321
+ view={ {
322
+ ...DEFAULT_VIEW,
323
+ fields: [ 'author' ],
324
+ titleField: 'title',
325
+ } }
326
+ // A bulk action is required for the dataview to be multi-selectable.
327
+ actions={ actions }
328
+ />
329
+ );
330
+ const firstItemElement = screen.getByText( data[ 0 ].title );
331
+ const thirdItemElement = screen.getByText( data[ 2 ].title );
332
+ const user = userEvent.setup();
333
+ await user.click( firstItemElement );
334
+
335
+ // First item should be selected.
336
+ expect(
337
+ screen.getByRole( 'checkbox', { name: data[ 0 ].title } )
338
+ ).toBeChecked();
339
+ await user.keyboard( '{Control>}' );
340
+ await user.click( thirdItemElement );
341
+
342
+ // Both items should be selected.
343
+ expect(
344
+ screen.getByRole( 'checkbox', { name: data[ 0 ].title } )
345
+ ).toBeChecked();
346
+ expect(
347
+ screen.getByRole( 'checkbox', { name: data[ 2 ].title } )
348
+ ).toBeChecked();
349
+
350
+ // Don't keep the modifier pressed down, that's just mean.
351
+ await user.keyboard( '{/Control}' );
352
+ } );
288
353
  } );
289
354
 
290
355
  describe( 'in grid view', () => {
@@ -363,6 +428,70 @@ describe( 'DataViews component', () => {
363
428
  await user.click( imageField );
364
429
  expect( mediaClickItemCallback ).toHaveBeenCalledWith( data[ 0 ] );
365
430
  } );
431
+
432
+ it( 'accepts click for single selection', async () => {
433
+ render(
434
+ <DataViewWrapper
435
+ view={ {
436
+ ...DEFAULT_VIEW,
437
+ fields: [ 'author' ],
438
+ titleField: 'title',
439
+ } }
440
+ // A bulk action is required for the dataview to be multi-selectable.
441
+ actions={ actions }
442
+ />
443
+ );
444
+ const firstItemElement = screen.getByText( data[ 0 ].title );
445
+ const thirdItemElement = screen.getByText( data[ 2 ].title );
446
+ const user = userEvent.setup();
447
+ await user.click( firstItemElement );
448
+
449
+ // First item should be selected.
450
+ expect(
451
+ screen.getByRole( 'checkbox', { name: data[ 0 ].title } )
452
+ ).toBeChecked();
453
+ await user.click( thirdItemElement );
454
+
455
+ // Third item should be selected. First item was deselected.
456
+ expect(
457
+ screen.getByRole( 'checkbox', { name: data[ 2 ].title } )
458
+ ).toBeChecked();
459
+ } );
460
+
461
+ it( 'accepts ctrl/cmd key and click for non-consecutive multi-selection', async () => {
462
+ render(
463
+ <DataViewWrapper
464
+ view={ {
465
+ ...DEFAULT_VIEW,
466
+ fields: [ 'author' ],
467
+ titleField: 'title',
468
+ } }
469
+ // A bulk action is required for the dataview to be multi-selectable.
470
+ actions={ actions }
471
+ />
472
+ );
473
+ const firstItemElement = screen.getByText( data[ 0 ].title );
474
+ const thirdItemElement = screen.getByText( data[ 2 ].title );
475
+ const user = userEvent.setup();
476
+ await user.click( firstItemElement );
477
+
478
+ // First item should be selected.
479
+ expect(
480
+ screen.getByRole( 'checkbox', { name: data[ 0 ].title } )
481
+ ).toBeChecked();
482
+ await user.keyboard( '{Control>}' );
483
+ await user.click( thirdItemElement );
484
+
485
+ // Both items should be selected.
486
+ expect(
487
+ screen.getByRole( 'checkbox', { name: data[ 0 ].title } )
488
+ ).toBeChecked();
489
+ expect(
490
+ screen.getByRole( 'checkbox', { name: data[ 2 ].title } )
491
+ ).toBeChecked();
492
+
493
+ await user.keyboard( '{/Control}' );
494
+ } );
366
495
  } );
367
496
 
368
497
  describe( 'in list view', () => {
@@ -55,7 +55,9 @@ describe( 'filters', () => {
55
55
  fields
56
56
  );
57
57
  expect( result ).toHaveLength( 2 );
58
- expect( result[ 0 ].description ).toBe( "Earth's satellite" );
58
+ expect( result[ 0 ].description ).toBe(
59
+ 'The Moon is Earth’s only natural satellite, orbiting at an average distance of 384,400 kilometers with a synchronous rotation that leads to fixed lunar phases as seen from Earth. Its cratered surface and subtle glow define night skies, inspiring exploration missions and influencing tides and biological rhythms worldwide.'
60
+ );
59
61
  } );
60
62
 
61
63
  it( 'should perform case-insensitive and accent-insensitive search', () => {
@@ -71,6 +73,54 @@ describe( 'filters', () => {
71
73
  expect( result[ 0 ].description ).toBe( 'La planète Vénus' );
72
74
  } );
73
75
 
76
+ it( 'should search over array fields when enableGlobalSearch is true', () => {
77
+ const fieldsWithArraySearch = fields.map( ( field ) =>
78
+ field.id === 'categories'
79
+ ? { ...field, enableGlobalSearch: true }
80
+ : field
81
+ );
82
+
83
+ const { data: result } = filterSortAndPaginate(
84
+ data,
85
+ {
86
+ search: 'Moon',
87
+ filters: [],
88
+ },
89
+ fieldsWithArraySearch
90
+ );
91
+
92
+ // Should find items with "satellite" in categories
93
+ expect( result ).toHaveLength( 3 );
94
+ expect( result.map( ( r ) => r.title ).sort() ).toEqual( [
95
+ 'Europa',
96
+ 'Io',
97
+ 'Moon',
98
+ ] );
99
+ } );
100
+
101
+ it( 'should search over array fields case-insensitively', () => {
102
+ const fieldsWithArraySearch = fields.map( ( field ) =>
103
+ field.id === 'categories'
104
+ ? { ...field, enableGlobalSearch: true }
105
+ : field
106
+ );
107
+
108
+ const { data: result } = filterSortAndPaginate(
109
+ data,
110
+ {
111
+ search: 'planet',
112
+ filters: [],
113
+ },
114
+ fieldsWithArraySearch
115
+ );
116
+
117
+ // Should find items with "Planet" in categories (case-insensitive)
118
+ expect( result ).toHaveLength( 8 );
119
+ expect( result.map( ( r ) => r.title ) ).toContain( 'Neptune' );
120
+ expect( result.map( ( r ) => r.title ) ).toContain( 'Mercury' );
121
+ expect( result.map( ( r ) => r.title ) ).toContain( 'Earth' );
122
+ } );
123
+
74
124
  it( 'should search using IS filter', () => {
75
125
  const { data: result } = filterSortAndPaginate(
76
126
  data,
@@ -374,7 +424,7 @@ describe( 'filters', () => {
374
424
  fields
375
425
  );
376
426
  expect( result.map( ( r ) => r.description ) ).toEqual( [
377
- "Earth's satellite",
427
+ 'The Moon is Earths only natural satellite, orbiting at an average distance of 384,400 kilometers with a synchronous rotation that leads to fixed lunar phases as seen from Earth. Its cratered surface and subtle glow define night skies, inspiring exploration missions and influencing tides and biological rhythms worldwide.',
378
428
  'Moon of Jupiter',
379
429
  'Moon of Jupiter',
380
430
  'La planète Vénus',
@@ -35,7 +35,7 @@ describe( 'validation', () => {
35
35
  expect( result ).toBe( true );
36
36
  } );
37
37
 
38
- it( 'integer field is invalid if value is not integer', () => {
38
+ it( 'integer field is invalid if value is not integer when not empty', () => {
39
39
  const item = { id: 1, order: 'd' };
40
40
  const fields: Field< {} >[] = [
41
41
  {
@@ -48,19 +48,6 @@ describe( 'validation', () => {
48
48
  expect( result ).toBe( false );
49
49
  } );
50
50
 
51
- it( 'integer field is invalid if value is empty', () => {
52
- const item = { id: 1, order: '' };
53
- const fields: Field< {} >[] = [
54
- {
55
- id: 'order',
56
- type: 'integer',
57
- },
58
- ];
59
- const form = { fields: [ 'order' ] };
60
- const result = isItemValid( item, fields, form );
61
- expect( result ).toBe( false );
62
- } );
63
-
64
51
  it( 'integer field is invalid if value is not one of the elements', () => {
65
52
  const item = { id: 1, author: 3 };
66
53
  const fields: Field< {} >[] = [
@@ -121,7 +108,9 @@ describe( 'validation', () => {
121
108
  { value: 'a', label: 'A' },
122
109
  { value: 'b', label: 'B' },
123
110
  ],
124
- isValid: () => true, // Overrides the validation provided for integer types.
111
+ isValid: {
112
+ custom: () => null, // Overrides the validation provided for integer types.
113
+ },
125
114
  },
126
115
  ];
127
116
  const form = { fields: [ 'order' ] };
package/src/types.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import type { ReactElement, ComponentType, ComponentProps } from 'react';
4
+ import type {
5
+ ReactElement,
6
+ ReactNode,
7
+ ComponentType,
8
+ ComponentProps,
9
+ } from 'react';
5
10
 
6
11
  /**
7
12
  * Internal dependencies
@@ -100,10 +105,6 @@ export type FieldType =
100
105
  | 'email'
101
106
  | 'array';
102
107
 
103
- export type ValidationContext = {
104
- elements?: Option[];
105
- };
106
-
107
108
  /**
108
109
  * An abstract interface for Field based on the field type.
109
110
  */
@@ -116,7 +117,7 @@ export type FieldTypeDefinition< Item > = {
116
117
  /**
117
118
  * Callback used to validate the field.
118
119
  */
119
- isValid: ( item: Item, context?: ValidationContext ) => boolean;
120
+ isValid: Rules< Item >;
120
121
 
121
122
  /**
122
123
  * Callback used to render an edit control for the field or control name.
@@ -145,6 +146,11 @@ export type FieldTypeDefinition< Item > = {
145
146
  enableSorting: boolean;
146
147
  };
147
148
 
149
+ export type Rules< Item > = {
150
+ required?: boolean;
151
+ custom?: ( item: Item, field: NormalizedField< Item > ) => null | string;
152
+ };
153
+
148
154
  /**
149
155
  * A dataview field for a specific property of a data type.
150
156
  */
@@ -198,7 +204,7 @@ export type Field< Item > = {
198
204
  /**
199
205
  * Callback used to validate the field.
200
206
  */
201
- isValid?: ( item: Item, context?: ValidationContext ) => boolean;
207
+ isValid?: Rules< Item >;
202
208
 
203
209
  /**
204
210
  * Callback used to decide if a field should be displayed.
@@ -250,7 +256,7 @@ export type NormalizedField< Item > = Omit< Field< Item >, 'Edit' > & {
250
256
  render: ComponentType< DataViewRenderFieldProps< Item > >;
251
257
  Edit: ComponentType< DataFormControlProps< Item > > | null;
252
258
  sort: ( a: Item, b: Item, direction: SortDirection ) => number;
253
- isValid: ( item: Item, context?: ValidationContext ) => boolean;
259
+ isValid: Rules< Item >;
254
260
  enableHiding: boolean;
255
261
  enableSorting: boolean;
256
262
  filterBy: NormalizedFilterByConfig | false;
@@ -280,6 +286,9 @@ export type DataFormControlProps< Item > = {
280
286
  export type DataViewRenderFieldProps< Item > = {
281
287
  item: Item;
282
288
  field: NormalizedField< Item >;
289
+ config?: {
290
+ sizes: string;
291
+ };
283
292
  };
284
293
 
285
294
  /**
@@ -300,6 +309,11 @@ export interface Filter {
300
309
  * The value to filter by.
301
310
  */
302
311
  value: any;
312
+
313
+ /**
314
+ * Whether the filter can be edited by the user.
315
+ */
316
+ isLocked?: boolean;
303
317
  }
304
318
 
305
319
  export interface NormalizedFilter {
@@ -337,6 +351,11 @@ export interface NormalizedFilter {
337
351
  * Whether it is a primary filter.
338
352
  */
339
353
  isPrimary: boolean;
354
+
355
+ /**
356
+ * Whether the filter can be edited by the user.
357
+ */
358
+ isLocked: boolean;
340
359
  }
341
360
 
342
361
  interface ViewBase {
@@ -616,6 +635,7 @@ export interface ViewBaseProps< Item > {
616
635
  ) => ReactElement;
617
636
  isItemClickable: ( item: Item ) => boolean;
618
637
  view: View;
638
+ empty: ReactNode;
619
639
  }
620
640
 
621
641
  export interface ViewTableProps< Item > extends ViewBaseProps< Item > {
package/src/validation.ts CHANGED
@@ -21,7 +21,36 @@ export function isItemValid< Item >(
21
21
  const _fields = normalizeFields(
22
22
  fields.filter( ( { id } ) => !! form.fields?.includes( id ) )
23
23
  );
24
+
25
+ const isEmptyNullOrUndefined = ( value: any ) =>
26
+ [ undefined, '', null ].includes( value );
27
+
24
28
  return _fields.every( ( field ) => {
25
- return field.isValid( item, { elements: field.elements } );
29
+ const value = field.getValue( { item } );
30
+
31
+ if ( field.isValid.required ) {
32
+ if (
33
+ ( field.type === 'text' && isEmptyNullOrUndefined( value ) ) ||
34
+ ( field.type === 'email' && isEmptyNullOrUndefined( value ) ) ||
35
+ ( field.type === 'integer' &&
36
+ isEmptyNullOrUndefined( value ) ) ||
37
+ ( field.type === undefined && isEmptyNullOrUndefined( value ) )
38
+ ) {
39
+ return false;
40
+ }
41
+
42
+ if ( field.type === 'boolean' && value !== true ) {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ if (
48
+ typeof field.isValid.custom === 'function' &&
49
+ field.isValid.custom( item, field ) !== null
50
+ ) {
51
+ return false;
52
+ }
53
+
54
+ return true;
26
55
  } );
27
56
  }
package/tsconfig.json CHANGED
@@ -17,6 +17,7 @@
17
17
  { "path": "../element" },
18
18
  { "path": "../i18n" },
19
19
  { "path": "../icons" },
20
+ { "path": "../keycodes" },
20
21
  { "path": "../primitives" },
21
22
  { "path": "../private-apis" },
22
23
  { "path": "../url" },