@wordpress/dataviews 10.3.1-next.2f1c7c01b.0 → 10.4.1-next.dc3f6d3c1.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 (149) hide show
  1. package/CHANGELOG.md +23 -5
  2. package/README.md +413 -148
  3. package/build/components/dataviews-filters/filter.js +11 -1
  4. package/build/components/dataviews-filters/filter.js.map +2 -2
  5. package/build/components/dataviews-view-config/index.js +11 -396
  6. package/build/components/dataviews-view-config/index.js.map +3 -3
  7. package/build/components/dataviews-view-config/properties-section.js +177 -0
  8. package/build/components/dataviews-view-config/properties-section.js.map +7 -0
  9. package/build/constants.js +3 -0
  10. package/build/constants.js.map +2 -2
  11. package/build/dataform-controls/date.js +23 -7
  12. package/build/dataform-controls/date.js.map +2 -2
  13. package/build/dataform-controls/email.js +1 -1
  14. package/build/dataform-controls/email.js.map +1 -1
  15. package/build/dataform-layouts/details/index.js +78 -0
  16. package/build/dataform-layouts/details/index.js.map +7 -0
  17. package/build/dataform-layouts/index.js +5 -0
  18. package/build/dataform-layouts/index.js.map +3 -3
  19. package/build/dataform-layouts/normalize-form.js +5 -0
  20. package/build/dataform-layouts/normalize-form.js.map +2 -2
  21. package/build/dataviews-layouts/index.js +9 -0
  22. package/build/dataviews-layouts/index.js.map +3 -3
  23. package/build/dataviews-layouts/picker-table/index.js +422 -0
  24. package/build/dataviews-layouts/picker-table/index.js.map +7 -0
  25. package/build/dataviews-layouts/table/column-header-menu.js.map +2 -2
  26. package/build/dataviews-layouts/table/column-primary.js +1 -6
  27. package/build/dataviews-layouts/table/column-primary.js.map +2 -2
  28. package/build/dataviews-layouts/table/index.js +47 -2
  29. package/build/dataviews-layouts/table/index.js.map +2 -2
  30. package/build/field-types/date.js +4 -2
  31. package/build/field-types/date.js.map +2 -2
  32. package/build/types/dataform.js.map +1 -1
  33. package/build/types/dataviews.js.map +1 -1
  34. package/build/types/field-api.js.map +1 -1
  35. package/build/utils/normalize-fields.js +23 -3
  36. package/build/utils/normalize-fields.js.map +2 -2
  37. package/build/utils/week-starts-on.js +59 -0
  38. package/build/utils/week-starts-on.js.map +7 -0
  39. package/build-module/components/dataviews-filters/filter.js +11 -1
  40. package/build-module/components/dataviews-filters/filter.js.map +2 -2
  41. package/build-module/components/dataviews-view-config/index.js +15 -412
  42. package/build-module/components/dataviews-view-config/index.js.map +2 -2
  43. package/build-module/components/dataviews-view-config/properties-section.js +149 -0
  44. package/build-module/components/dataviews-view-config/properties-section.js.map +7 -0
  45. package/build-module/constants.js +2 -0
  46. package/build-module/constants.js.map +2 -2
  47. package/build-module/dataform-controls/date.js +23 -7
  48. package/build-module/dataform-controls/date.js.map +2 -2
  49. package/build-module/dataform-controls/email.js +2 -2
  50. package/build-module/dataform-controls/email.js.map +1 -1
  51. package/build-module/dataform-layouts/details/index.js +47 -0
  52. package/build-module/dataform-layouts/details/index.js.map +7 -0
  53. package/build-module/dataform-layouts/index.js +5 -0
  54. package/build-module/dataform-layouts/index.js.map +2 -2
  55. package/build-module/dataform-layouts/normalize-form.js +5 -0
  56. package/build-module/dataform-layouts/normalize-form.js.map +2 -2
  57. package/build-module/dataviews-layouts/index.js +11 -1
  58. package/build-module/dataviews-layouts/index.js.map +2 -2
  59. package/build-module/dataviews-layouts/picker-table/index.js +397 -0
  60. package/build-module/dataviews-layouts/picker-table/index.js.map +7 -0
  61. package/build-module/dataviews-layouts/table/column-header-menu.js.map +2 -2
  62. package/build-module/dataviews-layouts/table/column-primary.js +1 -6
  63. package/build-module/dataviews-layouts/table/column-primary.js.map +2 -2
  64. package/build-module/dataviews-layouts/table/index.js +48 -3
  65. package/build-module/dataviews-layouts/table/index.js.map +2 -2
  66. package/build-module/field-types/date.js +5 -3
  67. package/build-module/field-types/date.js.map +2 -2
  68. package/build-module/utils/normalize-fields.js +23 -3
  69. package/build-module/utils/normalize-fields.js.map +2 -2
  70. package/build-module/utils/week-starts-on.js +32 -0
  71. package/build-module/utils/week-starts-on.js.map +7 -0
  72. package/build-style/style-rtl.css +58 -54
  73. package/build-style/style.css +58 -54
  74. package/build-types/components/dataviews-filters/filter.d.ts.map +1 -1
  75. package/build-types/components/dataviews-filters/utils.d.ts.map +1 -1
  76. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  77. package/build-types/components/dataviews-view-config/properties-section.d.ts +4 -0
  78. package/build-types/components/dataviews-view-config/properties-section.d.ts.map +1 -0
  79. package/build-types/constants.d.ts +1 -0
  80. package/build-types/constants.d.ts.map +1 -1
  81. package/build-types/dataform-controls/date.d.ts.map +1 -1
  82. package/build-types/dataform-layouts/details/index.d.ts +6 -0
  83. package/build-types/dataform-layouts/details/index.d.ts.map +1 -0
  84. package/build-types/dataform-layouts/get-summary-fields.d.ts.map +1 -1
  85. package/build-types/dataform-layouts/index.d.ts +5 -0
  86. package/build-types/dataform-layouts/index.d.ts.map +1 -1
  87. package/build-types/dataform-layouts/normalize-form.d.ts.map +1 -1
  88. package/build-types/dataviews-layouts/index.d.ts +8 -0
  89. package/build-types/dataviews-layouts/index.d.ts.map +1 -1
  90. package/build-types/dataviews-layouts/picker-table/index.d.ts +4 -0
  91. package/build-types/dataviews-layouts/picker-table/index.d.ts.map +1 -0
  92. package/build-types/dataviews-layouts/table/column-header-menu.d.ts +3 -3
  93. package/build-types/dataviews-layouts/table/column-header-menu.d.ts.map +1 -1
  94. package/build-types/dataviews-layouts/table/column-primary.d.ts.map +1 -1
  95. package/build-types/dataviews-layouts/table/index.d.ts.map +1 -1
  96. package/build-types/field-types/date.d.ts.map +1 -1
  97. package/build-types/stories/dataform.story.d.ts +3 -0
  98. package/build-types/stories/dataform.story.d.ts.map +1 -1
  99. package/build-types/stories/dataviews-picker.story.d.ts +2 -0
  100. package/build-types/stories/dataviews-picker.story.d.ts.map +1 -1
  101. package/build-types/stories/dataviews.story.d.ts +7 -1
  102. package/build-types/stories/dataviews.story.d.ts.map +1 -1
  103. package/build-types/stories/field-types.story.d.ts +27 -1
  104. package/build-types/stories/field-types.story.d.ts.map +1 -1
  105. package/build-types/types/dataform.d.ts +11 -3
  106. package/build-types/types/dataform.d.ts.map +1 -1
  107. package/build-types/types/dataviews.d.ts +23 -2
  108. package/build-types/types/dataviews.d.ts.map +1 -1
  109. package/build-types/types/field-api.d.ts +28 -1
  110. package/build-types/types/field-api.d.ts.map +1 -1
  111. package/build-types/utils/normalize-fields.d.ts.map +1 -1
  112. package/build-types/utils/week-starts-on.d.ts +20 -0
  113. package/build-types/utils/week-starts-on.d.ts.map +1 -0
  114. package/build-wp/index.js +1497 -1188
  115. package/package.json +15 -15
  116. package/src/components/dataviews/style.scss +2 -0
  117. package/src/components/dataviews-filters/filter.tsx +11 -1
  118. package/src/components/dataviews-footer/style.scss +1 -1
  119. package/src/components/dataviews-view-config/index.tsx +8 -504
  120. package/src/components/dataviews-view-config/properties-section.tsx +201 -0
  121. package/src/components/dataviews-view-config/style.scss +2 -39
  122. package/src/constants.ts +1 -0
  123. package/src/dataform-controls/date.tsx +24 -6
  124. package/src/dataform-controls/email.tsx +2 -2
  125. package/src/dataform-layouts/details/index.tsx +71 -0
  126. package/src/dataform-layouts/details/style.scss +5 -0
  127. package/src/dataform-layouts/index.tsx +5 -0
  128. package/src/dataform-layouts/normalize-form.ts +6 -0
  129. package/src/dataviews-layouts/index.ts +10 -0
  130. package/src/dataviews-layouts/list/style.scss +1 -0
  131. package/src/dataviews-layouts/picker-table/index.tsx +487 -0
  132. package/src/dataviews-layouts/picker-table/style.scss +47 -0
  133. package/src/dataviews-layouts/table/column-header-menu.tsx +3 -2
  134. package/src/dataviews-layouts/table/column-primary.tsx +4 -7
  135. package/src/dataviews-layouts/table/index.tsx +55 -2
  136. package/src/dataviews-layouts/table/style.scss +36 -19
  137. package/src/field-types/date.tsx +11 -5
  138. package/src/stories/dataform.story.tsx +84 -0
  139. package/src/stories/dataviews-picker.story.tsx +11 -6
  140. package/src/stories/dataviews.story.tsx +10 -2
  141. package/src/stories/field-types.story.tsx +67 -2
  142. package/src/style.scss +2 -0
  143. package/src/test/normalize-fields.ts +53 -0
  144. package/src/types/dataform.ts +18 -3
  145. package/src/types/dataviews.ts +36 -2
  146. package/src/types/field-api.ts +42 -1
  147. package/src/utils/normalize-fields.ts +51 -2
  148. package/src/utils/week-starts-on.ts +46 -0
  149. package/tsconfig.tsbuildinfo +1 -1
package/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # The `@wordpress/dataviews` package
2
2
 
3
- The DataViews package offers two React components and a few utilities to work with a list of data:
3
+ This package offers three React components and a few utilities to work with a list of data:
4
4
 
5
5
  - `DataViews`: to render the dataset using different types of layouts (table, grid, list) and interaction capabilities (search, filters, sorting, etc.).
6
+ - `DataViewsPicker`: to render the dataset optimized for selection or picking of items.
6
7
  - `DataForm`: to edit the items of the dataset.
7
8
 
8
9
  ## Installation
@@ -17,17 +18,19 @@ npm install @wordpress/dataviews --save
17
18
 
18
19
  <div class="callout callout-info">At <a href="https://wordpress.github.io/gutenberg/">WordPress Gutenberg's Storybook</a> there's an <a href="https://wordpress.github.io/gutenberg/?path=/docs/dataviews-dataviews--docs">example implementation of the Dataviews component</a>.</div>
19
20
 
20
- **Important note** If you're trying to use the `DataViews` component in a WordPress plugin or theme and you're building your scripts using the `@wordpress/scripts` package, you need to import the components from `@wordpress/dataviews/wp` instead of `@wordpress/dataviews`.
21
-
22
21
  ### Usage
23
22
 
24
23
  The `DataViews` component receives data and some other configuration to render the dataset. It'll call the `onChangeView` callback every time the user has interacted with the dataset in some way (sorted, filtered, changed layout, etc.):
25
24
 
26
25
  ![DataViews flow](https://developer.wordpress.org/files/2024/09/368600071-20aa078f-7c3d-406d-8dd0-8b764addd22a.png 'DataViews flow')
27
26
 
27
+ <div class="callout callout-info">If you're trying to use the DataViews component in a WordPress plugin or theme and you are building your scripts using the `@wordpress/scripts` package, you need to import the components from `@wordpress/dataviews/wp` instead of `@wordpress/dataviews`.</div>
28
+
28
29
  Example:
29
30
 
30
31
  ```jsx
32
+ import { DataViews } from '@wordpress/dataviews';
33
+
31
34
  const Example = () => {
32
35
  const onChangeView = () => {
33
36
  /* React to user changes. */
@@ -116,29 +119,32 @@ const STATUSES = [
116
119
  { value: 'publish', label: __( 'Published' ) },
117
120
  { value: 'trash', label: __( 'Trash' ) },
118
121
  ];
122
+ const AUTHORS = [
123
+ { value: 1, label: 'Admin' },
124
+ { value: 2, label: 'User' },
125
+ ];
126
+
119
127
  const fields = [
120
128
  {
121
129
  id: 'title',
130
+ type: 'text',
122
131
  label: 'Title',
123
132
  enableHiding: false,
124
133
  },
125
134
  {
126
135
  id: 'date',
136
+ type: 'date',
127
137
  label: 'Date',
128
- render: ( { item } ) => {
129
- return <time>{ getFormattedDate( item.date ) }</time>;
130
- },
131
138
  },
132
139
  {
133
140
  id: 'author',
141
+ type: 'integer',
134
142
  label: 'Author',
135
143
  render: ( { item } ) => {
136
- return <a href="...">{ item.author }</a>;
144
+ return AUTHORS.find( ( { value } ) => value === item.author )?.label ??
145
+ item.author,
137
146
  },
138
- elements: [
139
- { value: 1, label: 'Admin' },
140
- { value: 2, label: 'User' },
141
- ],
147
+ elements: AUTHORS,
142
148
  filterBy: {
143
149
  operators: [ 'is', 'isNot' ],
144
150
  },
@@ -146,6 +152,7 @@ const fields = [
146
152
  },
147
153
  {
148
154
  id: 'status',
155
+ type: 'text',
149
156
  label: 'Status',
150
157
  getValue: ( { item } ) =>
151
158
  STATUSES.find( ( { value } ) => value === item.status )?.label ??
@@ -197,10 +204,8 @@ Properties:
197
204
  - `perPage`: number of records to show per page.
198
205
  - `page`: the page that is visible.
199
206
  - `sort`:
200
-
201
207
  - `field`: the field used for sorting the dataset.
202
208
  - `direction`: the direction to use for sorting, one of `asc` or `desc`.
203
-
204
209
  - `titleField`: The id of the field representing the title of the record.
205
210
  - `mediaField`: The id of the field representing the media of the record.
206
211
  - `descriptionField`: The id of the field representing the description of the record.
@@ -517,7 +522,7 @@ The component behaves differently to a regular `DataViews` component in the foll
517
522
 
518
523
  There are also a few differences in the implementation:
519
524
 
520
- - Currently only the `pickerGrid` layout is supported for `DataViewsPicker`. This layout is very similar to the regular `grid` layout.
525
+ - Only the `pickerGrid` and `pickerTable` layout types are supported for `DataViewsPicker`. These layouts are similar to the regular `grid` and `table` layouts respectively.
521
526
  - The picker component is used as a 'controlled' component, so `selection` and `onChangeSelection` should be provided as props. This is so that implementers can access the full range of selected items across pages.
522
527
  - An optional `itemListLabel` prop can be supplied to the `DataViewsPicker` component. This is added as an `aria-label` to the `listbox` element, and should be supplied if there's no heading element associated with the `DataViewsPicker` UI.
523
528
  - The `isItemClickable`, `renderItemLink` and `onClickItem` prop are unsupported for `DataViewsPicker`.
@@ -572,6 +577,113 @@ const Example = () => {
572
577
 
573
578
  ### Properties
574
579
 
580
+ The `DataViewsPicker` component accepts most of the same properties as `DataViews`, with some key differences noted below.
581
+
582
+ #### `data`: `Object[]`
583
+
584
+ Same as `DataViews`. A one-dimensional array of objects.
585
+
586
+ #### `fields`: `Object[]`
587
+
588
+ Same as `DataViews`. The fields describe the visible items for each record in the dataset. See "Fields API" for a description of every property.
589
+
590
+ #### `view`: `Object`
591
+
592
+ Same as `DataViews`. The view object configures how the dataset is visible to the user. Note that only the `pickerGrid` and `pickerTable` layout types are supported.
593
+
594
+ #### `onChangeView`: `function`
595
+
596
+ Same as `DataViews`. Callback executed when the view has changed.
597
+
598
+ #### `actions`: `Object[]`
599
+
600
+ A list of actions that can be performed on the dataset. See "Actions API" for more details.
601
+
602
+ **Important differences from `DataViews`:**
603
+ - Only `callback` style actions are supported. `RenderModal` is unsupported.
604
+ - The `isEligible` callback for actions is unsupported.
605
+ - The `isPrimary` option is used to render a `primary` variant of `Button`.
606
+ - To implement multi-selection, ensure all actions have `supportsBulk: true`. For single selection use `supportsBulk: false`.
607
+
608
+ #### `paginationInfo`: `Object`
609
+
610
+ Same as `DataViews`. Contains `totalItems` and `totalPages` properties, and optionally `infiniteScrollHandler`.
611
+
612
+ #### `search`: `boolean`
613
+
614
+ Same as `DataViews`. Whether the search input is enabled. `true` by default.
615
+
616
+ #### `searchLabel`: `string`
617
+
618
+ Same as `DataViews`. What text to show in the search input. "Search" by default.
619
+
620
+ #### `isLoading`: `boolean`
621
+
622
+ Same as `DataViews`. Whether the data is loading. `false` by default.
623
+
624
+ #### `defaultLayouts`: `Record< string, view >`
625
+
626
+ Limits the available layouts. Only `pickerGrid` and `pickerTable` are supported for `DataViewsPicker`.
627
+
628
+ Example:
629
+
630
+ ```js
631
+ const defaultLayouts = {
632
+ pickerGrid: {
633
+ showTitle: false,
634
+ },
635
+ pickerTable: {},
636
+ };
637
+ ```
638
+
639
+ #### `selection`: `string[]`
640
+
641
+ **Required** for `DataViewsPicker`. The list of selected items' ids.
642
+
643
+ Unlike `DataViews`, the picker component must be used as a controlled component, so this prop is required along with `onChangeSelection`.
644
+
645
+ #### `onChangeSelection`: `function`
646
+
647
+ **Required** for `DataViewsPicker`. Callback that signals the user selected one or more items. It receives the list of selected items' IDs as a parameter.
648
+
649
+ #### `getItemId`: `function`
650
+
651
+ Same as `DataViews`. A function that receives an item and returns a unique identifier for it. Optional, defaults to returning `item.id`.
652
+
653
+ #### `itemListLabel`: `string`
654
+
655
+ Optional. An accessible label for the list of items. This is added as an `aria-label` to the `listbox` element, and should be supplied if there's no heading element associated with the `DataViewsPicker` UI.
656
+
657
+ Example:
658
+
659
+ ```js
660
+ {
661
+ itemListLabel: 'Select a page';
662
+ }
663
+ ```
664
+
665
+ #### `config`: { perPageSizes: number[] }
666
+
667
+ Same as `DataViews`. Optional. Pass an object with a list of `perPageSizes` to control the available item counts per page.
668
+
669
+ #### `empty`: React node
670
+
671
+ Same as `DataViews`. An element to display when the `data` prop is empty.
672
+
673
+ #### `children`: React node
674
+
675
+ Optional. Custom UI to render instead of the default picker layout. When provided, you can use the same subcomponents as `DataViews` for free composition.
676
+
677
+ **Unsupported properties:**
678
+
679
+ The following `DataViews` properties are **not supported** by `DataViewsPicker`:
680
+
681
+ - `isItemClickable`
682
+ - `renderItemLink`
683
+ - `onClickItem`
684
+ - `getItemLevel`
685
+ - `header`
686
+
575
687
  ## `DataForm`
576
688
 
577
689
  <div class="callout callout-info">At <a href="https://wordpress.github.io/gutenberg/">WordPress Gutenberg's Storybook</a> there's and <a href="https://wordpress.github.io/gutenberg/?path=/docs/dataviews-dataform--docs">example implementation of the DataForm component</a>.</div>
@@ -621,7 +733,7 @@ const fields = [
621
733
  },
622
734
  {
623
735
  id: 'author',
624
- type: 'text'
736
+ type: 'text',
625
737
  label: 'Author',
626
738
  elements: [
627
739
  { value: 1, label: 'Admin' },
@@ -996,7 +1108,7 @@ Example:
996
1108
 
997
1109
  ```js
998
1110
  {
999
- id: 'field_id';
1111
+ id: 'title',
1000
1112
  }
1001
1113
  ```
1002
1114
 
@@ -1004,16 +1116,16 @@ Example:
1004
1116
 
1005
1117
  Field type. One of `text`, `integer`, `number`, `datetime`, `date`, `media`, `boolean`, `email`, `password`, `telephone`, `color`, `url`, `array`.
1006
1118
 
1007
- If a field declares a `type`, it gets default implementations for the `sort`, `isValid`, and `Edit` functions if no other values are specified.
1008
-
1009
1119
  - Type: `string`.
1010
1120
  - Optional.
1121
+ - By declaring a type, the field gets a default implementation for all the necessary functions (sorting, render, editing, etc.).
1011
1122
 
1012
1123
  Example:
1013
1124
 
1014
1125
  ```js
1015
1126
  {
1016
- type: 'text';
1127
+ id: 'title',
1128
+ type: 'text',
1017
1129
  }
1018
1130
  ```
1019
1131
 
@@ -1029,27 +1141,32 @@ Example:
1029
1141
 
1030
1142
  ```js
1031
1143
  {
1032
- label: 'Title';
1144
+ id: 'title',
1145
+ type: 'text',
1146
+ label: 'Title',
1033
1147
  }
1034
1148
  ```
1035
1149
 
1036
1150
  ### `header`
1037
1151
 
1038
- React component used by the layouts to display the field name — useful to add icons, etc. It's complementary to the `label` property.
1152
+ React element used by some layouts (table, grid) to display the field name — useful to add icons, etc.
1039
1153
 
1040
- - Type: React component.
1154
+ - Type: React element.
1041
1155
  - Optional.
1042
1156
  - Defaults to the `label` value.
1043
- - Props: none.
1044
- - Returns a React element that represents the field's name.
1045
1157
 
1046
1158
  Example:
1047
1159
 
1048
1160
  ```js
1049
1161
  {
1050
- header: () => {
1051
- /* Returns a react element. */
1052
- };
1162
+ id: 'title',
1163
+ type: 'text',
1164
+ header: (
1165
+ <HStack spacing={ 1 } justify="start">
1166
+ <Icon icon={ icon } />
1167
+ <span>Title</span>
1168
+ </HStack>
1169
+ ),
1053
1170
  }
1054
1171
  ```
1055
1172
 
@@ -1094,6 +1211,7 @@ const item = {
1094
1211
  // Field definition
1095
1212
  {
1096
1213
  id: 'title',
1214
+ type: 'text',
1097
1215
  label: 'Title'
1098
1216
  // getValue: automatically becomes ( { item } ) => item.title
1099
1217
  // setValue: automatically becomes ( { value } ) => ( { title: value } )
@@ -1118,6 +1236,7 @@ const item = {
1118
1236
  // Field definition - using dot notation (automatic)
1119
1237
  {
1120
1238
  id: 'user.profile.name',
1239
+ type: 'text',
1121
1240
  label: 'User Name'
1122
1241
  // getValue: automatically becomes ( { item } ) => item.user.profile.name
1123
1242
  // setValue: automatically becomes ( { value } ) => ( { user: { profile: { name: value } } } )
@@ -1126,6 +1245,7 @@ const item = {
1126
1245
  // Alternative - using simple ID with custom functions
1127
1246
  {
1128
1247
  id: 'userName',
1248
+ type: 'text',
1129
1249
  label: 'User Name',
1130
1250
  getValue: ( { item } ) => item.user.profile.name,
1131
1251
  setValue: ( { value } ) => ( {
@@ -1153,6 +1273,7 @@ const item = {
1153
1273
  // Field definition - transform boolean to string options
1154
1274
  {
1155
1275
  id: 'notifications',
1276
+ type: 'boolean',
1156
1277
  label: 'Notifications',
1157
1278
  Edit: 'radio',
1158
1279
  elements: [
@@ -1171,21 +1292,25 @@ const item = {
1171
1292
 
1172
1293
  ### `render`
1173
1294
 
1174
- React component that renders the field. This is used by the layouts.
1295
+ React component that renders the field.
1175
1296
 
1176
1297
  - Type: React component.
1177
1298
  - Optional.
1178
- - Defaults to `getValue`.
1299
+ - The field `type` provides a default render based on `getValue` and `elements` (if provided).
1179
1300
  - Props
1180
1301
  - `item` value to be processed.
1302
+ - `field` the own field config. Useful to access `getValue`, `elements`, etc.
1181
1303
  - `config` object containing configuration options for the field. It's optional. So far, the only object property available is `sizes`: in fields that are set to be the media field, layouts can pass down the expected size reserved for them so that the field can react accordingly.
1182
1304
  - Returns a React element that represents the field's value.
1183
1305
 
1184
- Example:
1306
+ Example of a custom render function:
1185
1307
 
1186
1308
  ```js
1187
1309
  {
1188
- render: ( { item } ) => {
1310
+ id: 'title',
1311
+ type: 'text',
1312
+ label: 'Title',
1313
+ render: ( { item, field, config } ) => {
1189
1314
  /* React element to be displayed. */
1190
1315
  };
1191
1316
  }
@@ -1195,101 +1320,139 @@ Example:
1195
1320
 
1196
1321
  React component that renders the control to edit the field.
1197
1322
 
1198
- - Type: React component | `string`. If it's a string, it needs to be one of `array`, `checkbox`, `color`, `datetime`, `date`, `email`, `telephone`, `url`, `integer`, `number`, `password`, `radio`, `select`, `text`, `toggle`, `textarea`, `toggleGroup`.
1199
- - Required by DataForm. Optional if the field provided a `type`.
1200
- - Props:
1201
- - `data`: the item to be processed
1202
- - `field`: the field definition
1203
- - `onChange`: the callback with the updates
1204
- - `hideLabelFromVision`: boolean representing if the label should be hidden
1205
- - Returns a React element to edit the field's value.
1323
+ - Type: `string` | `object` | React component.
1324
+ - Optional.
1325
+ - The field `type` provides a default implementation.
1206
1326
 
1207
- Example:
1327
+ Fields that provide a `type` will have a default Edit control:
1208
1328
 
1209
1329
  ```js
1210
- // A custom control defined by the field.
1211
1330
  {
1212
- Edit: ( { data, field, onChange, hideLabelFromVision } ) => {
1213
- const value = field.getValue( { item: data } );
1214
-
1215
- return (
1216
- <CustomTimePicker
1217
- value={ value }
1218
- onChange={ onChange }
1219
- hideLabelFromVision
1220
- />
1221
- );
1222
- };
1331
+ id: 'categories',
1332
+ type: 'text',
1333
+ label: 'Categories',
1223
1334
  }
1224
1335
  ```
1225
1336
 
1337
+ Field authors can override the default Edit control by providing a string that maps to one of the bundled UI controls: `array`, `checkbox`, `color`, `date`, `datetime`, `email`, `integer`, `number`, `password`, `radio`, `select`, `telephone`, `text`, `textarea`, `toggle`, `toggleGroup`, or `url`.
1338
+
1226
1339
  ```js
1227
- // Use one of the core controls.
1228
1340
  {
1229
- Edit: 'radio';
1341
+ id: 'categories',
1342
+ type: 'text',
1343
+ label: 'Categories',
1344
+ Edit: 'radio',
1230
1345
  }
1231
1346
  ```
1232
1347
 
1348
+ Additionally, some of the bundled Edit controls are configurable via a config object:
1349
+
1350
+ - `textarea` configuration:
1351
+
1233
1352
  ```js
1234
- // Edit is optional when field's type is present.
1235
- // The field will use the default Edit function for text.
1236
1353
  {
1237
- type: 'text';
1354
+ id: 'description',
1355
+ type: 'text',
1356
+ label: 'Description',
1357
+ Edit: {
1358
+ control: 'textarea',
1359
+ rows: 5
1360
+ }
1238
1361
  }
1239
1362
  ```
1240
1363
 
1364
+ - `text` configuration:
1365
+
1241
1366
  ```js
1242
- // Edit can be provided even if field's type is present.
1243
- // The field will use its own custom control.
1244
1367
  {
1368
+ id: 'title',
1245
1369
  type: 'text',
1246
- Edit: 'radio'
1370
+ label: 'Title',
1371
+ Edit: {
1372
+ control: 'text',
1373
+ prefix: ReactComponent,
1374
+ suffix: ReactComponent,
1375
+ }
1376
+ }
1377
+ ```
1378
+
1379
+ Finally, the field author can always provide its own custom `Edit` control. It receives the following props:
1380
+
1381
+ - `data`: the item to be processed
1382
+ - `field`: the field definition
1383
+ - `onChange`: the callback with the updates
1384
+ - `hideLabelFromVision`: boolean representing if the label should be hidden
1385
+ - `validity`: object representing the validity of the field's value (see validity section)
1386
+ - `config`: object representing extra config for the component:
1387
+ - `prefix`: a React component to be rendered as a prefix
1388
+ - `suffix`: a React component to be rendered as a suffix
1389
+ - `rows`: the number of rows to display (e.g., in the text area component)
1390
+
1391
+ ```js
1392
+ {
1393
+ id: 'time',
1394
+ type: 'datetime',
1395
+ label: 'Time of day',
1396
+ Edit: ( {
1397
+ data,
1398
+ field,
1399
+ onChange,
1400
+ hideLabelFromVision,
1401
+ validity,
1402
+ config,
1403
+ } ) => {
1404
+ const value = field.getValue( { item: data } );
1405
+
1406
+ return (
1407
+ <CustomTimePicker
1408
+ value={ value }
1409
+ onChange={ onChange }
1410
+ hideLabelFromVision
1411
+ />
1412
+ );
1413
+ };
1247
1414
  }
1248
1415
  ```
1249
1416
 
1417
+
1250
1418
  ### `sort`
1251
1419
 
1252
1420
  Function to sort the records.
1253
1421
 
1254
1422
  - Type: `function`.
1255
1423
  - Optional.
1256
- - Args
1257
- - `a`: the first item to compare
1258
- - `b`: the second item to compare
1259
- - `direction`: either `asc` (ascending) or `desc` (descending)
1260
- - Returns a number where:
1261
- - a negative value indicates that `a` should come before `b`
1262
- - a positive value indicates that `a` should come after `b`
1263
- - 0 indicates that `a` and `b` are considered equal
1264
1424
 
1265
- Example:
1425
+ When the field declares a type, it gets a default sort function:
1266
1426
 
1267
1427
  ```js
1268
- // A custom sort function defined by the field.
1269
1428
  {
1270
- sort: ( a, b, direction ) => {
1271
- return direction === 'asc'
1272
- ? a.localeCompare( b )
1273
- : b.localeCompare( a );
1274
- };
1429
+ id: 'title',
1430
+ type: 'text',
1431
+ label: 'Title',
1275
1432
  }
1276
1433
  ```
1277
1434
 
1278
- ```js
1279
- // If field type is provided,
1280
- // the field gets a default sort function.
1281
- {
1282
- type: 'number';
1283
- }
1284
- ```
1435
+ The default sorting can be overriden by providing a custom sort function. It takes the following arguments:
1436
+
1437
+ - `a`: the first item to compare
1438
+ - `b`: the second item to compare
1439
+ - `direction`: either `asc` (ascending) or `desc` (descending)
1440
+
1441
+ It should return a number where:
1442
+
1443
+ - a negative value indicates that `a` should come before `b`
1444
+ - a positive value indicates that `a` should come after `b`
1445
+ - 0 indicates that `a` and `b` are considered equal
1285
1446
 
1286
1447
  ```js
1287
- // Even if a field type is provided,
1288
- // fields can override the default sort function assigned for that type.
1289
1448
  {
1290
- type: 'number';
1449
+ id: 'title',
1450
+ type: 'text',
1451
+ label: 'Title',
1291
1452
  sort: ( a, b, direction ) => {
1292
- /* Custom sort */
1453
+ return direction === 'asc'
1454
+ ? a.localeCompare( b )
1455
+ : b.localeCompare( a );
1293
1456
  };
1294
1457
  }
1295
1458
  ```
@@ -1299,41 +1462,34 @@ Example:
1299
1462
  Object that contains the validation rules for the field. If a rule is not met, the control will be marked as invalid and a message will be displayed.
1300
1463
 
1301
1464
  - `required`: boolean indicating whether the field is required or not. Disabled by default.
1302
- - `elements`: boolean restricting selection to the provided list of elements only. Enabled by default. The `array` Edit control uses it to restrict the input values as well.
1465
+ - `elements`: boolean restricting selection to the provided list of elements only. Enabled by default. The `array` Edit control uses it to restrict the input values.
1303
1466
  - `custom`: a function that validates a field's value. If the value is invalid, the function should return a string explaining why the value is invalid. Otherwise, the function must return null.
1304
1467
 
1305
- Example:
1306
-
1307
- ```js
1308
- {
1309
- isValid: {
1310
- custom: ( item: Item, field: NormalizedField<Item> ) => {
1311
- if ( /* item value is invalid */) {
1312
- return 'Reason why item value is invalid';
1313
- }
1314
-
1315
- return null;
1316
- }
1317
- }
1318
- }
1319
- ```
1320
-
1321
- Note that fields that define a type (e.g., `integer`) come with default validation for the type. For example, the `integer` type if the value is a valid integer:
1468
+ Fields that define a type come with default validation for the type. For example, the `integer` type ensures that the value is a valid integer:
1322
1469
 
1323
1470
  ```js
1324
1471
  {
1472
+ id: 'itemsSold',
1325
1473
  type: 'integer',
1474
+ label: 'Items sold',
1326
1475
  }
1327
1476
  ```
1328
1477
 
1329
- However, this can be overriden by the field author:
1478
+ The validation rules can be overriden by the field author. For example, to set the field as required, or to provide a custom validation so that only even numbers are valid:
1330
1479
 
1331
1480
  ```js
1332
1481
  {
1482
+ id: 'itemsSold',
1333
1483
  type: 'integer',
1484
+ label: 'Items sold',
1334
1485
  isValid: {
1486
+ required: true,
1335
1487
  custom: ( item: Item, field: NormalizedField<Item> ) => {
1336
- /* Your custom validation logic. */
1488
+ if ( field.getValue({ item }) % 2 !== 0 ) {
1489
+ return 'Integer must be an even number.';
1490
+ }
1491
+
1492
+ return null;
1337
1493
  }
1338
1494
  }
1339
1495
  }
@@ -1343,6 +1499,9 @@ Fields that define their own Edit component have access to the validation rules
1343
1499
 
1344
1500
  ```js
1345
1501
  {
1502
+ id: 'itemsSold',
1503
+ type: 'integer',
1504
+ label: 'Items sold',
1346
1505
  Edit: ( { field } ) => {
1347
1506
  return <input required={ !! field.isValid.required } />;
1348
1507
  };
@@ -1359,15 +1518,28 @@ Function that indicates if the field should be visible.
1359
1518
  - `item`: the data to be processed
1360
1519
  - Returns a `boolean` indicating if the field should be visible (`true`) or not (`false`).
1361
1520
 
1362
- Example:
1521
+ This can be useful to hide fields based on the state of other fields. For example, a `staticHomepage` field can be hidden depending on the value of the `homepageDisplay` field:
1363
1522
 
1364
1523
  ```js
1365
- // Custom isVisible function.
1366
1524
  {
1367
- isVisible: ( item ) => {
1368
- /* Custom implementation. */
1369
- };
1370
- }
1525
+ id: 'homepageDisplay',
1526
+ type: 'text',
1527
+ label: 'Homepage display',
1528
+ elements: [
1529
+ { value: 'latest', label: 'Latest post' },
1530
+ { value: 'static', label: 'Static page' },
1531
+ ],
1532
+ },
1533
+ {
1534
+ id: 'staticHomepage',
1535
+ type: 'text',
1536
+ label: 'Static homepage',
1537
+ elements: [
1538
+ { value: 'welcome', label: 'Welcome to my website' },
1539
+ { value: 'about', label: 'About' },
1540
+ ],
1541
+ isVisible: ( item ) => item.homepageDisplay === 'static',
1542
+ },
1371
1543
  ```
1372
1544
 
1373
1545
  ### `enableSorting`
@@ -1378,11 +1550,14 @@ Boolean indicating if the field is sortable.
1378
1550
  - Optional.
1379
1551
  - Defaults to `true`.
1380
1552
 
1381
- Example:
1553
+ Example to disable sorting by a field:
1382
1554
 
1383
1555
  ```js
1384
1556
  {
1385
- enableSorting: true;
1557
+ id: 'title',
1558
+ type: 'text',
1559
+ label: 'Title',
1560
+ enableSorting: false,
1386
1561
  }
1387
1562
  ```
1388
1563
 
@@ -1394,11 +1569,14 @@ Boolean indicating if the field can be hidden.
1394
1569
  - Optional.
1395
1570
  - Defaults to `true`.
1396
1571
 
1397
- Example:
1572
+ Example to disable hiding of a field:
1398
1573
 
1399
1574
  ```js
1400
1575
  {
1401
- enableHiding: true;
1576
+ id: 'title',
1577
+ type: 'text',
1578
+ label: 'Title',
1579
+ enableHiding: false,
1402
1580
  }
1403
1581
  ```
1404
1582
 
@@ -1410,17 +1588,20 @@ Boolean indicating if the field is searchable.
1410
1588
  - Optional.
1411
1589
  - Defaults to `false`.
1412
1590
 
1413
- Example:
1591
+ Example to enable global search for a field:
1414
1592
 
1415
1593
  ```js
1416
1594
  {
1417
- enableGlobalSearch: true;
1595
+ id: 'title',
1596
+ type: 'text',
1597
+ label: 'Title',
1598
+ enableGlobalSearch: true,
1418
1599
  }
1419
1600
  ```
1420
1601
 
1421
1602
  ### `elements`
1422
1603
 
1423
- List of valid values for a field. If provided, the field's filter will use these as predefined options instead of using the field's `Edit` function for user input (unless `filterBy` is set to `false`, see below).
1604
+ List of valid values for a field. If provided, the field's filter will use these as predefined options to chose from.
1424
1605
 
1425
1606
  - Type: `array` of objects.
1426
1607
  - Optional.
@@ -1433,6 +1614,9 @@ Example:
1433
1614
 
1434
1615
  ```js
1435
1616
  {
1617
+ id: 'selectedProduct',
1618
+ type: 'integer',
1619
+ label: 'Selected product',
1436
1620
  elements: [
1437
1621
  { value: '1', label: 'Product A' },
1438
1622
  { value: '2', label: 'Product B' },
@@ -1450,6 +1634,9 @@ Note this function may be called many times in the lifetime of the DataViews/Dat
1450
1634
 
1451
1635
  ```js
1452
1636
  {
1637
+ id: 'selectedProduct',
1638
+ type: 'integer',
1639
+ label: 'Selected product',
1453
1640
  getElements: () => {
1454
1641
  return Promise.resolve( [
1455
1642
  { value: '1', label: 'Product A' },
@@ -1463,7 +1650,7 @@ Note this function may be called many times in the lifetime of the DataViews/Dat
1463
1650
 
1464
1651
  ### `filterBy`
1465
1652
 
1466
- Configuration of the filters. By default, fields have filtering enabled using the field's `Edit` function for user input. When `elements` are provided, the filter will use those as predefined options instead. Set to `false` to opt the field out of filtering entirely.
1653
+ Configuration of the filters. Set to `false` to opt the field out of filtering entirely.
1467
1654
 
1468
1655
  - Type: `object` | `boolean`.
1469
1656
  - Optional.
@@ -1472,7 +1659,95 @@ Configuration of the filters. By default, fields have filtering enabled using th
1472
1659
  - `operators`: the list of operators supported by the field. See "operators" below. A filter will support the `isAny` and `isNone` multi-selection operators by default.
1473
1660
  - `isPrimary`: boolean, optional. Indicates if the filter is primary. A primary filter is always visible and is not listed in the "Add filter" component, except for the list layout where it behaves like a secondary filter.
1474
1661
 
1475
- Operators:
1662
+ By default, fields have filtering enabled by using the field's `Edit` function:
1663
+
1664
+ ```js
1665
+ {
1666
+ id: 'product',
1667
+ type: 'text',
1668
+ label: 'Product',
1669
+ }
1670
+ ```
1671
+
1672
+ If the field provides `elements`, the filter will use those as predefined options instead:
1673
+
1674
+ ```js
1675
+ {
1676
+ id: 'product',
1677
+ type: 'text',
1678
+ label: 'Title',
1679
+ elements: [
1680
+ { value: 'a', label: 'Product A' },
1681
+ { value: 'b', label: 'Product B' },
1682
+ { value: 'c', label: 'Product C' },
1683
+ { value: 'd', label: 'Product D' },
1684
+ ]
1685
+ }
1686
+ ```
1687
+
1688
+ A field can opt-out of filtering by setting `filterBy` to `false`:
1689
+
1690
+ ```js
1691
+ {
1692
+ id: 'product',
1693
+ type: 'text',
1694
+ label: 'Product',
1695
+ filterBy: false;
1696
+ }
1697
+ ```
1698
+
1699
+ Fields can declare its filter as primary, which means it'll always be visible and can't be removed by the user:
1700
+
1701
+ ```js
1702
+ {
1703
+ id: 'title',
1704
+ type: 'text',
1705
+ label: 'Title',
1706
+ filterBy: {
1707
+ isPrimary: true;
1708
+ }
1709
+ }
1710
+ ```
1711
+
1712
+ Filters come with default operators per field type, but this is configurable by the field. For example, a field can enable only single-selection operators for the filter:
1713
+
1714
+ ```js
1715
+ {
1716
+ id: 'product',
1717
+ type: 'text',
1718
+ label: 'Product',
1719
+ elements: [
1720
+ { value: 'a', label: 'Product A' },
1721
+ { value: 'b', label: 'Product B' },
1722
+ { value: 'c', label: 'Product C' },
1723
+ { value: 'd', label: 'Product D' },
1724
+ ],
1725
+ filterBy: {
1726
+ operators: [ `is`, `isNot` ];
1727
+ }
1728
+ }
1729
+ ```
1730
+
1731
+ Or multi-selection operators:
1732
+
1733
+ ```js
1734
+ {
1735
+ id: 'product',
1736
+ type: 'text',
1737
+ label: 'Product',
1738
+ elements: [
1739
+ { value: 'a', label: 'Product A' },
1740
+ { value: 'b', label: 'Product B' },
1741
+ { value: 'c', label: 'Product C' },
1742
+ { value: 'd', label: 'Product D' },
1743
+ ],
1744
+ filterBy: {
1745
+ operators: [ `isAny`, `isNone`, `isAll`, `isNotAll` ];
1746
+ }
1747
+ }
1748
+ ```
1749
+
1750
+ The next table lists all available operators:
1476
1751
 
1477
1752
  | Operator | Selection | Description | Example |
1478
1753
  | -------------------- | -------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
@@ -1501,39 +1776,29 @@ Operators:
1501
1776
 
1502
1777
  `is`, `isNot`, `on`, `notOn`, `lessThan`, `greaterThan`, `lessThanOrEqual`, `greaterThanOrEqual`, `before`, `after`, `beforeInc`, `afterInc`, `contains`, `notContains`, and `startsWith` are single-selection operators, while `isAny`, `isNone`, `isAll`, and `isNotAll` are multi-selection. `between` is a special operator that requires two values and it's not supported for preset layout. A filter with no operators declared will support the `isAny` and `isNone` multi-selection operators by default. A filter cannot mix single-selection & multi-selection operators; if a single-selection operator is present in the list of valid operators, the multi-selection ones will be discarded, and the filter won't allow selecting more than one item.
1503
1778
 
1504
- Example:
1505
1779
 
1506
- ```js
1507
- // Set a filter as primary.
1508
- {
1509
- filterBy: {
1510
- isPrimary: true;
1511
- }
1512
- }
1513
- ```
1514
1780
 
1515
- ```js
1516
- // Configure a filter as single-selection.
1517
- {
1518
- filterBy: {
1519
- operators: [ `is`, `isNot` ];
1520
- }
1521
- }
1522
- ```
1781
+ ### `format`
1523
1782
 
1524
- ```js
1525
- // Configure a filter as multi-selection with all the options.
1526
- {
1527
- filterBy: {
1528
- operators: [ `isAny`, `isNone`, `isAll`, `isNotAll` ];
1529
- }
1530
- }
1531
- ```
1783
+ Display format configuration for fields. Currently supported for date fields. This configuration affects how the field is displayed in the `render` method, the `Edit` control, and filter controls.
1784
+
1785
+ - Type: `object`.
1786
+ - Optional.
1787
+ - Properties:
1788
+ - `date`: The format string using PHP date format (e.g., 'F j, Y' for 'March 10, 2023'). Optional, defaults to WordPress "Date Format" setting.
1789
+ - `weekStartsOn`: Specifies the first day of the week for calendar controls. One of `'sunday'`, `'monday'`, `'tuesday'`, `'wednesday'`, `'thursday'`, `'friday'`, `'saturday'`. Optional, defaults to WordPress "Week Starts On" setting.
1790
+
1791
+ Example:
1532
1792
 
1533
1793
  ```js
1534
- // Opt out of filtering entirely.
1535
1794
  {
1536
- filterBy: false;
1795
+ id: 'publishDate',
1796
+ type: 'date',
1797
+ label: 'Publish Date',
1798
+ format: {
1799
+ date: 'F j, Y',
1800
+ weekStartsOn: 'monday',
1801
+ },
1537
1802
  }
1538
1803
  ```
1539
1804