@wordpress/dataviews 14.1.1-next.v.202604091042.0 → 14.2.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 (179) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/build/components/dataform-controls/array.cjs +2 -2
  3. package/build/components/dataform-controls/array.cjs.map +2 -2
  4. package/build/components/dataform-controls/date.cjs +16 -5
  5. package/build/components/dataform-controls/date.cjs.map +3 -3
  6. package/build/components/dataform-controls/datetime.cjs +6 -2
  7. package/build/components/dataform-controls/datetime.cjs.map +3 -3
  8. package/build/components/dataform-controls/utils/use-disabled-date-matchers.cjs +48 -0
  9. package/build/components/dataform-controls/utils/use-disabled-date-matchers.cjs.map +7 -0
  10. package/build/components/dataform-layouts/card/index.cjs.map +2 -2
  11. package/build/components/dataform-layouts/panel/summary-button.cjs +0 -1
  12. package/build/components/dataform-layouts/panel/summary-button.cjs.map +2 -2
  13. package/build/components/dataviews-context/index.cjs.map +1 -1
  14. package/build/components/dataviews-filters/search-widget.cjs +2 -7
  15. package/build/components/dataviews-filters/search-widget.cjs.map +2 -2
  16. package/build/components/dataviews-layouts/activity/activity-item.cjs +2 -3
  17. package/build/components/dataviews-layouts/activity/activity-item.cjs.map +2 -2
  18. package/build/components/dataviews-layouts/grid/composite-grid.cjs +2 -2
  19. package/build/components/dataviews-layouts/grid/composite-grid.cjs.map +2 -2
  20. package/build/components/dataviews-layouts/list/index.cjs +2 -2
  21. package/build/components/dataviews-layouts/list/index.cjs.map +2 -2
  22. package/build/components/dataviews-layouts/picker-grid/index.cjs +3 -6
  23. package/build/components/dataviews-layouts/picker-grid/index.cjs.map +2 -2
  24. package/build/components/dataviews-layouts/picker-table/index.cjs +15 -12
  25. package/build/components/dataviews-layouts/picker-table/index.cjs.map +2 -2
  26. package/build/components/dataviews-layouts/table/index.cjs +0 -1
  27. package/build/components/dataviews-layouts/table/index.cjs.map +2 -2
  28. package/build/dataviews/index.cjs +10 -8
  29. package/build/dataviews/index.cjs.map +2 -2
  30. package/build/dataviews-picker/index.cjs +16 -9
  31. package/build/dataviews-picker/index.cjs.map +2 -2
  32. package/build/field-types/date.cjs +4 -1
  33. package/build/field-types/date.cjs.map +2 -2
  34. package/build/field-types/datetime.cjs +4 -1
  35. package/build/field-types/datetime.cjs.map +2 -2
  36. package/build/field-types/utils/get-is-valid.cjs +29 -24
  37. package/build/field-types/utils/get-is-valid.cjs.map +2 -2
  38. package/build/field-types/utils/is-valid-date-boundary.cjs +64 -0
  39. package/build/field-types/utils/is-valid-date-boundary.cjs.map +7 -0
  40. package/build/types/dataviews.cjs.map +1 -1
  41. package/build/types/field-api.cjs.map +1 -1
  42. package/build-module/components/dataform-controls/array.mjs +2 -2
  43. package/build-module/components/dataform-controls/array.mjs.map +2 -2
  44. package/build-module/components/dataform-controls/date.mjs +16 -5
  45. package/build-module/components/dataform-controls/date.mjs.map +2 -2
  46. package/build-module/components/dataform-controls/datetime.mjs +6 -2
  47. package/build-module/components/dataform-controls/datetime.mjs.map +2 -2
  48. package/build-module/components/dataform-controls/utils/use-disabled-date-matchers.mjs +27 -0
  49. package/build-module/components/dataform-controls/utils/use-disabled-date-matchers.mjs.map +7 -0
  50. package/build-module/components/dataform-layouts/card/index.mjs.map +2 -2
  51. package/build-module/components/dataform-layouts/panel/summary-button.mjs +0 -1
  52. package/build-module/components/dataform-layouts/panel/summary-button.mjs.map +2 -2
  53. package/build-module/components/dataviews-context/index.mjs.map +1 -1
  54. package/build-module/components/dataviews-filters/search-widget.mjs +3 -13
  55. package/build-module/components/dataviews-filters/search-widget.mjs.map +2 -2
  56. package/build-module/components/dataviews-layouts/activity/activity-item.mjs +2 -3
  57. package/build-module/components/dataviews-layouts/activity/activity-item.mjs.map +2 -2
  58. package/build-module/components/dataviews-layouts/grid/composite-grid.mjs +2 -2
  59. package/build-module/components/dataviews-layouts/grid/composite-grid.mjs.map +2 -2
  60. package/build-module/components/dataviews-layouts/list/index.mjs +2 -3
  61. package/build-module/components/dataviews-layouts/list/index.mjs.map +2 -2
  62. package/build-module/components/dataviews-layouts/picker-grid/index.mjs +3 -6
  63. package/build-module/components/dataviews-layouts/picker-grid/index.mjs.map +2 -2
  64. package/build-module/components/dataviews-layouts/picker-table/index.mjs +15 -12
  65. package/build-module/components/dataviews-layouts/picker-table/index.mjs.map +2 -2
  66. package/build-module/components/dataviews-layouts/table/index.mjs +0 -1
  67. package/build-module/components/dataviews-layouts/table/index.mjs.map +2 -2
  68. package/build-module/dataviews/index.mjs +10 -8
  69. package/build-module/dataviews/index.mjs.map +2 -2
  70. package/build-module/dataviews-picker/index.mjs +16 -9
  71. package/build-module/dataviews-picker/index.mjs.map +2 -2
  72. package/build-module/field-types/date.mjs +4 -1
  73. package/build-module/field-types/date.mjs.map +2 -2
  74. package/build-module/field-types/datetime.mjs +4 -1
  75. package/build-module/field-types/datetime.mjs.map +2 -2
  76. package/build-module/field-types/utils/get-is-valid.mjs +29 -24
  77. package/build-module/field-types/utils/get-is-valid.mjs.map +2 -2
  78. package/build-module/field-types/utils/is-valid-date-boundary.mjs +38 -0
  79. package/build-module/field-types/utils/is-valid-date-boundary.mjs.map +7 -0
  80. package/build-style/style-rtl.css +12 -13
  81. package/build-style/style.css +12 -13
  82. package/build-types/components/dataform-controls/array.d.ts.map +1 -1
  83. package/build-types/components/dataform-controls/date.d.ts.map +1 -1
  84. package/build-types/components/dataform-controls/datetime.d.ts.map +1 -1
  85. package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts +16 -0
  86. package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts.map +1 -0
  87. package/build-types/components/dataform-layouts/card/index.d.ts.map +1 -1
  88. package/build-types/components/dataform-layouts/panel/summary-button.d.ts.map +1 -1
  89. package/build-types/components/dataviews-context/index.d.ts +2 -2
  90. package/build-types/components/dataviews-context/index.d.ts.map +1 -1
  91. package/build-types/components/dataviews-filters/search-widget.d.ts.map +1 -1
  92. package/build-types/components/dataviews-layouts/activity/activity-item.d.ts.map +1 -1
  93. package/build-types/components/dataviews-layouts/list/index.d.ts.map +1 -1
  94. package/build-types/components/dataviews-layouts/picker-grid/index.d.ts.map +1 -1
  95. package/build-types/components/dataviews-layouts/picker-table/index.d.ts.map +1 -1
  96. package/build-types/components/dataviews-layouts/table/index.d.ts.map +1 -1
  97. package/build-types/dataform/stories/index.story.d.ts.map +1 -1
  98. package/build-types/dataform/stories/layout-regular.d.ts.map +1 -1
  99. package/build-types/dataform/stories/validation.d.ts.map +1 -1
  100. package/build-types/dataviews/index.d.ts +1 -1
  101. package/build-types/dataviews/index.d.ts.map +1 -1
  102. package/build-types/dataviews/stories/free-composition.d.ts.map +1 -1
  103. package/build-types/dataviews/stories/index.story.d.ts.map +1 -1
  104. package/build-types/dataviews/stories/layout-activity.d.ts.map +1 -1
  105. package/build-types/dataviews/stories/layout-grid.d.ts.map +1 -1
  106. package/build-types/dataviews/stories/layout-list.d.ts.map +1 -1
  107. package/build-types/dataviews/stories/layout-table.d.ts.map +1 -1
  108. package/build-types/dataviews/stories/with-card.d.ts.map +1 -1
  109. package/build-types/dataviews-picker/index.d.ts +3 -2
  110. package/build-types/dataviews-picker/index.d.ts.map +1 -1
  111. package/build-types/dataviews-picker/stories/index.story.d.ts.map +1 -1
  112. package/build-types/field-types/date.d.ts +3 -0
  113. package/build-types/field-types/date.d.ts.map +1 -1
  114. package/build-types/field-types/datetime.d.ts +3 -0
  115. package/build-types/field-types/datetime.d.ts.map +1 -1
  116. package/build-types/field-types/utils/get-is-valid.d.ts.map +1 -1
  117. package/build-types/field-types/utils/is-valid-date-boundary.d.ts +7 -0
  118. package/build-types/field-types/utils/is-valid-date-boundary.d.ts.map +1 -0
  119. package/build-types/types/dataviews.d.ts +8 -0
  120. package/build-types/types/dataviews.d.ts.map +1 -1
  121. package/build-types/types/field-api.d.ts +11 -9
  122. package/build-types/types/field-api.d.ts.map +1 -1
  123. package/build-wp/index.js +1173 -1017
  124. package/package.json +16 -16
  125. package/src/components/dataform-controls/array.tsx +3 -2
  126. package/src/components/dataform-controls/date.tsx +17 -2
  127. package/src/components/dataform-controls/datetime.tsx +15 -1
  128. package/src/components/dataform-controls/utils/use-disabled-date-matchers.ts +48 -0
  129. package/src/components/dataform-layouts/card/index.tsx +0 -3
  130. package/src/components/dataform-layouts/panel/style.scss +4 -5
  131. package/src/components/dataform-layouts/panel/summary-button.tsx +0 -1
  132. package/src/components/dataviews-context/index.ts +2 -2
  133. package/src/components/dataviews-filters/search-widget.tsx +4 -14
  134. package/src/components/dataviews-filters/style.scss +2 -2
  135. package/src/components/dataviews-layouts/activity/activity-item.tsx +2 -3
  136. package/src/components/dataviews-layouts/activity/style.scss +1 -1
  137. package/src/components/dataviews-layouts/grid/composite-grid.tsx +3 -3
  138. package/src/components/dataviews-layouts/grid/style.scss +1 -1
  139. package/src/components/dataviews-layouts/list/index.tsx +2 -3
  140. package/src/components/dataviews-layouts/list/style.scss +1 -1
  141. package/src/components/dataviews-layouts/picker-grid/index.tsx +5 -9
  142. package/src/components/dataviews-layouts/picker-grid/style.scss +1 -1
  143. package/src/components/dataviews-layouts/picker-table/index.tsx +9 -7
  144. package/src/components/dataviews-layouts/picker-table/style.scss +1 -1
  145. package/src/components/dataviews-layouts/table/index.tsx +0 -2
  146. package/src/dataform/stories/content.story.tsx +1 -1
  147. package/src/dataform/stories/data-adapter.tsx +6 -6
  148. package/src/dataform/stories/layout-card.tsx +8 -8
  149. package/src/dataform/stories/layout-details.tsx +5 -5
  150. package/src/dataform/stories/layout-panel.tsx +9 -9
  151. package/src/dataform/stories/layout-regular.tsx +16 -9
  152. package/src/dataform/stories/layout-row.tsx +9 -9
  153. package/src/dataform/stories/validation.tsx +25 -10
  154. package/src/dataviews/index.tsx +11 -7
  155. package/src/dataviews/stories/empty.tsx +6 -6
  156. package/src/dataviews/stories/fixtures.tsx +2 -2
  157. package/src/dataviews/stories/free-composition.tsx +10 -13
  158. package/src/dataviews/stories/infinite-scroll.tsx +4 -4
  159. package/src/dataviews/stories/layout-activity.tsx +7 -9
  160. package/src/dataviews/stories/layout-custom.tsx +1 -1
  161. package/src/dataviews/stories/layout-grid.tsx +5 -7
  162. package/src/dataviews/stories/layout-list.tsx +6 -8
  163. package/src/dataviews/stories/layout-table.tsx +5 -7
  164. package/src/dataviews/stories/minimal-ui.tsx +1 -1
  165. package/src/dataviews/stories/with-card.tsx +4 -7
  166. package/src/dataviews/style.scss +1 -1
  167. package/src/dataviews/test/dataviews.tsx +73 -6
  168. package/src/dataviews-picker/index.tsx +17 -7
  169. package/src/dataviews-picker/stories/index.story.tsx +1 -5
  170. package/src/dataviews-picker/test/dataviews-picker.tsx +79 -2
  171. package/src/field-types/date.tsx +3 -0
  172. package/src/field-types/datetime.tsx +3 -0
  173. package/src/field-types/stories/index.story.tsx +1 -1
  174. package/src/field-types/test/normalize-fields.ts +44 -0
  175. package/src/field-types/utils/get-is-valid.ts +44 -31
  176. package/src/field-types/utils/is-valid-date-boundary.ts +80 -0
  177. package/src/hooks/test/use-form-validity.ts +479 -0
  178. package/src/types/dataviews.ts +9 -0
  179. package/src/types/field-api.ts +11 -9
@@ -15,7 +15,7 @@ import {
15
15
  pin,
16
16
  link,
17
17
  } from '@wordpress/icons';
18
- import { Button, __experimentalText as Text } from '@wordpress/components';
18
+ import { Button, __experimentalText as WCText } from '@wordpress/components';
19
19
  import { Stack } from '@wordpress/ui';
20
20
 
21
21
  /**
@@ -555,7 +555,7 @@ export const orderEventActions: Action< OrderEvent >[] = [
555
555
  };
556
556
  return (
557
557
  <Stack direction="column" gap="xl">
558
- <Text>{ label }</Text>
558
+ <WCText>{ label }</WCText>
559
559
  <Stack direction="row" gap="sm" justify="right">
560
560
  <Button
561
561
  __next40pxDefaultSize
@@ -640,13 +640,11 @@ const LayoutActivityComponent = ( {
640
640
 
641
641
  return (
642
642
  <div
643
- style={
644
- {
645
- height: '100%',
646
- maxWidth: fullWidth ? undefined : '400px',
647
- '--wp-dataviews-color-background': backgroundColor,
648
- } as React.CSSProperties
649
- }
643
+ style={ {
644
+ height: '100%',
645
+ maxWidth: fullWidth ? undefined : '400px',
646
+ '--wp-dataviews-color-background': backgroundColor,
647
+ } }
650
648
  >
651
649
  <DataViews
652
650
  getItemId={ ( item ) => item.id.toString() }
@@ -130,7 +130,7 @@ export const LayoutCustomComponent = ( {
130
130
  view={ view }
131
131
  fields={ fields }
132
132
  onChangeView={ setView }
133
- defaultLayouts={ { table: {} } }
133
+ defaultLayouts={ { table: true } }
134
134
  >
135
135
  <div style={ { padding: '2px', height: containerHeight } }>
136
136
  <DataViews.Search />
@@ -61,12 +61,10 @@ export const LayoutTableComponent = ( {
61
61
  }, [ view ] );
62
62
  return (
63
63
  <div
64
- style={
65
- {
66
- height: '100%',
67
- '--wp-dataviews-color-background': backgroundColor,
68
- } as React.CSSProperties
69
- }
64
+ style={ {
65
+ height: '100%',
66
+ '--wp-dataviews-color-background': backgroundColor,
67
+ } }
70
68
  >
71
69
  <DataViews
72
70
  getItemId={ ( item ) => item.id.toString() }
@@ -97,7 +95,7 @@ export const LayoutTableComponent = ( {
97
95
  ) }
98
96
  isItemClickable={ () => hasClickableItems }
99
97
  defaultLayouts={ {
100
- [ LAYOUT_GRID ]: {},
98
+ [ LAYOUT_GRID ]: true,
101
99
  } }
102
100
  config={ { perPageSizes } }
103
101
  />
@@ -63,13 +63,11 @@ export const LayoutTableComponent = ( {
63
63
  }, [ view ] );
64
64
  return (
65
65
  <div
66
- style={
67
- {
68
- height: '100%',
69
- maxWidth: fullWidth ? undefined : '400px',
70
- '--wp-dataviews-color-background': backgroundColor,
71
- } as React.CSSProperties
72
- }
66
+ style={ {
67
+ height: '100%',
68
+ maxWidth: fullWidth ? undefined : '400px',
69
+ '--wp-dataviews-color-background': backgroundColor,
70
+ } }
73
71
  >
74
72
  <DataViews
75
73
  getItemId={ ( item ) => item.id.toString() }
@@ -100,7 +98,7 @@ export const LayoutTableComponent = ( {
100
98
  ) }
101
99
  isItemClickable={ () => hasClickableItems }
102
100
  defaultLayouts={ {
103
- [ LAYOUT_LIST ]: {},
101
+ [ LAYOUT_LIST ]: true,
104
102
  } }
105
103
  config={ { perPageSizes } }
106
104
  />
@@ -62,12 +62,10 @@ export const LayoutTableComponent = ( {
62
62
  }, [ view ] );
63
63
  return (
64
64
  <div
65
- style={
66
- {
67
- height: '100%',
68
- '--wp-dataviews-color-background': backgroundColor,
69
- } as React.CSSProperties
70
- }
65
+ style={ {
66
+ height: '100%',
67
+ '--wp-dataviews-color-background': backgroundColor,
68
+ } }
71
69
  >
72
70
  <DataViews
73
71
  getItemId={ ( item ) => item.id.toString() }
@@ -98,7 +96,7 @@ export const LayoutTableComponent = ( {
98
96
  ) }
99
97
  isItemClickable={ () => hasClickableItems }
100
98
  defaultLayouts={ {
101
- [ LAYOUT_TABLE ]: {},
99
+ [ LAYOUT_TABLE ]: true,
102
100
  } }
103
101
  config={ { perPageSizes } }
104
102
  />
@@ -54,7 +54,7 @@ const MinimalUIComponent = ( {
54
54
  view={ view }
55
55
  fields={ _fields }
56
56
  onChangeView={ setView }
57
- defaultLayouts={ { [ layout ]: {} } }
57
+ defaultLayouts={ { [ layout ]: true } }
58
58
  >
59
59
  <DataViews.Layout />
60
60
  <DataViews.Footer />
@@ -2,9 +2,6 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { useState, useMemo } from '@wordpress/element';
5
- // TODO: enable in the ESlint rule once we complete
6
- // https://github.com/WordPress/gutenberg/issues/76135.
7
- // eslint-disable-next-line @wordpress/use-recommended-components
8
5
  import { Card } from '@wordpress/ui';
9
6
 
10
7
  /**
@@ -59,10 +56,10 @@ const WithCardComponent = ( {
59
56
  ( action ) => ! action.supportsBulk
60
57
  ) }
61
58
  defaultLayouts={ {
62
- [ LAYOUT_TABLE ]: {},
63
- [ LAYOUT_GRID ]: {},
64
- [ LAYOUT_LIST ]: {},
65
- [ LAYOUT_ACTIVITY ]: {},
59
+ [ LAYOUT_TABLE ]: true,
60
+ [ LAYOUT_GRID ]: true,
61
+ [ LAYOUT_LIST ]: true,
62
+ [ LAYOUT_ACTIVITY ]: true,
66
63
  } }
67
64
  />
68
65
  </Card.FullBleed>
@@ -118,7 +118,7 @@
118
118
  }
119
119
 
120
120
  .dataviews-title-field--clickable {
121
- cursor: pointer;
121
+ cursor: var(--wpds-cursor-control);
122
122
  color: $gray-800;
123
123
  &:hover {
124
124
  color: var(--wp-admin-theme-color);
@@ -19,7 +19,7 @@ import {
19
19
  LAYOUT_LIST,
20
20
  LAYOUT_TABLE,
21
21
  } from '../../constants';
22
- import type { Action, View } from '../../types';
22
+ import type { Action, SupportedLayouts, View } from '../../types';
23
23
  import filterSortAndPaginate from '../../utils/filter-sort-and-paginate';
24
24
 
25
25
  type Data = {
@@ -38,11 +38,11 @@ const DEFAULT_VIEW = {
38
38
  filters: [],
39
39
  };
40
40
 
41
- const defaultLayouts = {
42
- [ LAYOUT_TABLE ]: {},
43
- [ LAYOUT_GRID ]: {},
44
- [ LAYOUT_LIST ]: {},
45
- [ LAYOUT_ACTIVITY ]: {},
41
+ const defaultLayouts: SupportedLayouts = {
42
+ [ LAYOUT_TABLE ]: true,
43
+ [ LAYOUT_GRID ]: true,
44
+ [ LAYOUT_LIST ]: true,
45
+ [ LAYOUT_ACTIVITY ]: true,
46
46
  };
47
47
 
48
48
  const fields = [
@@ -737,4 +737,71 @@ describe( 'DataViews component', () => {
737
737
  ).toEqual( 3 );
738
738
  } );
739
739
  } );
740
+ describe( 'Default layouts', () => {
741
+ /**
742
+ * A minimal wrapper that intentionally omits the `defaultLayouts` prop so
743
+ * DataViews falls back to its internal DEFAULT_LAYOUTS constant
744
+ * ({ table: true, grid: true, list: true }).
745
+ */
746
+ function DataViewWrapperWithoutDefaultLayouts() {
747
+ const [ view, setView ] = useState< View >( {
748
+ ...DEFAULT_VIEW,
749
+ fields: [ 'title', 'order', 'author' ],
750
+ } );
751
+
752
+ const { data: shownData, paginationInfo } = useMemo( () => {
753
+ return filterSortAndPaginate( data, view, fields );
754
+ }, [ view ] );
755
+
756
+ return (
757
+ <DataViews
758
+ getItemId={ ( item: Data ) => item.id.toString() }
759
+ paginationInfo={ paginationInfo }
760
+ data={ shownData }
761
+ view={ view }
762
+ fields={ fields }
763
+ onChangeView={ setView }
764
+ // No `defaultLayouts` prop — falls back to DEFAULT_LAYOUTS
765
+ />
766
+ );
767
+ }
768
+
769
+ it( 'renders Table, Grid, and List layout options when defaultLayouts is not provided', async () => {
770
+ render( <DataViewWrapperWithoutDefaultLayouts /> );
771
+
772
+ const user = userEvent.setup();
773
+
774
+ // All three default layouts are available, so the Layout switcher
775
+ // button (rendered by ViewTypeMenu) must be present.
776
+ const layoutButton = screen.getByRole( 'button', {
777
+ name: 'Layout',
778
+ } );
779
+ expect( layoutButton ).toBeInTheDocument();
780
+
781
+ // Open the layout menu.
782
+ await user.click( layoutButton );
783
+
784
+ // Table, Grid, and List options must all appear.
785
+ expect(
786
+ screen.getByRole( 'menuitemradio', { name: 'Table' } )
787
+ ).toBeInTheDocument();
788
+ expect(
789
+ screen.getByRole( 'menuitemradio', { name: 'Grid' } )
790
+ ).toBeInTheDocument();
791
+ expect(
792
+ screen.getByRole( 'menuitemradio', { name: 'List' } )
793
+ ).toBeInTheDocument();
794
+
795
+ // Table is the default active layout.
796
+ expect(
797
+ screen.getByRole( 'menuitemradio', { name: 'Table' } )
798
+ ).toBeChecked();
799
+ expect(
800
+ screen.getByRole( 'menuitemradio', { name: 'Grid' } )
801
+ ).not.toBeChecked();
802
+ expect(
803
+ screen.getByRole( 'menuitemradio', { name: 'List' } )
804
+ ).not.toBeChecked();
805
+ } );
806
+ } );
740
807
  } );
@@ -62,7 +62,7 @@ type DataViewsPickerProps< Item > = {
62
62
  totalItems: number;
63
63
  totalPages: number;
64
64
  };
65
- defaultLayouts: SupportedLayouts;
65
+ defaultLayouts?: SupportedLayouts;
66
66
  selection: string[];
67
67
  onChangeSelection: ( items: string[] ) => void;
68
68
  children?: ReactNode;
@@ -71,12 +71,17 @@ type DataViewsPickerProps< Item > = {
71
71
  };
72
72
  itemListLabel?: string;
73
73
  empty?: ReactNode;
74
+ onReset?: ( () => void ) | false;
74
75
  } & ( Item extends ItemWithId
75
76
  ? { getItemId?: ( item: Item ) => string }
76
77
  : { getItemId: ( item: Item ) => string } );
77
78
 
78
79
  const defaultGetItemId = ( item: ItemWithId ) => item.id;
79
80
  const EMPTY_ARRAY: any[] = [];
81
+ const DEFAULT_PICKER_LAYOUTS: SupportedLayouts = {
82
+ pickerGrid: true,
83
+ pickerTable: true,
84
+ };
80
85
 
81
86
  type DefaultUIProps = Pick<
82
87
  DataViewsPickerProps< any >,
@@ -132,13 +137,14 @@ function DataViewsPicker< Item >( {
132
137
  getItemId = defaultGetItemId,
133
138
  isLoading = false,
134
139
  paginationInfo,
135
- defaultLayouts: defaultLayoutsProperty,
140
+ defaultLayouts: defaultLayoutsProperty = DEFAULT_PICKER_LAYOUTS,
136
141
  selection,
137
142
  onChangeSelection,
138
143
  children,
139
144
  config = { perPageSizes: [ 10, 20, 50, 100 ] },
140
145
  itemListLabel,
141
146
  empty,
147
+ onReset,
142
148
  }: DataViewsPickerProps< Item > ) {
143
149
  // useData ensures data loading is correct whether infinite scroll is enabled or pagination is used.
144
150
  const { data: displayData, setVisibleEntries } = useData( {
@@ -198,17 +204,20 @@ function DataViewsPicker< Item >( {
198
204
  }
199
205
  }, [ hasPrimaryOrLockedFilters, isShowingFilter ] );
200
206
 
201
- // Filter out DataViewsPicker layouts.
207
+ // Filter out non-picker layouts and normalize `true` to `{}`.
202
208
  const defaultLayouts = useMemo(
203
209
  () =>
204
210
  Object.fromEntries(
205
- Object.entries( defaultLayoutsProperty ).filter(
206
- ( [ layoutType ] ) => {
211
+ Object.entries( defaultLayoutsProperty )
212
+ .filter( ( [ layoutType ] ) => {
207
213
  return dataViewsPickerLayouts.some(
208
214
  ( viewLayout ) => viewLayout.type === layoutType
209
215
  );
210
- }
211
- )
216
+ } )
217
+ .map( ( [ key, value ] ) => [
218
+ key,
219
+ value === true ? {} : value,
220
+ ] )
212
221
  ),
213
222
  [ defaultLayoutsProperty ]
214
223
  );
@@ -243,6 +252,7 @@ function DataViewsPicker< Item >( {
243
252
  config,
244
253
  itemListLabel,
245
254
  empty,
255
+ onReset,
246
256
  hasInitiallyLoaded: true,
247
257
  intersectionObserver,
248
258
  } }
@@ -14,7 +14,7 @@ import { Stack } from '@wordpress/ui';
14
14
  * Internal dependencies
15
15
  */
16
16
  import DataViewsPicker from '../index';
17
- import { LAYOUT_PICKER_GRID, LAYOUT_PICKER_TABLE } from '../../constants';
17
+ import { LAYOUT_PICKER_GRID } from '../../constants';
18
18
  import filterSortAndPaginate from '../../utils/filter-sort-and-paginate';
19
19
  import type { ActionButton, View } from '../../types';
20
20
  import { data, fields, type SpaceObject } from './fixtures';
@@ -187,10 +187,6 @@ const DataViewsPickerContent = ( {
187
187
  onChangeView={ setView }
188
188
  config={ { perPageSizes } }
189
189
  itemListLabel="Galactic Bodies"
190
- defaultLayouts={ {
191
- [ LAYOUT_PICKER_GRID ]: {},
192
- [ LAYOUT_PICKER_TABLE ]: {},
193
- } }
194
190
  />
195
191
  </>
196
192
  );
@@ -14,7 +14,12 @@ import { useMemo, useState } from '@wordpress/element';
14
14
  */
15
15
  import DataViewsPicker from '../index';
16
16
  import { LAYOUT_PICKER_GRID } from '../../constants';
17
- import type { ActionButton, View, ViewPickerGrid } from '../../types';
17
+ import type {
18
+ ActionButton,
19
+ SupportedLayouts,
20
+ View,
21
+ ViewPickerGrid,
22
+ } from '../../types';
18
23
  import filterSortAndPaginate from '../../utils/filter-sort-and-paginate';
19
24
 
20
25
  type Data = {
@@ -107,7 +112,7 @@ function Picker( {
107
112
  paginationInfo,
108
113
  data: shownData,
109
114
  view,
110
- defaultLayouts: { [ LAYOUT_PICKER_GRID ]: {} },
115
+ defaultLayouts: { [ LAYOUT_PICKER_GRID ]: true } as SupportedLayouts,
111
116
  fields: [],
112
117
  onChangeView: setView,
113
118
  multiselect,
@@ -475,4 +480,76 @@ describe( 'DataViews Picker', () => {
475
480
  } );
476
481
  } );
477
482
  } );
483
+
484
+ describe( 'Default layouts', () => {
485
+ /**
486
+ * A minimal Picker that intentionally omits the `defaultLayouts` prop so
487
+ * that DataViewsPicker falls back to its internal DEFAULT_PICKER_LAYOUTS
488
+ * constant ({ pickerGrid: true, pickerTable: true }).
489
+ */
490
+ function PickerWithoutDefaultLayouts() {
491
+ const [ view, setView ] = useState< View >( {
492
+ type: LAYOUT_PICKER_GRID,
493
+ fields: [],
494
+ titleField: 'title',
495
+ mediaField: 'image',
496
+ search: '',
497
+ page: 1,
498
+ perPage: 10,
499
+ filters: [],
500
+ } satisfies ViewPickerGrid );
501
+
502
+ const [ selection, setSelection ] = useState< string[] >( [] );
503
+
504
+ const { data: shownData, paginationInfo } = useMemo( () => {
505
+ return filterSortAndPaginate( data, view, [] );
506
+ }, [ view ] );
507
+
508
+ return (
509
+ <DataViewsPicker
510
+ getItemId={ ( item: Data ) => item.id.toString() }
511
+ paginationInfo={ paginationInfo }
512
+ data={ shownData }
513
+ view={ view }
514
+ fields={ [] }
515
+ onChangeView={ setView }
516
+ selection={ selection }
517
+ onChangeSelection={ setSelection }
518
+ // No `defaultLayouts` prop falls back to DEFAULT_PICKER_LAYOUTS
519
+ />
520
+ );
521
+ }
522
+
523
+ it( 'renders both picker layout options when defaultLayouts is not provided', async () => {
524
+ render( <PickerWithoutDefaultLayouts /> );
525
+
526
+ const user = userEvent.setup();
527
+
528
+ // Both picker layouts are available, so the Layout switcher button
529
+ // (rendered by ViewTypeMenu) must be present.
530
+ const layoutButton = screen.getByRole( 'button', {
531
+ name: 'Layout',
532
+ } );
533
+ expect( layoutButton ).toBeInTheDocument();
534
+
535
+ // Open the layout menu.
536
+ await user.click( layoutButton );
537
+
538
+ // Both "Grid" and "Table" picker layout options must appear in the menu.
539
+ expect(
540
+ screen.getByRole( 'menuitemradio', { name: 'Grid' } )
541
+ ).toBeInTheDocument();
542
+ expect(
543
+ screen.getByRole( 'menuitemradio', { name: 'Table' } )
544
+ ).toBeInTheDocument();
545
+
546
+ // The grid layout is active by default.
547
+ expect(
548
+ screen.getByRole( 'menuitemradio', { name: 'Grid' } )
549
+ ).toBeChecked();
550
+ expect(
551
+ screen.getByRole( 'menuitemradio', { name: 'Table' } )
552
+ ).not.toBeChecked();
553
+ } );
554
+ } );
478
555
  } );
@@ -21,6 +21,7 @@ import {
21
21
  OPERATOR_BETWEEN,
22
22
  } from '../constants';
23
23
  import isValidRequired from './utils/is-valid-required';
24
+ import { isValidMaxDate, isValidMinDate } from './utils/is-valid-date-boundary';
24
25
  import render from './utils/render-default';
25
26
 
26
27
  const format = {
@@ -91,5 +92,7 @@ export default {
91
92
  validate: {
92
93
  required: isValidRequired,
93
94
  elements: isValidElements,
95
+ min: isValidMinDate,
96
+ max: isValidMaxDate,
94
97
  },
95
98
  } satisfies FieldType< any >;
@@ -20,6 +20,7 @@ import {
20
20
  OPERATOR_OVER,
21
21
  } from '../constants';
22
22
  import isValidRequired from './utils/is-valid-required';
23
+ import { isValidMaxDate, isValidMinDate } from './utils/is-valid-date-boundary';
23
24
  import render from './utils/render-default';
24
25
 
25
26
  const format = {
@@ -88,5 +89,7 @@ export default {
88
89
  validate: {
89
90
  required: isValidRequired,
90
91
  elements: isValidElements,
92
+ min: isValidMinDate,
93
+ max: isValidMaxDate,
91
94
  },
92
95
  } satisfies FieldType< any >;
@@ -715,7 +715,7 @@ const FieldTypeStory = ( {
715
715
  },
716
716
  ] }
717
717
  defaultLayouts={ {
718
- table: {},
718
+ table: true,
719
719
  } }
720
720
  selection={ selectedIds.map( ( id ) => id.toString() ) }
721
721
  onChangeSelection={ ( newSelection ) =>
@@ -339,6 +339,50 @@ describe( 'normalizeFields: default getValue', () => {
339
339
  } );
340
340
  } );
341
341
 
342
+ describe( 'validation normalization', () => {
343
+ it( 'ignores string min/max rules on numeric fields', () => {
344
+ const fields: Field< {} >[] = [
345
+ {
346
+ id: 'price',
347
+ type: 'number',
348
+ isValid: {
349
+ min: '1',
350
+ max: '10',
351
+ },
352
+ },
353
+ ];
354
+ const normalizedFields = normalizeFields( fields );
355
+ expect( normalizedFields[ 0 ].isValid.min ).toBeUndefined();
356
+ expect( normalizedFields[ 0 ].isValid.max ).toBeUndefined();
357
+ } );
358
+
359
+ it( 'ignores numeric min/max rules on date-like fields', () => {
360
+ const fields: Field< {} >[] = [
361
+ {
362
+ id: 'publishDate',
363
+ type: 'date',
364
+ isValid: {
365
+ min: 1,
366
+ max: 10,
367
+ },
368
+ },
369
+ {
370
+ id: 'publishedAt',
371
+ type: 'datetime',
372
+ isValid: {
373
+ min: 1,
374
+ max: 10,
375
+ },
376
+ },
377
+ ];
378
+ const normalizedFields = normalizeFields( fields );
379
+ expect( normalizedFields[ 0 ].isValid.min ).toBeUndefined();
380
+ expect( normalizedFields[ 0 ].isValid.max ).toBeUndefined();
381
+ expect( normalizedFields[ 1 ].isValid.min ).toBeUndefined();
382
+ expect( normalizedFields[ 1 ].isValid.max ).toBeUndefined();
383
+ } );
384
+ } );
385
+
342
386
  describe( 'format normalization', () => {
343
387
  it( 'applies default format when not provided for date fields', () => {
344
388
  const fields: Field< {} >[] = [