@wordpress/dataviews 0.7.0 → 0.9.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 (79) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +49 -18
  3. package/build/constants.js +28 -10
  4. package/build/constants.js.map +1 -1
  5. package/build/dataviews.js +3 -7
  6. package/build/dataviews.js.map +1 -1
  7. package/build/filter-and-sort-data-view.js +147 -0
  8. package/build/filter-and-sort-data-view.js.map +1 -0
  9. package/build/filter-summary.js +33 -12
  10. package/build/filter-summary.js.map +1 -1
  11. package/build/filters.js +11 -16
  12. package/build/filters.js.map +1 -1
  13. package/build/index.js +3 -9
  14. package/build/index.js.map +1 -1
  15. package/build/item-actions.js +20 -39
  16. package/build/item-actions.js.map +1 -1
  17. package/build/normalize-fields.js +25 -0
  18. package/build/normalize-fields.js.map +1 -0
  19. package/build/pagination.js +2 -2
  20. package/build/pagination.js.map +1 -1
  21. package/build/search-widget.js +34 -10
  22. package/build/search-widget.js.map +1 -1
  23. package/build/utils.js +25 -67
  24. package/build/utils.js.map +1 -1
  25. package/build/view-grid.js +25 -12
  26. package/build/view-grid.js.map +1 -1
  27. package/build/view-list.js +122 -58
  28. package/build/view-list.js.map +1 -1
  29. package/build/view-table.js +53 -8
  30. package/build/view-table.js.map +1 -1
  31. package/build-module/constants.js +27 -9
  32. package/build-module/constants.js.map +1 -1
  33. package/build-module/dataviews.js +3 -7
  34. package/build-module/dataviews.js.map +1 -1
  35. package/build-module/filter-and-sort-data-view.js +139 -0
  36. package/build-module/filter-and-sort-data-view.js.map +1 -0
  37. package/build-module/filter-summary.js +34 -13
  38. package/build-module/filter-summary.js.map +1 -1
  39. package/build-module/filters.js +12 -17
  40. package/build-module/filters.js.map +1 -1
  41. package/build-module/index.js +1 -1
  42. package/build-module/index.js.map +1 -1
  43. package/build-module/item-actions.js +20 -39
  44. package/build-module/item-actions.js.map +1 -1
  45. package/build-module/normalize-fields.js +19 -0
  46. package/build-module/normalize-fields.js.map +1 -0
  47. package/build-module/pagination.js +2 -2
  48. package/build-module/pagination.js.map +1 -1
  49. package/build-module/search-widget.js +35 -11
  50. package/build-module/search-widget.js.map +1 -1
  51. package/build-module/utils.js +25 -66
  52. package/build-module/utils.js.map +1 -1
  53. package/build-module/view-grid.js +26 -13
  54. package/build-module/view-grid.js.map +1 -1
  55. package/build-module/view-list.js +124 -60
  56. package/build-module/view-list.js.map +1 -1
  57. package/build-module/view-table.js +55 -10
  58. package/build-module/view-table.js.map +1 -1
  59. package/build-style/style-rtl.css +41 -11
  60. package/build-style/style.css +41 -11
  61. package/package.json +11 -11
  62. package/src/constants.js +35 -9
  63. package/src/dataviews.js +3 -7
  64. package/src/filter-and-sort-data-view.js +154 -0
  65. package/src/filter-summary.js +76 -23
  66. package/src/filters.js +20 -26
  67. package/src/index.js +1 -1
  68. package/src/item-actions.js +19 -55
  69. package/src/normalize-fields.js +17 -0
  70. package/src/pagination.js +2 -2
  71. package/src/search-widget.js +63 -21
  72. package/src/stories/fixtures.js +87 -2
  73. package/src/stories/index.story.js +5 -74
  74. package/src/style.scss +53 -14
  75. package/src/test/filter-and-sort-data-view.js +276 -0
  76. package/src/utils.js +38 -56
  77. package/src/view-grid.js +36 -11
  78. package/src/view-list.js +159 -69
  79. package/src/view-table.js +71 -9
package/src/dataviews.js CHANGED
@@ -13,6 +13,7 @@ import Filters from './filters';
13
13
  import Search from './search';
14
14
  import { VIEW_LAYOUTS, LAYOUT_TABLE, LAYOUT_GRID } from './constants';
15
15
  import BulkActions from './bulk-actions';
16
+ import { normalizeFields } from './normalize-fields';
16
17
 
17
18
  const defaultGetItemId = ( item ) => item.id;
18
19
  const defaultOnSelectionChange = () => {};
@@ -33,7 +34,7 @@ export default function DataViews( {
33
34
  fields,
34
35
  search = true,
35
36
  searchLabel = undefined,
36
- actions,
37
+ actions = [],
37
38
  data,
38
39
  getItemId = defaultGetItemId,
39
40
  isLoading = false,
@@ -76,12 +77,7 @@ export default function DataViews( {
76
77
  const ViewComponent = VIEW_LAYOUTS.find(
77
78
  ( v ) => v.type === view.type
78
79
  ).component;
79
- const _fields = useMemo( () => {
80
- return fields.map( ( field ) => ( {
81
- ...field,
82
- render: field.render || field.getValue,
83
- } ) );
84
- }, [ fields ] );
80
+ const _fields = useMemo( () => normalizeFields( fields ), [ fields ] );
85
81
 
86
82
  const hasPossibleBulkAction = useSomeItemHasAPossibleBulkAction(
87
83
  actions,
@@ -0,0 +1,154 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import removeAccents from 'remove-accents';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import {
10
+ OPERATOR_IS,
11
+ OPERATOR_IS_NOT,
12
+ OPERATOR_IS_NONE,
13
+ OPERATOR_IS_ANY,
14
+ OPERATOR_IS_ALL,
15
+ OPERATOR_IS_NOT_ALL,
16
+ } from './constants';
17
+ import { normalizeFields } from './normalize-fields';
18
+
19
+ function normalizeSearchInput( input = '' ) {
20
+ return removeAccents( input.trim().toLowerCase() );
21
+ }
22
+
23
+ const EMPTY_ARRAY = [];
24
+
25
+ /**
26
+ * Applies the filtering, sorting and pagination to the raw data based on the view configuration.
27
+ *
28
+ * @param {any[]} data Raw data.
29
+ * @param {Object} view View config.
30
+ * @param {Object[]} fields Fields config.
31
+ *
32
+ * @return {Object} { data: any[], paginationInfo: { totalItems: number, totalPages: number } }
33
+ */
34
+ export function filterSortAndPaginate( data, view, fields ) {
35
+ if ( ! data ) {
36
+ return {
37
+ data: EMPTY_ARRAY,
38
+ paginationInfo: { totalItems: 0, totalPages: 0 },
39
+ };
40
+ }
41
+ const _fields = normalizeFields( fields );
42
+ let filteredData = [ ...data ];
43
+ // Handle global search.
44
+ if ( view.search ) {
45
+ const normalizedSearch = normalizeSearchInput( view.search );
46
+ filteredData = filteredData.filter( ( item ) => {
47
+ return _fields
48
+ .filter( ( field ) => field.enableGlobalSearch )
49
+ .map( ( field ) => {
50
+ return normalizeSearchInput( field.getValue( { item } ) );
51
+ } )
52
+ .some( ( field ) => field.includes( normalizedSearch ) );
53
+ } );
54
+ }
55
+
56
+ if ( view.filters.length > 0 ) {
57
+ view.filters.forEach( ( filter ) => {
58
+ const field = _fields.find(
59
+ ( _field ) => _field.id === filter.field
60
+ );
61
+ if (
62
+ filter.operator === OPERATOR_IS_ANY &&
63
+ filter?.value?.length > 0
64
+ ) {
65
+ filteredData = filteredData.filter( ( item ) => {
66
+ const fieldValue = field.getValue( { item } );
67
+ if ( Array.isArray( fieldValue ) ) {
68
+ return filter.value.some( ( filterValue ) =>
69
+ fieldValue.includes( filterValue )
70
+ );
71
+ } else if ( typeof fieldValue === 'string' ) {
72
+ return filter.value.includes( fieldValue );
73
+ }
74
+ return false;
75
+ } );
76
+ } else if (
77
+ filter.operator === OPERATOR_IS_NONE &&
78
+ filter?.value?.length > 0
79
+ ) {
80
+ filteredData = filteredData.filter( ( item ) => {
81
+ const fieldValue = field.getValue( { item } );
82
+ if ( Array.isArray( fieldValue ) ) {
83
+ return ! filter.value.some( ( filterValue ) =>
84
+ fieldValue.includes( filterValue )
85
+ );
86
+ } else if ( typeof fieldValue === 'string' ) {
87
+ return ! filter.value.includes( fieldValue );
88
+ }
89
+ return false;
90
+ } );
91
+ } else if (
92
+ filter.operator === OPERATOR_IS_ALL &&
93
+ filter?.value?.length > 0
94
+ ) {
95
+ filteredData = filteredData.filter( ( item ) => {
96
+ return filter.value.every( ( value ) => {
97
+ return field.getValue( { item } ).includes( value );
98
+ } );
99
+ } );
100
+ } else if (
101
+ filter.operator === OPERATOR_IS_NOT_ALL &&
102
+ filter?.value?.length > 0
103
+ ) {
104
+ filteredData = filteredData.filter( ( item ) => {
105
+ return filter.value.every( ( value ) => {
106
+ return ! field.getValue( { item } ).includes( value );
107
+ } );
108
+ } );
109
+ } else if ( filter.operator === OPERATOR_IS ) {
110
+ filteredData = filteredData.filter( ( item ) => {
111
+ return filter.value === field.getValue( { item } );
112
+ } );
113
+ } else if ( filter.operator === OPERATOR_IS_NOT ) {
114
+ filteredData = filteredData.filter( ( item ) => {
115
+ return filter.value !== field.getValue( { item } );
116
+ } );
117
+ }
118
+ } );
119
+ }
120
+
121
+ // Handle sorting.
122
+ if ( view.sort ) {
123
+ const fieldId = view.sort.field;
124
+ const fieldToSort = _fields.find( ( field ) => {
125
+ return field.id === fieldId;
126
+ } );
127
+ filteredData.sort( ( a, b ) => {
128
+ const valueA = fieldToSort.getValue( { item: a } ) ?? '';
129
+ const valueB = fieldToSort.getValue( { item: b } ) ?? '';
130
+ return view.sort.direction === 'asc'
131
+ ? valueA.localeCompare( valueB )
132
+ : valueB.localeCompare( valueA );
133
+ } );
134
+ }
135
+
136
+ // Handle pagination.
137
+ const hasPagination = view.page && view.perPage;
138
+ const start = hasPagination ? ( view.page - 1 ) * view.perPage : 0;
139
+ const totalItems = filteredData?.length || 0;
140
+ const totalPages = hasPagination
141
+ ? Math.ceil( totalItems / view.perPage )
142
+ : 1;
143
+ filteredData = hasPagination
144
+ ? filteredData?.slice( start, start + view.perPage )
145
+ : filteredData;
146
+
147
+ return {
148
+ data: filteredData,
149
+ paginationInfo: {
150
+ totalItems,
151
+ totalPages,
152
+ },
153
+ };
154
+ }
@@ -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,7 @@ 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 { ALL_OPERATORS, OPERATOR_IS, OPERATOR_IS_NOT } from './constants';
14
14
  import { __experimentalHStack as HStack } from '@wordpress/components';
15
15
 
16
16
  const Filters = memo( function Filters( {
@@ -23,7 +23,7 @@ const Filters = memo( function Filters( {
23
23
  const addFilterRef = useRef();
24
24
  const filters = [];
25
25
  fields.forEach( ( field ) => {
26
- if ( ! field.type ) {
26
+ if ( ! field.elements?.length ) {
27
27
  return;
28
28
  }
29
29
 
@@ -32,30 +32,24 @@ const Filters = memo( function Filters( {
32
32
  return;
33
33
  }
34
34
 
35
- switch ( field.type ) {
36
- case ENUMERATION_TYPE:
37
- if ( ! field.elements?.length ) {
38
- return;
39
- }
40
-
41
- const isPrimary = !! field.filterBy?.isPrimary;
42
- filters.push( {
43
- field: field.id,
44
- name: field.header,
45
- elements: field.elements,
46
- operators,
47
- isVisible:
48
- isPrimary ||
49
- view.filters.some(
50
- ( f ) =>
51
- f.field === field.id &&
52
- [ OPERATOR_IN, OPERATOR_NOT_IN ].includes(
53
- f.operator
54
- )
55
- ),
56
- isPrimary,
57
- } );
58
- }
35
+ const isPrimary = !! field.filterBy?.isPrimary;
36
+ filters.push( {
37
+ field: field.id,
38
+ name: field.header,
39
+ elements: field.elements,
40
+ singleSelection: operators.some( ( op ) =>
41
+ [ OPERATOR_IS, OPERATOR_IS_NOT ].includes( op )
42
+ ),
43
+ operators,
44
+ isVisible:
45
+ isPrimary ||
46
+ view.filters.some(
47
+ ( f ) =>
48
+ f.field === field.id &&
49
+ ALL_OPERATORS.includes( f.operator )
50
+ ),
51
+ isPrimary,
52
+ } );
59
53
  } );
60
54
  // Sort filters by primary property. We need the primary filters to be first.
61
55
  // Then we sort by name.
package/src/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { default as DataViews } from './dataviews';
2
- export { sortByTextFields, getPaginationResults } from './utils';
3
2
  export { VIEW_LAYOUTS } from './constants';
3
+ export { filterSortAndPaginate } from './filter-and-sort-data-view';
@@ -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
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Apply default values and normalize the fields config.
3
+ *
4
+ * @param {Object[]} fields Raw Fields.
5
+ * @return {Object[]} Normalized fields.
6
+ */
7
+ export function normalizeFields( fields ) {
8
+ return fields.map( ( field ) => {
9
+ const getValue = field.getValue || ( ( { item } ) => item[ field.id ] );
10
+
11
+ return {
12
+ ...field,
13
+ getValue,
14
+ render: field.render || getValue,
15
+ };
16
+ } );
17
+ }
package/src/pagination.js CHANGED
@@ -36,11 +36,11 @@ const Pagination = memo( function Pagination( {
36
36
  { createInterpolateElement(
37
37
  sprintf(
38
38
  // translators: %s: Total number of pages.
39
- _x( 'Page <CurrenPageControl /> of %s', 'paging' ),
39
+ _x( 'Page <CurrentPageControl /> of %s', 'paging' ),
40
40
  totalPages
41
41
  ),
42
42
  {
43
- CurrenPageControl: (
43
+ CurrentPageControl: (
44
44
  <SelectControl
45
45
  aria-label={ __( 'Current page' ) }
46
46
  value={ view.page }