@wordpress/dataviews 0.6.0 → 0.7.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 (63) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/add-filter.js +0 -2
  3. package/build/add-filter.js.map +1 -1
  4. package/build/bulk-actions.js +41 -3
  5. package/build/bulk-actions.js.map +1 -1
  6. package/build/dataviews.js +23 -16
  7. package/build/dataviews.js.map +1 -1
  8. package/build/filters.js +8 -1
  9. package/build/filters.js.map +1 -1
  10. package/build/pagination.js +2 -1
  11. package/build/pagination.js.map +1 -1
  12. package/build/reset-filters.js +2 -1
  13. package/build/reset-filters.js.map +1 -1
  14. package/build/search-widget.js +89 -4
  15. package/build/search-widget.js.map +1 -1
  16. package/build/single-selection-checkbox.js +7 -2
  17. package/build/single-selection-checkbox.js.map +1 -1
  18. package/build/view-actions.js.map +1 -1
  19. package/build/view-grid.js +10 -14
  20. package/build/view-grid.js.map +1 -1
  21. package/build/view-list.js +1 -1
  22. package/build/view-list.js.map +1 -1
  23. package/build/view-table.js +78 -45
  24. package/build/view-table.js.map +1 -1
  25. package/build-module/add-filter.js +0 -2
  26. package/build-module/add-filter.js.map +1 -1
  27. package/build-module/bulk-actions.js +40 -4
  28. package/build-module/bulk-actions.js.map +1 -1
  29. package/build-module/dataviews.js +24 -17
  30. package/build-module/dataviews.js.map +1 -1
  31. package/build-module/filters.js +8 -1
  32. package/build-module/filters.js.map +1 -1
  33. package/build-module/pagination.js +2 -1
  34. package/build-module/pagination.js.map +1 -1
  35. package/build-module/reset-filters.js +2 -1
  36. package/build-module/reset-filters.js.map +1 -1
  37. package/build-module/search-widget.js +91 -6
  38. package/build-module/search-widget.js.map +1 -1
  39. package/build-module/single-selection-checkbox.js +7 -2
  40. package/build-module/single-selection-checkbox.js.map +1 -1
  41. package/build-module/view-actions.js.map +1 -1
  42. package/build-module/view-grid.js +11 -15
  43. package/build-module/view-grid.js.map +1 -1
  44. package/build-module/view-list.js +2 -2
  45. package/build-module/view-list.js.map +1 -1
  46. package/build-module/view-table.js +80 -47
  47. package/build-module/view-table.js.map +1 -1
  48. package/build-style/style-rtl.css +67 -43
  49. package/build-style/style.css +67 -43
  50. package/package.json +11 -11
  51. package/src/add-filter.js +0 -2
  52. package/src/bulk-actions.js +54 -4
  53. package/src/dataviews.js +55 -44
  54. package/src/filters.js +6 -1
  55. package/src/pagination.js +6 -1
  56. package/src/reset-filters.js +2 -1
  57. package/src/search-widget.js +131 -6
  58. package/src/single-selection-checkbox.js +7 -1
  59. package/src/style.scss +99 -70
  60. package/src/view-actions.js +1 -1
  61. package/src/view-grid.js +11 -13
  62. package/src/view-list.js +2 -1
  63. package/src/view-table.js +113 -78
@@ -8,12 +8,27 @@ import removeAccents from 'remove-accents';
8
8
  /**
9
9
  * WordPress dependencies
10
10
  */
11
- import { __ } from '@wordpress/i18n';
11
+ import { __, sprintf } from '@wordpress/i18n';
12
12
  import { useState, useMemo, useDeferredValue } from '@wordpress/element';
13
- import { VisuallyHidden, Icon } from '@wordpress/components';
13
+ import {
14
+ VisuallyHidden,
15
+ Icon,
16
+ privateApis as componentsPrivateApis,
17
+ } from '@wordpress/components';
14
18
  import { search } from '@wordpress/icons';
15
19
  import { SVG, Circle } from '@wordpress/primitives';
16
20
 
21
+ /**
22
+ * Internal dependencies
23
+ */
24
+ import { unlock } from './lock-unlock';
25
+
26
+ const {
27
+ CompositeV2: Composite,
28
+ CompositeItemV2: CompositeItem,
29
+ useCompositeStoreV2: useCompositeStore,
30
+ } = unlock( componentsPrivateApis );
31
+
17
32
  const radioCheck = (
18
33
  <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
19
34
  <Circle cx={ 12 } cy={ 12 } r={ 3 }></Circle>
@@ -24,7 +39,112 @@ function normalizeSearchInput( input = '' ) {
24
39
  return removeAccents( input.trim().toLowerCase() );
25
40
  }
26
41
 
27
- export default function SearchWidget( { filter, view, onChangeView } ) {
42
+ function ListBox( { view, filter, onChangeView } ) {
43
+ const compositeStore = useCompositeStore( {
44
+ virtualFocus: true,
45
+ focusLoop: true,
46
+ // When we have no or just one operators, we can set the first item as active.
47
+ // We do that by passing `undefined` to `defaultActiveId`. Otherwise, we set it to `null`,
48
+ // so the first item is not selected, since the focus is on the operators control.
49
+ defaultActiveId: filter.operators?.length === 1 ? undefined : null,
50
+ } );
51
+ const selectedFilter = view.filters.find(
52
+ ( _filter ) => _filter.field === filter.field
53
+ );
54
+ const selectedValues = selectedFilter?.value;
55
+ return (
56
+ <Composite
57
+ store={ compositeStore }
58
+ role="listbox"
59
+ className="dataviews-search-widget-listbox"
60
+ aria-label={ sprintf(
61
+ /* translators: List of items for a filter. 1: Filter name. e.g.: "List of: Author". */
62
+ __( 'List of: %1$s' ),
63
+ filter.name
64
+ ) }
65
+ onFocusVisible={ () => {
66
+ if ( ! compositeStore.getState().activeId ) {
67
+ compositeStore.move( compositeStore.first() );
68
+ }
69
+ } }
70
+ render={ <Ariakit.CompositeTypeahead store={ compositeStore } /> }
71
+ >
72
+ { filter.elements.map( ( element ) => (
73
+ <Ariakit.CompositeHover
74
+ store={ compositeStore }
75
+ key={ element.value }
76
+ render={
77
+ <CompositeItem
78
+ render={
79
+ <div
80
+ aria-label={ element.label }
81
+ role="option"
82
+ className="dataviews-search-widget-listitem"
83
+ />
84
+ }
85
+ onClick={ () => {
86
+ const currentFilter = view.filters.find(
87
+ ( _filter ) =>
88
+ _filter.field === filter.field
89
+ );
90
+ const newFilters = currentFilter
91
+ ? [
92
+ ...view.filters.map(
93
+ ( _filter ) => {
94
+ if (
95
+ _filter.field ===
96
+ filter.field
97
+ ) {
98
+ return {
99
+ ..._filter,
100
+ operator:
101
+ currentFilter.operator ||
102
+ filter
103
+ .operators[ 0 ],
104
+ value: element.value,
105
+ };
106
+ }
107
+ return _filter;
108
+ }
109
+ ),
110
+ ]
111
+ : [
112
+ ...view.filters,
113
+ {
114
+ field: filter.field,
115
+ operator: filter.operators[ 0 ],
116
+ value: element.value,
117
+ },
118
+ ];
119
+ onChangeView( {
120
+ ...view,
121
+ page: 1,
122
+ filters: newFilters,
123
+ } );
124
+ } }
125
+ />
126
+ }
127
+ >
128
+ <span className="dataviews-search-widget-listitem-check">
129
+ { selectedValues === element.value && (
130
+ <Icon icon={ radioCheck } />
131
+ ) }
132
+ </span>
133
+ <span>
134
+ { element.label }
135
+ { !! element.description && (
136
+ <span className="dataviews-search-widget-listitem-description">
137
+ { element.description }
138
+ </span>
139
+ ) }
140
+ </span>
141
+ </Ariakit.CompositeHover>
142
+ ) ) }
143
+ </Composite>
144
+ );
145
+ }
146
+
147
+ function ComboboxList( { view, filter, onChangeView } ) {
28
148
  const [ searchValue, setSearchValue ] = useState( '' );
29
149
  const deferredSearchValue = useDeferredValue( searchValue );
30
150
  const selectedFilter = view.filters.find(
@@ -97,12 +217,12 @@ export default function SearchWidget( { filter, view, onChangeView } ) {
97
217
  <Ariakit.ComboboxItem
98
218
  key={ element.value }
99
219
  value={ element.value }
100
- className="dataviews-search-widget-filter-combobox-item"
220
+ className="dataviews-search-widget-listitem"
101
221
  hideOnClick={ false }
102
222
  setValueOnClick={ false }
103
223
  focusOnHover
104
224
  >
105
- <span className="dataviews-search-widget-filter-combobox-item-check">
225
+ <span className="dataviews-search-widget-listitem-check">
106
226
  { selectedValues === element.value && (
107
227
  <Icon icon={ radioCheck } />
108
228
  ) }
@@ -113,7 +233,7 @@ export default function SearchWidget( { filter, view, onChangeView } ) {
113
233
  value={ element.label }
114
234
  />
115
235
  { !! element.description && (
116
- <span className="dataviews-search-widget-filter-combobox-item-description">
236
+ <span className="dataviews-search-widget-listitem-description">
117
237
  { element.description }
118
238
  </span>
119
239
  ) }
@@ -126,3 +246,8 @@ export default function SearchWidget( { filter, view, onChangeView } ) {
126
246
  </Ariakit.ComboboxProvider>
127
247
  );
128
248
  }
249
+
250
+ export default function SearchWidget( props ) {
251
+ const Widget = props.filter.elements.length > 10 ? ComboboxList : ListBox;
252
+ return <Widget { ...props } />;
253
+ }
@@ -11,6 +11,7 @@ export default function SingleSelectionCheckbox( {
11
11
  data,
12
12
  getItemId,
13
13
  primaryField,
14
+ disabled,
14
15
  } ) {
15
16
  const id = getItemId( item );
16
17
  const isSelected = selection.includes( id );
@@ -31,9 +32,14 @@ export default function SingleSelectionCheckbox( {
31
32
  <CheckboxControl
32
33
  className="dataviews-view-table-selection-checkbox"
33
34
  __nextHasNoMarginBottom
34
- checked={ isSelected }
35
35
  label={ selectionLabel }
36
+ aria-disabled={ disabled }
37
+ checked={ isSelected }
36
38
  onChange={ () => {
39
+ if ( disabled ) {
40
+ return;
41
+ }
42
+
37
43
  if ( ! isSelected ) {
38
44
  onSelectionChange(
39
45
  data.filter( ( _item ) => {
package/src/style.scss CHANGED
@@ -4,17 +4,16 @@
4
4
  overflow: auto;
5
5
  box-sizing: border-box;
6
6
  scroll-padding-bottom: $grid-unit-80;
7
-
8
- > div {
9
- min-height: 100%;
10
- }
11
7
  }
12
8
 
13
9
  .dataviews-filters__view-actions {
14
10
  padding: $grid-unit-15 $grid-unit-40 0;
15
- .components-search-control {
16
- flex-grow: 1;
11
+ margin-bottom: $grid-unit-15;
12
+ flex-shrink: 0;
13
+ position: sticky;
14
+ left: 0;
17
15
 
16
+ .components-search-control {
18
17
  .components-base-control__field {
19
18
  max-width: 240px;
20
19
  }
@@ -22,11 +21,18 @@
22
21
  }
23
22
 
24
23
  .dataviews-filters__container {
25
- padding: 0 $grid-unit-40;
26
- }
24
+ padding-right: $grid-unit-40;
27
25
 
28
- .dataviews-filters__view-actions.components-h-stack {
29
- align-items: center;
26
+ .dataviews-filters__reset-button[aria-disabled="true"] {
27
+ &,
28
+ &:hover {
29
+ opacity: 0;
30
+ }
31
+
32
+ &:focus {
33
+ opacity: 1;
34
+ }
35
+ }
30
36
  }
31
37
 
32
38
  .dataviews-filters-button {
@@ -34,22 +40,25 @@
34
40
  }
35
41
 
36
42
  .dataviews-pagination {
37
- margin-top: auto;
38
43
  position: sticky;
39
44
  bottom: 0;
40
- background-color: rgba($white, 0.8);
41
- backdrop-filter: blur(6px);
45
+ left: 0;
46
+ background-color: $white;
42
47
  padding: $grid-unit-15 $grid-unit-40;
43
48
  border-top: $border-width solid $gray-100;
44
49
  color: $gray-700;
50
+ flex-shrink: 0;
45
51
  }
46
52
 
47
- .dataviews-filters-options {
48
- margin: $grid-unit-40 0 $grid-unit-20;
53
+ .dataviews-pagination__page-selection {
54
+ font-size: 11px;
55
+ text-transform: uppercase;
56
+ font-weight: 500;
57
+ color: $gray-900;
49
58
  }
50
59
 
51
- .dataviews-view-table-wrapper {
52
- overflow-x: auto;
60
+ .dataviews-filters-options {
61
+ margin: $grid-unit-40 0 $grid-unit-20;
53
62
  }
54
63
 
55
64
  .dataviews-view-table {
@@ -118,7 +127,7 @@
118
127
  background-color: #f8f8f8;
119
128
  }
120
129
 
121
- .components-checkbox-control__input {
130
+ .components-checkbox-control__input.components-checkbox-control__input {
122
131
  opacity: 0;
123
132
 
124
133
  &:checked,
@@ -145,17 +154,18 @@
145
154
  }
146
155
  }
147
156
  thead {
157
+ position: sticky;
158
+ inset-block-start: 0;
159
+ z-index: z-index(".dataviews-view-table thead");
160
+
148
161
  tr {
149
162
  border: 0;
150
163
  }
151
164
  th {
152
- position: sticky;
153
- top: -1px;
154
165
  background-color: $white;
155
166
  box-shadow: inset 0 -#{$border-width} 0 $gray-100;
156
167
  padding-top: $grid-unit-10;
157
168
  padding-bottom: $grid-unit-10;
158
- z-index: 1;
159
169
  font-size: 11px;
160
170
  text-transform: uppercase;
161
171
  font-weight: 500;
@@ -202,6 +212,12 @@
202
212
  .dataviews-view-table__actions-column {
203
213
  width: 1%;
204
214
  }
215
+
216
+ &:has(tr.is-selected) {
217
+ .components-checkbox-control__input {
218
+ opacity: 1;
219
+ }
220
+ }
205
221
  }
206
222
 
207
223
  .dataviews-view-list__primary-field,
@@ -212,7 +228,6 @@
212
228
  color: $gray-900;
213
229
  text-overflow: ellipsis;
214
230
  white-space: nowrap;
215
- overflow: hidden;
216
231
  display: block;
217
232
  width: 100%;
218
233
 
@@ -228,6 +243,7 @@
228
243
  &:hover {
229
244
  color: $gray-900;
230
245
  }
246
+ @include link-reset();
231
247
  }
232
248
 
233
249
  button.components-button.is-link {
@@ -466,6 +482,10 @@
466
482
  .dataviews-no-results,
467
483
  .dataviews-loading {
468
484
  padding: 0 $grid-unit-40;
485
+ flex-grow: 1;
486
+ display: flex;
487
+ align-items: center;
488
+ justify-content: center;
469
489
  }
470
490
 
471
491
  .dataviews-view-table-selection-checkbox label {
@@ -493,9 +513,6 @@
493
513
  margin-left: $grid-unit-10;
494
514
  }
495
515
 
496
- .dataviews-view-grid__card.has-no-pointer-events * {
497
- pointer-events: none;
498
- }
499
516
  .dataviews-filter-summary__popover {
500
517
  .components-popover__content {
501
518
  width: 230px;
@@ -510,56 +527,62 @@
510
527
  overflow: auto;
511
528
  border-top: 1px solid $gray-200;
512
529
 
513
- .dataviews-search-widget-filter-combobox-item {
514
- display: flex;
515
- align-items: center;
516
- gap: $grid-unit-10;
517
- border-radius: $radius-block-ui;
518
- box-sizing: border-box;
519
- padding: $grid-unit-10 $grid-unit-15;
520
- cursor: default;
521
- margin-block-end: 2px;
522
-
523
- &:last-child {
524
- margin-block-end: 0;
530
+ .dataviews-search-widget-filter-combobox-item-value {
531
+ [data-user-value] {
532
+ font-weight: 600;
525
533
  }
534
+ }
535
+ }
526
536
 
527
- &:hover,
528
- &[data-active-item],
529
- &:focus {
530
- background-color: var(--wp-admin-theme-color);
531
- color: $white;
537
+ .dataviews-search-widget-listbox {
538
+ max-height: $grid-unit * 23;
539
+ padding: $grid-unit-05;
540
+ overflow: auto;
541
+ }
532
542
 
533
- .dataviews-search-widget-filter-combobox-item-check {
534
- fill: $white;
535
- }
543
+ .dataviews-search-widget-listitem {
544
+ display: flex;
545
+ align-items: center;
546
+ gap: $grid-unit-10;
547
+ border-radius: $radius-block-ui;
548
+ box-sizing: border-box;
549
+ padding: $grid-unit-10 $grid-unit-15;
550
+ cursor: default;
551
+ margin-block-end: 2px;
536
552
 
537
- .dataviews-search-widget-filter-combobox-item-description {
538
- color: $white;
539
- }
540
- }
553
+ &:last-child {
554
+ margin-block-end: 0;
555
+ }
541
556
 
542
- .dataviews-search-widget-filter-combobox-item-check {
543
- width: 24px;
544
- height: 24px;
545
- flex-shrink: 0;
546
- }
557
+ &:hover,
558
+ &[data-active-item],
559
+ &:focus {
560
+ background-color: var(--wp-admin-theme-color);
561
+ color: $white;
547
562
 
548
- .dataviews-search-widget-filter-combobox-item-value {
549
- [data-user-value] {
550
- font-weight: 600;
551
- }
563
+ .dataviews-search-widget-listitem-check {
564
+ fill: $white;
552
565
  }
553
566
 
554
- .dataviews-search-widget-filter-combobox-item-description {
555
- display: block;
556
- overflow: hidden;
557
- text-overflow: ellipsis;
558
- font-size: $helptext-font-size;
559
- line-height: 16px;
560
- color: $gray-700;
567
+ .dataviews-search-widget-listitem-description {
568
+ color: $white;
561
569
  }
562
570
  }
571
+
572
+ .dataviews-search-widget-listitem-check {
573
+ width: 24px;
574
+ height: 24px;
575
+ flex-shrink: 0;
576
+ }
577
+
578
+ .dataviews-search-widget-listitem-description {
579
+ display: block;
580
+ overflow: hidden;
581
+ text-overflow: ellipsis;
582
+ font-size: $helptext-font-size;
583
+ line-height: 16px;
584
+ color: $gray-700;
585
+ }
563
586
  }
564
587
 
565
588
  .dataviews-search-widget-filter-combobox__wrapper {
@@ -615,8 +638,12 @@
615
638
  }
616
639
 
617
640
  .dataviews-filter-summary__operators-container {
618
- padding: $grid-unit-10 $grid-unit-10 $grid-unit-05;
619
- padding-bottom: 0;
641
+ padding: $grid-unit-10 $grid-unit-10 0;
642
+
643
+ &:has(+ .dataviews-search-widget-listbox) {
644
+ border-bottom: 1px solid $gray-200;
645
+ padding-bottom: $grid-unit-10;
646
+ }
620
647
 
621
648
  &:empty {
622
649
  display: none;
@@ -648,7 +675,8 @@
648
675
  }
649
676
 
650
677
  &:hover,
651
- &:focus-visible {
678
+ &:focus-visible,
679
+ &[aria-expanded="true"] {
652
680
  background: $gray-200;
653
681
  color: $gray-900;
654
682
  }
@@ -657,8 +685,9 @@
657
685
  color: var(--wp-admin-theme-color);
658
686
  background: rgba(var(--wp-admin-theme-color--rgb), 0.04);
659
687
 
660
- &:hover {
661
- background: rgba(var(--wp-admin-theme-color--rgb), 0.08);
688
+ &:hover,
689
+ &[aria-expanded="true"] {
690
+ background: rgba(var(--wp-admin-theme-color--rgb), 0.12);
662
691
  }
663
692
  }
664
693
 
@@ -56,7 +56,7 @@ function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) {
56
56
  value={ availableView.type }
57
57
  name="view-actions-available-view"
58
58
  checked={ availableView.type === view.type }
59
- hideOnClick={ true }
59
+ hideOnClick
60
60
  onChange={ ( e ) => {
61
61
  onChangeView( {
62
62
  ...view,
package/src/view-grid.js CHANGED
@@ -11,10 +11,10 @@ import {
11
11
  __experimentalHStack as HStack,
12
12
  __experimentalVStack as VStack,
13
13
  Tooltip,
14
+ Spinner,
14
15
  } from '@wordpress/components';
15
16
  import { __ } from '@wordpress/i18n';
16
17
  import { useAsyncList } from '@wordpress/compose';
17
- import { useState } from '@wordpress/element';
18
18
 
19
19
  /**
20
20
  * Internal dependencies
@@ -22,6 +22,8 @@ import { useState } from '@wordpress/element';
22
22
  import ItemActions from './item-actions';
23
23
  import SingleSelectionCheckbox from './single-selection-checkbox';
24
24
 
25
+ import { useHasAPossibleBulkAction } from './bulk-actions';
26
+
25
27
  function GridItem( {
26
28
  selection,
27
29
  data,
@@ -33,7 +35,7 @@ function GridItem( {
33
35
  primaryField,
34
36
  visibleFields,
35
37
  } ) {
36
- const [ hasNoPointerEvents, setHasNoPointerEvents ] = useState( false );
38
+ const hasBulkAction = useHasAPossibleBulkAction( actions, item );
37
39
  const id = getItemId( item );
38
40
  const isSelected = selection.includes( id );
39
41
  return (
@@ -41,12 +43,12 @@ function GridItem( {
41
43
  spacing={ 0 }
42
44
  key={ id }
43
45
  className={ classnames( 'dataviews-view-grid__card', {
44
- 'is-selected': isSelected,
45
- 'has-no-pointer-events': hasNoPointerEvents,
46
+ 'is-selected': hasBulkAction && isSelected,
46
47
  } ) }
47
- onMouseDown={ ( event ) => {
48
- if ( event.ctrlKey || event.metaKey ) {
49
- setHasNoPointerEvents( true );
48
+ onClickCapture={ ( event ) => {
49
+ if ( hasBulkAction && ( event.ctrlKey || event.metaKey ) ) {
50
+ event.stopPropagation();
51
+ event.preventDefault();
50
52
  if ( ! isSelected ) {
51
53
  onSelectionChange(
52
54
  data.filter( ( _item ) => {
@@ -70,11 +72,6 @@ function GridItem( {
70
72
  }
71
73
  }
72
74
  } }
73
- onClick={ () => {
74
- if ( hasNoPointerEvents ) {
75
- setHasNoPointerEvents( false );
76
- }
77
- } }
78
75
  >
79
76
  <div className="dataviews-view-grid__media">
80
77
  { mediaField?.render( { item } ) }
@@ -91,6 +88,7 @@ function GridItem( {
91
88
  getItemId={ getItemId }
92
89
  data={ data }
93
90
  primaryField={ primaryField }
91
+ disabled={ ! hasBulkAction }
94
92
  />
95
93
  <HStack className="dataviews-view-grid__primary-field">
96
94
  { primaryField?.render( { item } ) }
@@ -186,7 +184,7 @@ export default function ViewGrid( {
186
184
  'dataviews-no-results': ! isLoading,
187
185
  } ) }
188
186
  >
189
- <p>{ isLoading ? __( 'Loading…' ) : __( 'No results' ) }</p>
187
+ <p>{ isLoading ? <Spinner /> : __( 'No results' ) }</p>
190
188
  </div>
191
189
  ) }
192
190
  </>
package/src/view-list.js CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  __experimentalHStack as HStack,
12
12
  __experimentalVStack as VStack,
13
13
  Button,
14
+ Spinner,
14
15
  } from '@wordpress/components';
15
16
  import { ENTER, SPACE } from '@wordpress/keycodes';
16
17
  import { info } from '@wordpress/icons';
@@ -60,7 +61,7 @@ export default function ViewList( {
60
61
  } ) }
61
62
  >
62
63
  { ! hasData && (
63
- <p>{ isLoading ? __( 'Loading…' ) : __( 'No results' ) }</p>
64
+ <p>{ isLoading ? <Spinner /> : __( 'No results' ) }</p>
64
65
  ) }
65
66
  </div>
66
67
  );