@wordpress/dataviews 0.8.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.
- package/CHANGELOG.md +10 -0
- package/README.md +3 -5
- package/build/constants.js +1 -4
- package/build/constants.js.map +1 -1
- package/build/dataviews.js +2 -12
- package/build/dataviews.js.map +1 -1
- package/build/filter-and-sort-data-view.js +147 -0
- package/build/filter-and-sort-data-view.js.map +1 -0
- package/build/filters.js +11 -17
- package/build/filters.js.map +1 -1
- package/build/index.js +3 -9
- package/build/index.js.map +1 -1
- package/build/normalize-fields.js +25 -0
- package/build/normalize-fields.js.map +1 -0
- package/build/utils.js +1 -65
- package/build/utils.js.map +1 -1
- package/build/view-grid.js +21 -11
- package/build/view-grid.js.map +1 -1
- package/build/view-list.js +122 -58
- package/build/view-list.js.map +1 -1
- package/build/view-table.js +27 -13
- package/build/view-table.js.map +1 -1
- package/build-module/constants.js +0 -3
- package/build-module/constants.js.map +1 -1
- package/build-module/dataviews.js +2 -12
- package/build-module/dataviews.js.map +1 -1
- package/build-module/filter-and-sort-data-view.js +139 -0
- package/build-module/filter-and-sort-data-view.js.map +1 -0
- package/build-module/filters.js +12 -18
- package/build-module/filters.js.map +1 -1
- package/build-module/index.js +1 -1
- package/build-module/index.js.map +1 -1
- package/build-module/normalize-fields.js +19 -0
- package/build-module/normalize-fields.js.map +1 -0
- package/build-module/utils.js +0 -63
- package/build-module/utils.js.map +1 -1
- package/build-module/view-grid.js +22 -12
- package/build-module/view-grid.js.map +1 -1
- package/build-module/view-list.js +124 -60
- package/build-module/view-list.js.map +1 -1
- package/build-module/view-table.js +28 -14
- package/build-module/view-table.js.map +1 -1
- package/build-style/style-rtl.css +32 -8
- package/build-style/style.css +32 -8
- package/package.json +11 -11
- package/src/constants.js +0 -3
- package/src/dataviews.js +2 -12
- package/src/filter-and-sort-data-view.js +154 -0
- package/src/filters.js +20 -32
- package/src/index.js +1 -1
- package/src/normalize-fields.js +17 -0
- package/src/stories/fixtures.js +75 -1
- package/src/stories/index.story.js +5 -113
- package/src/style.scss +44 -11
- package/src/test/filter-and-sort-data-view.js +276 -0
- package/src/utils.js +0 -52
- package/src/view-grid.js +32 -10
- package/src/view-list.js +159 -69
- package/src/view-table.js +29 -13
|
@@ -7,13 +7,9 @@ import { useState, useMemo, useCallback } from '@wordpress/element';
|
|
|
7
7
|
* Internal dependencies
|
|
8
8
|
*/
|
|
9
9
|
import { DataViews } from '../index';
|
|
10
|
-
import { DEFAULT_VIEW, actions, data } from './fixtures';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
LAYOUT_TABLE,
|
|
14
|
-
OPERATOR_IS_NONE,
|
|
15
|
-
OPERATOR_IS_ANY,
|
|
16
|
-
} from '../constants';
|
|
10
|
+
import { DEFAULT_VIEW, actions, data, fields } from './fixtures';
|
|
11
|
+
import { LAYOUT_GRID, LAYOUT_TABLE } from '../constants';
|
|
12
|
+
import { filterSortAndPaginate } from '../filter-and-sort-data-view';
|
|
17
13
|
|
|
18
14
|
const meta = {
|
|
19
15
|
title: 'DataViews/DataViews',
|
|
@@ -31,114 +27,10 @@ const defaultConfigPerViewType = {
|
|
|
31
27
|
},
|
|
32
28
|
};
|
|
33
29
|
|
|
34
|
-
function normalizeSearchInput( input = '' ) {
|
|
35
|
-
return input.trim().toLowerCase();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const fields = [
|
|
39
|
-
{
|
|
40
|
-
header: 'Image',
|
|
41
|
-
id: 'image',
|
|
42
|
-
render: ( { item } ) => {
|
|
43
|
-
return (
|
|
44
|
-
<img src={ item.image } alt="" style={ { width: '100%' } } />
|
|
45
|
-
);
|
|
46
|
-
},
|
|
47
|
-
width: 50,
|
|
48
|
-
enableSorting: false,
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
header: 'Title',
|
|
52
|
-
id: 'title',
|
|
53
|
-
maxWidth: 400,
|
|
54
|
-
enableHiding: false,
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
header: 'Type',
|
|
58
|
-
id: 'type',
|
|
59
|
-
maxWidth: 400,
|
|
60
|
-
enableHiding: false,
|
|
61
|
-
type: 'enumeration',
|
|
62
|
-
elements: [
|
|
63
|
-
{ value: 'Not a planet', label: 'Not a planet' },
|
|
64
|
-
{ value: 'Ice giant', label: 'Ice giant' },
|
|
65
|
-
{ value: 'Terrestrial', label: 'Terrestrial' },
|
|
66
|
-
{ value: 'Gas giant', label: 'Gas giant' },
|
|
67
|
-
],
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
header: 'Description',
|
|
71
|
-
id: 'description',
|
|
72
|
-
maxWidth: 200,
|
|
73
|
-
enableSorting: false,
|
|
74
|
-
},
|
|
75
|
-
];
|
|
76
|
-
|
|
77
30
|
export const Default = ( props ) => {
|
|
78
31
|
const [ view, setView ] = useState( DEFAULT_VIEW );
|
|
79
|
-
const { shownData, paginationInfo } = useMemo( () => {
|
|
80
|
-
|
|
81
|
-
// Handle global search.
|
|
82
|
-
if ( view.search ) {
|
|
83
|
-
const normalizedSearch = normalizeSearchInput( view.search );
|
|
84
|
-
filteredData = filteredData.filter( ( item ) => {
|
|
85
|
-
return [
|
|
86
|
-
normalizeSearchInput( item.title ),
|
|
87
|
-
normalizeSearchInput( item.description ),
|
|
88
|
-
].some( ( field ) => field.includes( normalizedSearch ) );
|
|
89
|
-
} );
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if ( view.filters.length > 0 ) {
|
|
93
|
-
view.filters.forEach( ( filter ) => {
|
|
94
|
-
if (
|
|
95
|
-
filter.field === 'type' &&
|
|
96
|
-
filter.operator === OPERATOR_IS_ANY &&
|
|
97
|
-
filter?.value?.length > 0
|
|
98
|
-
) {
|
|
99
|
-
filteredData = filteredData.filter( ( item ) => {
|
|
100
|
-
return filter.value.includes( item.type );
|
|
101
|
-
} );
|
|
102
|
-
} else if (
|
|
103
|
-
filter.field === 'type' &&
|
|
104
|
-
filter.operator === OPERATOR_IS_NONE &&
|
|
105
|
-
filter?.value?.length > 0
|
|
106
|
-
) {
|
|
107
|
-
filteredData = filteredData.filter( ( item ) => {
|
|
108
|
-
return ! filter.value.includes( item.type );
|
|
109
|
-
} );
|
|
110
|
-
}
|
|
111
|
-
} );
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Handle sorting.
|
|
115
|
-
if ( view.sort ) {
|
|
116
|
-
const stringSortingFields = [ 'title' ];
|
|
117
|
-
const fieldId = view.sort.field;
|
|
118
|
-
if ( stringSortingFields.includes( fieldId ) ) {
|
|
119
|
-
const fieldToSort = fields.find( ( field ) => {
|
|
120
|
-
return field.id === fieldId;
|
|
121
|
-
} );
|
|
122
|
-
filteredData.sort( ( a, b ) => {
|
|
123
|
-
const valueA = fieldToSort.getValue( { item: a } ) ?? '';
|
|
124
|
-
const valueB = fieldToSort.getValue( { item: b } ) ?? '';
|
|
125
|
-
return view.sort.direction === 'asc'
|
|
126
|
-
? valueA.localeCompare( valueB )
|
|
127
|
-
: valueB.localeCompare( valueA );
|
|
128
|
-
} );
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
// Handle pagination.
|
|
132
|
-
const start = ( view.page - 1 ) * view.perPage;
|
|
133
|
-
const totalItems = filteredData?.length || 0;
|
|
134
|
-
filteredData = filteredData?.slice( start, start + view.perPage );
|
|
135
|
-
return {
|
|
136
|
-
shownData: filteredData,
|
|
137
|
-
paginationInfo: {
|
|
138
|
-
totalItems,
|
|
139
|
-
totalPages: Math.ceil( totalItems / view.perPage ),
|
|
140
|
-
},
|
|
141
|
-
};
|
|
32
|
+
const { data: shownData, paginationInfo } = useMemo( () => {
|
|
33
|
+
return filterSortAndPaginate( data, view, fields );
|
|
142
34
|
}, [ view ] );
|
|
143
35
|
const onChangeView = useCallback(
|
|
144
36
|
( newView ) => {
|
package/src/style.scss
CHANGED
|
@@ -190,6 +190,12 @@
|
|
|
190
190
|
> * {
|
|
191
191
|
flex-grow: 1;
|
|
192
192
|
}
|
|
193
|
+
|
|
194
|
+
&.dataviews-view-table__primary-field {
|
|
195
|
+
a {
|
|
196
|
+
flex-grow: 0;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
193
199
|
}
|
|
194
200
|
}
|
|
195
201
|
.dataviews-view-table-header-button {
|
|
@@ -244,10 +250,10 @@
|
|
|
244
250
|
white-space: nowrap;
|
|
245
251
|
overflow: hidden;
|
|
246
252
|
display: block;
|
|
247
|
-
|
|
253
|
+
flex-grow: 0;
|
|
248
254
|
|
|
249
255
|
&:hover {
|
|
250
|
-
color:
|
|
256
|
+
color: var(--wp-admin-theme-color);
|
|
251
257
|
}
|
|
252
258
|
@include link-reset();
|
|
253
259
|
}
|
|
@@ -327,7 +333,24 @@
|
|
|
327
333
|
}
|
|
328
334
|
|
|
329
335
|
.dataviews-view-grid__field {
|
|
330
|
-
|
|
336
|
+
align-items: flex-start;
|
|
337
|
+
|
|
338
|
+
&:not(.is-column) {
|
|
339
|
+
align-items: center;
|
|
340
|
+
|
|
341
|
+
.dataviews-view-grid__field-name {
|
|
342
|
+
width: 35%;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.dataviews-view-grid__field-value {
|
|
346
|
+
width: 65%;
|
|
347
|
+
overflow: hidden;
|
|
348
|
+
text-overflow: ellipsis;
|
|
349
|
+
white-space: nowrap;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.dataviews-view-grid__field-name {
|
|
331
354
|
color: $gray-700;
|
|
332
355
|
}
|
|
333
356
|
}
|
|
@@ -340,6 +363,7 @@
|
|
|
340
363
|
|
|
341
364
|
li {
|
|
342
365
|
margin: 0;
|
|
366
|
+
cursor: pointer;
|
|
343
367
|
|
|
344
368
|
.dataviews-view-list__item-wrapper {
|
|
345
369
|
position: relative;
|
|
@@ -355,14 +379,21 @@
|
|
|
355
379
|
background: $gray-100;
|
|
356
380
|
height: 1px;
|
|
357
381
|
}
|
|
358
|
-
}
|
|
359
382
|
|
|
360
|
-
|
|
361
|
-
|
|
383
|
+
> * {
|
|
384
|
+
width: 100%;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
362
387
|
|
|
363
|
-
|
|
364
|
-
|
|
388
|
+
&:not(.is-selected) {
|
|
389
|
+
&:hover,
|
|
390
|
+
&:focus-within {
|
|
365
391
|
color: var(--wp-admin-theme-color);
|
|
392
|
+
|
|
393
|
+
.dataviews-view-list__primary-field,
|
|
394
|
+
.dataviews-view-list__fields {
|
|
395
|
+
color: var(--wp-admin-theme-color);
|
|
396
|
+
}
|
|
366
397
|
}
|
|
367
398
|
}
|
|
368
399
|
}
|
|
@@ -388,8 +419,9 @@
|
|
|
388
419
|
.dataviews-view-list__item {
|
|
389
420
|
padding: $grid-unit-15 0 $grid-unit-15 $grid-unit-30;
|
|
390
421
|
width: 100%;
|
|
391
|
-
|
|
392
|
-
|
|
422
|
+
scroll-margin: $grid-unit-10 0;
|
|
423
|
+
|
|
424
|
+
&:focus-visible {
|
|
393
425
|
&::before {
|
|
394
426
|
position: absolute;
|
|
395
427
|
content: "";
|
|
@@ -404,6 +436,7 @@
|
|
|
404
436
|
}
|
|
405
437
|
.dataviews-view-list__primary-field {
|
|
406
438
|
min-height: $grid-unit-05 * 5;
|
|
439
|
+
overflow: hidden;
|
|
407
440
|
}
|
|
408
441
|
}
|
|
409
442
|
|
|
@@ -449,7 +482,7 @@
|
|
|
449
482
|
line-height: $grid-unit-20;
|
|
450
483
|
|
|
451
484
|
.dataviews-view-list__field {
|
|
452
|
-
&:empty {
|
|
485
|
+
&:has(.dataviews-view-list__field-value:empty) {
|
|
453
486
|
display: none;
|
|
454
487
|
}
|
|
455
488
|
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { filterSortAndPaginate } from '../filter-and-sort-data-view';
|
|
5
|
+
import { data, fields } from '../stories/fixtures';
|
|
6
|
+
|
|
7
|
+
describe( 'filters', () => {
|
|
8
|
+
it( 'should return empty if the data is empty', () => {
|
|
9
|
+
expect( filterSortAndPaginate( null, {}, [] ) ).toStrictEqual( {
|
|
10
|
+
data: [],
|
|
11
|
+
paginationInfo: { totalItems: 0, totalPages: 0 },
|
|
12
|
+
} );
|
|
13
|
+
} );
|
|
14
|
+
|
|
15
|
+
it( 'should return the same data if no filters are applied', () => {
|
|
16
|
+
expect(
|
|
17
|
+
filterSortAndPaginate(
|
|
18
|
+
data,
|
|
19
|
+
{
|
|
20
|
+
filters: [],
|
|
21
|
+
},
|
|
22
|
+
[]
|
|
23
|
+
)
|
|
24
|
+
).toStrictEqual( {
|
|
25
|
+
data,
|
|
26
|
+
paginationInfo: { totalItems: data.length, totalPages: 1 },
|
|
27
|
+
} );
|
|
28
|
+
} );
|
|
29
|
+
|
|
30
|
+
it( 'should search using searchable fields (title)', () => {
|
|
31
|
+
const { data: result } = filterSortAndPaginate(
|
|
32
|
+
data,
|
|
33
|
+
{
|
|
34
|
+
search: 'Neptu',
|
|
35
|
+
filters: [],
|
|
36
|
+
},
|
|
37
|
+
fields
|
|
38
|
+
);
|
|
39
|
+
expect( result ).toHaveLength( 1 );
|
|
40
|
+
expect( result[ 0 ].title ).toBe( 'Neptune' );
|
|
41
|
+
} );
|
|
42
|
+
|
|
43
|
+
it( 'should search using searchable fields (description)', () => {
|
|
44
|
+
const { data: result } = filterSortAndPaginate(
|
|
45
|
+
data,
|
|
46
|
+
{
|
|
47
|
+
search: 'photo',
|
|
48
|
+
filters: [],
|
|
49
|
+
},
|
|
50
|
+
fields
|
|
51
|
+
);
|
|
52
|
+
expect( result ).toHaveLength( 1 );
|
|
53
|
+
expect( result[ 0 ].description ).toBe( 'NASA photo' );
|
|
54
|
+
} );
|
|
55
|
+
|
|
56
|
+
it( 'should perform case-insensitive and accent-insensitive search', () => {
|
|
57
|
+
const { data: result } = filterSortAndPaginate(
|
|
58
|
+
data,
|
|
59
|
+
{
|
|
60
|
+
search: 'nete ven',
|
|
61
|
+
filters: [],
|
|
62
|
+
},
|
|
63
|
+
fields
|
|
64
|
+
);
|
|
65
|
+
expect( result ).toHaveLength( 1 );
|
|
66
|
+
expect( result[ 0 ].description ).toBe( 'La planète Vénus' );
|
|
67
|
+
} );
|
|
68
|
+
|
|
69
|
+
it( 'should search using IS filter', () => {
|
|
70
|
+
const { data: result } = filterSortAndPaginate(
|
|
71
|
+
data,
|
|
72
|
+
{
|
|
73
|
+
filters: [
|
|
74
|
+
{
|
|
75
|
+
field: 'type',
|
|
76
|
+
operator: 'is',
|
|
77
|
+
value: 'Ice giant',
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
fields
|
|
82
|
+
);
|
|
83
|
+
expect( result ).toHaveLength( 2 );
|
|
84
|
+
expect( result[ 0 ].title ).toBe( 'Neptune' );
|
|
85
|
+
expect( result[ 1 ].title ).toBe( 'Uranus' );
|
|
86
|
+
} );
|
|
87
|
+
|
|
88
|
+
it( 'should search using IS NOT filter', () => {
|
|
89
|
+
const { data: result } = filterSortAndPaginate(
|
|
90
|
+
data,
|
|
91
|
+
{
|
|
92
|
+
filters: [
|
|
93
|
+
{
|
|
94
|
+
field: 'type',
|
|
95
|
+
operator: 'isNot',
|
|
96
|
+
value: 'Ice giant',
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
fields
|
|
101
|
+
);
|
|
102
|
+
expect( result ).toHaveLength( 9 );
|
|
103
|
+
expect( result[ 0 ].title ).toBe( 'Apollo' );
|
|
104
|
+
expect( result[ 1 ].title ).toBe( 'Space' );
|
|
105
|
+
expect( result[ 2 ].title ).toBe( 'NASA' );
|
|
106
|
+
expect( result[ 3 ].title ).toBe( 'Mercury' );
|
|
107
|
+
expect( result[ 4 ].title ).toBe( 'Venus' );
|
|
108
|
+
expect( result[ 5 ].title ).toBe( 'Earth' );
|
|
109
|
+
expect( result[ 6 ].title ).toBe( 'Mars' );
|
|
110
|
+
expect( result[ 7 ].title ).toBe( 'Jupiter' );
|
|
111
|
+
expect( result[ 8 ].title ).toBe( 'Saturn' );
|
|
112
|
+
} );
|
|
113
|
+
|
|
114
|
+
it( 'should search using IS ANY filter for STRING values', () => {
|
|
115
|
+
const { data: result } = filterSortAndPaginate(
|
|
116
|
+
data,
|
|
117
|
+
{
|
|
118
|
+
filters: [
|
|
119
|
+
{
|
|
120
|
+
field: 'type',
|
|
121
|
+
operator: 'isAny',
|
|
122
|
+
value: [ 'Ice giant' ],
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
fields
|
|
127
|
+
);
|
|
128
|
+
expect( result ).toHaveLength( 2 );
|
|
129
|
+
expect( result[ 0 ].title ).toBe( 'Neptune' );
|
|
130
|
+
expect( result[ 1 ].title ).toBe( 'Uranus' );
|
|
131
|
+
} );
|
|
132
|
+
|
|
133
|
+
it( 'should search using IS NONE filter for STRING values', () => {
|
|
134
|
+
const { data: result } = filterSortAndPaginate(
|
|
135
|
+
data,
|
|
136
|
+
{
|
|
137
|
+
filters: [
|
|
138
|
+
{
|
|
139
|
+
field: 'type',
|
|
140
|
+
operator: 'isNone',
|
|
141
|
+
value: [ 'Ice giant', 'Gas giant', 'Terrestrial' ],
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
fields
|
|
146
|
+
);
|
|
147
|
+
expect( result ).toHaveLength( 3 );
|
|
148
|
+
expect( result[ 0 ].title ).toBe( 'Apollo' );
|
|
149
|
+
expect( result[ 1 ].title ).toBe( 'Space' );
|
|
150
|
+
expect( result[ 2 ].title ).toBe( 'NASA' );
|
|
151
|
+
} );
|
|
152
|
+
|
|
153
|
+
it( 'should search using IS ANY filter for ARRAY values', () => {
|
|
154
|
+
const { data: result } = filterSortAndPaginate(
|
|
155
|
+
data,
|
|
156
|
+
{
|
|
157
|
+
filters: [
|
|
158
|
+
{
|
|
159
|
+
field: 'categories',
|
|
160
|
+
operator: 'isAny',
|
|
161
|
+
value: [ 'NASA' ],
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
fields
|
|
166
|
+
);
|
|
167
|
+
expect( result ).toHaveLength( 2 );
|
|
168
|
+
expect( result[ 0 ].title ).toBe( 'Apollo' );
|
|
169
|
+
expect( result[ 1 ].title ).toBe( 'NASA' );
|
|
170
|
+
} );
|
|
171
|
+
|
|
172
|
+
it( 'should search using IS NONE filter for ARRAY values', () => {
|
|
173
|
+
const { data: result } = filterSortAndPaginate(
|
|
174
|
+
data,
|
|
175
|
+
{
|
|
176
|
+
filters: [
|
|
177
|
+
{
|
|
178
|
+
field: 'categories',
|
|
179
|
+
operator: 'isNone',
|
|
180
|
+
value: [ 'Space' ],
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
fields
|
|
185
|
+
);
|
|
186
|
+
expect( result ).toHaveLength( 1 );
|
|
187
|
+
expect( result[ 0 ].title ).toBe( 'NASA' );
|
|
188
|
+
} );
|
|
189
|
+
|
|
190
|
+
it( 'should search using IS ALL filter', () => {
|
|
191
|
+
const { data: result } = filterSortAndPaginate(
|
|
192
|
+
data,
|
|
193
|
+
{
|
|
194
|
+
filters: [
|
|
195
|
+
{
|
|
196
|
+
field: 'categories',
|
|
197
|
+
operator: 'isAll',
|
|
198
|
+
value: [ 'Planet', 'Solar system' ],
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
fields
|
|
203
|
+
);
|
|
204
|
+
expect( result ).toHaveLength( 7 );
|
|
205
|
+
expect( result[ 0 ].title ).toBe( 'Neptune' );
|
|
206
|
+
expect( result[ 1 ].title ).toBe( 'Mercury' );
|
|
207
|
+
expect( result[ 2 ].title ).toBe( 'Venus' );
|
|
208
|
+
expect( result[ 3 ].title ).toBe( 'Earth' );
|
|
209
|
+
expect( result[ 4 ].title ).toBe( 'Mars' );
|
|
210
|
+
expect( result[ 5 ].title ).toBe( 'Jupiter' );
|
|
211
|
+
expect( result[ 6 ].title ).toBe( 'Saturn' );
|
|
212
|
+
} );
|
|
213
|
+
|
|
214
|
+
it( 'should search using IS NOT ALL filter', () => {
|
|
215
|
+
const { data: result } = filterSortAndPaginate(
|
|
216
|
+
data,
|
|
217
|
+
{
|
|
218
|
+
filters: [
|
|
219
|
+
{
|
|
220
|
+
field: 'categories',
|
|
221
|
+
operator: 'isNotAll',
|
|
222
|
+
value: [ 'Planet', 'Solar system' ],
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
fields
|
|
227
|
+
);
|
|
228
|
+
expect( result ).toHaveLength( 3 );
|
|
229
|
+
expect( result[ 0 ].title ).toBe( 'Apollo' );
|
|
230
|
+
expect( result[ 1 ].title ).toBe( 'Space' );
|
|
231
|
+
expect( result[ 2 ].title ).toBe( 'NASA' );
|
|
232
|
+
} );
|
|
233
|
+
} );
|
|
234
|
+
|
|
235
|
+
describe( 'sorting', () => {
|
|
236
|
+
it( 'should sort', () => {
|
|
237
|
+
const { data: result } = filterSortAndPaginate(
|
|
238
|
+
data,
|
|
239
|
+
{
|
|
240
|
+
sort: { field: 'title', direction: 'desc' },
|
|
241
|
+
filters: [
|
|
242
|
+
{
|
|
243
|
+
field: 'type',
|
|
244
|
+
operator: 'isAny',
|
|
245
|
+
value: [ 'Ice giant' ],
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
},
|
|
249
|
+
fields
|
|
250
|
+
);
|
|
251
|
+
expect( result ).toHaveLength( 2 );
|
|
252
|
+
expect( result[ 0 ].title ).toBe( 'Uranus' );
|
|
253
|
+
expect( result[ 1 ].title ).toBe( 'Neptune' );
|
|
254
|
+
} );
|
|
255
|
+
} );
|
|
256
|
+
|
|
257
|
+
describe( 'pagination', () => {
|
|
258
|
+
it( 'should paginate', () => {
|
|
259
|
+
const { data: result, paginationInfo } = filterSortAndPaginate(
|
|
260
|
+
data,
|
|
261
|
+
{
|
|
262
|
+
perPage: 2,
|
|
263
|
+
page: 2,
|
|
264
|
+
filters: [],
|
|
265
|
+
},
|
|
266
|
+
fields
|
|
267
|
+
);
|
|
268
|
+
expect( result ).toHaveLength( 2 );
|
|
269
|
+
expect( result[ 0 ].title ).toBe( 'NASA' );
|
|
270
|
+
expect( result[ 1 ].title ).toBe( 'Neptune' );
|
|
271
|
+
expect( paginationInfo ).toStrictEqual( {
|
|
272
|
+
totalItems: data.length,
|
|
273
|
+
totalPages: 6,
|
|
274
|
+
} );
|
|
275
|
+
} );
|
|
276
|
+
} );
|
package/src/utils.js
CHANGED
|
@@ -9,58 +9,6 @@ import {
|
|
|
9
9
|
OPERATOR_IS_NONE,
|
|
10
10
|
} from './constants';
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* Helper util to sort data by text fields, when sorting is done client side.
|
|
14
|
-
*
|
|
15
|
-
* @param {Object} params Function params.
|
|
16
|
-
* @param {Object[]} params.data Data to sort.
|
|
17
|
-
* @param {Object} params.view Current view object.
|
|
18
|
-
* @param {Object[]} params.fields Array of available fields.
|
|
19
|
-
* @param {string[]} params.textFields Array of the field ids to sort.
|
|
20
|
-
*
|
|
21
|
-
* @return {Object[]} Sorted data.
|
|
22
|
-
*/
|
|
23
|
-
export const sortByTextFields = ( { data, view, fields, textFields } ) => {
|
|
24
|
-
const sortedData = [ ...data ];
|
|
25
|
-
const fieldId = view.sort.field;
|
|
26
|
-
if ( textFields.includes( fieldId ) ) {
|
|
27
|
-
const fieldToSort = fields.find( ( field ) => {
|
|
28
|
-
return field.id === fieldId;
|
|
29
|
-
} );
|
|
30
|
-
sortedData.sort( ( a, b ) => {
|
|
31
|
-
const valueA = fieldToSort.getValue( { item: a } ) ?? '';
|
|
32
|
-
const valueB = fieldToSort.getValue( { item: b } ) ?? '';
|
|
33
|
-
return view.sort.direction === 'asc'
|
|
34
|
-
? valueA.localeCompare( valueB )
|
|
35
|
-
: valueB.localeCompare( valueA );
|
|
36
|
-
} );
|
|
37
|
-
}
|
|
38
|
-
return sortedData;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Helper util to get the paginated data and the paginateInfo needed,
|
|
43
|
-
* when pagination is done client side.
|
|
44
|
-
*
|
|
45
|
-
* @param {Object} params Function params.
|
|
46
|
-
* @param {Object[]} params.data Available data.
|
|
47
|
-
* @param {Object} params.view Current view object.
|
|
48
|
-
*
|
|
49
|
-
* @return {Object} Paginated data and paginationInfo.
|
|
50
|
-
*/
|
|
51
|
-
export function getPaginationResults( { data, view } ) {
|
|
52
|
-
const start = ( view.page - 1 ) * view.perPage;
|
|
53
|
-
const totalItems = data?.length || 0;
|
|
54
|
-
data = data?.slice( start, start + view.perPage );
|
|
55
|
-
return {
|
|
56
|
-
data,
|
|
57
|
-
paginationInfo: {
|
|
58
|
-
totalItems,
|
|
59
|
-
totalPages: Math.ceil( totalItems / view.perPage ),
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
12
|
export const sanitizeOperators = ( field ) => {
|
|
65
13
|
let operators = field.filterBy?.operators;
|
|
66
14
|
|
package/src/view-grid.js
CHANGED
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
__experimentalGrid as Grid,
|
|
11
11
|
__experimentalHStack as HStack,
|
|
12
12
|
__experimentalVStack as VStack,
|
|
13
|
-
Tooltip,
|
|
14
13
|
Spinner,
|
|
14
|
+
Flex,
|
|
15
|
+
FlexItem,
|
|
15
16
|
} from '@wordpress/components';
|
|
16
17
|
import { __ } from '@wordpress/i18n';
|
|
17
18
|
import { useAsyncList } from '@wordpress/compose';
|
|
@@ -34,6 +35,7 @@ function GridItem( {
|
|
|
34
35
|
mediaField,
|
|
35
36
|
primaryField,
|
|
36
37
|
visibleFields,
|
|
38
|
+
displayAsColumnFields,
|
|
37
39
|
} ) {
|
|
38
40
|
const hasBulkAction = useHasAPossibleBulkAction( actions, item );
|
|
39
41
|
const id = getItemId( item );
|
|
@@ -107,17 +109,34 @@ function GridItem( {
|
|
|
107
109
|
return null;
|
|
108
110
|
}
|
|
109
111
|
return (
|
|
110
|
-
<
|
|
111
|
-
className=
|
|
112
|
+
<Flex
|
|
113
|
+
className={ classnames(
|
|
114
|
+
'dataviews-view-grid__field',
|
|
115
|
+
displayAsColumnFields?.includes( field.id )
|
|
116
|
+
? 'is-column'
|
|
117
|
+
: 'is-row'
|
|
118
|
+
) }
|
|
112
119
|
key={ field.id }
|
|
113
|
-
|
|
120
|
+
gap={ 1 }
|
|
121
|
+
justify="flex-start"
|
|
122
|
+
expanded
|
|
123
|
+
style={ { height: 'auto' } }
|
|
124
|
+
direction={
|
|
125
|
+
displayAsColumnFields?.includes( field.id )
|
|
126
|
+
? 'column'
|
|
127
|
+
: 'row'
|
|
128
|
+
}
|
|
114
129
|
>
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
<FlexItem className="dataviews-view-grid__field-name">
|
|
131
|
+
{ field.header }
|
|
132
|
+
</FlexItem>
|
|
133
|
+
<FlexItem
|
|
134
|
+
className="dataviews-view-grid__field-value"
|
|
135
|
+
style={ { maxHeight: 'none' } }
|
|
136
|
+
>
|
|
137
|
+
{ renderedValue }
|
|
138
|
+
</FlexItem>
|
|
139
|
+
</Flex>
|
|
121
140
|
);
|
|
122
141
|
} ) }
|
|
123
142
|
</VStack>
|
|
@@ -175,6 +194,9 @@ export default function ViewGrid( {
|
|
|
175
194
|
mediaField={ mediaField }
|
|
176
195
|
primaryField={ primaryField }
|
|
177
196
|
visibleFields={ visibleFields }
|
|
197
|
+
displayAsColumnFields={
|
|
198
|
+
view.layout.displayAsColumnFields
|
|
199
|
+
}
|
|
178
200
|
/>
|
|
179
201
|
);
|
|
180
202
|
} ) }
|