@wordpress/dataviews 5.0.1-next.719a03cbe.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 (254) hide show
  1. package/CHANGELOG.md +34 -4
  2. package/README.md +55 -26
  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 +91 -18
  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 +66 -0
  52. package/build/field-types/date.js.map +1 -0
  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 +20 -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 +28 -14
  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 +93 -20
  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 +60 -0
  121. package/build-module/field-types/date.js.map +1 -0
  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 +20 -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 +28 -14
  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 +84 -41
  142. package/build-style/style.css +84 -41
  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 +1 -0
  148. package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
  149. package/build-types/components/dataviews/stories/index.story.d.ts +16 -2
  150. package/build-types/components/dataviews/stories/index.story.d.ts.map +1 -1
  151. package/build-types/components/dataviews-context/index.d.ts +4 -2
  152. package/build-types/components/dataviews-context/index.d.ts.map +1 -1
  153. package/build-types/components/dataviews-filters/filter.d.ts.map +1 -1
  154. package/build-types/components/dataviews-filters/index.d.ts.map +1 -1
  155. package/build-types/components/dataviews-filters/input-widget.d.ts.map +1 -1
  156. package/build-types/components/dataviews-filters/reset-filters.d.ts.map +1 -1
  157. package/build-types/components/dataviews-layout/index.d.ts.map +1 -1
  158. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  159. package/build-types/components/stories/index.story.d.ts +4 -0
  160. package/build-types/components/stories/index.story.d.ts.map +1 -1
  161. package/build-types/constants.d.ts +2 -2
  162. package/build-types/dataform-controls/boolean.d.ts.map +1 -1
  163. package/build-types/dataform-controls/date.d.ts +3 -0
  164. package/build-types/dataform-controls/date.d.ts.map +1 -0
  165. package/build-types/dataform-controls/datetime.d.ts.map +1 -1
  166. package/build-types/dataform-controls/email.d.ts.map +1 -1
  167. package/build-types/dataform-controls/index.d.ts.map +1 -1
  168. package/build-types/dataform-controls/integer.d.ts.map +1 -1
  169. package/build-types/dataform-controls/relative-date-control.d.ts +46 -0
  170. package/build-types/dataform-controls/relative-date-control.d.ts.map +1 -0
  171. package/build-types/dataform-controls/select.d.ts.map +1 -1
  172. package/build-types/dataform-controls/text.d.ts.map +1 -1
  173. package/build-types/dataviews-layouts/grid/index.d.ts +1 -1
  174. package/build-types/dataviews-layouts/grid/index.d.ts.map +1 -1
  175. package/build-types/dataviews-layouts/grid/preview-size-picker.d.ts +0 -1
  176. package/build-types/dataviews-layouts/grid/preview-size-picker.d.ts.map +1 -1
  177. package/build-types/dataviews-layouts/index.d.ts +3 -3
  178. package/build-types/dataviews-layouts/list/index.d.ts.map +1 -1
  179. package/build-types/dataviews-layouts/table/column-primary.d.ts.map +1 -1
  180. package/build-types/dataviews-layouts/table/index.d.ts +1 -1
  181. package/build-types/dataviews-layouts/table/index.d.ts.map +1 -1
  182. package/build-types/field-types/array.d.ts.map +1 -1
  183. package/build-types/field-types/boolean.d.ts +5 -4
  184. package/build-types/field-types/boolean.d.ts.map +1 -1
  185. package/build-types/field-types/date.d.ts +20 -0
  186. package/build-types/field-types/date.d.ts.map +1 -0
  187. package/build-types/field-types/datetime.d.ts +4 -3
  188. package/build-types/field-types/datetime.d.ts.map +1 -1
  189. package/build-types/field-types/email.d.ts +4 -3
  190. package/build-types/field-types/email.d.ts.map +1 -1
  191. package/build-types/field-types/index.d.ts.map +1 -1
  192. package/build-types/field-types/integer.d.ts +4 -3
  193. package/build-types/field-types/integer.d.ts.map +1 -1
  194. package/build-types/field-types/media.d.ts +4 -3
  195. package/build-types/field-types/media.d.ts.map +1 -1
  196. package/build-types/field-types/text.d.ts +4 -3
  197. package/build-types/field-types/text.d.ts.map +1 -1
  198. package/build-types/filter-and-sort-data-view.d.ts.map +1 -1
  199. package/build-types/normalize-fields.d.ts.map +1 -1
  200. package/build-types/types.d.ts +25 -8
  201. package/build-types/types.d.ts.map +1 -1
  202. package/build-types/validation.d.ts.map +1 -1
  203. package/build-wp/index.js +2196 -739
  204. package/package.json +15 -14
  205. package/src/components/dataform/stories/index.story.tsx +229 -2
  206. package/src/components/dataviews/index.tsx +30 -10
  207. package/src/components/dataviews/stories/fixtures.tsx +82 -59
  208. package/src/components/dataviews/stories/index.story.tsx +65 -8
  209. package/src/components/dataviews/stories/style.css +6 -0
  210. package/src/components/dataviews-context/index.ts +8 -2
  211. package/src/components/dataviews-filters/filter.tsx +17 -7
  212. package/src/components/dataviews-filters/index.tsx +17 -2
  213. package/src/components/dataviews-filters/input-widget.tsx +7 -1
  214. package/src/components/dataviews-filters/reset-filters.tsx +4 -2
  215. package/src/components/dataviews-filters/style.scss +8 -2
  216. package/src/components/dataviews-layout/index.tsx +3 -0
  217. package/src/components/dataviews-view-config/index.tsx +5 -3
  218. package/src/components/stories/index.story.tsx +21 -0
  219. package/src/dataform-controls/boolean.tsx +19 -2
  220. package/src/dataform-controls/date.tsx +499 -0
  221. package/src/dataform-controls/datetime.tsx +5 -91
  222. package/src/dataform-controls/email.tsx +19 -2
  223. package/src/dataform-controls/index.tsx +2 -0
  224. package/src/dataform-controls/integer.tsx +30 -4
  225. package/src/dataform-controls/relative-date-control.tsx +106 -0
  226. package/src/dataform-controls/select.tsx +23 -13
  227. package/src/dataform-controls/style.scss +19 -2
  228. package/src/dataform-controls/text.tsx +19 -2
  229. package/src/dataviews-layouts/grid/index.tsx +168 -55
  230. package/src/dataviews-layouts/grid/preview-size-picker.tsx +48 -73
  231. package/src/dataviews-layouts/grid/style.scss +21 -26
  232. package/src/dataviews-layouts/list/index.tsx +7 -4
  233. package/src/dataviews-layouts/list/style.scss +3 -3
  234. package/src/dataviews-layouts/table/column-primary.tsx +29 -5
  235. package/src/dataviews-layouts/table/index.tsx +134 -42
  236. package/src/dataviews-layouts/table/style.scss +45 -1
  237. package/src/field-types/array.tsx +33 -21
  238. package/src/field-types/boolean.tsx +15 -9
  239. package/src/field-types/date.ts +92 -0
  240. package/src/field-types/datetime.tsx +19 -13
  241. package/src/field-types/email.tsx +26 -21
  242. package/src/field-types/index.tsx +23 -8
  243. package/src/field-types/integer.tsx +26 -22
  244. package/src/field-types/media.tsx +19 -13
  245. package/src/field-types/text.tsx +19 -13
  246. package/src/filter-and-sort-data-view.ts +38 -13
  247. package/src/normalize-fields.ts +4 -8
  248. package/src/test/dataviews.tsx +129 -0
  249. package/src/test/filter-and-sort-data-view.js +150 -31
  250. package/src/test/validation.ts +4 -15
  251. package/src/types.ts +34 -8
  252. package/src/validation.ts +30 -1
  253. package/tsconfig.json +1 -0
  254. package/tsconfig.tsbuildinfo +1 -1
@@ -20,6 +20,7 @@ import {
20
20
  __experimentalText as Text,
21
21
  __experimentalHStack as HStack,
22
22
  __experimentalVStack as VStack,
23
+ Button,
23
24
  } from '@wordpress/components';
24
25
  import { __, _n } from '@wordpress/i18n';
25
26
 
@@ -53,7 +54,7 @@ const defaultLayouts = {
53
54
  [ LAYOUT_LIST ]: {},
54
55
  };
55
56
 
56
- export const Default = () => {
57
+ export const Default = ( { perPageSizes = [ 10, 25, 50, 100 ] } ) => {
57
58
  const [ view, setView ] = useState< View >( {
58
59
  ...DEFAULT_VIEW,
59
60
  fields: [ 'categories' ],
@@ -86,10 +87,22 @@ export const Default = () => {
86
87
  ) }
87
88
  isItemClickable={ () => true }
88
89
  defaultLayouts={ defaultLayouts }
90
+ perPageSizes={ perPageSizes }
89
91
  />
90
92
  );
91
93
  };
92
94
 
95
+ Default.args = {
96
+ perPageSizes: [ 10, 25, 50, 100 ],
97
+ };
98
+
99
+ Default.argTypes = {
100
+ perPageSizes: {
101
+ control: 'object',
102
+ description: 'Array of available page sizes',
103
+ },
104
+ };
105
+
93
106
  export const Empty = () => {
94
107
  const [ view, setView ] = useState< View >( {
95
108
  ...DEFAULT_VIEW,
@@ -110,6 +123,27 @@ export const Empty = () => {
110
123
  );
111
124
  };
112
125
 
126
+ export const CustomEmpty = () => {
127
+ const [ view, setView ] = useState< View >( {
128
+ ...DEFAULT_VIEW,
129
+ fields: [ 'title', 'description', 'categories' ],
130
+ } );
131
+
132
+ return (
133
+ <DataViews
134
+ getItemId={ ( item ) => item.id.toString() }
135
+ paginationInfo={ { totalItems: 0, totalPages: 0 } }
136
+ data={ [] }
137
+ view={ view }
138
+ fields={ fields }
139
+ onChangeView={ setView }
140
+ actions={ actions }
141
+ defaultLayouts={ defaultLayouts }
142
+ empty={ view.search ? 'No sites found' : 'No sites' }
143
+ />
144
+ );
145
+ };
146
+
113
147
  export const FieldsNoSortableNoHidable = () => {
114
148
  const [ view, setView ] = useState< View >( {
115
149
  ...DEFAULT_VIEW,
@@ -261,6 +295,19 @@ export const FreeComposition = () => {
261
295
  table: {},
262
296
  grid: {},
263
297
  } }
298
+ empty={
299
+ <VStack
300
+ justify="space-around"
301
+ alignment="center"
302
+ className="free-composition-dataviews-empty"
303
+ >
304
+ <Text size={ 18 } as="p">
305
+ No planets
306
+ </Text>
307
+ <Text variant="muted">{ `Try a different search because “${ view.search }” returned no results.` }</Text>
308
+ <Button variant="secondary">Create new planet</Button>
309
+ </VStack>
310
+ }
264
311
  >
265
312
  <PlanetOverview planets={ planets } />
266
313
  </DataViews>
@@ -300,14 +347,21 @@ export const WithCard = () => {
300
347
  );
301
348
  };
302
349
 
303
- export const CustomPerPageSizes = () => {
350
+ export const GroupByLayout = () => {
304
351
  const [ view, setView ] = useState< View >( {
305
- ...DEFAULT_VIEW,
306
- fields: [ 'categories' ],
352
+ type: LAYOUT_GRID,
353
+ search: '',
354
+ page: 1,
355
+ perPage: 20,
356
+ filters: [],
357
+ fields: [ 'satellites' ],
307
358
  titleField: 'title',
308
359
  descriptionField: 'description',
309
360
  mediaField: 'image',
310
- perPage: 3,
361
+ groupByField: 'type',
362
+ layout: {
363
+ badgeFields: [ 'satellites' ],
364
+ },
311
365
  } );
312
366
  const { data: shownData, paginationInfo } = useMemo( () => {
313
367
  return filterSortAndPaginate( data, view, fields );
@@ -320,9 +374,12 @@ export const CustomPerPageSizes = () => {
320
374
  view={ view }
321
375
  fields={ fields }
322
376
  onChangeView={ setView }
323
- actions={ actions.filter( ( action ) => ! action.supportsBulk ) }
324
- defaultLayouts={ defaultLayouts }
325
- perPageSizes={ [ 3, 6, 12, 24 ] }
377
+ actions={ actions }
378
+ defaultLayouts={ {
379
+ [ LAYOUT_GRID ]: {},
380
+ [ LAYOUT_LIST ]: {},
381
+ [ LAYOUT_TABLE ]: {},
382
+ } }
326
383
  />
327
384
  );
328
385
  };
@@ -23,3 +23,9 @@
23
23
  .free-composition-dataviews-layout thead {
24
24
  inset-block-start: 67px;
25
25
  }
26
+
27
+ .free-composition-dataviews-empty {
28
+ border: 1px solid #000;
29
+ border-radius: 8px;
30
+ padding: 24px;
31
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import type { ComponentProps, ReactElement } from 'react';
4
+ import type { ComponentProps, ReactElement, ReactNode } from 'react';
5
5
 
6
6
  /**
7
7
  * WordPress dependencies
@@ -47,11 +47,15 @@ type DataViewsContextType< Item > = {
47
47
  isItemClickable: ( item: Item ) => boolean;
48
48
  containerWidth: number;
49
49
  containerRef: React.MutableRefObject< HTMLDivElement | null >;
50
+ resizeObserverRef:
51
+ | ( ( element?: HTMLDivElement | null ) => void )
52
+ | React.RefObject< HTMLDivElement >;
50
53
  defaultLayouts: SupportedLayouts;
51
54
  filters: NormalizedFilter[];
52
55
  isShowingFilter: boolean;
53
56
  setIsShowingFilter: ( value: boolean ) => void;
54
- perPageSizes?: [ number, number, number, number ];
57
+ perPageSizes: number[];
58
+ empty?: ReactNode;
55
59
  };
56
60
 
57
61
  const DataViewsContext = createContext< DataViewsContextType< any > >( {
@@ -72,10 +76,12 @@ const DataViewsContext = createContext< DataViewsContextType< any > >( {
72
76
  renderItemLink: undefined,
73
77
  containerWidth: 0,
74
78
  containerRef: createRef(),
79
+ resizeObserverRef: () => {},
75
80
  defaultLayouts: { list: {}, grid: {}, table: {} },
76
81
  filters: [],
77
82
  isShowingFilter: false,
78
83
  setIsShowingFilter: () => {},
84
+ perPageSizes: [],
79
85
  } );
80
86
 
81
87
  export default DataViewsContext;
@@ -309,9 +309,9 @@ const FilterText = ( {
309
309
 
310
310
  return createInterpolateElement(
311
311
  sprintf(
312
- /* translators: 1: Filter name. 2: Min value. 3: Max value. e.g.: "Item count between (inc): 10-180". */
312
+ /* translators: 1: Filter name. 2: Min value. 3: Max value. e.g.: "Item count between (inc): 10 and 180". */
313
313
  __(
314
- '<Name>%1$s between (inc): </Name><Value>%2$s-%3$s</Value>'
314
+ '<Name>%1$s between (inc): </Name><Value>%2$s and %3$s</Value>'
315
315
  ),
316
316
  filter.name,
317
317
  label[ 0 ],
@@ -497,8 +497,9 @@ export default function Filter( {
497
497
  }
498
498
 
499
499
  const isPrimary = filter.isPrimary;
500
- const hasValues = filterInView?.value !== undefined;
501
- const canResetOrRemove = ! isPrimary || hasValues;
500
+ const isLocked = filterInView?.isLocked;
501
+ const hasValues = ! isLocked && filterInView?.value !== undefined;
502
+ const canResetOrRemove = ! isLocked && ( ! isPrimary || hasValues );
502
503
  return (
503
504
  <Dropdown
504
505
  defaultOpen={ openedFilter === filter.field }
@@ -523,17 +524,26 @@ export default function Filter( {
523
524
  {
524
525
  'has-reset': canResetOrRemove,
525
526
  'has-values': hasValues,
527
+ 'is-not-clickable': isLocked,
526
528
  }
527
529
  ) }
528
530
  role="button"
529
- tabIndex={ 0 }
530
- onClick={ onToggle }
531
+ tabIndex={ isLocked ? -1 : 0 }
532
+ onClick={ () => {
533
+ if ( ! isLocked ) {
534
+ onToggle();
535
+ }
536
+ } }
531
537
  onKeyDown={ ( event ) => {
532
- if ( [ ENTER, SPACE ].includes( event.key ) ) {
538
+ if (
539
+ ! isLocked &&
540
+ [ ENTER, SPACE ].includes( event.key )
541
+ ) {
533
542
  onToggle();
534
543
  event.preventDefault();
535
544
  }
536
545
  } }
546
+ aria-disabled={ isLocked }
537
547
  aria-pressed={ isOpen }
538
548
  aria-expanded={ isOpen }
539
549
  ref={ toggleRef }
@@ -36,6 +36,10 @@ export function useFilters( fields: NormalizedField< any >[], view: View ) {
36
36
 
37
37
  const operators = field.filterBy.operators;
38
38
  const isPrimary = !! field.filterBy?.isPrimary;
39
+ const isLocked =
40
+ view.filters?.some(
41
+ ( f ) => f.field === field.id && !! f.isLocked
42
+ ) ?? false;
39
43
  filters.push( {
40
44
  field: field.id,
41
45
  name: field.label,
@@ -45,6 +49,7 @@ export function useFilters( fields: NormalizedField< any >[], view: View ) {
45
49
  ),
46
50
  operators,
47
51
  isVisible:
52
+ isLocked ||
48
53
  isPrimary ||
49
54
  !! view.filters?.some(
50
55
  ( f ) =>
@@ -52,11 +57,21 @@ export function useFilters( fields: NormalizedField< any >[], view: View ) {
52
57
  ALL_OPERATORS.includes( f.operator )
53
58
  ),
54
59
  isPrimary,
60
+ isLocked,
55
61
  } );
56
62
  } );
57
- // Sort filters by primary property. We need the primary filters to be first.
58
- // Then we sort by name.
63
+
64
+ // Sort filters by:
65
+ // - locked filters go first
66
+ // - primary filters go next
67
+ // - then, sort by name
59
68
  filters.sort( ( a, b ) => {
69
+ if ( a.isLocked && ! b.isLocked ) {
70
+ return -1;
71
+ }
72
+ if ( ! a.isLocked && b.isLocked ) {
73
+ return 1;
74
+ }
60
75
  if ( a.isPrimary && ! b.isPrimary ) {
61
76
  return -1;
62
77
  }
@@ -62,7 +62,13 @@ export default function InputWidget( {
62
62
  ..._filter,
63
63
  operator:
64
64
  currentFilter.operator || filter.operators[ 0 ],
65
- value: nextValue,
65
+ // Consider empty strings as undefined:
66
+ //
67
+ // - undefined as value means the filter is unset: the filter widget displays no value and the search returns all records
68
+ // - empty string as value means "search empty string": returns only the records that have an empty string as value
69
+ //
70
+ // In practice, this means the filter will not be able to find an empty string as the value.
71
+ value: nextValue === '' ? undefined : nextValue,
66
72
  }
67
73
  : _filter
68
74
  ),
@@ -28,7 +28,8 @@ export default function ResetFilter( {
28
28
  ! view.search &&
29
29
  ! view.filters?.some(
30
30
  ( _filter ) =>
31
- _filter.value !== undefined || ! isPrimary( _filter.field )
31
+ ! _filter.isLocked &&
32
+ ( _filter.value !== undefined || ! isPrimary( _filter.field ) )
32
33
  );
33
34
  return (
34
35
  <Button
@@ -42,7 +43,8 @@ export default function ResetFilter( {
42
43
  ...view,
43
44
  page: 1,
44
45
  search: '',
45
- filters: [],
46
+ filters:
47
+ view.filters?.filter( ( f ) => !! f.isLocked ) || [],
46
48
  } );
47
49
  } }
48
50
  >
@@ -22,7 +22,9 @@
22
22
  line-height: $default-line-height;
23
23
 
24
24
  .components-popover__content {
25
- width: 230px;
25
+ width: 100%;
26
+ min-width: 230px;
27
+ max-width: 250px;
26
28
  border-radius: $grid-unit-05;
27
29
  }
28
30
 
@@ -78,11 +80,15 @@
78
80
  align-items: center;
79
81
  box-sizing: border-box;
80
82
 
83
+ &.is-not-clickable {
84
+ cursor: default;
85
+ }
86
+
81
87
  &.has-reset {
82
88
  padding-inline-end: $button-size-small + $grid-unit-05;
83
89
  }
84
90
 
85
- &:hover,
91
+ &:hover:not(&.is-not-clickable),
86
92
  &:focus-visible,
87
93
  &[aria-expanded="true"] {
88
94
  background: $gray-200;
@@ -7,6 +7,7 @@ import type { ComponentType } from 'react';
7
7
  * WordPress dependencies
8
8
  */
9
9
  import { useContext } from '@wordpress/element';
10
+ import { __ } from '@wordpress/i18n';
10
11
 
11
12
  /**
12
13
  * Internal dependencies
@@ -35,6 +36,7 @@ export default function DataViewsLayout( { className }: DataViewsLayoutProps ) {
35
36
  onClickItem,
36
37
  isItemClickable,
37
38
  renderItemLink,
39
+ empty = __( 'No results' ),
38
40
  } = useContext( DataViewsContext );
39
41
 
40
42
  const ViewComponent = VIEW_LAYOUTS.find( ( v ) => v.type === view.type )
@@ -57,6 +59,7 @@ export default function DataViewsLayout( { className }: DataViewsLayoutProps ) {
57
59
  renderItemLink={ renderItemLink }
58
60
  isItemClickable={ isItemClickable }
59
61
  view={ view }
62
+ empty={ empty }
60
63
  />
61
64
  );
62
65
  }
@@ -213,10 +213,12 @@ function SortDirectionControl() {
213
213
  );
214
214
  }
215
215
 
216
- const PAGE_SIZE_VALUES = [ 10, 20, 50, 100 ];
217
216
  function ItemsPerPageControl() {
218
217
  const { view, perPageSizes, onChangeView } = useContext( DataViewsContext );
219
- const pageSizeValues = perPageSizes ?? PAGE_SIZE_VALUES;
218
+ if ( perPageSizes.length < 2 || perPageSizes.length > 6 ) {
219
+ return null;
220
+ }
221
+
220
222
  return (
221
223
  <ToggleGroupControl
222
224
  __nextHasNoMarginBottom
@@ -238,7 +240,7 @@ function ItemsPerPageControl() {
238
240
  } );
239
241
  } }
240
242
  >
241
- { pageSizeValues.map( ( value ) => {
243
+ { perPageSizes.map( ( value ) => {
242
244
  return (
243
245
  <ToggleGroupControlOption
244
246
  key={ value }
@@ -266,6 +266,27 @@ export const DateTime = ( {
266
266
  );
267
267
  };
268
268
 
269
+ export const Date = ( {
270
+ type,
271
+ labelPosition,
272
+ }: {
273
+ type: 'default' | 'regular' | 'panel';
274
+ labelPosition: 'default' | 'top' | 'side' | 'none';
275
+ } ) => {
276
+ const dateFields = useMemo(
277
+ () => fields.filter( ( field ) => field.type === 'date' ),
278
+ []
279
+ );
280
+
281
+ return (
282
+ <FieldTypeStory
283
+ fields={ dateFields }
284
+ type={ type }
285
+ labelPosition={ labelPosition }
286
+ />
287
+ );
288
+ };
289
+
269
290
  export const Email = ( {
270
291
  type,
271
292
  labelPosition,
@@ -1,12 +1,15 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { ToggleControl } from '@wordpress/components';
4
+ import { privateApis } from '@wordpress/components';
5
5
 
6
6
  /**
7
7
  * Internal dependencies
8
8
  */
9
9
  import type { DataFormControlProps } from '../types';
10
+ import { unlock } from '../lock-unlock';
11
+
12
+ const { ValidatedToggleControl } = unlock( privateApis );
10
13
 
11
14
  export default function Boolean< Item >( {
12
15
  field,
@@ -17,7 +20,21 @@ export default function Boolean< Item >( {
17
20
  const { id, getValue, label } = field;
18
21
 
19
22
  return (
20
- <ToggleControl
23
+ <ValidatedToggleControl
24
+ required={ !! field.isValid.required }
25
+ customValidator={ ( newValue: any ) => {
26
+ if ( field.isValid?.custom ) {
27
+ return field.isValid.custom(
28
+ {
29
+ ...data,
30
+ [ id ]: newValue,
31
+ },
32
+ field
33
+ );
34
+ }
35
+
36
+ return null;
37
+ } }
21
38
  hidden={ hideLabelFromVision }
22
39
  __nextHasNoMarginBottom
23
40
  label={ label }