@wordpress/dataviews 1.2.0 → 2.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 (150) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +33 -30
  3. package/build/add-filter.js +30 -22
  4. package/build/add-filter.js.map +1 -1
  5. package/build/bulk-actions-toolbar.js +74 -69
  6. package/build/bulk-actions-toolbar.js.map +1 -1
  7. package/build/bulk-actions.js +69 -56
  8. package/build/bulk-actions.js.map +1 -1
  9. package/build/constants.js +17 -10
  10. package/build/constants.js.map +1 -1
  11. package/build/dataviews.js +63 -56
  12. package/build/dataviews.js.map +1 -1
  13. package/build/filter-summary.js +105 -95
  14. package/build/filter-summary.js.map +1 -1
  15. package/build/filters.js +18 -17
  16. package/build/filters.js.map +1 -1
  17. package/build/index.js.map +1 -1
  18. package/build/item-actions.js +79 -65
  19. package/build/item-actions.js.map +1 -1
  20. package/build/layouts.js.map +1 -1
  21. package/build/pagination.js +60 -57
  22. package/build/pagination.js.map +1 -1
  23. package/build/reset-filters.js +9 -4
  24. package/build/reset-filters.js.map +1 -1
  25. package/build/search-widget.js +108 -89
  26. package/build/search-widget.js.map +1 -1
  27. package/build/search.js +13 -6
  28. package/build/search.js.map +1 -1
  29. package/build/single-selection-checkbox.js +2 -2
  30. package/build/single-selection-checkbox.js.map +1 -1
  31. package/build/types.js.map +1 -1
  32. package/build/utils.js +3 -15
  33. package/build/utils.js.map +1 -1
  34. package/build/view-actions.js +168 -120
  35. package/build/view-actions.js.map +1 -1
  36. package/build/view-grid.js +113 -99
  37. package/build/view-grid.js.map +1 -1
  38. package/build/view-list.js +154 -132
  39. package/build/view-list.js.map +1 -1
  40. package/build/view-table.js +220 -192
  41. package/build/view-table.js.map +1 -1
  42. package/build-module/add-filter.js +30 -22
  43. package/build-module/add-filter.js.map +1 -1
  44. package/build-module/bulk-actions-toolbar.js +76 -69
  45. package/build-module/bulk-actions-toolbar.js.map +1 -1
  46. package/build-module/bulk-actions.js +71 -56
  47. package/build-module/bulk-actions.js.map +1 -1
  48. package/build-module/constants.js +16 -9
  49. package/build-module/constants.js.map +1 -1
  50. package/build-module/dataviews.js +64 -56
  51. package/build-module/dataviews.js.map +1 -1
  52. package/build-module/filter-summary.js +106 -96
  53. package/build-module/filter-summary.js.map +1 -1
  54. package/build-module/filters.js +18 -17
  55. package/build-module/filters.js.map +1 -1
  56. package/build-module/index.js.map +1 -1
  57. package/build-module/item-actions.js +81 -65
  58. package/build-module/item-actions.js.map +1 -1
  59. package/build-module/layouts.js.map +1 -1
  60. package/build-module/pagination.js +61 -58
  61. package/build-module/pagination.js.map +1 -1
  62. package/build-module/reset-filters.js +9 -4
  63. package/build-module/reset-filters.js.map +1 -1
  64. package/build-module/search-widget.js +109 -89
  65. package/build-module/search-widget.js.map +1 -1
  66. package/build-module/search.js +13 -6
  67. package/build-module/search.js.map +1 -1
  68. package/build-module/single-selection-checkbox.js +2 -3
  69. package/build-module/single-selection-checkbox.js.map +1 -1
  70. package/build-module/types.js.map +1 -1
  71. package/build-module/utils.js +2 -13
  72. package/build-module/utils.js.map +1 -1
  73. package/build-module/view-actions.js +170 -121
  74. package/build-module/view-actions.js.map +1 -1
  75. package/build-module/view-grid.js +115 -99
  76. package/build-module/view-grid.js.map +1 -1
  77. package/build-module/view-list.js +155 -132
  78. package/build-module/view-list.js.map +1 -1
  79. package/build-module/view-table.js +223 -194
  80. package/build-module/view-table.js.map +1 -1
  81. package/build-style/style-rtl.css +115 -22
  82. package/build-style/style.css +115 -22
  83. package/build-types/add-filter.d.ts +9 -6
  84. package/build-types/add-filter.d.ts.map +1 -1
  85. package/build-types/bulk-actions-toolbar.d.ts +11 -7
  86. package/build-types/bulk-actions-toolbar.d.ts.map +1 -1
  87. package/build-types/bulk-actions.d.ts.map +1 -1
  88. package/build-types/constants.d.ts +19 -32
  89. package/build-types/constants.d.ts.map +1 -1
  90. package/build-types/dataviews.d.ts +21 -14
  91. package/build-types/dataviews.d.ts.map +1 -1
  92. package/build-types/filter-summary.d.ts +13 -5
  93. package/build-types/filter-summary.d.ts.map +1 -1
  94. package/build-types/filters.d.ts +11 -1
  95. package/build-types/filters.d.ts.map +1 -1
  96. package/build-types/index.d.ts +3 -3
  97. package/build-types/index.d.ts.map +1 -1
  98. package/build-types/item-actions.d.ts +5 -7
  99. package/build-types/item-actions.d.ts.map +1 -1
  100. package/build-types/layouts.d.ts +8 -4
  101. package/build-types/layouts.d.ts.map +1 -1
  102. package/build-types/reset-filters.d.ts +12 -5
  103. package/build-types/reset-filters.d.ts.map +1 -1
  104. package/build-types/search-widget.d.ts +9 -1
  105. package/build-types/search-widget.d.ts.map +1 -1
  106. package/build-types/search.d.ts +11 -1
  107. package/build-types/search.d.ts.map +1 -1
  108. package/build-types/types.d.ts +78 -10
  109. package/build-types/types.d.ts.map +1 -1
  110. package/build-types/utils.d.ts +2 -1
  111. package/build-types/utils.d.ts.map +1 -1
  112. package/build-types/view-actions.d.ts +10 -1
  113. package/build-types/view-actions.d.ts.map +1 -1
  114. package/build-types/view-grid.d.ts +1 -12
  115. package/build-types/view-grid.d.ts.map +1 -1
  116. package/build-types/view-list.d.ts +2 -14
  117. package/build-types/view-list.d.ts.map +1 -1
  118. package/build-types/view-table.d.ts +3 -12
  119. package/build-types/view-table.d.ts.map +1 -1
  120. package/package.json +11 -12
  121. package/src/{add-filter.js → add-filter.tsx} +17 -1
  122. package/src/{bulk-actions-toolbar.js → bulk-actions-toolbar.tsx} +68 -40
  123. package/src/bulk-actions.tsx +5 -1
  124. package/src/constants.ts +12 -5
  125. package/src/{dataviews.js → dataviews.tsx} +41 -12
  126. package/src/{filter-summary.js → filter-summary.tsx} +35 -6
  127. package/src/{filters.js → filters.tsx} +18 -6
  128. package/src/item-actions.tsx +21 -15
  129. package/src/pagination.tsx +1 -1
  130. package/src/{reset-filters.js → reset-filters.tsx} +17 -2
  131. package/src/{search-widget.js → search-widget.tsx} +27 -7
  132. package/src/{search.js → search.tsx} +22 -5
  133. package/src/style.scss +102 -25
  134. package/src/types.ts +105 -10
  135. package/src/{utils.js → utils.ts} +5 -13
  136. package/src/{view-actions.js → view-actions.tsx} +105 -49
  137. package/src/view-grid.tsx +4 -20
  138. package/src/view-list.tsx +13 -23
  139. package/src/{view-table.js → view-table.tsx} +91 -32
  140. package/tsconfig.json +0 -3
  141. package/tsconfig.tsbuildinfo +1 -1
  142. package/build/dropdown-menu-helper.js +0 -71
  143. package/build/dropdown-menu-helper.js.map +0 -1
  144. package/build-module/dropdown-menu-helper.js +0 -64
  145. package/build-module/dropdown-menu-helper.js.map +0 -1
  146. package/build-types/dropdown-menu-helper.d.ts +0 -6
  147. package/build-types/dropdown-menu-helper.d.ts.map +0 -1
  148. package/src/dropdown-menu-helper.js +0 -61
  149. /package/src/{index.js → index.ts} +0 -0
  150. /package/src/{layouts.js → layouts.ts} +0 -0
@@ -1,3 +1,8 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import type { ChangeEvent } from 'react';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
@@ -13,8 +18,9 @@ import { settings } from '@wordpress/icons';
13
18
  * Internal dependencies
14
19
  */
15
20
  import { unlock } from './lock-unlock';
16
- import { SORTING_DIRECTIONS } from './constants';
21
+ import { SORTING_DIRECTIONS, sortLabels } from './constants';
17
22
  import { VIEW_LAYOUTS } from './layouts';
23
+ import type { AnyItem, NormalizedField, View } from './types';
18
24
 
19
25
  const {
20
26
  DropdownMenuV2: DropdownMenu,
@@ -25,7 +31,41 @@ const {
25
31
  DropdownMenuItemLabelV2: DropdownMenuItemLabel,
26
32
  } = unlock( componentsPrivateApis );
27
33
 
28
- function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) {
34
+ interface ViewTypeMenuProps {
35
+ view: View;
36
+ onChangeView: ( view: View ) => void;
37
+ supportedLayouts?: string[];
38
+ }
39
+
40
+ interface PageSizeMenuProps {
41
+ view: View;
42
+ onChangeView: ( view: View ) => void;
43
+ }
44
+
45
+ interface FieldsVisibilityMenuProps< Item extends AnyItem > {
46
+ view: View;
47
+ onChangeView: ( view: View ) => void;
48
+ fields: NormalizedField< Item >[];
49
+ }
50
+
51
+ interface SortMenuProps< Item extends AnyItem > {
52
+ fields: NormalizedField< Item >[];
53
+ view: View;
54
+ onChangeView: ( view: View ) => void;
55
+ }
56
+
57
+ interface ViewActionsProps< Item extends AnyItem > {
58
+ fields: NormalizedField< Item >[];
59
+ view: View;
60
+ onChangeView: ( view: View ) => void;
61
+ supportedLayouts?: string[];
62
+ }
63
+
64
+ function ViewTypeMenu( {
65
+ view,
66
+ onChangeView,
67
+ supportedLayouts,
68
+ }: ViewTypeMenuProps ) {
29
69
  let _availableViews = VIEW_LAYOUTS;
30
70
  if ( supportedLayouts ) {
31
71
  _availableViews = _availableViews.filter( ( _view ) =>
@@ -41,7 +81,7 @@ function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) {
41
81
  trigger={
42
82
  <DropdownMenuItem
43
83
  suffix={
44
- <span aria-hidden="true">{ activeView.label }</span>
84
+ <span aria-hidden="true">{ activeView?.label }</span>
45
85
  }
46
86
  >
47
87
  <DropdownMenuItemLabel>
@@ -58,11 +98,18 @@ function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) {
58
98
  name="view-actions-available-view"
59
99
  checked={ availableView.type === view.type }
60
100
  hideOnClick
61
- onChange={ ( e ) => {
62
- onChangeView( {
63
- ...view,
64
- type: e.target.value,
65
- } );
101
+ onChange={ ( e: ChangeEvent< HTMLInputElement > ) => {
102
+ switch ( e.target.value ) {
103
+ case 'list':
104
+ case 'grid':
105
+ case 'table':
106
+ return onChangeView( {
107
+ ...view,
108
+ type: e.target.value,
109
+ layout: {},
110
+ } );
111
+ }
112
+ throw new Error( 'Invalid dataview' );
66
113
  } }
67
114
  >
68
115
  <DropdownMenuItemLabel>
@@ -76,7 +123,7 @@ function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) {
76
123
  }
77
124
 
78
125
  const PAGE_SIZE_VALUES = [ 10, 20, 50, 100 ];
79
- function PageSizeMenu( { view, onChangeView } ) {
126
+ function PageSizeMenu( { view, onChangeView }: PageSizeMenuProps ) {
80
127
  return (
81
128
  <DropdownMenu
82
129
  trigger={
@@ -114,7 +161,11 @@ function PageSizeMenu( { view, onChangeView } ) {
114
161
  );
115
162
  }
116
163
 
117
- function FieldsVisibilityMenu( { view, onChangeView, fields } ) {
164
+ function FieldsVisibilityMenu< Item extends AnyItem >( {
165
+ view,
166
+ onChangeView,
167
+ fields,
168
+ }: FieldsVisibilityMenuProps< Item > ) {
118
169
  const hidableFields = fields.filter(
119
170
  ( field ) =>
120
171
  field.enableHiding !== false && field.id !== view.layout.mediaField
@@ -164,7 +215,11 @@ function FieldsVisibilityMenu( { view, onChangeView, fields } ) {
164
215
  );
165
216
  }
166
217
 
167
- function SortMenu( { fields, view, onChangeView } ) {
218
+ function SortMenu< Item extends AnyItem >( {
219
+ fields,
220
+ view,
221
+ onChangeView,
222
+ }: SortMenuProps< Item > ) {
168
223
  const sortableFields = fields.filter(
169
224
  ( field ) => field.enableSorting !== false
170
225
  );
@@ -206,43 +261,41 @@ function SortMenu( { fields, view, onChangeView } ) {
206
261
  minWidth: '220px',
207
262
  } }
208
263
  >
209
- { Object.entries( SORTING_DIRECTIONS ).map(
210
- ( [ direction, info ] ) => {
211
- const isChecked =
212
- currentSortedField !== undefined &&
213
- sortedDirection === direction &&
214
- field.id === currentSortedField.id;
264
+ { SORTING_DIRECTIONS.map( ( direction ) => {
265
+ const isChecked =
266
+ currentSortedField !== undefined &&
267
+ sortedDirection === direction &&
268
+ field.id === currentSortedField.id;
215
269
 
216
- const value = `${ field.id }-${ direction }`;
270
+ const value = `${ field.id }-${ direction }`;
217
271
 
218
- return (
219
- <DropdownMenuRadioItem
220
- key={ value }
221
- // All sorting radio items share the same name, so that
222
- // selecting a sorting option automatically deselects the
223
- // previously selected one, even if it is displayed in
224
- // another submenu. The field and direction are passed via
225
- // the `value` prop.
226
- name="view-actions-sorting"
227
- value={ value }
228
- checked={ isChecked }
229
- onChange={ () => {
230
- onChangeView( {
231
- ...view,
232
- sort: {
233
- field: field.id,
234
- direction,
235
- },
236
- } );
237
- } }
238
- >
239
- <DropdownMenuItemLabel>
240
- { info.label }
241
- </DropdownMenuItemLabel>
242
- </DropdownMenuRadioItem>
243
- );
244
- }
245
- ) }
272
+ return (
273
+ <DropdownMenuRadioItem
274
+ key={ value }
275
+ // All sorting radio items share the same name, so that
276
+ // selecting a sorting option automatically deselects the
277
+ // previously selected one, even if it is displayed in
278
+ // another submenu. The field and direction are passed via
279
+ // the `value` prop.
280
+ name="view-actions-sorting"
281
+ value={ value }
282
+ checked={ isChecked }
283
+ onChange={ () => {
284
+ onChangeView( {
285
+ ...view,
286
+ sort: {
287
+ field: field.id,
288
+ direction,
289
+ },
290
+ } );
291
+ } }
292
+ >
293
+ <DropdownMenuItemLabel>
294
+ { sortLabels[ direction ] }
295
+ </DropdownMenuItemLabel>
296
+ </DropdownMenuRadioItem>
297
+ );
298
+ } ) }
246
299
  </DropdownMenu>
247
300
  );
248
301
  } ) }
@@ -250,12 +303,12 @@ function SortMenu( { fields, view, onChangeView } ) {
250
303
  );
251
304
  }
252
305
 
253
- const ViewActions = memo( function ViewActions( {
306
+ function _ViewActions< Item extends AnyItem >( {
254
307
  fields,
255
308
  view,
256
309
  onChangeView,
257
310
  supportedLayouts,
258
- } ) {
311
+ }: ViewActionsProps< Item > ) {
259
312
  return (
260
313
  <DropdownMenu
261
314
  trigger={
@@ -286,6 +339,9 @@ const ViewActions = memo( function ViewActions( {
286
339
  </DropdownMenuGroup>
287
340
  </DropdownMenu>
288
341
  );
289
- } );
342
+ }
343
+
344
+ // A type assertion is used here to keep the type argument.
345
+ const ViewActions = memo( _ViewActions ) as typeof _ViewActions;
290
346
 
291
347
  export default ViewActions;
package/src/view-grid.tsx CHANGED
@@ -22,12 +22,7 @@ import { __ } from '@wordpress/i18n';
22
22
  import ItemActions from './item-actions';
23
23
  import SingleSelectionCheckbox from './single-selection-checkbox';
24
24
  import { useHasAPossibleBulkAction } from './bulk-actions';
25
- import type {
26
- Action,
27
- AnyItem,
28
- NormalizedField,
29
- ViewGrid as ViewGridType,
30
- } from './types';
25
+ import type { Action, AnyItem, NormalizedField, ViewGridProps } from './types';
31
26
 
32
27
  interface GridItemProps< Item extends AnyItem > {
33
28
  selection: string[];
@@ -40,18 +35,7 @@ interface GridItemProps< Item extends AnyItem > {
40
35
  primaryField?: NormalizedField< Item >;
41
36
  visibleFields: NormalizedField< Item >[];
42
37
  badgeFields: NormalizedField< Item >[];
43
- columnFields: string[];
44
- }
45
-
46
- interface ViewGridProps< Item extends AnyItem > {
47
- actions: Action< Item >[];
48
- data: Item[];
49
- fields: NormalizedField< Item >[];
50
- getItemId: ( item: Item ) => string;
51
- isLoading: boolean;
52
- onSelectionChange: ( items: Item[] ) => void;
53
- selection: string[];
54
- view: ViewGridType;
38
+ columnFields?: string[];
55
39
  }
56
40
 
57
41
  function GridItem< Item extends AnyItem >( {
@@ -147,7 +131,7 @@ function GridItem< Item extends AnyItem >( {
147
131
  return (
148
132
  <FlexItem
149
133
  key={ field.id }
150
- className={ 'dataviews-view-grid__field-value' }
134
+ className="dataviews-view-grid__field-value"
151
135
  >
152
136
  { renderedValue }
153
137
  </FlexItem>
@@ -244,7 +228,7 @@ export default function ViewGrid< Item extends AnyItem >( {
244
228
  <>
245
229
  { hasData && (
246
230
  <Grid
247
- gap={ 6 }
231
+ gap={ 8 }
248
232
  columns={ 2 }
249
233
  alignment="top"
250
234
  className="dataviews-view-grid"
package/src/view-list.tsx CHANGED
@@ -32,27 +32,10 @@ import { moreVertical } from '@wordpress/icons';
32
32
  * Internal dependencies
33
33
  */
34
34
  import { unlock } from './lock-unlock';
35
- import type {
36
- Action,
37
- AnyItem,
38
- NormalizedField,
39
- ViewList as ViewListType,
40
- } from './types';
35
+ import type { Action, AnyItem, NormalizedField, ViewListProps } from './types';
41
36
 
42
37
  import { ActionsDropdownMenuGroup, ActionModal } from './item-actions';
43
38
 
44
- interface ListViewProps< Item extends AnyItem > {
45
- actions: Action< Item >[];
46
- data: Item[];
47
- fields: NormalizedField< Item >[];
48
- getItemId: ( item: Item ) => string;
49
- id: string;
50
- isLoading: boolean;
51
- onSelectionChange: ( selection: Item[] ) => void;
52
- selection: string[];
53
- view: ViewListType;
54
- }
55
-
56
39
  interface ListViewItemProps< Item extends AnyItem > {
57
40
  actions: Action< Item >[];
58
41
  id?: string;
@@ -122,6 +105,11 @@ function ListItem< Item extends AnyItem >( {
122
105
  }, [ actions, item ] );
123
106
 
124
107
  const [ isModalOpen, setIsModalOpen ] = useState( false );
108
+ const primaryActionLabel =
109
+ primaryAction &&
110
+ ( typeof primaryAction.label === 'string'
111
+ ? primaryAction.label
112
+ : primaryAction.label( [ item ] ) );
125
113
 
126
114
  return (
127
115
  <CompositeRow
@@ -137,7 +125,8 @@ function ListItem< Item extends AnyItem >( {
137
125
  >
138
126
  <HStack
139
127
  className="dataviews-view-list__item-wrapper"
140
- alignment="top"
128
+ alignment="center"
129
+ spacing={ 0 }
141
130
  >
142
131
  <div role="gridcell">
143
132
  <CompositeItem
@@ -161,7 +150,7 @@ function ListItem< Item extends AnyItem >( {
161
150
  <div className="dataviews-view-list__media-placeholder"></div>
162
151
  ) }
163
152
  </div>
164
- <VStack spacing={ 1 }>
153
+ <VStack spacing={ 0 }>
165
154
  <span
166
155
  className="dataviews-view-list__primary-field"
167
156
  id={ labelId }
@@ -209,7 +198,7 @@ function ListItem< Item extends AnyItem >( {
209
198
  store={ store }
210
199
  render={
211
200
  <Button
212
- label={ primaryAction.label }
201
+ label={ primaryActionLabel }
213
202
  icon={ primaryAction.icon }
214
203
  isDestructive={
215
204
  primaryAction.isDestructive
@@ -240,7 +229,7 @@ function ListItem< Item extends AnyItem >( {
240
229
  store={ store }
241
230
  render={
242
231
  <Button
243
- label={ primaryAction.label }
232
+ label={ primaryActionLabel }
244
233
  icon={ primaryAction.icon }
245
234
  isDestructive={
246
235
  primaryAction.isDestructive
@@ -266,6 +255,7 @@ function ListItem< Item extends AnyItem >( {
266
255
  size="compact"
267
256
  icon={ moreVertical }
268
257
  label={ __( 'Actions' ) }
258
+ __experimentalIsFocusable
269
259
  disabled={ ! actions.length }
270
260
  onKeyDown={ ( event: {
271
261
  key: string;
@@ -311,7 +301,7 @@ function ListItem< Item extends AnyItem >( {
311
301
  }
312
302
 
313
303
  export default function ViewList< Item extends AnyItem >(
314
- props: ListViewProps< Item >
304
+ props: ViewListProps< Item >
315
305
  ) {
316
306
  const {
317
307
  actions,
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import clsx from 'clsx';
5
+ import type { ReactNode, Ref, PropsWithoutRef, RefAttributes } from 'react';
5
6
 
6
7
  /**
7
8
  * WordPress dependencies
@@ -33,11 +34,24 @@ import SingleSelectionCheckbox from './single-selection-checkbox';
33
34
  import { unlock } from './lock-unlock';
34
35
  import ItemActions from './item-actions';
35
36
  import { sanitizeOperators } from './utils';
36
- import { SORTING_DIRECTIONS } from './constants';
37
+ import {
38
+ SORTING_DIRECTIONS,
39
+ sortArrows,
40
+ sortLabels,
41
+ sortValues,
42
+ } from './constants';
37
43
  import {
38
44
  useSomeItemHasAPossibleBulkAction,
39
45
  useHasAPossibleBulkAction,
40
46
  } from './bulk-actions';
47
+ import type {
48
+ Action,
49
+ AnyItem,
50
+ NormalizedField,
51
+ SortDirection,
52
+ ViewTable as ViewTableType,
53
+ ViewTableProps,
54
+ } from './types';
41
55
 
42
56
  const {
43
57
  DropdownMenuV2: DropdownMenu,
@@ -48,7 +62,35 @@ const {
48
62
  DropdownMenuSeparatorV2: DropdownMenuSeparator,
49
63
  } = unlock( componentsPrivateApis );
50
64
 
51
- function WithDropDownMenuSeparators( { children } ) {
65
+ interface HeaderMenuProps< Item extends AnyItem > {
66
+ field: NormalizedField< Item >;
67
+ view: ViewTableType;
68
+ onChangeView: ( view: ViewTableType ) => void;
69
+ onHide: ( field: NormalizedField< Item > ) => void;
70
+ setOpenedFilter: ( fieldId: string ) => void;
71
+ }
72
+
73
+ interface BulkSelectionCheckboxProps< Item extends AnyItem > {
74
+ selection: string[];
75
+ onSelectionChange: ( items: Item[] ) => void;
76
+ data: Item[];
77
+ actions: Action< Item >[];
78
+ }
79
+
80
+ interface TableRowProps< Item extends AnyItem > {
81
+ hasBulkActions: boolean;
82
+ item: Item;
83
+ actions: Action< Item >[];
84
+ id: string;
85
+ visibleFields: NormalizedField< Item >[];
86
+ primaryField?: NormalizedField< Item >;
87
+ selection: string[];
88
+ getItemId: ( item: Item ) => string;
89
+ onSelectionChange: ( items: Item[] ) => void;
90
+ data: Item[];
91
+ }
92
+
93
+ function WithDropDownMenuSeparators( { children }: { children: ReactNode } ) {
52
94
  return Children.toArray( children )
53
95
  .filter( Boolean )
54
96
  .map( ( child, i ) => (
@@ -59,11 +101,15 @@ function WithDropDownMenuSeparators( { children } ) {
59
101
  ) );
60
102
  }
61
103
 
62
- const sortArrows = { asc: '↑', desc: '↓' };
63
-
64
- const HeaderMenu = forwardRef( function HeaderMenu(
65
- { field, view, onChangeView, onHide, setOpenedFilter },
66
- ref
104
+ const _HeaderMenu = forwardRef( function HeaderMenu< Item extends AnyItem >(
105
+ {
106
+ field,
107
+ view,
108
+ onChangeView,
109
+ onHide,
110
+ setOpenedFilter,
111
+ }: HeaderMenuProps< Item >,
112
+ ref: Ref< HTMLButtonElement >
67
113
  ) {
68
114
  const isHidable = field.enableHiding !== false;
69
115
  const isSortable = field.enableSorting !== false;
@@ -92,9 +138,9 @@ const HeaderMenu = forwardRef( function HeaderMenu(
92
138
  variant="tertiary"
93
139
  >
94
140
  { field.header }
95
- { isSorted && (
141
+ { view.sort && isSorted && (
96
142
  <span aria-hidden="true">
97
- { isSorted && sortArrows[ view.sort.direction ] }
143
+ { sortArrows[ view.sort.direction ] }
98
144
  </span>
99
145
  ) }
100
146
  </Button>
@@ -104,9 +150,10 @@ const HeaderMenu = forwardRef( function HeaderMenu(
104
150
  <WithDropDownMenuSeparators>
105
151
  { isSortable && (
106
152
  <DropdownMenuGroup>
107
- { Object.entries( SORTING_DIRECTIONS ).map(
108
- ( [ direction, info ] ) => {
153
+ { SORTING_DIRECTIONS.map(
154
+ ( direction: SortDirection ) => {
109
155
  const isChecked =
156
+ view.sort &&
110
157
  isSorted &&
111
158
  view.sort.direction === direction;
112
159
 
@@ -134,7 +181,7 @@ const HeaderMenu = forwardRef( function HeaderMenu(
134
181
  } }
135
182
  >
136
183
  <DropdownMenuItemLabel>
137
- { info.label }
184
+ { sortLabels[ direction ] }
138
185
  </DropdownMenuItemLabel>
139
186
  </DropdownMenuRadioItem>
140
187
  );
@@ -191,16 +238,24 @@ const HeaderMenu = forwardRef( function HeaderMenu(
191
238
  );
192
239
  } );
193
240
 
194
- function BulkSelectionCheckbox( {
241
+ // @ts-expect-error Lift the `Item` type argument through the forwardRef.
242
+ const HeaderMenu: < Item extends AnyItem >(
243
+ props: PropsWithoutRef< HeaderMenuProps< Item > > &
244
+ RefAttributes< HTMLButtonElement >
245
+ ) => ReturnType< typeof _HeaderMenu > = _HeaderMenu;
246
+
247
+ function BulkSelectionCheckbox< Item extends AnyItem >( {
195
248
  selection,
196
249
  onSelectionChange,
197
250
  data,
198
251
  actions,
199
- } ) {
252
+ }: BulkSelectionCheckboxProps< Item > ) {
200
253
  const selectableItems = useMemo( () => {
201
254
  return data.filter( ( item ) => {
202
255
  return actions.some(
203
- ( action ) => action.supportsBulk && action.isEligible( item )
256
+ ( action ) =>
257
+ action.supportsBulk &&
258
+ ( ! action.isEligible || action.isEligible( item ) )
204
259
  );
205
260
  } );
206
261
  }, [ data, actions ] );
@@ -210,7 +265,7 @@ function BulkSelectionCheckbox( {
210
265
  className="dataviews-view-table-selection-checkbox"
211
266
  __nextHasNoMarginBottom
212
267
  checked={ areAllSelected }
213
- indeterminate={ ! areAllSelected && selection.length }
268
+ indeterminate={ ! areAllSelected && !! selection.length }
214
269
  onChange={ () => {
215
270
  if ( areAllSelected ) {
216
271
  onSelectionChange( [] );
@@ -225,7 +280,7 @@ function BulkSelectionCheckbox( {
225
280
  );
226
281
  }
227
282
 
228
- function TableRow( {
283
+ function TableRow< Item extends AnyItem >( {
229
284
  hasBulkActions,
230
285
  item,
231
286
  actions,
@@ -236,7 +291,7 @@ function TableRow( {
236
291
  getItemId,
237
292
  onSelectionChange,
238
293
  data,
239
- } ) {
294
+ }: TableRowProps< Item > ) {
240
295
  const hasPossibleBulkAction = useHasAPossibleBulkAction( actions, item );
241
296
  const isSelected = selection.includes( id );
242
297
 
@@ -270,7 +325,7 @@ function TableRow( {
270
325
  onClick={ () => {
271
326
  if (
272
327
  ! isTouchDevice.current &&
273
- document.getSelection().type !== 'Range'
328
+ document.getSelection()?.type !== 'Range'
274
329
  ) {
275
330
  if ( ! isSelected ) {
276
331
  onSelectionChange(
@@ -305,7 +360,6 @@ function TableRow( {
305
360
  >
306
361
  <div className="dataviews-view-table__cell-content-wrapper">
307
362
  <SingleSelectionCheckbox
308
- id={ id }
309
363
  item={ item }
310
364
  selection={ selection }
311
365
  onSelectionChange={ onSelectionChange }
@@ -361,7 +415,7 @@ function TableRow( {
361
415
  );
362
416
  }
363
417
 
364
- function ViewTable( {
418
+ function ViewTable< Item extends AnyItem >( {
365
419
  actions,
366
420
  data,
367
421
  fields,
@@ -372,10 +426,13 @@ function ViewTable( {
372
426
  selection,
373
427
  setOpenedFilter,
374
428
  view,
375
- } ) {
376
- const headerMenuRefs = useRef( new Map() );
377
- const headerMenuToFocusRef = useRef();
378
- const [ nextHeaderMenuToFocus, setNextHeaderMenuToFocus ] = useState();
429
+ }: ViewTableProps< Item > ) {
430
+ const headerMenuRefs = useRef<
431
+ Map< string, { node: HTMLButtonElement; fallback: string } >
432
+ >( new Map() );
433
+ const headerMenuToFocusRef = useRef< HTMLButtonElement >();
434
+ const [ nextHeaderMenuToFocus, setNextHeaderMenuToFocus ] =
435
+ useState< HTMLButtonElement >();
379
436
  const hasBulkActions = useSomeItemHasAPossibleBulkAction( actions, data );
380
437
 
381
438
  useEffect( () => {
@@ -393,13 +450,15 @@ function ViewTable( {
393
450
  // Clearing out the focus directive is necessary to make sure
394
451
  // future renders don't cause unexpected focus jumps.
395
452
  headerMenuToFocusRef.current = nextHeaderMenuToFocus;
396
- setNextHeaderMenuToFocus();
453
+ setNextHeaderMenuToFocus( undefined );
397
454
  return;
398
455
  }
399
456
 
400
- const onHide = ( field ) => {
457
+ const onHide = ( field: NormalizedField< Item > ) => {
401
458
  const hidden = headerMenuRefs.current.get( field.id );
402
- const fallback = headerMenuRefs.current.get( hidden.fallback );
459
+ const fallback = hidden
460
+ ? headerMenuRefs.current.get( hidden.fallback )
461
+ : undefined;
403
462
  setNextHeaderMenuToFocus( fallback?.node );
404
463
  };
405
464
  const visibleFields = fields.filter(
@@ -408,7 +467,6 @@ function ViewTable( {
408
467
  ! [ view.layout.mediaField ].includes( field.id )
409
468
  );
410
469
  const hasData = !! data?.length;
411
- const sortValues = { asc: 'ascending', desc: 'descending' };
412
470
 
413
471
  const primaryField = fields.find(
414
472
  ( field ) => field.id === view.layout.primaryField
@@ -450,8 +508,9 @@ function ViewTable( {
450
508
  } }
451
509
  data-field-id={ field.id }
452
510
  aria-sort={
453
- view.sort?.field === field.id &&
454
- sortValues[ view.sort.direction ]
511
+ view.sort?.field === field.id
512
+ ? sortValues[ view.sort.direction ]
513
+ : undefined
455
514
  }
456
515
  scope="col"
457
516
  >
@@ -504,7 +563,7 @@ function ViewTable( {
504
563
  item={ item }
505
564
  hasBulkActions={ hasBulkActions }
506
565
  actions={ actions }
507
- id={ getItemId( item ) || index }
566
+ id={ getItemId( item ) || index.toString() }
508
567
  visibleFields={ visibleFields }
509
568
  primaryField={ primaryField }
510
569
  selection={ selection }
package/tsconfig.json CHANGED
@@ -4,17 +4,14 @@
4
4
  "compilerOptions": {
5
5
  "rootDir": "src",
6
6
  "declarationDir": "build-types",
7
- "skipLibCheck": true,
8
7
  "checkJs": false
9
8
  },
10
9
  "references": [
11
- { "path": "../a11y" },
12
10
  { "path": "../components" },
13
11
  { "path": "../compose" },
14
12
  { "path": "../element" },
15
13
  { "path": "../i18n" },
16
14
  { "path": "../icons" },
17
- { "path": "../keycodes" },
18
15
  { "path": "../primitives" },
19
16
  { "path": "../private-apis" }
20
17
  ],