@wordpress/dataviews 0.8.0 → 1.0.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 +16 -0
- package/README.md +3 -13
- package/build/add-filter.js.map +1 -1
- package/build/bulk-actions.js.map +1 -1
- package/build/constants.js +1 -4
- package/build/constants.js.map +1 -1
- package/build/dataviews.js +3 -17
- package/build/dataviews.js.map +1 -1
- package/build/dropdown-menu-helper.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/filter-summary.js +4 -2
- package/build/filter-summary.js.map +1 -1
- 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/item-actions.js.map +1 -1
- package/build/lock-unlock.js.map +1 -1
- package/build/normalize-fields.js +25 -0
- package/build/normalize-fields.js.map +1 -0
- package/build/pagination.js.map +1 -1
- package/build/reset-filters.js.map +1 -1
- package/build/search-widget.js +5 -4
- package/build/search-widget.js.map +1 -1
- package/build/search.js.map +1 -1
- package/build/single-selection-checkbox.js +1 -1
- package/build/single-selection-checkbox.js.map +1 -1
- package/build/utils.js +1 -65
- package/build/utils.js.map +1 -1
- package/build/view-actions.js.map +1 -1
- package/build/view-grid.js +57 -19
- package/build/view-grid.js.map +1 -1
- package/build/view-list.js +112 -66
- package/build/view-list.js.map +1 -1
- package/build/view-table.js +32 -24
- package/build/view-table.js.map +1 -1
- package/build-module/add-filter.js.map +1 -1
- package/build-module/bulk-actions.js.map +1 -1
- package/build-module/constants.js +0 -3
- package/build-module/constants.js.map +1 -1
- package/build-module/dataviews.js +3 -17
- package/build-module/dataviews.js.map +1 -1
- package/build-module/dropdown-menu-helper.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/filter-summary.js +3 -2
- package/build-module/filter-summary.js.map +1 -1
- 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/item-actions.js.map +1 -1
- package/build-module/lock-unlock.js.map +1 -1
- package/build-module/normalize-fields.js +19 -0
- package/build-module/normalize-fields.js.map +1 -0
- package/build-module/pagination.js.map +1 -1
- package/build-module/reset-filters.js.map +1 -1
- package/build-module/search-widget.js +4 -3
- package/build-module/search-widget.js.map +1 -1
- package/build-module/search.js.map +1 -1
- package/build-module/single-selection-checkbox.js +1 -1
- package/build-module/single-selection-checkbox.js.map +1 -1
- package/build-module/utils.js +0 -63
- package/build-module/utils.js.map +1 -1
- package/build-module/view-actions.js.map +1 -1
- package/build-module/view-grid.js +58 -20
- package/build-module/view-grid.js.map +1 -1
- package/build-module/view-list.js +114 -68
- package/build-module/view-list.js.map +1 -1
- package/build-module/view-table.js +33 -25
- package/build-module/view-table.js.map +1 -1
- package/build-style/style-rtl.css +75 -39
- package/build-style/style.css +75 -39
- package/package.json +11 -11
- package/src/constants.js +0 -3
- package/src/dataviews.js +2 -16
- package/src/filter-and-sort-data-view.js +154 -0
- package/src/filter-summary.js +4 -4
- package/src/filters.js +20 -32
- package/src/index.js +1 -1
- package/src/normalize-fields.js +17 -0
- package/src/search-widget.js +4 -3
- package/src/single-selection-checkbox.js +1 -1
- package/src/stories/fixtures.js +75 -1
- package/src/stories/index.story.js +5 -113
- package/src/style.scss +89 -49
- package/src/test/filter-and-sort-data-view.js +276 -0
- package/src/utils.js +0 -52
- package/src/view-grid.js +97 -36
- package/src/view-list.js +147 -77
- package/src/view-table.js +36 -24
|
@@ -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
|
+
}
|
package/src/filter-summary.js
CHANGED
|
@@ -18,7 +18,9 @@ import {
|
|
|
18
18
|
import { __, sprintf } from '@wordpress/i18n';
|
|
19
19
|
import { useRef, createInterpolateElement } from '@wordpress/element';
|
|
20
20
|
import { closeSmall } from '@wordpress/icons';
|
|
21
|
-
|
|
21
|
+
|
|
22
|
+
const ENTER = 'Enter';
|
|
23
|
+
const SPACE = ' ';
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Internal dependencies
|
|
@@ -229,9 +231,7 @@ export default function FilterSummary( {
|
|
|
229
231
|
tabIndex={ 0 }
|
|
230
232
|
onClick={ onToggle }
|
|
231
233
|
onKeyDown={ ( event ) => {
|
|
232
|
-
if (
|
|
233
|
-
[ ENTER, SPACE ].includes( event.keyCode )
|
|
234
|
-
) {
|
|
234
|
+
if ( [ ENTER, SPACE ].includes( event.key ) ) {
|
|
235
235
|
onToggle();
|
|
236
236
|
event.preventDefault();
|
|
237
237
|
}
|
package/src/filters.js
CHANGED
|
@@ -10,12 +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 {
|
|
14
|
-
ENUMERATION_TYPE,
|
|
15
|
-
ALL_OPERATORS,
|
|
16
|
-
OPERATOR_IS,
|
|
17
|
-
OPERATOR_IS_NOT,
|
|
18
|
-
} from './constants';
|
|
13
|
+
import { ALL_OPERATORS, OPERATOR_IS, OPERATOR_IS_NOT } from './constants';
|
|
19
14
|
import { __experimentalHStack as HStack } from '@wordpress/components';
|
|
20
15
|
|
|
21
16
|
const Filters = memo( function Filters( {
|
|
@@ -28,7 +23,7 @@ const Filters = memo( function Filters( {
|
|
|
28
23
|
const addFilterRef = useRef();
|
|
29
24
|
const filters = [];
|
|
30
25
|
fields.forEach( ( field ) => {
|
|
31
|
-
if ( ! field.
|
|
26
|
+
if ( ! field.elements?.length ) {
|
|
32
27
|
return;
|
|
33
28
|
}
|
|
34
29
|
|
|
@@ -37,31 +32,24 @@ const Filters = memo( function Filters( {
|
|
|
37
32
|
return;
|
|
38
33
|
}
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
( f ) =>
|
|
59
|
-
f.field === field.id &&
|
|
60
|
-
ALL_OPERATORS.includes( f.operator )
|
|
61
|
-
),
|
|
62
|
-
isPrimary,
|
|
63
|
-
} );
|
|
64
|
-
}
|
|
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
|
+
} );
|
|
65
53
|
} );
|
|
66
54
|
// Sort filters by primary property. We need the primary filters to be first.
|
|
67
55
|
// Then we sort by name.
|
package/src/index.js
CHANGED
|
@@ -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/search-widget.js
CHANGED
|
@@ -39,6 +39,7 @@ function normalizeSearchInput( input = '' ) {
|
|
|
39
39
|
return removeAccents( input.trim().toLowerCase() );
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
const EMPTY_ARRAY = [];
|
|
42
43
|
const getCurrentValue = ( filterDefinition, currentFilter ) => {
|
|
43
44
|
if ( filterDefinition.singleSelection ) {
|
|
44
45
|
return currentFilter?.value;
|
|
@@ -52,7 +53,7 @@ const getCurrentValue = ( filterDefinition, currentFilter ) => {
|
|
|
52
53
|
return [ currentFilter.value ];
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
return
|
|
56
|
+
return EMPTY_ARRAY;
|
|
56
57
|
};
|
|
57
58
|
|
|
58
59
|
const getNewValue = ( filterDefinition, currentFilter, value ) => {
|
|
@@ -73,7 +74,7 @@ function ListBox( { view, filter, onChangeView } ) {
|
|
|
73
74
|
const compositeStore = useCompositeStore( {
|
|
74
75
|
virtualFocus: true,
|
|
75
76
|
focusLoop: true,
|
|
76
|
-
// When we have no or just one
|
|
77
|
+
// When we have no or just one operator, we can set the first item as active.
|
|
77
78
|
// We do that by passing `undefined` to `defaultActiveId`. Otherwise, we set it to `null`,
|
|
78
79
|
// so the first item is not selected, since the focus is on the operators control.
|
|
79
80
|
defaultActiveId: filter.operators?.length === 1 ? undefined : null,
|
|
@@ -198,7 +199,7 @@ function ComboboxList( { view, filter, onChangeView } ) {
|
|
|
198
199
|
}, [ filter.elements, deferredSearchValue ] );
|
|
199
200
|
return (
|
|
200
201
|
<Ariakit.ComboboxProvider
|
|
201
|
-
|
|
202
|
+
resetValueOnSelect={ false }
|
|
202
203
|
selectedValue={ currentValue }
|
|
203
204
|
setSelectedValue={ ( value ) => {
|
|
204
205
|
const newFilters = currentFilter
|
|
@@ -32,7 +32,7 @@ export default function SingleSelectionCheckbox( {
|
|
|
32
32
|
<CheckboxControl
|
|
33
33
|
className="dataviews-view-table-selection-checkbox"
|
|
34
34
|
__nextHasNoMarginBottom
|
|
35
|
-
label={ selectionLabel }
|
|
35
|
+
aria-label={ selectionLabel }
|
|
36
36
|
aria-disabled={ disabled }
|
|
37
37
|
checked={ isSelected }
|
|
38
38
|
onChange={ () => {
|
package/src/stories/fixtures.js
CHANGED
|
@@ -21,6 +21,7 @@ export const data = [
|
|
|
21
21
|
description: 'Apollo description',
|
|
22
22
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
23
23
|
type: 'Not a planet',
|
|
24
|
+
categories: [ 'Space', 'NASA' ],
|
|
24
25
|
},
|
|
25
26
|
{
|
|
26
27
|
id: 2,
|
|
@@ -28,6 +29,7 @@ export const data = [
|
|
|
28
29
|
description: 'Space description',
|
|
29
30
|
image: 'https://live.staticflickr.com/5678/21911065441_92e2d44708_b.jpg',
|
|
30
31
|
type: 'Not a planet',
|
|
32
|
+
categories: [ 'Space' ],
|
|
31
33
|
},
|
|
32
34
|
{
|
|
33
35
|
id: 3,
|
|
@@ -35,6 +37,7 @@ export const data = [
|
|
|
35
37
|
description: 'NASA photo',
|
|
36
38
|
image: 'https://live.staticflickr.com/742/21712365770_8f70a2c91e_b.jpg',
|
|
37
39
|
type: 'Not a planet',
|
|
40
|
+
categories: [ 'NASA' ],
|
|
38
41
|
},
|
|
39
42
|
{
|
|
40
43
|
id: 4,
|
|
@@ -42,6 +45,7 @@ export const data = [
|
|
|
42
45
|
description: 'Neptune description',
|
|
43
46
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
44
47
|
type: 'Ice giant',
|
|
48
|
+
categories: [ 'Space', 'Planet', 'Solar system' ],
|
|
45
49
|
},
|
|
46
50
|
{
|
|
47
51
|
id: 5,
|
|
@@ -49,13 +53,15 @@ export const data = [
|
|
|
49
53
|
description: 'Mercury description',
|
|
50
54
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
51
55
|
type: 'Terrestrial',
|
|
56
|
+
categories: [ 'Space', 'Planet', 'Solar system' ],
|
|
52
57
|
},
|
|
53
58
|
{
|
|
54
59
|
id: 6,
|
|
55
60
|
title: 'Venus',
|
|
56
|
-
description: '
|
|
61
|
+
description: 'La planète Vénus',
|
|
57
62
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
58
63
|
type: 'Terrestrial',
|
|
64
|
+
categories: [ 'Space', 'Planet', 'Solar system' ],
|
|
59
65
|
},
|
|
60
66
|
{
|
|
61
67
|
id: 7,
|
|
@@ -63,6 +69,7 @@ export const data = [
|
|
|
63
69
|
description: 'Earth description',
|
|
64
70
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
65
71
|
type: 'Terrestrial',
|
|
72
|
+
categories: [ 'Space', 'Planet', 'Solar system' ],
|
|
66
73
|
},
|
|
67
74
|
{
|
|
68
75
|
id: 8,
|
|
@@ -70,6 +77,7 @@ export const data = [
|
|
|
70
77
|
description: 'Mars description',
|
|
71
78
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
72
79
|
type: 'Terrestrial',
|
|
80
|
+
categories: [ 'Space', 'Planet', 'Solar system' ],
|
|
73
81
|
},
|
|
74
82
|
{
|
|
75
83
|
id: 9,
|
|
@@ -77,6 +85,7 @@ export const data = [
|
|
|
77
85
|
description: 'Jupiter description',
|
|
78
86
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
79
87
|
type: 'Gas giant',
|
|
88
|
+
categories: [ 'Space', 'Planet', 'Solar system' ],
|
|
80
89
|
},
|
|
81
90
|
{
|
|
82
91
|
id: 10,
|
|
@@ -84,6 +93,7 @@ export const data = [
|
|
|
84
93
|
description: 'Saturn description',
|
|
85
94
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
86
95
|
type: 'Gas giant',
|
|
96
|
+
categories: [ 'Space', 'Planet', 'Solar system' ],
|
|
87
97
|
},
|
|
88
98
|
{
|
|
89
99
|
id: 11,
|
|
@@ -91,6 +101,7 @@ export const data = [
|
|
|
91
101
|
description: 'Uranus description',
|
|
92
102
|
image: 'https://live.staticflickr.com/5725/21726228300_51333bd62c_b.jpg',
|
|
93
103
|
type: 'Ice giant',
|
|
104
|
+
categories: [ 'Space', 'Ice giant', 'Solar system' ],
|
|
94
105
|
},
|
|
95
106
|
];
|
|
96
107
|
|
|
@@ -135,3 +146,66 @@ export const actions = [
|
|
|
135
146
|
callback() {},
|
|
136
147
|
},
|
|
137
148
|
];
|
|
149
|
+
|
|
150
|
+
export const fields = [
|
|
151
|
+
{
|
|
152
|
+
header: 'Image',
|
|
153
|
+
id: 'image',
|
|
154
|
+
render: ( { item } ) => {
|
|
155
|
+
return (
|
|
156
|
+
<img src={ item.image } alt="" style={ { width: '100%' } } />
|
|
157
|
+
);
|
|
158
|
+
},
|
|
159
|
+
width: 50,
|
|
160
|
+
enableSorting: false,
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
header: 'Title',
|
|
164
|
+
id: 'title',
|
|
165
|
+
maxWidth: 400,
|
|
166
|
+
enableHiding: false,
|
|
167
|
+
enableGlobalSearch: true,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
header: 'Type',
|
|
171
|
+
id: 'type',
|
|
172
|
+
maxWidth: 400,
|
|
173
|
+
enableHiding: false,
|
|
174
|
+
type: 'enumeration',
|
|
175
|
+
elements: [
|
|
176
|
+
{ value: 'Not a planet', label: 'Not a planet' },
|
|
177
|
+
{ value: 'Ice giant', label: 'Ice giant' },
|
|
178
|
+
{ value: 'Terrestrial', label: 'Terrestrial' },
|
|
179
|
+
{ value: 'Gas giant', label: 'Gas giant' },
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
header: 'Description',
|
|
184
|
+
id: 'description',
|
|
185
|
+
maxWidth: 200,
|
|
186
|
+
enableSorting: false,
|
|
187
|
+
enableGlobalSearch: true,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
header: 'Categories',
|
|
191
|
+
id: 'categories',
|
|
192
|
+
type: 'enumeration',
|
|
193
|
+
elements: [
|
|
194
|
+
{ value: 'Space', label: 'Space' },
|
|
195
|
+
{ value: 'NASA', label: 'NASA' },
|
|
196
|
+
{ value: 'Planet', label: 'Planet' },
|
|
197
|
+
{ value: 'Solar system', label: 'Solar system' },
|
|
198
|
+
{ value: 'Ice giant', label: 'Ice giant' },
|
|
199
|
+
],
|
|
200
|
+
filterBy: {
|
|
201
|
+
operators: [ 'isAny', 'isNone', 'isAll', 'isNotAll' ],
|
|
202
|
+
},
|
|
203
|
+
getValue: ( { item } ) => {
|
|
204
|
+
return item.categories;
|
|
205
|
+
},
|
|
206
|
+
render: ( { item } ) => {
|
|
207
|
+
return item.categories.join( ',' );
|
|
208
|
+
},
|
|
209
|
+
enableSorting: false,
|
|
210
|
+
},
|
|
211
|
+
];
|
|
@@ -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 ) => {
|