@wordpress/dataviews 12.0.1-next.v.202602111440.0 → 12.0.1

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 (201) hide show
  1. package/CHANGELOG.md +37 -1
  2. package/README.md +3 -1
  3. package/build/components/dataform-controls/color.cjs +19 -30
  4. package/build/components/dataform-controls/color.cjs.map +3 -3
  5. package/build/components/dataform-controls/date.cjs.map +1 -1
  6. package/build/components/dataform-controls/datetime.cjs +12 -21
  7. package/build/components/dataform-controls/datetime.cjs.map +2 -2
  8. package/build/components/dataform-layouts/card/index.cjs +112 -164
  9. package/build/components/dataform-layouts/card/index.cjs.map +3 -3
  10. package/build/components/dataform-layouts/normalize-form.cjs +2 -1
  11. package/build/components/dataform-layouts/normalize-form.cjs.map +2 -2
  12. package/build/components/dataform-layouts/panel/summary-button.cjs +77 -49
  13. package/build/components/dataform-layouts/panel/summary-button.cjs.map +2 -2
  14. package/build/components/dataform-layouts/panel/utils/get-label-content.cjs +2 -2
  15. package/build/components/dataform-layouts/panel/utils/get-label-content.cjs.map +2 -2
  16. package/build/components/dataviews-bulk-actions/index.cjs +2 -2
  17. package/build/components/dataviews-bulk-actions/index.cjs.map +2 -2
  18. package/build/components/dataviews-context/index.cjs +1 -0
  19. package/build/components/dataviews-context/index.cjs.map +2 -2
  20. package/build/components/dataviews-filters/filter.cjs.map +1 -1
  21. package/build/components/dataviews-filters/toggle.cjs +6 -2
  22. package/build/components/dataviews-filters/toggle.cjs.map +2 -2
  23. package/build/components/dataviews-footer/index.cjs +28 -12
  24. package/build/components/dataviews-footer/index.cjs.map +3 -3
  25. package/build/components/dataviews-item-actions/index.cjs +0 -1
  26. package/build/components/dataviews-item-actions/index.cjs.map +2 -2
  27. package/build/components/dataviews-layout/index.cjs +12 -0
  28. package/build/components/dataviews-layout/index.cjs.map +2 -2
  29. package/build/components/dataviews-layouts/activity/index.cjs +41 -26
  30. package/build/components/dataviews-layouts/activity/index.cjs.map +3 -3
  31. package/build/components/dataviews-layouts/grid/composite-grid.cjs +2 -0
  32. package/build/components/dataviews-layouts/grid/composite-grid.cjs.map +2 -2
  33. package/build/components/dataviews-layouts/grid/index.cjs +19 -14
  34. package/build/components/dataviews-layouts/grid/index.cjs.map +3 -3
  35. package/build/components/dataviews-layouts/list/index.cjs +13 -9
  36. package/build/components/dataviews-layouts/list/index.cjs.map +2 -2
  37. package/build/components/dataviews-layouts/picker-table/index.cjs +1 -1
  38. package/build/components/dataviews-layouts/picker-table/index.cjs.map +2 -2
  39. package/build/components/dataviews-layouts/table/index.cjs +40 -19
  40. package/build/components/dataviews-layouts/table/index.cjs.map +2 -2
  41. package/build/dataviews/index.cjs +9 -2
  42. package/build/dataviews/index.cjs.map +3 -3
  43. package/build/dataviews-picker/index.cjs +1 -0
  44. package/build/dataviews-picker/index.cjs.map +2 -2
  45. package/build/hooks/use-data.cjs +46 -0
  46. package/build/hooks/use-data.cjs.map +7 -0
  47. package/build/hooks/use-delayed-loading.cjs +47 -0
  48. package/build/hooks/use-delayed-loading.cjs.map +7 -0
  49. package/build/types/dataform.cjs.map +1 -1
  50. package/build-module/components/dataform-controls/color.mjs +24 -31
  51. package/build-module/components/dataform-controls/color.mjs.map +2 -2
  52. package/build-module/components/dataform-controls/date.mjs.map +1 -1
  53. package/build-module/components/dataform-controls/datetime.mjs +13 -22
  54. package/build-module/components/dataform-controls/datetime.mjs.map +2 -2
  55. package/build-module/components/dataform-layouts/card/index.mjs +113 -161
  56. package/build-module/components/dataform-layouts/card/index.mjs.map +3 -3
  57. package/build-module/components/dataform-layouts/normalize-form.mjs +2 -1
  58. package/build-module/components/dataform-layouts/normalize-form.mjs.map +2 -2
  59. package/build-module/components/dataform-layouts/panel/summary-button.mjs +78 -50
  60. package/build-module/components/dataform-layouts/panel/summary-button.mjs.map +2 -2
  61. package/build-module/components/dataform-layouts/panel/utils/get-label-content.mjs +3 -3
  62. package/build-module/components/dataform-layouts/panel/utils/get-label-content.mjs.map +2 -2
  63. package/build-module/components/dataviews-bulk-actions/index.mjs +2 -2
  64. package/build-module/components/dataviews-bulk-actions/index.mjs.map +2 -2
  65. package/build-module/components/dataviews-context/index.mjs +1 -0
  66. package/build-module/components/dataviews-context/index.mjs.map +2 -2
  67. package/build-module/components/dataviews-filters/filter.mjs.map +1 -1
  68. package/build-module/components/dataviews-filters/toggle.mjs +6 -2
  69. package/build-module/components/dataviews-filters/toggle.mjs.map +2 -2
  70. package/build-module/components/dataviews-footer/index.mjs +28 -12
  71. package/build-module/components/dataviews-footer/index.mjs.map +2 -2
  72. package/build-module/components/dataviews-item-actions/index.mjs +0 -1
  73. package/build-module/components/dataviews-item-actions/index.mjs.map +2 -2
  74. package/build-module/components/dataviews-layout/index.mjs +12 -0
  75. package/build-module/components/dataviews-layout/index.mjs.map +2 -2
  76. package/build-module/components/dataviews-layouts/activity/index.mjs +41 -26
  77. package/build-module/components/dataviews-layouts/activity/index.mjs.map +2 -2
  78. package/build-module/components/dataviews-layouts/grid/composite-grid.mjs +2 -0
  79. package/build-module/components/dataviews-layouts/grid/composite-grid.mjs.map +2 -2
  80. package/build-module/components/dataviews-layouts/grid/index.mjs +19 -14
  81. package/build-module/components/dataviews-layouts/grid/index.mjs.map +2 -2
  82. package/build-module/components/dataviews-layouts/list/index.mjs +13 -9
  83. package/build-module/components/dataviews-layouts/list/index.mjs.map +2 -2
  84. package/build-module/components/dataviews-layouts/picker-table/index.mjs +1 -1
  85. package/build-module/components/dataviews-layouts/picker-table/index.mjs.map +2 -2
  86. package/build-module/components/dataviews-layouts/table/index.mjs +40 -19
  87. package/build-module/components/dataviews-layouts/table/index.mjs.map +2 -2
  88. package/build-module/dataviews/index.mjs +9 -2
  89. package/build-module/dataviews/index.mjs.map +2 -2
  90. package/build-module/dataviews-picker/index.mjs +1 -0
  91. package/build-module/dataviews-picker/index.mjs.map +2 -2
  92. package/build-module/hooks/use-data.mjs +25 -0
  93. package/build-module/hooks/use-data.mjs.map +7 -0
  94. package/build-module/hooks/use-delayed-loading.mjs +22 -0
  95. package/build-module/hooks/use-delayed-loading.mjs.map +7 -0
  96. package/build-style/style-rtl.css +109 -32
  97. package/build-style/style.css +109 -32
  98. package/build-types/components/dataform-controls/color.d.ts.map +1 -1
  99. package/build-types/components/dataform-controls/datetime.d.ts.map +1 -1
  100. package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts +16 -0
  101. package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts.map +1 -0
  102. package/build-types/components/dataform-layouts/card/index.d.ts +1 -10
  103. package/build-types/components/dataform-layouts/card/index.d.ts.map +1 -1
  104. package/build-types/components/dataform-layouts/normalize-form.d.ts.map +1 -1
  105. package/build-types/components/dataform-layouts/panel/summary-button.d.ts.map +1 -1
  106. package/build-types/components/dataform-layouts/panel/utils/get-label-content.d.ts +1 -1
  107. package/build-types/components/dataform-layouts/panel/utils/get-label-content.d.ts.map +1 -1
  108. package/build-types/components/dataviews-context/index.d.ts +1 -0
  109. package/build-types/components/dataviews-context/index.d.ts.map +1 -1
  110. package/build-types/components/dataviews-filters/filter.d.ts +1 -1
  111. package/build-types/components/dataviews-filters/filter.d.ts.map +1 -1
  112. package/build-types/components/dataviews-filters/toggle.d.ts.map +1 -1
  113. package/build-types/components/dataviews-footer/index.d.ts +1 -1
  114. package/build-types/components/dataviews-footer/index.d.ts.map +1 -1
  115. package/build-types/components/dataviews-item-actions/index.d.ts.map +1 -1
  116. package/build-types/components/dataviews-layout/index.d.ts +1 -1
  117. package/build-types/components/dataviews-layout/index.d.ts.map +1 -1
  118. package/build-types/components/dataviews-layouts/activity/index.d.ts.map +1 -1
  119. package/build-types/components/dataviews-layouts/grid/composite-grid.d.ts +2 -1
  120. package/build-types/components/dataviews-layouts/grid/composite-grid.d.ts.map +1 -1
  121. package/build-types/components/dataviews-layouts/grid/index.d.ts.map +1 -1
  122. package/build-types/components/dataviews-layouts/list/index.d.ts.map +1 -1
  123. package/build-types/components/dataviews-layouts/picker-activity/index.d.ts +3 -0
  124. package/build-types/components/dataviews-layouts/picker-activity/index.d.ts.map +1 -0
  125. package/build-types/components/dataviews-layouts/table/index.d.ts.map +1 -1
  126. package/build-types/components/dataviews-layouts/table/use-scroll-state.d.ts +25 -0
  127. package/build-types/components/dataviews-layouts/table/use-scroll-state.d.ts.map +1 -0
  128. package/build-types/components/dataviews-layouts/utils/grid-config-options.d.ts +2 -0
  129. package/build-types/components/dataviews-layouts/utils/grid-config-options.d.ts.map +1 -0
  130. package/build-types/components/dataviews-layouts/utils/use-infinite-scroll.d.ts +22 -0
  131. package/build-types/components/dataviews-layouts/utils/use-infinite-scroll.d.ts.map +1 -0
  132. package/build-types/dataform/stories/index.story.d.ts +9 -1
  133. package/build-types/dataform/stories/index.story.d.ts.map +1 -1
  134. package/build-types/dataform/stories/layout-panel.d.ts +3 -1
  135. package/build-types/dataform/stories/layout-panel.d.ts.map +1 -1
  136. package/build-types/dataviews/index.d.ts.map +1 -1
  137. package/build-types/dataviews/stories/empty.d.ts.map +1 -1
  138. package/build-types/dataviews/stories/free-composition.d.ts.map +1 -1
  139. package/build-types/dataviews/stories/layout-table.d.ts.map +1 -1
  140. package/build-types/dataviews/stories/minimal-ui.d.ts.map +1 -1
  141. package/build-types/dataviews/stories/with-card.d.ts.map +1 -1
  142. package/build-types/dataviews-picker/index.d.ts.map +1 -1
  143. package/build-types/field-types/utils/is-valid-date-boundary.d.ts +7 -0
  144. package/build-types/field-types/utils/is-valid-date-boundary.d.ts.map +1 -0
  145. package/build-types/hooks/test/use-data.d.ts +2 -0
  146. package/build-types/hooks/test/use-data.d.ts.map +1 -0
  147. package/build-types/hooks/use-data.d.ts +12 -0
  148. package/build-types/hooks/use-data.d.ts.map +1 -0
  149. package/build-types/hooks/use-delayed-loading.d.ts +4 -0
  150. package/build-types/hooks/use-delayed-loading.d.ts.map +1 -0
  151. package/build-types/hooks/use-infinite-scroll.d.ts +21 -0
  152. package/build-types/hooks/use-infinite-scroll.d.ts.map +1 -0
  153. package/build-types/hooks/use-selected-items.d.ts +19 -0
  154. package/build-types/hooks/use-selected-items.d.ts.map +1 -0
  155. package/build-types/types/dataform.d.ts +3 -0
  156. package/build-types/types/dataform.d.ts.map +1 -1
  157. package/build-wp/index.js +2568 -1864
  158. package/package.json +21 -21
  159. package/src/components/dataform-controls/color.tsx +29 -36
  160. package/src/components/dataform-controls/date.tsx +2 -2
  161. package/src/components/dataform-controls/datetime.tsx +19 -34
  162. package/src/components/dataform-controls/style.scss +4 -0
  163. package/src/components/dataform-layouts/card/index.tsx +118 -185
  164. package/src/components/dataform-layouts/normalize-form.ts +1 -0
  165. package/src/components/dataform-layouts/panel/style.scss +24 -20
  166. package/src/components/dataform-layouts/panel/summary-button.tsx +43 -12
  167. package/src/components/dataform-layouts/panel/utils/get-label-content.tsx +3 -5
  168. package/src/components/dataform-layouts/regular/style.scss +3 -2
  169. package/src/components/dataform-layouts/test/normalize-form.ts +5 -0
  170. package/src/components/dataviews-bulk-actions/index.tsx +2 -2
  171. package/src/components/dataviews-context/index.ts +2 -0
  172. package/src/components/dataviews-filters/filter.tsx +1 -1
  173. package/src/components/dataviews-filters/toggle.tsx +10 -3
  174. package/src/components/dataviews-footer/index.tsx +39 -12
  175. package/src/components/dataviews-footer/style.scss +6 -1
  176. package/src/components/dataviews-item-actions/index.tsx +0 -3
  177. package/src/components/dataviews-item-actions/style.scss +7 -0
  178. package/src/components/dataviews-layout/index.tsx +24 -0
  179. package/src/components/dataviews-layouts/activity/index.tsx +29 -22
  180. package/src/components/dataviews-layouts/activity/style.scss +5 -0
  181. package/src/components/dataviews-layouts/grid/composite-grid.tsx +5 -1
  182. package/src/components/dataviews-layouts/grid/index.tsx +19 -22
  183. package/src/components/dataviews-layouts/grid/style.scss +5 -0
  184. package/src/components/dataviews-layouts/list/index.tsx +18 -18
  185. package/src/components/dataviews-layouts/list/style.scss +5 -0
  186. package/src/components/dataviews-layouts/picker-table/index.tsx +1 -1
  187. package/src/components/dataviews-layouts/table/index.tsx +48 -22
  188. package/src/components/dataviews-layouts/table/style.scss +8 -2
  189. package/src/dataform/stories/index.story.tsx +5 -0
  190. package/src/dataform/stories/layout-panel.tsx +19 -2
  191. package/src/dataviews/index.tsx +11 -3
  192. package/src/dataviews/stories/empty.tsx +1 -7
  193. package/src/dataviews/stories/free-composition.tsx +0 -5
  194. package/src/dataviews/stories/layout-table.tsx +1 -7
  195. package/src/dataviews/stories/minimal-ui.tsx +0 -5
  196. package/src/dataviews/stories/with-card.tsx +1 -7
  197. package/src/dataviews/style.scss +25 -0
  198. package/src/dataviews-picker/index.tsx +2 -1
  199. package/src/hooks/use-data.ts +45 -0
  200. package/src/hooks/use-delayed-loading.ts +21 -0
  201. package/src/types/dataform.ts +4 -0
@@ -27,7 +27,7 @@
27
27
  align-items: center;
28
28
  }
29
29
 
30
- &:not([aria-disabled="true"]):hover {
30
+ &:not(.is-disabled):hover {
31
31
  color: var(--wp-admin-theme-color);
32
32
 
33
33
  .dataforms-layouts-panel__field-trigger-icon {
@@ -39,7 +39,7 @@
39
39
  }
40
40
  }
41
41
 
42
- &[aria-disabled="true"] {
42
+ &.is-disabled {
43
43
  cursor: default;
44
44
 
45
45
  .dataforms-layouts-panel__field-control {
@@ -49,29 +49,29 @@
49
49
  }
50
50
  }
51
51
 
52
+ .dataforms-layouts-panel__field-trigger--edit-always
53
+ .dataforms-layouts-panel__field-trigger-icon {
54
+ opacity: 1;
55
+ fill: currentColor;
56
+
57
+ &:hover,
58
+ &:focus-visible {
59
+ fill: var(--wp-admin-theme-color);
60
+ }
61
+ }
62
+
52
63
  .dataforms-layouts-panel__field-trigger-icon {
53
- appearance: none;
54
- background: none;
55
- border: none;
56
64
  padding: 0;
57
- margin: 0;
58
- cursor: pointer;
59
- display: flex;
60
- align-items: center;
61
- fill: var(--wp-admin-theme-color);
65
+ color: var(--wp-admin-theme-color);
62
66
  flex: 0 0 auto;
63
67
  opacity: 0;
64
68
  border-radius: var(--wpds-border-radius-xs);
65
69
 
66
- &::after {
67
- content: "";
68
- position: absolute;
69
- inset: 0;
70
- }
71
-
72
70
  &:focus-visible {
73
71
  opacity: 1;
74
- outline: var(--wpds-border-width-focus) solid var(--wp-admin-theme-color);
72
+ outline:
73
+ var(--wpds-border-width-focus) solid
74
+ var(--wp-admin-theme-color);
75
75
  }
76
76
  }
77
77
 
@@ -92,6 +92,7 @@
92
92
  line-height: $grid-unit-05 * 5;
93
93
  hyphens: auto;
94
94
  color: $gray-700;
95
+ text-transform: capitalize;
95
96
 
96
97
  .components-base-control__label {
97
98
  display: inline;
@@ -133,15 +134,18 @@
133
134
  }
134
135
  }
135
136
 
136
- .dataforms-layouts-panel__field-trigger--label-top .dataforms-layouts-panel__field-label {
137
+ .dataforms-layouts-panel__field-trigger--label-top
138
+ .dataforms-layouts-panel__field-label {
137
139
  width: 100%;
138
140
  }
139
141
 
140
- .dataforms-layouts-panel__field-trigger--label-top .dataforms-layouts-panel__field-control {
142
+ .dataforms-layouts-panel__field-trigger--label-top
143
+ .dataforms-layouts-panel__field-control {
141
144
  grid-column: 1 / -1;
142
145
  }
143
146
 
144
- .dataforms-layouts-panel__field-trigger--label-top .dataforms-layouts-panel__field-trigger-icon {
147
+ .dataforms-layouts-panel__field-trigger--label-top
148
+ .dataforms-layouts-panel__field-trigger-icon {
145
149
  grid-row: 1;
146
150
  grid-column: 2;
147
151
  }
@@ -6,10 +6,11 @@ import clsx from 'clsx';
6
6
  /**
7
7
  * WordPress dependencies
8
8
  */
9
- import { Icon, Tooltip } from '@wordpress/components';
9
+ import { Button, Icon, Tooltip } from '@wordpress/components';
10
10
  import { sprintf, _x } from '@wordpress/i18n';
11
11
  import { error as errorIcon, pencil } from '@wordpress/icons';
12
12
  import { useInstanceId } from '@wordpress/compose';
13
+ import { useRef } from '@wordpress/element';
13
14
 
14
15
  /**
15
16
  * Internal dependencies
@@ -45,15 +46,20 @@ export default function SummaryButton< Item >( {
45
46
  onClick: () => void;
46
47
  'aria-expanded'?: boolean;
47
48
  } ) {
48
- const labelPosition = ( field.layout as NormalizedPanelLayout )
49
- .labelPosition;
49
+ const { labelPosition, editVisibility } =
50
+ field.layout as NormalizedPanelLayout;
50
51
  const errorMessage = getFirstValidationError( validity );
51
52
  const showError = touched && !! errorMessage;
52
53
  const labelClassName = getLabelClassName( labelPosition, showError );
53
54
  const labelContent = getLabelContent( showError, errorMessage, fieldLabel );
54
55
  const className = clsx(
55
56
  'dataforms-layouts-panel__field-trigger',
56
- `dataforms-layouts-panel__field-trigger--label-${ labelPosition }`
57
+ `dataforms-layouts-panel__field-trigger--label-${ labelPosition }`,
58
+ {
59
+ 'is-disabled': disabled,
60
+ 'dataforms-layouts-panel__field-trigger--edit-always':
61
+ editVisibility === 'always',
62
+ }
57
63
  );
58
64
 
59
65
  const controlId = useInstanceId(
@@ -73,8 +79,34 @@ export default function SummaryButton< Item >( {
73
79
  fieldLabel || ''
74
80
  );
75
81
 
82
+ const rowRef = useRef< HTMLDivElement >( null );
83
+
84
+ const handleRowClick = () => {
85
+ const selection =
86
+ rowRef.current?.ownerDocument.defaultView?.getSelection();
87
+ if ( selection && selection.toString().length > 0 ) {
88
+ return;
89
+ }
90
+ onClick();
91
+ };
92
+
93
+ const handleKeyDown = ( event: React.KeyboardEvent ) => {
94
+ if (
95
+ event.target === event.currentTarget &&
96
+ ( event.key === 'Enter' || event.key === ' ' )
97
+ ) {
98
+ event.preventDefault();
99
+ onClick();
100
+ }
101
+ };
102
+
76
103
  return (
77
- <div className={ className } aria-disabled={ disabled || undefined }>
104
+ <div
105
+ ref={ rowRef }
106
+ className={ className }
107
+ onClick={ ! disabled ? handleRowClick : undefined }
108
+ onKeyDown={ ! disabled ? handleKeyDown : undefined }
109
+ >
78
110
  { labelPosition !== 'none' && (
79
111
  <span className={ labelClassName }>{ labelContent }</span>
80
112
  ) }
@@ -122,17 +154,16 @@ export default function SummaryButton< Item >( {
122
154
  ) }
123
155
  </span>
124
156
  { ! disabled && (
125
- <button
126
- type="button"
157
+ <Button
127
158
  className="dataforms-layouts-panel__field-trigger-icon"
128
- aria-label={ ariaLabel }
159
+ label={ ariaLabel }
160
+ showTooltip={ false }
161
+ icon={ pencil }
162
+ size="small"
129
163
  aria-expanded={ ariaExpanded }
130
164
  aria-haspopup="dialog"
131
165
  aria-describedby={ `${ controlId }` }
132
- onClick={ onClick }
133
- >
134
- <Icon icon={ pencil } size={ 24 } />
135
- </button>
166
+ />
136
167
  ) }
137
168
  </div>
138
169
  );
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { BaseControl, Icon, Tooltip } from '@wordpress/components';
4
+ import { Icon, Tooltip } from '@wordpress/components';
5
5
  import { error as errorIcon } from '@wordpress/icons';
6
6
 
7
7
  function getLabelContent(
@@ -13,13 +13,11 @@ function getLabelContent(
13
13
  <Tooltip text={ errorMessage } placement="top">
14
14
  <span className="dataforms-layouts-panel__field-label-error-content">
15
15
  <Icon icon={ errorIcon } size={ 16 } />
16
- <BaseControl.VisualLabel>
17
- { fieldLabel }
18
- </BaseControl.VisualLabel>
16
+ { fieldLabel }
19
17
  </span>
20
18
  </Tooltip>
21
19
  ) : (
22
- <BaseControl.VisualLabel>{ fieldLabel }</BaseControl.VisualLabel>
20
+ fieldLabel
23
21
  );
24
22
  }
25
23
 
@@ -8,8 +8,9 @@
8
8
  align-items: flex-start !important;
9
9
 
10
10
  .components-base-control__label,
11
- .components-input-control__label {
12
- color: $gray-700;
11
+ .components-input-control__label,
12
+ .components-form-token-field__label {
13
+ color: $gray-900;
13
14
  }
14
15
  }
15
16
 
@@ -139,6 +139,7 @@ describe( 'normalizeFormFields', () => {
139
139
  type: 'panel',
140
140
  openAs: 'dropdown',
141
141
  summary: [],
142
+ editVisibility: 'on-hover',
142
143
  },
143
144
  fields: [
144
145
  {
@@ -148,6 +149,7 @@ describe( 'normalizeFormFields', () => {
148
149
  labelPosition: 'side',
149
150
  openAs: 'dropdown',
150
151
  summary: [],
152
+ editVisibility: 'on-hover',
151
153
  },
152
154
  },
153
155
  ],
@@ -166,6 +168,7 @@ describe( 'normalizeFormFields', () => {
166
168
  type: 'panel',
167
169
  openAs: 'dropdown',
168
170
  summary: [],
171
+ editVisibility: 'on-hover',
169
172
  },
170
173
  fields: [
171
174
  {
@@ -175,6 +178,7 @@ describe( 'normalizeFormFields', () => {
175
178
  labelPosition: 'top',
176
179
  openAs: 'dropdown',
177
180
  summary: [],
181
+ editVisibility: 'on-hover',
178
182
  },
179
183
  },
180
184
  ],
@@ -358,6 +362,7 @@ describe( 'normalizeFormFields', () => {
358
362
  labelPosition: 'side',
359
363
  openAs: 'dropdown',
360
364
  summary: [],
365
+ editVisibility: 'on-hover',
361
366
  },
362
367
  },
363
368
  ],
@@ -320,7 +320,7 @@ function FooterContent< Item >( {
320
320
  const [ actionInProgress, setActionInProgress ] = useState< string | null >(
321
321
  null
322
322
  );
323
- const footerContentRef = useRef< JSX.Element | null >( null );
323
+ const footerContentRef = useRef< React.JSX.Element >( undefined );
324
324
  const isMobile = useViewportMatch( 'medium', '<' );
325
325
 
326
326
  const bulkActions = useMemo(
@@ -359,7 +359,7 @@ function FooterContent< Item >( {
359
359
  );
360
360
  if ( ! actionInProgress ) {
361
361
  if ( footerContentRef.current ) {
362
- footerContentRef.current = null;
362
+ footerContentRef.current = undefined;
363
363
  }
364
364
  return renderFooterContent(
365
365
  data,
@@ -56,6 +56,7 @@ type DataViewsContextType< Item > = {
56
56
  setIsShowingFilter: ( value: boolean ) => void;
57
57
  config: { perPageSizes: number[] };
58
58
  empty?: ReactNode;
59
+ hasInitiallyLoaded?: boolean;
59
60
  hasInfiniteScrollHandler: boolean;
60
61
  itemListLabel?: string;
61
62
  onReset?: ( () => void ) | false;
@@ -84,6 +85,7 @@ const DataViewsContext = createContext< DataViewsContextType< any > >( {
84
85
  filters: [],
85
86
  isShowingFilter: false,
86
87
  setIsShowingFilter: () => {},
88
+ hasInitiallyLoaded: false,
87
89
  hasInfiniteScrollHandler: false,
88
90
  config: {
89
91
  perPageSizes: [],
@@ -51,7 +51,7 @@ interface OperatorSelectorProps {
51
51
  }
52
52
 
53
53
  interface FilterProps extends OperatorSelectorProps {
54
- addFilterRef: RefObject< HTMLButtonElement >;
54
+ addFilterRef: RefObject< HTMLButtonElement | null >;
55
55
  openedFilter: string | null;
56
56
  fields: NormalizedField< any >[];
57
57
  }
@@ -31,13 +31,13 @@ function FiltersToggle() {
31
31
  },
32
32
  [ onChangeView, setIsShowingFilter ]
33
33
  );
34
- const visibleFilters = filters.filter( ( filter ) => filter.isVisible );
35
34
 
36
- const hasVisibleFilters = !! visibleFilters.length;
37
35
  if ( filters.length === 0 ) {
38
36
  return null;
39
37
  }
40
38
 
39
+ const hasVisibleFilters = filters.some( ( filter ) => filter.isVisible );
40
+
41
41
  const addFilterButtonProps = {
42
42
  label: __( 'Add filter' ),
43
43
  'aria-expanded': false,
@@ -54,12 +54,19 @@ function FiltersToggle() {
54
54
  setIsShowingFilter( ! isShowingFilter );
55
55
  },
56
56
  };
57
+ // When there are primary or locked filters, the filter bar is always
58
+ // visible and cannot be hidden, so the toggle button should be disabled.
59
+ const hasPrimaryOrLockedFilters = filters.some(
60
+ ( filter ) => filter.isPrimary || filter.isLocked
61
+ );
57
62
  const buttonComponent = (
58
63
  <Button
59
64
  ref={ buttonRef }
60
65
  className="dataviews-filters__visibility-toggle"
61
66
  size="compact"
62
67
  icon={ funnel }
68
+ disabled={ hasPrimaryOrLockedFilters }
69
+ accessibleWhenDisabled
63
70
  { ...( hasVisibleFilters
64
71
  ? toggleFiltersButtonProps
65
72
  : addFilterButtonProps ) }
@@ -92,7 +99,7 @@ function FilterVisibilityToggle( {
92
99
  filtersCount,
93
100
  children,
94
101
  }: {
95
- buttonRef: React.RefObject< HTMLButtonElement >;
102
+ buttonRef: React.RefObject< HTMLButtonElement | null >;
96
103
  filtersCount?: number;
97
104
  children: React.ReactNode;
98
105
  } ) {
@@ -1,3 +1,8 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import clsx from 'clsx';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
@@ -14,6 +19,7 @@ import {
14
19
  useSomeItemHasAPossibleBulkAction,
15
20
  } from '../dataviews-bulk-actions';
16
21
  import { LAYOUT_GRID, LAYOUT_TABLE } from '../../constants';
22
+ import { useDelayedLoading } from '../../hooks/use-delayed-loading';
17
23
 
18
24
  const EMPTY_ARRAY: [] = [];
19
25
 
@@ -23,30 +29,51 @@ export default function DataViewsFooter() {
23
29
  paginationInfo: { totalItems = 0, totalPages },
24
30
  data,
25
31
  actions = EMPTY_ARRAY,
32
+ isLoading,
33
+ hasInitiallyLoaded,
34
+ hasInfiniteScrollHandler,
26
35
  } = useContext( DataViewsContext );
36
+
37
+ const isRefreshing =
38
+ !! isLoading &&
39
+ hasInitiallyLoaded &&
40
+ ! hasInfiniteScrollHandler &&
41
+ !! data?.length;
42
+
43
+ const isDelayedRefreshing = useDelayedLoading( !! isRefreshing );
44
+
27
45
  const hasBulkActions =
28
46
  useSomeItemHasAPossibleBulkAction( actions, data ) &&
29
47
  [ LAYOUT_TABLE, LAYOUT_GRID ].includes( view.type );
30
48
 
31
49
  if (
32
- ! totalItems ||
33
- ! totalPages ||
34
- ( totalPages <= 1 && ! hasBulkActions )
50
+ ! isRefreshing &&
51
+ ( ! totalItems ||
52
+ ! totalPages ||
53
+ ( totalPages <= 1 && ! hasBulkActions ) )
35
54
  ) {
36
55
  return null;
37
56
  }
38
57
  return (
39
- !! totalItems && (
40
- <Stack
41
- direction="row"
42
- justify="end"
43
- align="center"
58
+ ( !! totalItems || isRefreshing ) && (
59
+ <div
44
60
  className="dataviews-footer"
45
- gap="sm"
61
+ // @ts-ignore
62
+ inert={ isRefreshing ? 'true' : undefined }
46
63
  >
47
- { hasBulkActions && <BulkActionsFooter /> }
48
- <DataViewsPagination />
49
- </Stack>
64
+ <Stack
65
+ direction="row"
66
+ justify="end"
67
+ align="center"
68
+ className={ clsx( 'dataviews-footer__content', {
69
+ 'is-refreshing': isDelayedRefreshing,
70
+ } ) }
71
+ gap="sm"
72
+ >
73
+ { hasBulkActions && <BulkActionsFooter /> }
74
+ <DataViewsPagination />
75
+ </Stack>
76
+ </div>
50
77
  )
51
78
  );
52
79
  }
@@ -1,6 +1,7 @@
1
1
  @use "@wordpress/base-styles/colors" as *;
2
2
  @use "@wordpress/base-styles/variables" as *;
3
3
  @use "@wordpress/base-styles/z-index" as *;
4
+ @use "../../dataviews/style" as *;
4
5
 
5
6
  .dataviews-footer {
6
7
  position: sticky;
@@ -16,10 +17,14 @@
16
17
  }
17
18
 
18
19
  z-index: z-index(".dataviews-footer");
20
+
21
+ .is-refreshing {
22
+ @include dataviews-refreshing();
23
+ }
19
24
  }
20
25
 
21
26
  @container (max-width: 560px) {
22
- .dataviews-footer {
27
+ .dataviews-footer__content {
23
28
  flex-direction: column !important;
24
29
 
25
30
  .dataviews-bulk-actions-footer__container {
@@ -171,9 +171,6 @@ export function ActionsMenuGroup< Item >( {
171
171
  return (
172
172
  <Menu.Group>
173
173
  { renderActionGroup( primaryActions ) }
174
- { primaryActions.length > 0 && regularActions.length > 0 && (
175
- <Menu.Separator />
176
- ) }
177
174
  { renderActionGroup( regularActions ) }
178
175
  </Menu.Group>
179
176
  );
@@ -5,3 +5,10 @@
5
5
  .dataviews-action-modal {
6
6
  z-index: z-index(".dataviews-action-modal");
7
7
  }
8
+
9
+ // TODO: the way forward here would be to use the new unstyle button coming
10
+ // from wordpress/ui package, but we're not ready to use it yet.
11
+ // See https://github.com/WordPress/gutenberg/pull/75721/
12
+ .dataviews-item-actions .components-button:not(.dataviews-all-actions-button) {
13
+ padding: 0 $grid-unit-05;
14
+ }
@@ -7,6 +7,7 @@ import type { ComponentType } from 'react';
7
7
  * WordPress dependencies
8
8
  */
9
9
  import { useContext } from '@wordpress/element';
10
+ import { Spinner } from '@wordpress/components';
10
11
  import { __ } from '@wordpress/i18n';
11
12
 
12
13
  /**
@@ -14,6 +15,7 @@ import { __ } from '@wordpress/i18n';
14
15
  */
15
16
  import DataViewsContext from '../dataviews-context';
16
17
  import { VIEW_LAYOUTS } from '../dataviews-layouts';
18
+ import { useDelayedLoading } from '../../hooks/use-delayed-loading';
17
19
  import type { ViewBaseProps } from '../../types';
18
20
 
19
21
  type DataViewsLayoutProps = {
@@ -27,6 +29,7 @@ export default function DataViewsLayout( { className }: DataViewsLayoutProps ) {
27
29
  fields,
28
30
  getItemId,
29
31
  getItemLevel,
32
+ hasInitiallyLoaded,
30
33
  isLoading,
31
34
  view,
32
35
  onChangeView,
@@ -40,6 +43,27 @@ export default function DataViewsLayout( { className }: DataViewsLayoutProps ) {
40
43
  empty = <p>{ __( 'No results' ) }</p>,
41
44
  } = useContext( DataViewsContext );
42
45
 
46
+ const isDelayedInitialLoading = useDelayedLoading( ! hasInitiallyLoaded, {
47
+ delay: 200,
48
+ } );
49
+ // Until the initial data load completes, show a spinner (or nothing if fast).
50
+ // After that, render the layout component which preserves previous data
51
+ // while loading subsequent requests.
52
+ if ( ! hasInitiallyLoaded ) {
53
+ // If the initial data load is fast, don't show the loading state at all.
54
+ if ( ! isDelayedInitialLoading ) {
55
+ return null;
56
+ }
57
+ // If the initial data load takes more than 200ms, show the loading state.
58
+ return (
59
+ <div className="dataviews-loading">
60
+ <p>
61
+ <Spinner />
62
+ </p>
63
+ </div>
64
+ );
65
+ }
66
+
43
67
  const ViewComponent = VIEW_LAYOUTS.find(
44
68
  ( v ) => v.type === view.type && defaultLayouts[ v.type ]
45
69
  )?.component as ComponentType< ViewBaseProps< any > >;
@@ -16,41 +16,40 @@ import type { ViewActivityProps } from '../../../types';
16
16
  import getDataByGroup from '../utils/get-data-by-group';
17
17
  import ActivityGroup from './activity-group';
18
18
  import ActivityItems from './activity-items';
19
+ import { useDelayedLoading } from '../../../hooks/use-delayed-loading';
19
20
 
20
21
  export default function ViewActivity< Item >(
21
22
  props: ViewActivityProps< Item >
22
23
  ) {
23
24
  const { empty, data, fields, isLoading, view, className } = props;
24
25
 
25
- // Handle empty/loading states
26
- const hasData = data?.length;
26
+ const isDelayedLoading = useDelayedLoading( !! isLoading );
27
+ const hasData = !! data?.length;
28
+
29
+ // Check if data should be grouped
30
+ const groupField = view.groupBy?.field
31
+ ? fields.find( ( field ) => field.id === view.groupBy?.field )
32
+ : null;
33
+ const dataByGroup =
34
+ hasData && groupField ? getDataByGroup( data, groupField ) : null;
35
+
36
+ const isInfiniteScroll = view.infiniteScrollEnabled && ! dataByGroup;
27
37
  if ( ! hasData ) {
28
38
  return (
29
39
  <div
30
- className={ clsx( {
31
- 'dataviews-loading': isLoading,
32
- 'dataviews-no-results': ! hasData && ! isLoading,
40
+ className={ clsx( 'dataviews-no-results', {
41
+ 'is-refreshing': isDelayedLoading,
33
42
  } ) }
34
43
  >
35
- { ! hasData &&
36
- ( isLoading ? (
37
- <p>
38
- <Spinner />
39
- </p>
40
- ) : (
41
- empty
42
- ) ) }
44
+ { empty }
43
45
  </div>
44
46
  );
45
47
  }
46
48
 
47
- const wrapperClassName = clsx( 'dataviews-view-activity', className );
48
-
49
- // Check if data should be grouped
50
- const groupField = view.groupBy?.field
51
- ? fields.find( ( field ) => field.id === view.groupBy?.field )
52
- : null;
53
- const dataByGroup = groupField ? getDataByGroup( data, groupField ) : null;
49
+ const isInert = ! isInfiniteScroll && !! isLoading;
50
+ const wrapperClassName = clsx( 'dataviews-view-activity', className, {
51
+ 'is-refreshing': ! isInfiniteScroll && isDelayedLoading,
52
+ } );
54
53
 
55
54
  // Convert dataByGroup entries into array.
56
55
  const groupedEntries = dataByGroup
@@ -60,7 +59,13 @@ export default function ViewActivity< Item >(
60
59
  // Render grouped activity
61
60
  if ( hasData && groupField && dataByGroup ) {
62
61
  return (
63
- <Stack direction="column" gap="sm" className={ wrapperClassName }>
62
+ <Stack
63
+ direction="column"
64
+ gap="sm"
65
+ className={ wrapperClassName }
66
+ // @ts-ignore
67
+ inert={ isInert ? 'true' : undefined }
68
+ >
64
69
  { groupedEntries.map(
65
70
  ( [ groupName, groupData ]: [ string, Item[] ] ) => (
66
71
  <ActivityGroup< Item >
@@ -87,10 +92,12 @@ export default function ViewActivity< Item >(
87
92
  <div
88
93
  className={ wrapperClassName }
89
94
  role={ view.infiniteScrollEnabled ? 'feed' : undefined }
95
+ // @ts-ignore
96
+ inert={ isInert ? 'true' : undefined }
90
97
  >
91
98
  <ActivityItems< Item > { ...props } />
92
99
  </div>
93
- { hasData && isLoading && (
100
+ { isInfiniteScroll && isLoading && (
94
101
  <p className="dataviews-loading-more">
95
102
  <Spinner />
96
103
  </p>
@@ -1,5 +1,6 @@
1
1
  @use "@wordpress/base-styles/colors" as *;
2
2
  @use "@wordpress/base-styles/variables" as *;
3
+ @use "../../../dataviews/style" as *;
3
4
 
4
5
  .dataviews-view-activity {
5
6
  margin: 0 0 auto;
@@ -211,6 +212,10 @@
211
212
  }
212
213
  }
213
214
 
215
+ &.is-refreshing {
216
+ @include dataviews-refreshing();
217
+ }
218
+
214
219
  & + .dataviews-pagination {
215
220
  justify-content: space-between;
216
221
  }
@@ -287,12 +287,13 @@ const GridItem = forwardRef( function GridItem< Item >(
287
287
  props: GridItemProps< Item > & {
288
288
  ref?: React.ForwardedRef< HTMLDivElement >;
289
289
  }
290
- ) => JSX.Element;
290
+ ) => React.ReactNode;
291
291
 
292
292
  interface CompositeGridProps< Item > {
293
293
  data: Item[];
294
294
  isInfiniteScroll: boolean;
295
295
  className?: string;
296
+ inert?: string;
296
297
  isLoading?: boolean;
297
298
  view: ViewGridType;
298
299
  fields: NormalizedField< Item >[];
@@ -313,6 +314,7 @@ export default function CompositeGrid< Item >( {
313
314
  data,
314
315
  isInfiniteScroll,
315
316
  className,
317
+ inert,
316
318
  isLoading,
317
319
  view,
318
320
  fields,
@@ -375,6 +377,8 @@ export default function CompositeGrid< Item >( {
375
377
  aria-busy={ isLoading }
376
378
  aria-rowcount={ isInfiniteScroll ? undefined : totalRows }
377
379
  ref={ resizeObserverRef }
380
+ // @ts-ignore
381
+ inert={ inert }
378
382
  >
379
383
  { chunk( data, gridColumns ).map( ( row, i ) => (
380
384
  <Composite.Row