@wordpress/dataviews 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +78 -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 +153 -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 +80 -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 +154 -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 +109 -20
  82. package/build-style/style.css +109 -20
  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 +20 -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 +97 -23
  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 +12 -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 +1 @@
1
- {"version":3,"file":"view-list.d.ts","sourceRoot":"","sources":["../src/view-list.tsx"],"names":[],"mappings":";AAkCA,OAAO,KAAK,EACX,MAAM,EACN,OAAO,EACP,eAAe,EACf,QAAQ,IAAI,YAAY,EACxB,MAAM,SAAS,CAAC;AAIjB,UAAU,aAAa,CAAE,IAAI,SAAS,OAAO;IAC5C,OAAO,EAAE,MAAM,CAAE,IAAI,CAAE,EAAE,CAAC;IAC1B,IAAI,EAAE,IAAI,EAAE,CAAC;IACb,MAAM,EAAE,eAAe,CAAE,IAAI,CAAE,EAAE,CAAC;IAClC,SAAS,EAAE,CAAE,IAAI,EAAE,IAAI,KAAM,MAAM,CAAC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,iBAAiB,EAAE,CAAE,SAAS,EAAE,IAAI,EAAE,KAAM,IAAI,CAAC;IACjD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,EAAE,YAAY,CAAC;CACnB;AAmQD,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAE,IAAI,SAAS,OAAO,EACrD,KAAK,EAAE,aAAa,CAAE,IAAI,CAAE,+BA2G5B"}
1
+ {"version":3,"file":"view-list.d.ts","sourceRoot":"","sources":["../src/view-list.tsx"],"names":[],"mappings":";AAkCA,OAAO,KAAK,EAAU,OAAO,EAAmB,aAAa,EAAE,MAAM,SAAS,CAAC;AA2Q/E,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAE,IAAI,SAAS,OAAO,EACrD,KAAK,EAAE,aAAa,CAAE,IAAI,CAAE,+BA2G5B"}
@@ -1,14 +1,5 @@
1
+ /// <reference types="react" />
2
+ import type { AnyItem, ViewTableProps } from './types';
3
+ declare function ViewTable<Item extends AnyItem>({ actions, data, fields, getItemId, isLoading, onChangeView, onSelectionChange, selection, setOpenedFilter, view, }: ViewTableProps<Item>): import("react").JSX.Element | undefined;
1
4
  export default ViewTable;
2
- declare function ViewTable({ actions, data, fields, getItemId, isLoading, onChangeView, onSelectionChange, selection, setOpenedFilter, view, }: {
3
- actions: any;
4
- data: any;
5
- fields: any;
6
- getItemId: any;
7
- isLoading?: boolean | undefined;
8
- onChangeView: any;
9
- onSelectionChange: any;
10
- selection: any;
11
- setOpenedFilter: any;
12
- view: any;
13
- }): import("react").JSX.Element | undefined;
14
5
  //# sourceMappingURL=view-table.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"view-table.d.ts","sourceRoot":"","sources":["../src/view-table.js"],"names":[],"mappings":";AA2WA;;;;;;;;;;;4CAuKC"}
1
+ {"version":3,"file":"view-table.d.ts","sourceRoot":"","sources":["../src/view-table.tsx"],"names":[],"mappings":";AA8CA,OAAO,KAAK,EAEX,OAAO,EAIP,cAAc,EACd,MAAM,SAAS,CAAC;AA4WjB,iBAAS,SAAS,CAAE,IAAI,SAAS,OAAO,EAAI,EAC3C,OAAO,EACP,IAAI,EACJ,MAAM,EACN,SAAS,EACT,SAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,IAAI,GACJ,EAAE,cAAc,CAAE,IAAI,CAAE,2CAiKxB;AAED,eAAe,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/dataviews",
3
- "version": "1.2.0",
3
+ "version": "2.0.0",
4
4
  "description": "DataViews is a component that provides an API to render datasets using different types of layouts (table, grid, list, etc.).",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -19,7 +19,8 @@
19
19
  "url": "https://github.com/WordPress/gutenberg/issues"
20
20
  },
21
21
  "engines": {
22
- "node": ">=12"
22
+ "node": ">=18.12.0",
23
+ "npm": ">=8.19.2"
23
24
  },
24
25
  "main": "build/index.js",
25
26
  "module": "build-module/index.js",
@@ -29,15 +30,13 @@
29
30
  "dependencies": {
30
31
  "@ariakit/react": "^0.3.12",
31
32
  "@babel/runtime": "^7.16.0",
32
- "@wordpress/a11y": "^3.58.0",
33
- "@wordpress/components": "^27.6.0",
34
- "@wordpress/compose": "^6.35.0",
35
- "@wordpress/element": "^5.35.0",
36
- "@wordpress/i18n": "^4.58.0",
37
- "@wordpress/icons": "^9.49.0",
38
- "@wordpress/keycodes": "^3.58.0",
39
- "@wordpress/primitives": "^3.56.0",
40
- "@wordpress/private-apis": "^0.40.0",
33
+ "@wordpress/components": "^28.0.0",
34
+ "@wordpress/compose": "^7.0.0",
35
+ "@wordpress/element": "^6.0.0",
36
+ "@wordpress/i18n": "^5.0.0",
37
+ "@wordpress/icons": "^10.0.0",
38
+ "@wordpress/primitives": "^4.0.0",
39
+ "@wordpress/private-apis": "^1.0.0",
41
40
  "clsx": "^2.1.1",
42
41
  "remove-accents": "^0.5.0"
43
42
  },
@@ -47,5 +46,5 @@
47
46
  "publishConfig": {
48
47
  "access": "public"
49
48
  },
50
- "gitHead": "42f38f287506a6b3ed8ccba839b18ad066821044"
49
+ "gitHead": "2f30cddff15723ac7017fd009fc5913b7b419400"
51
50
  }
@@ -1,3 +1,8 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import type { Ref } from 'react';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
@@ -12,6 +17,7 @@ import { forwardRef } from '@wordpress/element';
12
17
  * Internal dependencies
13
18
  */
14
19
  import { unlock } from './lock-unlock';
20
+ import type { NormalizedFilter, View } from './types';
15
21
 
16
22
  const {
17
23
  DropdownMenuV2: DropdownMenu,
@@ -19,7 +25,17 @@ const {
19
25
  DropdownMenuItemLabelV2: DropdownMenuItemLabel,
20
26
  } = unlock( componentsPrivateApis );
21
27
 
22
- function AddFilter( { filters, view, onChangeView, setOpenedFilter }, ref ) {
28
+ interface AddFilterProps {
29
+ filters: NormalizedFilter[];
30
+ view: View;
31
+ onChangeView: ( view: View ) => void;
32
+ setOpenedFilter: ( filter: string | null ) => void;
33
+ }
34
+
35
+ function AddFilter(
36
+ { filters, view, onChangeView, setOpenedFilter }: AddFilterProps,
37
+ ref: Ref< HTMLButtonElement >
38
+ ) {
23
39
  if ( ! filters.length || filters.every( ( { isPrimary } ) => isPrimary ) ) {
24
40
  return null;
25
41
  }
@@ -17,6 +17,30 @@ import { useReducedMotion } from '@wordpress/compose';
17
17
  * Internal dependencies
18
18
  */
19
19
  import { ActionWithModal } from './item-actions';
20
+ import type { Action, AnyItem } from './types';
21
+ import type { ActionTriggerProps } from './item-actions';
22
+
23
+ interface ActionButtonProps< Item extends AnyItem > {
24
+ action: Action< Item >;
25
+ selectedItems: Item[];
26
+ actionInProgress: string | null;
27
+ setActionInProgress: ( actionId: string | null ) => void;
28
+ }
29
+
30
+ interface ToolbarContentProps< Item extends AnyItem > {
31
+ selection: string[];
32
+ actionsToShow: Action< Item >[];
33
+ selectedItems: Item[];
34
+ onSelectionChange: ( selection: Item[] ) => void;
35
+ }
36
+
37
+ interface BulkActionsToolbarProps< Item extends AnyItem > {
38
+ data: Item[];
39
+ selection: string[];
40
+ actions: Action< Item >[];
41
+ onSelectionChange: ( selection: Item[] ) => void;
42
+ getItemId: ( item: Item ) => string;
43
+ }
20
44
 
21
45
  const SNACKBAR_VARIANTS = {
22
46
  init: {
@@ -37,11 +61,18 @@ const SNACKBAR_VARIANTS = {
37
61
  },
38
62
  };
39
63
 
40
- function ActionTrigger( { action, onClick, isBusy } ) {
64
+ function ActionTrigger< Item extends AnyItem >( {
65
+ action,
66
+ onClick,
67
+ isBusy,
68
+ items,
69
+ }: ActionTriggerProps< Item > ) {
70
+ const label =
71
+ typeof action.label === 'string' ? action.label : action.label( items );
41
72
  return (
42
73
  <ToolbarButton
43
74
  disabled={ isBusy }
44
- label={ action.label }
75
+ label={ label }
45
76
  icon={ action.icon }
46
77
  isDestructive={ action.isDestructive }
47
78
  size="compact"
@@ -53,32 +84,26 @@ function ActionTrigger( { action, onClick, isBusy } ) {
53
84
  );
54
85
  }
55
86
 
56
- const EMPTY_ARRAY = [];
87
+ const EMPTY_ARRAY: [] = [];
57
88
 
58
- function ActionButton( {
89
+ function ActionButton< Item extends AnyItem >( {
59
90
  action,
60
91
  selectedItems,
61
92
  actionInProgress,
62
93
  setActionInProgress,
63
- } ) {
94
+ }: ActionButtonProps< Item > ) {
64
95
  const selectedEligibleItems = useMemo( () => {
65
96
  return selectedItems.filter( ( item ) => {
66
- return action.isEligible( item );
97
+ return ! action.isEligible || action.isEligible( item );
67
98
  } );
68
99
  }, [ action, selectedItems ] );
69
- if ( !! action.RenderModal ) {
100
+ if ( 'RenderModal' in action ) {
70
101
  return (
71
102
  <ActionWithModal
72
103
  key={ action.id }
73
104
  action={ action }
74
105
  items={ selectedEligibleItems }
75
106
  ActionTrigger={ ActionTrigger }
76
- onActionStart={ () => {
77
- setActionInProgress( action.id );
78
- } }
79
- onActionPerformed={ () => {
80
- setActionInProgress( null );
81
- } }
82
107
  />
83
108
  );
84
109
  }
@@ -86,25 +111,23 @@ function ActionButton( {
86
111
  <ActionTrigger
87
112
  key={ action.id }
88
113
  action={ action }
89
- items={ selectedItems }
90
114
  onClick={ () => {
91
115
  setActionInProgress( action.id );
92
- action.callback( selectedItems, () => {
93
- setActionInProgress( action.id );
94
- } );
116
+ action.callback( selectedItems );
95
117
  } }
118
+ items={ selectedEligibleItems }
96
119
  isBusy={ actionInProgress === action.id }
97
120
  />
98
121
  );
99
122
  }
100
123
 
101
- function renderToolbarContent(
102
- selection,
103
- actionsToShow,
104
- selectedItems,
105
- actionInProgress,
106
- setActionInProgress,
107
- setSelection
124
+ function renderToolbarContent< Item extends AnyItem >(
125
+ selection: string[],
126
+ actionsToShow: Action< Item >[],
127
+ selectedItems: Item[],
128
+ actionInProgress: string | null,
129
+ setActionInProgress: ( actionId: string | null ) => void,
130
+ onSelectionChange: ( selection: Item[] ) => void
108
131
  ) {
109
132
  return (
110
133
  <>
@@ -144,7 +167,7 @@ function renderToolbarContent(
144
167
  label={ __( 'Cancel' ) }
145
168
  disabled={ !! actionInProgress }
146
169
  onClick={ () => {
147
- setSelection( EMPTY_ARRAY );
170
+ onSelectionChange( EMPTY_ARRAY );
148
171
  } }
149
172
  />
150
173
  </ToolbarGroup>
@@ -152,14 +175,16 @@ function renderToolbarContent(
152
175
  );
153
176
  }
154
177
 
155
- function ToolbarContent( {
178
+ function ToolbarContent< Item extends AnyItem >( {
156
179
  selection,
157
180
  actionsToShow,
158
181
  selectedItems,
159
- setSelection,
160
- } ) {
161
- const [ actionInProgress, setActionInProgress ] = useState( null );
162
- const buttons = useRef( null );
182
+ onSelectionChange,
183
+ }: ToolbarContentProps< Item > ) {
184
+ const [ actionInProgress, setActionInProgress ] = useState< string | null >(
185
+ null
186
+ );
187
+ const buttons = useRef< JSX.Element | null >( null );
163
188
  if ( ! actionInProgress ) {
164
189
  if ( buttons.current ) {
165
190
  buttons.current = null;
@@ -170,7 +195,7 @@ function ToolbarContent( {
170
195
  selectedItems,
171
196
  actionInProgress,
172
197
  setActionInProgress,
173
- setSelection
198
+ onSelectionChange
174
199
  );
175
200
  } else if ( ! buttons.current ) {
176
201
  buttons.current = renderToolbarContent(
@@ -179,19 +204,19 @@ function ToolbarContent( {
179
204
  selectedItems,
180
205
  actionInProgress,
181
206
  setActionInProgress,
182
- setSelection
207
+ onSelectionChange
183
208
  );
184
209
  }
185
210
  return buttons.current;
186
211
  }
187
212
 
188
- export default function BulkActionsToolbar( {
213
+ export default function BulkActionsToolbar< Item extends AnyItem >( {
189
214
  data,
190
215
  selection,
191
216
  actions = EMPTY_ARRAY,
192
- setSelection,
217
+ onSelectionChange,
193
218
  getItemId,
194
- } ) {
219
+ }: BulkActionsToolbarProps< Item > ) {
195
220
  const isReducedMotion = useReducedMotion();
196
221
  const selectedItems = useMemo( () => {
197
222
  return data.filter( ( item ) =>
@@ -205,7 +230,10 @@ export default function BulkActionsToolbar( {
205
230
  return (
206
231
  action.supportsBulk &&
207
232
  action.icon &&
208
- selectedItems.some( ( item ) => action.isEligible( item ) )
233
+ selectedItems.some(
234
+ ( item ) =>
235
+ ! action.isEligible || action.isEligible( item )
236
+ )
209
237
  );
210
238
  } ),
211
239
  [ actions, selectedItems ]
@@ -222,9 +250,9 @@ export default function BulkActionsToolbar( {
222
250
  <AnimatePresence>
223
251
  <motion.div
224
252
  layout={ ! isReducedMotion } // See https://www.framer.com/docs/animation/#layout-animations
225
- initial={ 'init' }
226
- animate={ 'open' }
227
- exit={ 'exit' }
253
+ initial="init"
254
+ animate="open"
255
+ exit="exit"
228
256
  variants={ isReducedMotion ? undefined : SNACKBAR_VARIANTS }
229
257
  className="dataviews-bulk-actions"
230
258
  >
@@ -234,7 +262,7 @@ export default function BulkActionsToolbar( {
234
262
  selection={ selection }
235
263
  actionsToShow={ actionsToShow }
236
264
  selectedItems={ selectedItems }
237
- setSelection={ setSelection }
265
+ onSelectionChange={ onSelectionChange }
238
266
  />
239
267
  </div>
240
268
  </Toolbar>
@@ -94,9 +94,13 @@ function ActionWithModal< Item extends AnyItem >( {
94
94
  const onCloseModal = useCallback( () => {
95
95
  setActionWithModal( undefined );
96
96
  }, [ setActionWithModal ] );
97
+ const label =
98
+ typeof action.label === 'string'
99
+ ? action.label
100
+ : action.label( selectedItems );
97
101
  return (
98
102
  <Modal
99
- title={ ! hideModalHeader ? action.label : undefined }
103
+ title={ ! hideModalHeader ? label : undefined }
100
104
  __experimentalHideHeader={ !! hideModalHeader }
101
105
  onRequestClose={ onCloseModal }
102
106
  overlayClassName="dataviews-action-modal"
package/src/constants.ts CHANGED
@@ -3,6 +3,11 @@
3
3
  */
4
4
  import { __ } from '@wordpress/i18n';
5
5
 
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import type { Operator } from './types';
10
+
6
11
  // Filter operators.
7
12
  export const OPERATOR_IS = 'is';
8
13
  export const OPERATOR_IS_NOT = 'isNot';
@@ -19,7 +24,7 @@ export const ALL_OPERATORS = [
19
24
  OPERATOR_IS_ALL,
20
25
  OPERATOR_IS_NOT_ALL,
21
26
  ];
22
- export const OPERATORS = {
27
+ export const OPERATORS: Record< Operator, { key: string; label: string } > = {
23
28
  [ OPERATOR_IS ]: {
24
29
  key: 'is-filter',
25
30
  label: __( 'Is' ),
@@ -46,10 +51,12 @@ export const OPERATORS = {
46
51
  },
47
52
  };
48
53
 
49
- // Sorting
50
- export const SORTING_DIRECTIONS = {
51
- asc: { label: __( 'Sort ascending' ) },
52
- desc: { label: __( 'Sort descending' ) },
54
+ export const SORTING_DIRECTIONS = [ 'asc', 'desc' ] as const;
55
+ export const sortArrows = { asc: '↑', desc: '↓' };
56
+ export const sortValues = { asc: 'ascending', desc: 'descending' } as const;
57
+ export const sortLabels = {
58
+ asc: __( 'Sort ascending' ),
59
+ desc: __( 'Sort descending' ),
53
60
  };
54
61
 
55
62
  // View layouts.
@@ -1,3 +1,8 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import type { ComponentType } from 'react';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
@@ -16,21 +21,46 @@ import { VIEW_LAYOUTS } from './layouts';
16
21
  import BulkActions from './bulk-actions';
17
22
  import { normalizeFields } from './normalize-fields';
18
23
  import BulkActionsToolbar from './bulk-actions-toolbar';
24
+ import type { Action, AnyItem, Field, View, ViewBaseProps } from './types';
25
+
26
+ interface DataViewsProps< Item extends AnyItem > {
27
+ view: View;
28
+ onChangeView: ( view: View ) => void;
29
+ fields: Field< Item >[];
30
+ search?: boolean;
31
+ searchLabel?: string;
32
+ actions?: Action< Item >[];
33
+ data: Item[];
34
+ getItemId?: ( item: Item ) => string;
35
+ isLoading?: boolean;
36
+ paginationInfo: {
37
+ totalItems: number;
38
+ totalPages: number;
39
+ };
40
+ supportedLayouts: string[];
41
+ onSelectionChange?: ( items: Item[] ) => void;
42
+ }
19
43
 
20
- const defaultGetItemId = ( item ) => item.id;
44
+ const defaultGetItemId = ( item: AnyItem ) => item.id;
21
45
  const defaultOnSelectionChange = () => {};
22
46
 
23
- function useSomeItemHasAPossibleBulkAction( actions, data ) {
47
+ function useSomeItemHasAPossibleBulkAction< Item extends AnyItem >(
48
+ actions: Action< Item >[],
49
+ data: Item[]
50
+ ) {
24
51
  return useMemo( () => {
25
52
  return data.some( ( item ) => {
26
53
  return actions.some( ( action ) => {
27
- return action.supportsBulk && action.isEligible( item );
54
+ return (
55
+ action.supportsBulk &&
56
+ ( ! action.isEligible || action.isEligible( item ) )
57
+ );
28
58
  } );
29
59
  } );
30
60
  }, [ actions, data ] );
31
61
  }
32
62
 
33
- export default function DataViews( {
63
+ export default function DataViews< Item extends AnyItem >( {
34
64
  view,
35
65
  onChangeView,
36
66
  fields,
@@ -43,9 +73,9 @@ export default function DataViews( {
43
73
  paginationInfo,
44
74
  supportedLayouts,
45
75
  onSelectionChange = defaultOnSelectionChange,
46
- } ) {
47
- const [ selection, setSelection ] = useState( [] );
48
- const [ openedFilter, setOpenedFilter ] = useState( null );
76
+ }: DataViewsProps< Item > ) {
77
+ const [ selection, setSelection ] = useState< string[] >( [] );
78
+ const [ openedFilter, setOpenedFilter ] = useState< string | null >( null );
49
79
 
50
80
  useEffect( () => {
51
81
  if (
@@ -67,16 +97,15 @@ export default function DataViews( {
67
97
  }, [ selection, data, getItemId, onSelectionChange ] );
68
98
 
69
99
  const onSetSelection = useCallback(
70
- ( items ) => {
100
+ ( items: Item[] ) => {
71
101
  setSelection( items.map( ( item ) => getItemId( item ) ) );
72
102
  onSelectionChange( items );
73
103
  },
74
104
  [ setSelection, getItemId, onSelectionChange ]
75
105
  );
76
106
 
77
- const ViewComponent = VIEW_LAYOUTS.find(
78
- ( v ) => v.type === view.type
79
- ).component;
107
+ const ViewComponent = VIEW_LAYOUTS.find( ( v ) => v.type === view.type )
108
+ ?.component as ComponentType< ViewBaseProps< Item > >;
80
109
  const _fields = useMemo( () => normalizeFields( fields ), [ fields ] );
81
110
 
82
111
  const hasPossibleBulkAction = useSomeItemHasAPossibleBulkAction(
@@ -150,7 +179,7 @@ export default function DataViews( {
150
179
  data={ data }
151
180
  actions={ actions }
152
181
  selection={ selection }
153
- setSelection={ setSelection }
182
+ onSelectionChange={ onSetSelection }
154
183
  getItemId={ getItemId }
155
184
  />
156
185
  ) }
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import clsx from 'clsx';
5
+ import type { RefObject } from 'react';
5
6
 
6
7
  /**
7
8
  * WordPress dependencies
@@ -35,8 +36,30 @@ import {
35
36
  OPERATOR_IS_ALL,
36
37
  OPERATOR_IS_NOT_ALL,
37
38
  } from './constants';
39
+ import type { Filter, NormalizedFilter, Operator, Option, View } from './types';
38
40
 
39
- const FilterText = ( { activeElements, filterInView, filter } ) => {
41
+ interface FilterTextProps {
42
+ activeElements: Option[];
43
+ filterInView?: Filter;
44
+ filter: NormalizedFilter;
45
+ }
46
+
47
+ interface OperatorSelectorProps {
48
+ filter: NormalizedFilter;
49
+ view: View;
50
+ onChangeView: ( view: View ) => void;
51
+ }
52
+
53
+ interface FilterSummaryProps extends OperatorSelectorProps {
54
+ addFilterRef: RefObject< HTMLButtonElement >;
55
+ openedFilter: string | null;
56
+ }
57
+
58
+ const FilterText = ( {
59
+ activeElements,
60
+ filterInView,
61
+ filter,
62
+ }: FilterTextProps ) => {
40
63
  if ( activeElements === undefined || activeElements.length === 0 ) {
41
64
  return filter.name;
42
65
  }
@@ -125,7 +148,11 @@ const FilterText = ( { activeElements, filterInView, filter } ) => {
125
148
  );
126
149
  };
127
150
 
128
- function OperatorSelector( { filter, view, onChangeView } ) {
151
+ function OperatorSelector( {
152
+ filter,
153
+ view,
154
+ onChangeView,
155
+ }: OperatorSelectorProps ) {
129
156
  const operatorOptions = filter.operators?.map( ( operator ) => ( {
130
157
  value: operator,
131
158
  label: OPERATORS[ operator ]?.label,
@@ -150,13 +177,14 @@ function OperatorSelector( { filter, view, onChangeView } ) {
150
177
  value={ value }
151
178
  options={ operatorOptions }
152
179
  onChange={ ( newValue ) => {
180
+ const operator = newValue as Operator;
153
181
  const newFilters = currentFilter
154
182
  ? [
155
183
  ...view.filters.map( ( _filter ) => {
156
184
  if ( _filter.field === filter.field ) {
157
185
  return {
158
186
  ..._filter,
159
- operator: newValue,
187
+ operator,
160
188
  };
161
189
  }
162
190
  return _filter;
@@ -166,7 +194,8 @@ function OperatorSelector( { filter, view, onChangeView } ) {
166
194
  ...view.filters,
167
195
  {
168
196
  field: filter.field,
169
- operator: newValue,
197
+ operator,
198
+ value: undefined,
170
199
  },
171
200
  ];
172
201
  onChangeView( {
@@ -188,8 +217,8 @@ export default function FilterSummary( {
188
217
  addFilterRef,
189
218
  openedFilter,
190
219
  ...commonProps
191
- } ) {
192
- const toggleRef = useRef();
220
+ }: FilterSummaryProps ) {
221
+ const toggleRef = useRef< HTMLDivElement >( null );
193
222
  const { filter, view, onChangeView } = commonProps;
194
223
  const filterInView = view.filters.find( ( f ) => f.field === filter.field );
195
224
  const activeElements = filter.elements.filter( ( element ) => {
@@ -2,6 +2,7 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { memo, useRef } from '@wordpress/element';
5
+ import { __experimentalHStack as HStack } from '@wordpress/components';
5
6
 
6
7
  /**
7
8
  * Internal dependencies
@@ -11,17 +12,25 @@ import AddFilter from './add-filter';
11
12
  import ResetFilters from './reset-filters';
12
13
  import { sanitizeOperators } from './utils';
13
14
  import { ALL_OPERATORS, OPERATOR_IS, OPERATOR_IS_NOT } from './constants';
14
- import { __experimentalHStack as HStack } from '@wordpress/components';
15
+ import type { AnyItem, NormalizedField, NormalizedFilter, View } from './types';
15
16
 
16
- const Filters = memo( function Filters( {
17
+ interface FiltersProps< Item extends AnyItem > {
18
+ fields: NormalizedField< Item >[];
19
+ view: View;
20
+ onChangeView: ( view: View ) => void;
21
+ openedFilter: string | null;
22
+ setOpenedFilter: ( openedFilter: string | null ) => void;
23
+ }
24
+
25
+ function _Filters< Item extends AnyItem >( {
17
26
  fields,
18
27
  view,
19
28
  onChangeView,
20
29
  openedFilter,
21
30
  setOpenedFilter,
22
- } ) {
23
- const addFilterRef = useRef();
24
- const filters = [];
31
+ }: FiltersProps< Item > ) {
32
+ const addFilterRef = useRef< HTMLButtonElement >( null );
33
+ const filters: NormalizedFilter[] = [];
25
34
  fields.forEach( ( field ) => {
26
35
  if ( ! field.elements?.length ) {
27
36
  return;
@@ -108,6 +117,9 @@ const Filters = memo( function Filters( {
108
117
  { filterComponents }
109
118
  </HStack>
110
119
  );
111
- } );
120
+ }
121
+
122
+ // A type assertion is used here to keep the type argument.
123
+ const Filters = memo( _Filters ) as typeof _Filters;
112
124
 
113
125
  export default Filters;