@wordpress/dataviews 0.4.1 → 0.5.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 (77) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +1 -0
  3. package/build/add-filter.js +25 -108
  4. package/build/add-filter.js.map +1 -1
  5. package/build/constants.js +9 -18
  6. package/build/constants.js.map +1 -1
  7. package/build/dataviews.js +22 -16
  8. package/build/dataviews.js.map +1 -1
  9. package/build/dropdown-menu-helper.js +1 -2
  10. package/build/dropdown-menu-helper.js.map +1 -1
  11. package/build/filter-summary.js +146 -78
  12. package/build/filter-summary.js.map +1 -1
  13. package/build/filters.js +32 -18
  14. package/build/filters.js.map +1 -1
  15. package/build/pagination.js +1 -2
  16. package/build/pagination.js.map +1 -1
  17. package/build/reset-filters.js +4 -1
  18. package/build/reset-filters.js.map +1 -1
  19. package/build/search-widget.js +111 -0
  20. package/build/search-widget.js.map +1 -0
  21. package/build/search.js +2 -3
  22. package/build/search.js.map +1 -1
  23. package/build/single-selection-checkbox.js +54 -0
  24. package/build/single-selection-checkbox.js.map +1 -0
  25. package/build/utils.js +14 -1
  26. package/build/utils.js.map +1 -1
  27. package/build/view-actions.js +2 -3
  28. package/build/view-actions.js.map +1 -1
  29. package/build/view-grid.js +92 -22
  30. package/build/view-grid.js.map +1 -1
  31. package/build/view-list.js +2 -1
  32. package/build/view-list.js.map +1 -1
  33. package/build/view-table.js +43 -132
  34. package/build/view-table.js.map +1 -1
  35. package/build-module/add-filter.js +28 -111
  36. package/build-module/add-filter.js.map +1 -1
  37. package/build-module/dataviews.js +23 -17
  38. package/build-module/dataviews.js.map +1 -1
  39. package/build-module/filter-summary.js +147 -80
  40. package/build-module/filter-summary.js.map +1 -1
  41. package/build-module/filters.js +32 -17
  42. package/build-module/filters.js.map +1 -1
  43. package/build-module/reset-filters.js +4 -1
  44. package/build-module/reset-filters.js.map +1 -1
  45. package/build-module/search-widget.js +101 -0
  46. package/build-module/search-widget.js.map +1 -0
  47. package/build-module/search.js +1 -1
  48. package/build-module/search.js.map +1 -1
  49. package/build-module/single-selection-checkbox.js +47 -0
  50. package/build-module/single-selection-checkbox.js.map +1 -0
  51. package/build-module/utils.js +12 -0
  52. package/build-module/utils.js.map +1 -1
  53. package/build-module/view-actions.js +1 -1
  54. package/build-module/view-actions.js.map +1 -1
  55. package/build-module/view-grid.js +92 -22
  56. package/build-module/view-grid.js.map +1 -1
  57. package/build-module/view-list.js +2 -1
  58. package/build-module/view-list.js.map +1 -1
  59. package/build-module/view-table.js +43 -131
  60. package/build-module/view-table.js.map +1 -1
  61. package/build-style/style-rtl.css +248 -44
  62. package/build-style/style.css +248 -44
  63. package/package.json +12 -11
  64. package/src/add-filter.js +39 -230
  65. package/src/dataviews.js +31 -20
  66. package/src/filter-summary.js +190 -136
  67. package/src/filters.js +42 -29
  68. package/src/reset-filters.js +12 -2
  69. package/src/search-widget.js +128 -0
  70. package/src/search.js +1 -1
  71. package/src/single-selection-checkbox.js +59 -0
  72. package/src/style.scss +254 -44
  73. package/src/utils.js +15 -0
  74. package/src/view-actions.js +1 -2
  75. package/src/view-grid.js +127 -53
  76. package/src/view-list.js +5 -1
  77. package/src/view-table.js +57 -230
@@ -1,45 +1,53 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import classnames from 'classnames';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
4
9
  import {
5
- Button,
6
- privateApis as componentsPrivateApis,
10
+ Dropdown,
11
+ __experimentalVStack as VStack,
12
+ __experimentalHStack as HStack,
13
+ FlexItem,
14
+ SelectControl,
15
+ Tooltip,
7
16
  Icon,
8
17
  } from '@wordpress/components';
9
- import { chevronDown } from '@wordpress/icons';
10
18
  import { __, sprintf } from '@wordpress/i18n';
11
- import { Children, Fragment } from '@wordpress/element';
19
+ import { useRef, createInterpolateElement } from '@wordpress/element';
20
+ import { closeSmall } from '@wordpress/icons';
21
+ import { ENTER, SPACE } from '@wordpress/keycodes';
12
22
 
13
23
  /**
14
24
  * Internal dependencies
15
25
  */
26
+ import SearchWidget from './search-widget';
16
27
  import { OPERATOR_IN, OPERATOR_NOT_IN, OPERATORS } from './constants';
17
- import { unlock } from './lock-unlock';
18
- import { DropdownMenuRadioItemCustom } from './dropdown-menu-helper';
19
-
20
- const {
21
- DropdownMenuV2: DropdownMenu,
22
- DropdownMenuGroupV2: DropdownMenuGroup,
23
- DropdownMenuItemV2: DropdownMenuItem,
24
- DropdownMenuSeparatorV2: DropdownMenuSeparator,
25
- DropdownMenuItemLabelV2: DropdownMenuItemLabel,
26
- DropdownMenuItemHelpTextV2: DropdownMenuItemHelpText,
27
- } = unlock( componentsPrivateApis );
28
28
 
29
29
  const FilterText = ( { activeElement, filterInView, filter } ) => {
30
30
  if ( activeElement === undefined ) {
31
31
  return filter.name;
32
32
  }
33
33
 
34
+ const filterTextWrappers = {
35
+ Span1: <span className="dataviews-filter-summary__filter-text-name" />,
36
+ Span2: <span className="dataviews-filter-summary__filter-text-value" />,
37
+ };
38
+
34
39
  if (
35
40
  activeElement !== undefined &&
36
41
  filterInView?.operator === OPERATOR_IN
37
42
  ) {
38
- return sprintf(
39
- /* translators: 1: Filter name. 2: Filter value. e.g.: "Author is Admin". */
40
- __( '%1$s is %2$s' ),
41
- filter.name,
42
- activeElement.label
43
+ return createInterpolateElement(
44
+ sprintf(
45
+ /* translators: 1: Filter name. 2: Filter value. e.g.: "Author is Admin". */
46
+ __( '<Span1>%1$s </Span1><Span2>is %2$s</Span2>' ),
47
+ filter.name,
48
+ activeElement.label
49
+ ),
50
+ filterTextWrappers
43
51
  );
44
52
  }
45
53
 
@@ -47,11 +55,14 @@ const FilterText = ( { activeElement, filterInView, filter } ) => {
47
55
  activeElement !== undefined &&
48
56
  filterInView?.operator === OPERATOR_NOT_IN
49
57
  ) {
50
- return sprintf(
51
- /* translators: 1: Filter name. 2: Filter value. e.g.: "Author is not Admin". */
52
- __( '%1$s is not %2$s' ),
53
- filter.name,
54
- activeElement.label
58
+ return createInterpolateElement(
59
+ 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>' ),
62
+ filter.name,
63
+ activeElement.label
64
+ ),
65
+ filterTextWrappers
55
66
  );
56
67
  }
57
68
 
@@ -62,127 +73,170 @@ const FilterText = ( { activeElement, filterInView, filter } ) => {
62
73
  );
63
74
  };
64
75
 
65
- function WithSeparators( { children } ) {
66
- return Children.toArray( children )
67
- .filter( Boolean )
68
- .map( ( child, i ) => (
69
- <Fragment key={ i }>
70
- { i > 0 && <DropdownMenuSeparator /> }
71
- { child }
72
- </Fragment>
73
- ) );
76
+ function OperatorSelector( { filter, view, onChangeView } ) {
77
+ const operatorOptions = filter.operators?.map( ( operator ) => ( {
78
+ value: operator,
79
+ label: OPERATORS[ operator ]?.label,
80
+ } ) );
81
+ const currentFilter = view.filters.find(
82
+ ( _filter ) => _filter.field === filter.field
83
+ );
84
+ const value = currentFilter?.operator || filter.operators[ 0 ];
85
+ return (
86
+ operatorOptions.length > 1 && (
87
+ <HStack
88
+ spacing={ 2 }
89
+ justify="flex-start"
90
+ className="dataviews-filter-summary__operators-container"
91
+ >
92
+ <FlexItem className="dataviews-filter-summary__operators-filter-name">
93
+ { filter.name }
94
+ </FlexItem>
95
+
96
+ <SelectControl
97
+ label={ __( 'Conditions' ) }
98
+ value={ value }
99
+ options={ operatorOptions }
100
+ onChange={ ( newValue ) => {
101
+ const newFilters = currentFilter
102
+ ? [
103
+ ...view.filters.map( ( _filter ) => {
104
+ if ( _filter.field === filter.field ) {
105
+ return {
106
+ ..._filter,
107
+ operator: newValue,
108
+ };
109
+ }
110
+ return _filter;
111
+ } ),
112
+ ]
113
+ : [
114
+ ...view.filters,
115
+ {
116
+ field: filter.field,
117
+ operator: newValue,
118
+ },
119
+ ];
120
+ onChangeView( {
121
+ ...view,
122
+ page: 1,
123
+ filters: newFilters,
124
+ } );
125
+ } }
126
+ size="small"
127
+ __nextHasNoMarginBottom
128
+ hideLabelFromVision
129
+ />
130
+ </HStack>
131
+ )
132
+ );
74
133
  }
75
134
 
76
- export default function FilterSummary( { filter, view, onChangeView } ) {
135
+ export default function FilterSummary( {
136
+ addFilterRef,
137
+ openedFilter,
138
+ ...commonProps
139
+ } ) {
140
+ const toggleRef = useRef();
141
+ const { filter, view, onChangeView } = commonProps;
77
142
  const filterInView = view.filters.find( ( f ) => f.field === filter.field );
78
- const otherFilters = view.filters.filter(
79
- ( f ) => f.field !== filter.field
80
- );
81
143
  const activeElement = filter.elements.find(
82
144
  ( element ) => element.value === filterInView?.value
83
145
  );
84
- const activeOperator = filterInView?.operator || filter.operators[ 0 ];
85
-
146
+ const isPrimary = filter.isPrimary;
147
+ const hasValues = filterInView?.value !== undefined;
148
+ const canResetOrRemove = ! isPrimary || hasValues;
86
149
  return (
87
- <DropdownMenu
88
- key={ filter.field }
89
- trigger={
90
- <Button variant="tertiary" size="compact" label={ filter.name }>
91
- <FilterText
92
- activeElement={ activeElement }
93
- filterInView={ filterInView }
94
- filter={ filter }
95
- />
96
- <Icon icon={ chevronDown } style={ { flexShrink: 0 } } />
97
- </Button>
98
- }
99
- >
100
- <WithSeparators>
101
- <DropdownMenuGroup>
102
- { filter.elements.map( ( element ) => {
103
- const isActive = activeElement?.value === element.value;
104
- return (
105
- <DropdownMenuRadioItemCustom
106
- key={ element.value }
107
- name={ `filter-summary-${ filter.field }` }
108
- value={ element.value }
109
- checked={ isActive }
110
- onClick={ () =>
150
+ <Dropdown
151
+ defaultOpen={ openedFilter === filter.field }
152
+ contentClassName="dataviews-filter-summary__popover"
153
+ popoverProps={ { placement: 'bottom-start', role: 'dialog' } }
154
+ onClose={ () => {
155
+ toggleRef.current?.focus();
156
+ } }
157
+ renderToggle={ ( { isOpen, onToggle } ) => (
158
+ <div className="dataviews-filter-summary__chip-container">
159
+ <Tooltip
160
+ text={ sprintf(
161
+ /* translators: 1: Filter name. */
162
+ __( 'Filter by: %1$s' ),
163
+ filter.name.toLowerCase()
164
+ ) }
165
+ placement="top"
166
+ >
167
+ <div
168
+ className={ classnames(
169
+ 'dataviews-filter-summary__chip',
170
+ {
171
+ 'has-reset': canResetOrRemove,
172
+ 'has-values': hasValues,
173
+ }
174
+ ) }
175
+ role="button"
176
+ tabIndex={ 0 }
177
+ onClick={ onToggle }
178
+ onKeyDown={ ( event ) => {
179
+ if (
180
+ [ ENTER, SPACE ].includes( event.keyCode )
181
+ ) {
182
+ onToggle();
183
+ event.preventDefault();
184
+ }
185
+ } }
186
+ aria-pressed={ isOpen }
187
+ aria-expanded={ isOpen }
188
+ ref={ toggleRef }
189
+ >
190
+ <FilterText
191
+ activeElement={ activeElement }
192
+ filterInView={ filterInView }
193
+ filter={ filter }
194
+ />
195
+ </div>
196
+ </Tooltip>
197
+ { canResetOrRemove && (
198
+ <Tooltip
199
+ text={ isPrimary ? __( 'Reset' ) : __( 'Remove' ) }
200
+ placement="top"
201
+ >
202
+ <button
203
+ className={ classnames(
204
+ 'dataviews-filter-summary__chip-remove',
205
+ { 'has-values': hasValues }
206
+ ) }
207
+ onClick={ () => {
111
208
  onChangeView( {
112
209
  ...view,
113
210
  page: 1,
114
- filters: [
115
- ...otherFilters,
116
- {
117
- field: filter.field,
118
- operator: activeOperator,
119
- value: isActive
120
- ? undefined
121
- : element.value,
122
- },
123
- ],
124
- } )
125
- }
211
+ filters: view.filters.filter(
212
+ ( _filter ) =>
213
+ _filter.field !== filter.field
214
+ ),
215
+ } );
216
+ // If the filter is not primary and can be removed, it will be added
217
+ // back to the available filters from `Add filter` component.
218
+ if ( ! isPrimary ) {
219
+ addFilterRef.current?.focus();
220
+ } else {
221
+ // If is primary, focus the toggle button.
222
+ toggleRef.current?.focus();
223
+ }
224
+ } }
126
225
  >
127
- <DropdownMenuItemLabel>
128
- { element.label }
129
- </DropdownMenuItemLabel>
130
- { !! element.description && (
131
- <DropdownMenuItemHelpText>
132
- { element.description }
133
- </DropdownMenuItemHelpText>
134
- ) }
135
- </DropdownMenuRadioItemCustom>
136
- );
137
- } ) }
138
- </DropdownMenuGroup>
139
- { filter.operators.length > 1 && (
140
- <DropdownMenu
141
- trigger={
142
- <DropdownMenuItem
143
- suffix={
144
- <span aria-hidden="true">
145
- { OPERATORS[ activeOperator ]?.label }
146
- </span>
147
- }
148
- >
149
- <DropdownMenuItemLabel>
150
- { __( 'Conditions' ) }
151
- </DropdownMenuItemLabel>
152
- </DropdownMenuItem>
153
- }
154
- >
155
- { Object.entries( OPERATORS ).map(
156
- ( [ operator, { label, key } ] ) => (
157
- <DropdownMenuRadioItemCustom
158
- key={ key }
159
- name={ `filter-summary-${ filter.field }-conditions` }
160
- value={ operator }
161
- checked={ activeOperator === operator }
162
- onChange={ ( e ) => {
163
- onChangeView( {
164
- ...view,
165
- page: 1,
166
- filters: [
167
- ...otherFilters,
168
- {
169
- field: filter.field,
170
- operator: e.target.value,
171
- value: filterInView?.value,
172
- },
173
- ],
174
- } );
175
- } }
176
- >
177
- <DropdownMenuItemLabel>
178
- { label }
179
- </DropdownMenuItemLabel>
180
- </DropdownMenuRadioItemCustom>
181
- )
182
- ) }
183
- </DropdownMenu>
184
- ) }
185
- </WithSeparators>
186
- </DropdownMenu>
226
+ <Icon icon={ closeSmall } />
227
+ </button>
228
+ </Tooltip>
229
+ ) }
230
+ </div>
231
+ ) }
232
+ renderContent={ () => {
233
+ return (
234
+ <VStack spacing={ 0 } justify="flex-start">
235
+ <OperatorSelector { ...commonProps } />
236
+ <SearchWidget { ...commonProps } />
237
+ </VStack>
238
+ );
239
+ } }
240
+ />
187
241
  );
188
242
  }
package/src/filters.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { memo } from '@wordpress/element';
4
+ import { memo, useRef } from '@wordpress/element';
5
5
 
6
6
  /**
7
7
  * Internal dependencies
@@ -9,24 +9,17 @@ import { memo } from '@wordpress/element';
9
9
  import FilterSummary from './filter-summary';
10
10
  import AddFilter from './add-filter';
11
11
  import ResetFilters from './reset-filters';
12
- import {
13
- ENUMERATION_TYPE,
14
- OPERATOR_IN,
15
- OPERATOR_NOT_IN,
16
- LAYOUT_LIST,
17
- } from './constants';
12
+ import { sanitizeOperators } from './utils';
13
+ import { ENUMERATION_TYPE, OPERATOR_IN, OPERATOR_NOT_IN } from './constants';
18
14
 
19
- const sanitizeOperators = ( field ) => {
20
- let operators = field.filterBy?.operators;
21
- if ( ! operators || ! Array.isArray( operators ) ) {
22
- operators = [ OPERATOR_IN, OPERATOR_NOT_IN ];
23
- }
24
- return operators.filter( ( operator ) =>
25
- [ OPERATOR_IN, OPERATOR_NOT_IN ].includes( operator )
26
- );
27
- };
28
-
29
- const Filters = memo( function Filters( { fields, view, onChangeView } ) {
15
+ const Filters = memo( function Filters( {
16
+ fields,
17
+ view,
18
+ onChangeView,
19
+ openedFilter,
20
+ setOpenedFilter,
21
+ } ) {
22
+ const addFilterRef = useRef();
30
23
  const filters = [];
31
24
  fields.forEach( ( field ) => {
32
25
  if ( ! field.type ) {
@@ -43,34 +36,50 @@ const Filters = memo( function Filters( { fields, view, onChangeView } ) {
43
36
  if ( ! field.elements?.length ) {
44
37
  return;
45
38
  }
39
+
40
+ const isPrimary = !! field.filterBy?.isPrimary;
46
41
  filters.push( {
47
42
  field: field.id,
48
43
  name: field.header,
49
44
  elements: field.elements,
50
45
  operators,
51
- isVisible: view.filters.some(
52
- ( f ) =>
53
- f.field === field.id &&
54
- [ OPERATOR_IN, OPERATOR_NOT_IN ].includes(
55
- f.operator
56
- )
57
- ),
46
+ isVisible:
47
+ isPrimary ||
48
+ view.filters.some(
49
+ ( f ) =>
50
+ f.field === field.id &&
51
+ [ OPERATOR_IN, OPERATOR_NOT_IN ].includes(
52
+ f.operator
53
+ )
54
+ ),
55
+ isPrimary,
58
56
  } );
59
57
  }
60
58
  } );
61
-
59
+ // Sort filters by primary property. We need the primary filters to be first.
60
+ // Then we sort by name.
61
+ filters.sort( ( a, b ) => {
62
+ if ( a.isPrimary && ! b.isPrimary ) {
63
+ return -1;
64
+ }
65
+ if ( ! a.isPrimary && b.isPrimary ) {
66
+ return 1;
67
+ }
68
+ return a.name.localeCompare( b.name );
69
+ } );
62
70
  const addFilter = (
63
71
  <AddFilter
64
72
  key="add-filter"
65
73
  filters={ filters }
66
74
  view={ view }
67
75
  onChangeView={ onChangeView }
76
+ ref={ addFilterRef }
77
+ setOpenedFilter={ setOpenedFilter }
68
78
  />
69
79
  );
70
80
  const filterComponents = [
71
- addFilter,
72
81
  ...filters.map( ( filter ) => {
73
- if ( ! filter.isVisible || view.type === LAYOUT_LIST ) {
82
+ if ( ! filter.isVisible ) {
74
83
  return null;
75
84
  }
76
85
 
@@ -80,15 +89,19 @@ const Filters = memo( function Filters( { fields, view, onChangeView } ) {
80
89
  filter={ filter }
81
90
  view={ view }
82
91
  onChangeView={ onChangeView }
92
+ addFilterRef={ addFilterRef }
93
+ openedFilter={ openedFilter }
83
94
  />
84
95
  );
85
96
  } ),
97
+ addFilter,
86
98
  ];
87
99
 
88
- if ( filterComponents.length > 1 && view.type !== LAYOUT_LIST ) {
100
+ if ( filterComponents.length > 1 ) {
89
101
  filterComponents.push(
90
102
  <ResetFilters
91
103
  key="reset-filters"
104
+ filters={ filters }
92
105
  view={ view }
93
106
  onChangeView={ onChangeView }
94
107
  />
@@ -4,10 +4,20 @@
4
4
  import { Button } from '@wordpress/components';
5
5
  import { __ } from '@wordpress/i18n';
6
6
 
7
- export default function ResetFilter( { view, onChangeView } ) {
7
+ export default function ResetFilter( { filters, view, onChangeView } ) {
8
+ const isPrimary = ( field ) =>
9
+ filters.some(
10
+ ( _filter ) => _filter.field === field && _filter.isPrimary
11
+ );
12
+ const isDisabled =
13
+ ! view.search &&
14
+ ! view.filters?.some(
15
+ ( _filter ) =>
16
+ _filter.value !== undefined || ! isPrimary( _filter.field )
17
+ );
8
18
  return (
9
19
  <Button
10
- disabled={ view.search === '' && view.filters?.length === 0 }
20
+ disabled={ isDisabled }
11
21
  __experimentalIsFocusable
12
22
  size="compact"
13
23
  variant="tertiary"
@@ -0,0 +1,128 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ // eslint-disable-next-line no-restricted-imports
5
+ import * as Ariakit from '@ariakit/react';
6
+ import removeAccents from 'remove-accents';
7
+
8
+ /**
9
+ * WordPress dependencies
10
+ */
11
+ import { __ } from '@wordpress/i18n';
12
+ import { useState, useMemo, useDeferredValue } from '@wordpress/element';
13
+ import { VisuallyHidden, Icon } from '@wordpress/components';
14
+ import { search } from '@wordpress/icons';
15
+ import { SVG, Circle } from '@wordpress/primitives';
16
+
17
+ const radioCheck = (
18
+ <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
19
+ <Circle cx={ 12 } cy={ 12 } r={ 3 }></Circle>
20
+ </SVG>
21
+ );
22
+
23
+ function normalizeSearchInput( input = '' ) {
24
+ return removeAccents( input.trim().toLowerCase() );
25
+ }
26
+
27
+ export default function SearchWidget( { filter, view, onChangeView } ) {
28
+ const [ searchValue, setSearchValue ] = useState( '' );
29
+ const deferredSearchValue = useDeferredValue( searchValue );
30
+ const selectedFilter = view.filters.find(
31
+ ( _filter ) => _filter.field === filter.field
32
+ );
33
+ const selectedValues = selectedFilter?.value;
34
+ const matches = useMemo( () => {
35
+ const normalizedSearch = normalizeSearchInput( deferredSearchValue );
36
+ return filter.elements.filter( ( item ) =>
37
+ normalizeSearchInput( item.label ).includes( normalizedSearch )
38
+ );
39
+ }, [ filter.elements, deferredSearchValue ] );
40
+ return (
41
+ <Ariakit.ComboboxProvider
42
+ value={ searchValue }
43
+ setSelectedValue={ ( value ) => {
44
+ const currentFilter = view.filters.find(
45
+ ( _filter ) => _filter.field === filter.field
46
+ );
47
+ const newFilters = currentFilter
48
+ ? [
49
+ ...view.filters.map( ( _filter ) => {
50
+ if ( _filter.field === filter.field ) {
51
+ return {
52
+ ..._filter,
53
+ operator:
54
+ currentFilter.operator ||
55
+ filter.operators[ 0 ],
56
+ value,
57
+ };
58
+ }
59
+ return _filter;
60
+ } ),
61
+ ]
62
+ : [
63
+ ...view.filters,
64
+ {
65
+ field: filter.field,
66
+ operator: filter.operators[ 0 ],
67
+ value,
68
+ },
69
+ ];
70
+ onChangeView( {
71
+ ...view,
72
+ page: 1,
73
+ filters: newFilters,
74
+ } );
75
+ } }
76
+ setValue={ setSearchValue }
77
+ >
78
+ <div className="dataviews-search-widget-filter-combobox__wrapper">
79
+ <Ariakit.ComboboxLabel render={ <VisuallyHidden /> }>
80
+ { __( 'Search items' ) }
81
+ </Ariakit.ComboboxLabel>
82
+ <Ariakit.Combobox
83
+ autoSelect="always"
84
+ placeholder={ __( 'Search' ) }
85
+ className="dataviews-search-widget-filter-combobox__input"
86
+ />
87
+ <div className="dataviews-search-widget-filter-combobox__icon">
88
+ <Icon icon={ search } />
89
+ </div>
90
+ </div>
91
+ <Ariakit.ComboboxList
92
+ className="dataviews-search-widget-filter-combobox-list"
93
+ alwaysVisible
94
+ >
95
+ { matches.map( ( element ) => {
96
+ return (
97
+ <Ariakit.ComboboxItem
98
+ key={ element.value }
99
+ value={ element.value }
100
+ className="dataviews-search-widget-filter-combobox-item"
101
+ hideOnClick={ false }
102
+ setValueOnClick={ false }
103
+ focusOnHover
104
+ >
105
+ <span className="dataviews-search-widget-filter-combobox-item-check">
106
+ { selectedValues === element.value && (
107
+ <Icon icon={ radioCheck } />
108
+ ) }
109
+ </span>
110
+ <span>
111
+ <Ariakit.ComboboxItemValue
112
+ className="dataviews-search-widget-filter-combobox-item-value"
113
+ value={ element.label }
114
+ />
115
+ { !! element.description && (
116
+ <span className="dataviews-search-widget-filter-combobox-item-description">
117
+ { element.description }
118
+ </span>
119
+ ) }
120
+ </span>
121
+ </Ariakit.ComboboxItem>
122
+ );
123
+ } ) }
124
+ { ! matches.length && <p>{ __( 'No results found' ) }</p> }
125
+ </Ariakit.ComboboxList>
126
+ </Ariakit.ComboboxProvider>
127
+ );
128
+ }
package/src/search.js CHANGED
@@ -24,7 +24,7 @@ const Search = memo( function Search( { label, view, onChangeView } ) {
24
24
  search: debouncedSearch,
25
25
  } );
26
26
  }, [ debouncedSearch ] );
27
- const searchLabel = label || __( 'Filter list' );
27
+ const searchLabel = label || __( 'Search' );
28
28
  return (
29
29
  <SearchControl
30
30
  __nextHasNoMarginBottom