@wordpress/dataviews 0.6.0 → 0.8.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 (86) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +48 -15
  3. package/build/add-filter.js +0 -2
  4. package/build/add-filter.js.map +1 -1
  5. package/build/bulk-actions.js +41 -3
  6. package/build/bulk-actions.js.map +1 -1
  7. package/build/constants.js +28 -7
  8. package/build/constants.js.map +1 -1
  9. package/build/dataviews.js +34 -21
  10. package/build/dataviews.js.map +1 -1
  11. package/build/filter-summary.js +33 -12
  12. package/build/filter-summary.js.map +1 -1
  13. package/build/filters.js +10 -2
  14. package/build/filters.js.map +1 -1
  15. package/build/item-actions.js +20 -39
  16. package/build/item-actions.js.map +1 -1
  17. package/build/pagination.js +4 -3
  18. package/build/pagination.js.map +1 -1
  19. package/build/reset-filters.js +2 -1
  20. package/build/reset-filters.js.map +1 -1
  21. package/build/search-widget.js +117 -8
  22. package/build/search-widget.js.map +1 -1
  23. package/build/single-selection-checkbox.js +7 -2
  24. package/build/single-selection-checkbox.js.map +1 -1
  25. package/build/utils.js +24 -2
  26. package/build/utils.js.map +1 -1
  27. package/build/view-actions.js.map +1 -1
  28. package/build/view-grid.js +12 -13
  29. package/build/view-grid.js.map +1 -1
  30. package/build/view-list.js +1 -1
  31. package/build/view-list.js.map +1 -1
  32. package/build/view-table.js +111 -47
  33. package/build/view-table.js.map +1 -1
  34. package/build-module/add-filter.js +0 -2
  35. package/build-module/add-filter.js.map +1 -1
  36. package/build-module/bulk-actions.js +40 -4
  37. package/build-module/bulk-actions.js.map +1 -1
  38. package/build-module/constants.js +27 -6
  39. package/build-module/constants.js.map +1 -1
  40. package/build-module/dataviews.js +35 -22
  41. package/build-module/dataviews.js.map +1 -1
  42. package/build-module/filter-summary.js +34 -13
  43. package/build-module/filter-summary.js.map +1 -1
  44. package/build-module/filters.js +11 -3
  45. package/build-module/filters.js.map +1 -1
  46. package/build-module/item-actions.js +20 -39
  47. package/build-module/item-actions.js.map +1 -1
  48. package/build-module/pagination.js +4 -3
  49. package/build-module/pagination.js.map +1 -1
  50. package/build-module/reset-filters.js +2 -1
  51. package/build-module/reset-filters.js.map +1 -1
  52. package/build-module/search-widget.js +120 -11
  53. package/build-module/search-widget.js.map +1 -1
  54. package/build-module/single-selection-checkbox.js +7 -2
  55. package/build-module/single-selection-checkbox.js.map +1 -1
  56. package/build-module/utils.js +25 -3
  57. package/build-module/utils.js.map +1 -1
  58. package/build-module/view-actions.js.map +1 -1
  59. package/build-module/view-grid.js +13 -14
  60. package/build-module/view-grid.js.map +1 -1
  61. package/build-module/view-list.js +2 -2
  62. package/build-module/view-list.js.map +1 -1
  63. package/build-module/view-table.js +113 -49
  64. package/build-module/view-table.js.map +1 -1
  65. package/build-style/style-rtl.css +76 -46
  66. package/build-style/style.css +76 -46
  67. package/package.json +11 -11
  68. package/src/add-filter.js +0 -2
  69. package/src/bulk-actions.js +54 -4
  70. package/src/constants.js +35 -6
  71. package/src/dataviews.js +66 -49
  72. package/src/filter-summary.js +76 -23
  73. package/src/filters.js +16 -5
  74. package/src/item-actions.js +19 -55
  75. package/src/pagination.js +8 -3
  76. package/src/reset-filters.js +2 -1
  77. package/src/search-widget.js +182 -15
  78. package/src/single-selection-checkbox.js +7 -1
  79. package/src/stories/fixtures.js +12 -1
  80. package/src/stories/index.story.js +43 -4
  81. package/src/style.scss +108 -73
  82. package/src/utils.js +38 -4
  83. package/src/view-actions.js +1 -1
  84. package/src/view-grid.js +13 -12
  85. package/src/view-list.js +2 -1
  86. package/src/view-table.js +162 -81
@@ -7,7 +7,7 @@ import {
7
7
  Modal,
8
8
  } from '@wordpress/components';
9
9
  import { __, sprintf, _n } from '@wordpress/i18n';
10
- import { useMemo, useState, useCallback } from '@wordpress/element';
10
+ import { useMemo, useState, useCallback, useEffect } from '@wordpress/element';
11
11
 
12
12
  /**
13
13
  * Internal dependencies
@@ -21,6 +21,24 @@ const {
21
21
  DropdownMenuSeparatorV2: DropdownMenuSeparator,
22
22
  } = unlock( componentsPrivateApis );
23
23
 
24
+ export function useHasAPossibleBulkAction( actions, item ) {
25
+ return useMemo( () => {
26
+ return actions.some( ( action ) => {
27
+ return action.supportsBulk && action.isEligible( item );
28
+ } );
29
+ }, [ actions, item ] );
30
+ }
31
+
32
+ export function useSomeItemHasAPossibleBulkAction( actions, data ) {
33
+ return useMemo( () => {
34
+ return data.some( ( item ) => {
35
+ return actions.some( ( action ) => {
36
+ return action.supportsBulk && action.isEligible( item );
37
+ } );
38
+ } );
39
+ }, [ actions, data ] );
40
+ }
41
+
24
42
  function ActionWithModal( {
25
43
  action,
26
44
  selectedItems,
@@ -107,15 +125,47 @@ export default function BulkActions( {
107
125
  () => actions.filter( ( action ) => action.supportsBulk ),
108
126
  [ actions ]
109
127
  );
110
- const areAllSelected = selection && selection.length === data.length;
111
128
  const [ isMenuOpen, onMenuOpenChange ] = useState( false );
112
129
  const [ actionWithModal, setActionWithModal ] = useState();
130
+ const selectableItems = useMemo( () => {
131
+ return data.filter( ( item ) => {
132
+ return bulkActions.some( ( action ) => action.isEligible( item ) );
133
+ } );
134
+ }, [ data, bulkActions ] );
135
+
136
+ const numberSelectableItems = selectableItems.length;
137
+ const areAllSelected =
138
+ selection && selection.length === numberSelectableItems;
139
+
113
140
  const selectedItems = useMemo( () => {
114
141
  return data.filter( ( item ) =>
115
142
  selection.includes( getItemId( item ) )
116
143
  );
117
144
  }, [ selection, data, getItemId ] );
118
145
 
146
+ const hasNonSelectableItemSelected = useMemo( () => {
147
+ return selectedItems.some( ( item ) => {
148
+ return ! selectableItems.includes( item );
149
+ } );
150
+ }, [ selectedItems, selectableItems ] );
151
+ useEffect( () => {
152
+ if ( hasNonSelectableItemSelected ) {
153
+ onSelectionChange(
154
+ selectedItems.filter( ( selectedItem ) => {
155
+ return selectableItems.some( ( item ) => {
156
+ return getItemId( selectedItem ) === getItemId( item );
157
+ } );
158
+ } )
159
+ );
160
+ }
161
+ }, [
162
+ hasNonSelectableItemSelected,
163
+ selectedItems,
164
+ selectableItems,
165
+ getItemId,
166
+ onSelectionChange,
167
+ ] );
168
+
119
169
  if ( bulkActions.length === 0 ) {
120
170
  return null;
121
171
  }
@@ -157,9 +207,9 @@ export default function BulkActions( {
157
207
  disabled={ areAllSelected }
158
208
  hideOnClick={ false }
159
209
  onClick={ () => {
160
- onSelectionChange( data );
210
+ onSelectionChange( selectableItems );
161
211
  } }
162
- suffix={ data.length }
212
+ suffix={ numberSelectableItems }
163
213
  >
164
214
  { __( 'Select all' ) }
165
215
  </DropdownMenuItem>
package/src/constants.js CHANGED
@@ -20,17 +20,46 @@ import ViewList from './view-list';
20
20
  export const ENUMERATION_TYPE = 'enumeration';
21
21
 
22
22
  // Filter operators.
23
- export const OPERATOR_IN = 'in';
24
- export const OPERATOR_NOT_IN = 'notIn';
23
+ export const OPERATOR_IS = 'is';
24
+ export const OPERATOR_IS_NOT = 'isNot';
25
+ export const OPERATOR_IS_ANY = 'isAny';
26
+ export const OPERATOR_IS_NONE = 'isNone';
27
+ export const OPERATOR_IS_ALL = 'isAll';
28
+ export const OPERATOR_IS_NOT_ALL = 'isNotAll';
29
+
30
+ export const ALL_OPERATORS = [
31
+ OPERATOR_IS,
32
+ OPERATOR_IS_NOT,
33
+ OPERATOR_IS_ANY,
34
+ OPERATOR_IS_NONE,
35
+ OPERATOR_IS_ALL,
36
+ OPERATOR_IS_NOT_ALL,
37
+ ];
25
38
  export const OPERATORS = {
26
- [ OPERATOR_IN ]: {
27
- key: 'in-filter',
39
+ [ OPERATOR_IS ]: {
40
+ key: 'is-filter',
28
41
  label: __( 'Is' ),
29
42
  },
30
- [ OPERATOR_NOT_IN ]: {
31
- key: 'not-in-filter',
43
+ [ OPERATOR_IS_NOT ]: {
44
+ key: 'is-not-filter',
32
45
  label: __( 'Is not' ),
33
46
  },
47
+ [ OPERATOR_IS_ANY ]: {
48
+ key: 'is-any-filter',
49
+ label: __( 'Is any' ),
50
+ },
51
+ [ OPERATOR_IS_NONE ]: {
52
+ key: 'is-none-filter',
53
+ label: __( 'Is none' ),
54
+ },
55
+ [ OPERATOR_IS_ALL ]: {
56
+ key: 'is-all-filter',
57
+ label: __( 'Is all' ),
58
+ },
59
+ [ OPERATOR_IS_NOT_ALL ]: {
60
+ key: 'is-not-all-filter',
61
+ label: __( 'Is not all' ),
62
+ },
34
63
  };
35
64
 
36
65
  // Sorting
package/src/dataviews.js CHANGED
@@ -1,10 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import {
5
- __experimentalVStack as VStack,
6
- __experimentalHStack as HStack,
7
- } from '@wordpress/components';
4
+ import { __experimentalHStack as HStack } from '@wordpress/components';
8
5
  import { useMemo, useState, useCallback, useEffect } from '@wordpress/element';
9
6
 
10
7
  /**
@@ -20,13 +17,23 @@ import BulkActions from './bulk-actions';
20
17
  const defaultGetItemId = ( item ) => item.id;
21
18
  const defaultOnSelectionChange = () => {};
22
19
 
20
+ function useSomeItemHasAPossibleBulkAction( actions, data ) {
21
+ return useMemo( () => {
22
+ return data.some( ( item ) => {
23
+ return actions.some( ( action ) => {
24
+ return action.supportsBulk && action.isEligible( item );
25
+ } );
26
+ } );
27
+ }, [ actions, data ] );
28
+ }
29
+
23
30
  export default function DataViews( {
24
31
  view,
25
32
  onChangeView,
26
33
  fields,
27
34
  search = true,
28
35
  searchLabel = undefined,
29
- actions,
36
+ actions = [],
30
37
  data,
31
38
  getItemId = defaultGetItemId,
32
39
  isLoading = false,
@@ -70,18 +77,33 @@ export default function DataViews( {
70
77
  ( v ) => v.type === view.type
71
78
  ).component;
72
79
  const _fields = useMemo( () => {
73
- return fields.map( ( field ) => ( {
74
- ...field,
75
- render: field.render || field.getValue,
76
- } ) );
80
+ return fields.map( ( field ) => {
81
+ const getValue =
82
+ field.getValue || ( ( { item } ) => item[ field.id ] );
83
+
84
+ return {
85
+ ...field,
86
+ getValue,
87
+ render: field.render || getValue,
88
+ };
89
+ } );
77
90
  }, [ fields ] );
91
+
92
+ const hasPossibleBulkAction = useSomeItemHasAPossibleBulkAction(
93
+ actions,
94
+ data
95
+ );
78
96
  return (
79
97
  <div className="dataviews-wrapper">
80
- <VStack spacing={ 3 } justify="flex-start">
98
+ <HStack
99
+ alignment="top"
100
+ justify="start"
101
+ className="dataviews-filters__view-actions"
102
+ >
81
103
  <HStack
82
- alignment="flex-start"
83
104
  justify="start"
84
- className="dataviews-filters__view-actions"
105
+ className="dataviews-filters__container"
106
+ wrap
85
107
  >
86
108
  { search && (
87
109
  <Search
@@ -90,27 +112,6 @@ export default function DataViews( {
90
112
  onChangeView={ onChangeView }
91
113
  />
92
114
  ) }
93
- { [ LAYOUT_TABLE, LAYOUT_GRID ].includes( view.type ) && (
94
- <BulkActions
95
- actions={ actions }
96
- data={ data }
97
- onSelectionChange={ onSetSelection }
98
- selection={ selection }
99
- getItemId={ getItemId }
100
- />
101
- ) }
102
- <ViewActions
103
- fields={ _fields }
104
- view={ view }
105
- onChangeView={ onChangeView }
106
- supportedLayouts={ supportedLayouts }
107
- />
108
- </HStack>
109
- <HStack
110
- justify="start"
111
- className="dataviews-filters__container"
112
- wrap
113
- >
114
115
  <Filters
115
116
  fields={ _fields }
116
117
  view={ view }
@@ -119,26 +120,42 @@ export default function DataViews( {
119
120
  setOpenedFilter={ setOpenedFilter }
120
121
  />
121
122
  </HStack>
122
- <ViewComponent
123
+ { [ LAYOUT_TABLE, LAYOUT_GRID ].includes( view.type ) &&
124
+ hasPossibleBulkAction && (
125
+ <BulkActions
126
+ actions={ actions }
127
+ data={ data }
128
+ onSelectionChange={ onSetSelection }
129
+ selection={ selection }
130
+ getItemId={ getItemId }
131
+ />
132
+ ) }
133
+ <ViewActions
123
134
  fields={ _fields }
124
135
  view={ view }
125
136
  onChangeView={ onChangeView }
126
- actions={ actions }
127
- data={ data }
128
- getItemId={ getItemId }
129
- isLoading={ isLoading }
130
- onSelectionChange={ onSetSelection }
131
- onDetailsChange={ onDetailsChange }
132
- selection={ selection }
133
- deferredRendering={ deferredRendering }
134
- setOpenedFilter={ setOpenedFilter }
135
- />
136
- <Pagination
137
- view={ view }
138
- onChangeView={ onChangeView }
139
- paginationInfo={ paginationInfo }
137
+ supportedLayouts={ supportedLayouts }
140
138
  />
141
- </VStack>
139
+ </HStack>
140
+ <ViewComponent
141
+ fields={ _fields }
142
+ view={ view }
143
+ onChangeView={ onChangeView }
144
+ actions={ actions }
145
+ data={ data }
146
+ getItemId={ getItemId }
147
+ isLoading={ isLoading }
148
+ onSelectionChange={ onSetSelection }
149
+ onDetailsChange={ onDetailsChange }
150
+ selection={ selection }
151
+ deferredRendering={ deferredRendering }
152
+ setOpenedFilter={ setOpenedFilter }
153
+ />
154
+ <Pagination
155
+ view={ view }
156
+ onChangeView={ onChangeView }
157
+ paginationInfo={ paginationInfo }
158
+ />
142
159
  </div>
143
160
  );
144
161
  }
@@ -24,43 +24,93 @@ import { ENTER, SPACE } from '@wordpress/keycodes';
24
24
  * Internal dependencies
25
25
  */
26
26
  import SearchWidget from './search-widget';
27
- import { OPERATOR_IN, OPERATOR_NOT_IN, OPERATORS } from './constants';
27
+ import {
28
+ OPERATORS,
29
+ OPERATOR_IS,
30
+ OPERATOR_IS_NOT,
31
+ OPERATOR_IS_ANY,
32
+ OPERATOR_IS_NONE,
33
+ OPERATOR_IS_ALL,
34
+ OPERATOR_IS_NOT_ALL,
35
+ } from './constants';
28
36
 
29
- const FilterText = ( { activeElement, filterInView, filter } ) => {
30
- if ( activeElement === undefined ) {
37
+ const FilterText = ( { activeElements, filterInView, filter } ) => {
38
+ if ( activeElements === undefined || activeElements.length === 0 ) {
31
39
  return filter.name;
32
40
  }
33
41
 
34
42
  const filterTextWrappers = {
35
- Span1: <span className="dataviews-filter-summary__filter-text-name" />,
36
- Span2: <span className="dataviews-filter-summary__filter-text-value" />,
43
+ Name: <span className="dataviews-filter-summary__filter-text-name" />,
44
+ Value: <span className="dataviews-filter-summary__filter-text-value" />,
37
45
  };
38
46
 
39
- if (
40
- activeElement !== undefined &&
41
- filterInView?.operator === OPERATOR_IN
42
- ) {
47
+ if ( filterInView?.operator === OPERATOR_IS_ANY ) {
48
+ return createInterpolateElement(
49
+ sprintf(
50
+ /* translators: 1: Filter name. 3: Filter value. e.g.: "Author is any: Admin, Editor". */
51
+ __( '<Name>%1$s is any: </Name><Value>%2$s</Value>' ),
52
+ filter.name,
53
+ activeElements.map( ( element ) => element.label ).join( ', ' )
54
+ ),
55
+ filterTextWrappers
56
+ );
57
+ }
58
+
59
+ if ( filterInView?.operator === OPERATOR_IS_NONE ) {
60
+ return createInterpolateElement(
61
+ sprintf(
62
+ /* translators: 1: Filter name. 3: Filter value. e.g.: "Author is none: Admin, Editor". */
63
+ __( '<Name>%1$s is none: </Name><Value>%2$s</Value>' ),
64
+ filter.name,
65
+ activeElements.map( ( element ) => element.label ).join( ', ' )
66
+ ),
67
+ filterTextWrappers
68
+ );
69
+ }
70
+
71
+ if ( filterInView?.operator === OPERATOR_IS_ALL ) {
43
72
  return createInterpolateElement(
44
73
  sprintf(
45
- /* translators: 1: Filter name. 2: Filter value. e.g.: "Author is Admin". */
46
- __( '<Span1>%1$s </Span1><Span2>is %2$s</Span2>' ),
74
+ /* translators: 1: Filter name. 3: Filter value. e.g.: "Author is all: Admin, Editor". */
75
+ __( '<Name>%1$s is all: </Name><Value>%2$s</Value>' ),
47
76
  filter.name,
48
- activeElement.label
77
+ activeElements.map( ( element ) => element.label ).join( ', ' )
49
78
  ),
50
79
  filterTextWrappers
51
80
  );
52
81
  }
53
82
 
54
- if (
55
- activeElement !== undefined &&
56
- filterInView?.operator === OPERATOR_NOT_IN
57
- ) {
83
+ if ( filterInView?.operator === OPERATOR_IS_NOT_ALL ) {
58
84
  return createInterpolateElement(
59
85
  sprintf(
60
- /* translators: 1: Filter name. 2: Filter value. e.g.: "Author is not Admin". */
61
- __( '<Span1>%1$s </Span1><Span2>is not %2$s</Span2>' ),
86
+ /* translators: 1: Filter name. 3: Filter value. e.g.: "Author is not all: Admin, Editor". */
87
+ __( '<Name>%1$s is not all: </Name><Value>%2$s</Value>' ),
62
88
  filter.name,
63
- activeElement.label
89
+ activeElements.map( ( element ) => element.label ).join( ', ' )
90
+ ),
91
+ filterTextWrappers
92
+ );
93
+ }
94
+
95
+ if ( filterInView?.operator === OPERATOR_IS ) {
96
+ return createInterpolateElement(
97
+ sprintf(
98
+ /* translators: 1: Filter name. 3: Filter value. e.g.: "Author is: Admin". */
99
+ __( '<Name>%1$s is: </Name><Value>%2$s</Value>' ),
100
+ filter.name,
101
+ activeElements[ 0 ].label
102
+ ),
103
+ filterTextWrappers
104
+ );
105
+ }
106
+
107
+ if ( filterInView?.operator === OPERATOR_IS_NOT ) {
108
+ return createInterpolateElement(
109
+ sprintf(
110
+ /* translators: 1: Filter name. 3: Filter value. e.g.: "Author is not: Admin". */
111
+ __( '<Name>%1$s is not: </Name><Value>%2$s</Value>' ),
112
+ filter.name,
113
+ activeElements[ 0 ].label
64
114
  ),
65
115
  filterTextWrappers
66
116
  );
@@ -140,9 +190,12 @@ export default function FilterSummary( {
140
190
  const toggleRef = useRef();
141
191
  const { filter, view, onChangeView } = commonProps;
142
192
  const filterInView = view.filters.find( ( f ) => f.field === filter.field );
143
- const activeElement = filter.elements.find(
144
- ( element ) => element.value === filterInView?.value
145
- );
193
+ const activeElements = filter.elements.filter( ( element ) => {
194
+ if ( filter.singleSelection ) {
195
+ return element.value === filterInView?.value;
196
+ }
197
+ return filterInView?.value?.includes( element.value );
198
+ } );
146
199
  const isPrimary = filter.isPrimary;
147
200
  const hasValues = filterInView?.value !== undefined;
148
201
  const canResetOrRemove = ! isPrimary || hasValues;
@@ -188,7 +241,7 @@ export default function FilterSummary( {
188
241
  ref={ toggleRef }
189
242
  >
190
243
  <FilterText
191
- activeElement={ activeElement }
244
+ activeElements={ activeElements }
192
245
  filterInView={ filterInView }
193
246
  filter={ filter }
194
247
  />
package/src/filters.js CHANGED
@@ -10,7 +10,13 @@ import FilterSummary from './filter-summary';
10
10
  import AddFilter from './add-filter';
11
11
  import ResetFilters from './reset-filters';
12
12
  import { sanitizeOperators } from './utils';
13
- import { ENUMERATION_TYPE, OPERATOR_IN, OPERATOR_NOT_IN } from './constants';
13
+ import {
14
+ ENUMERATION_TYPE,
15
+ ALL_OPERATORS,
16
+ OPERATOR_IS,
17
+ OPERATOR_IS_NOT,
18
+ } from './constants';
19
+ import { __experimentalHStack as HStack } from '@wordpress/components';
14
20
 
15
21
  const Filters = memo( function Filters( {
16
22
  fields,
@@ -42,15 +48,16 @@ const Filters = memo( function Filters( {
42
48
  field: field.id,
43
49
  name: field.header,
44
50
  elements: field.elements,
51
+ singleSelection: operators.some( ( op ) =>
52
+ [ OPERATOR_IS, OPERATOR_IS_NOT ].includes( op )
53
+ ),
45
54
  operators,
46
55
  isVisible:
47
56
  isPrimary ||
48
57
  view.filters.some(
49
58
  ( f ) =>
50
59
  f.field === field.id &&
51
- [ OPERATOR_IN, OPERATOR_NOT_IN ].includes(
52
- f.operator
53
- )
60
+ ALL_OPERATORS.includes( f.operator )
54
61
  ),
55
62
  isPrimary,
56
63
  } );
@@ -108,7 +115,11 @@ const Filters = memo( function Filters( {
108
115
  );
109
116
  }
110
117
 
111
- return filterComponents;
118
+ return (
119
+ <HStack justify="flex-start" style={ { width: 'fit-content' } } wrap>
120
+ { filterComponents }
121
+ </HStack>
122
+ );
112
123
  } );
113
124
 
114
125
  export default Filters;
@@ -105,37 +105,28 @@ function ActionsDropdownMenuGroup( { actions, item } ) {
105
105
  }
106
106
 
107
107
  export default function ItemActions( { item, actions, isCompact } ) {
108
- const { primaryActions, secondaryActions } = useMemo( () => {
109
- return actions.reduce(
110
- ( accumulator, action ) => {
111
- // If an action is eligible for all items, doesn't need
112
- // to provide the `isEligible` function.
113
- if ( action.isEligible && ! action.isEligible( item ) ) {
114
- return accumulator;
115
- }
116
- if ( action.isPrimary && !! action.icon ) {
117
- accumulator.primaryActions.push( action );
118
- } else {
119
- accumulator.secondaryActions.push( action );
120
- }
121
- return accumulator;
122
- },
123
- { primaryActions: [], secondaryActions: [] }
108
+ const { primaryActions, eligibleActions } = useMemo( () => {
109
+ // If an action is eligible for all items, doesn't need
110
+ // to provide the `isEligible` function.
111
+ const _eligibleActions = actions.filter(
112
+ ( action ) => ! action.isEligible || action.isEligible( item )
113
+ );
114
+ const _primaryActions = _eligibleActions.filter(
115
+ ( action ) => action.isPrimary && !! action.icon
124
116
  );
117
+ return {
118
+ primaryActions: _primaryActions,
119
+ eligibleActions: _eligibleActions,
120
+ };
125
121
  }, [ actions, item ] );
126
122
  if ( isCompact ) {
127
- return (
128
- <CompactItemActions
129
- item={ item }
130
- primaryActions={ primaryActions }
131
- secondaryActions={ secondaryActions }
132
- />
133
- );
123
+ return <CompactItemActions item={ item } actions={ eligibleActions } />;
134
124
  }
135
125
  return (
136
126
  <HStack
137
127
  spacing={ 1 }
138
128
  justify="flex-end"
129
+ className="dataviews-item-actions"
139
130
  style={ {
140
131
  flexShrink: '0',
141
132
  width: 'auto',
@@ -161,27 +152,12 @@ export default function ItemActions( { item, actions, isCompact } ) {
161
152
  />
162
153
  );
163
154
  } ) }
164
- <DropdownMenu
165
- trigger={
166
- <Button
167
- size="compact"
168
- icon={ moreVertical }
169
- label={ __( 'Actions' ) }
170
- disabled={ ! secondaryActions.length }
171
- />
172
- }
173
- placement="bottom-end"
174
- >
175
- <ActionsDropdownMenuGroup
176
- actions={ secondaryActions }
177
- item={ item }
178
- />
179
- </DropdownMenu>
155
+ <CompactItemActions item={ item } actions={ eligibleActions } />
180
156
  </HStack>
181
157
  );
182
158
  }
183
159
 
184
- function CompactItemActions( { item, primaryActions, secondaryActions } ) {
160
+ function CompactItemActions( { item, actions } ) {
185
161
  return (
186
162
  <DropdownMenu
187
163
  trigger={
@@ -189,25 +165,13 @@ function CompactItemActions( { item, primaryActions, secondaryActions } ) {
189
165
  size="compact"
190
166
  icon={ moreVertical }
191
167
  label={ __( 'Actions' ) }
192
- disabled={
193
- ! primaryActions.length && ! secondaryActions.length
194
- }
168
+ disabled={ ! actions.length }
169
+ className="dataviews-all-actions-button"
195
170
  />
196
171
  }
197
172
  placement="bottom-end"
198
173
  >
199
- { !! primaryActions.length && (
200
- <ActionsDropdownMenuGroup
201
- actions={ primaryActions }
202
- item={ item }
203
- />
204
- ) }
205
- { !! secondaryActions.length && (
206
- <ActionsDropdownMenuGroup
207
- actions={ secondaryActions }
208
- item={ item }
209
- />
210
- ) }
174
+ <ActionsDropdownMenuGroup actions={ actions } item={ item } />
211
175
  </DropdownMenu>
212
176
  );
213
177
  }
package/src/pagination.js CHANGED
@@ -27,15 +27,20 @@ const Pagination = memo( function Pagination( {
27
27
  justify="end"
28
28
  className="dataviews-pagination"
29
29
  >
30
- <HStack justify="flex-start" expanded={ false } spacing={ 2 }>
30
+ <HStack
31
+ justify="flex-start"
32
+ expanded={ false }
33
+ spacing={ 2 }
34
+ className="dataviews-pagination__page-selection"
35
+ >
31
36
  { createInterpolateElement(
32
37
  sprintf(
33
38
  // translators: %s: Total number of pages.
34
- _x( 'Page <CurrenPageControl /> of %s', 'paging' ),
39
+ _x( 'Page <CurrentPageControl /> of %s', 'paging' ),
35
40
  totalPages
36
41
  ),
37
42
  {
38
- CurrenPageControl: (
43
+ CurrentPageControl: (
39
44
  <SelectControl
40
45
  aria-label={ __( 'Current page' ) }
41
46
  value={ view.page }
@@ -21,6 +21,7 @@ export default function ResetFilter( { filters, view, onChangeView } ) {
21
21
  __experimentalIsFocusable
22
22
  size="compact"
23
23
  variant="tertiary"
24
+ className="dataviews-filters__reset-button"
24
25
  onClick={ () => {
25
26
  onChangeView( {
26
27
  ...view,
@@ -30,7 +31,7 @@ export default function ResetFilter( { filters, view, onChangeView } ) {
30
31
  } );
31
32
  } }
32
33
  >
33
- { __( 'Reset filters' ) }
34
+ { __( 'Reset' ) }
34
35
  </Button>
35
36
  );
36
37
  }