-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DataViews: Add a utility to share filtering, sorting and pagination l…
…ogic (#59897) Co-authored-by: youknowriad <youknowriad@git.wordpress.org> Co-authored-by: oandregal <oandregal@git.wordpress.org> Co-authored-by: mcsf <mcsf@git.wordpress.org> Co-authored-by: ntsekouras <ntsekouras@git.wordpress.org>
- Loading branch information
1 parent
00efc92
commit e836da4
Showing
11 changed files
with
545 additions
and
276 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import removeAccents from 'remove-accents'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { | ||
OPERATOR_IS, | ||
OPERATOR_IS_NOT, | ||
OPERATOR_IS_NONE, | ||
OPERATOR_IS_ANY, | ||
OPERATOR_IS_ALL, | ||
OPERATOR_IS_NOT_ALL, | ||
} from './constants'; | ||
import { normalizeFields } from './normalize-fields'; | ||
|
||
function normalizeSearchInput( input = '' ) { | ||
return removeAccents( input.trim().toLowerCase() ); | ||
} | ||
|
||
const EMPTY_ARRAY = []; | ||
|
||
/** | ||
* Applies the filtering, sorting and pagination to the raw data based on the view configuration. | ||
* | ||
* @param {any[]} data Raw data. | ||
* @param {Object} view View config. | ||
* @param {Object[]} fields Fields config. | ||
* | ||
* @return {Object} { data: any[], paginationInfo: { totalItems: number, totalPages: number } } | ||
*/ | ||
export function filterSortAndPaginate( data, view, fields ) { | ||
if ( ! data ) { | ||
return { | ||
data: EMPTY_ARRAY, | ||
paginationInfo: { totalItems: 0, totalPages: 0 }, | ||
}; | ||
} | ||
const _fields = normalizeFields( fields ); | ||
let filteredData = [ ...data ]; | ||
// Handle global search. | ||
if ( view.search ) { | ||
const normalizedSearch = normalizeSearchInput( view.search ); | ||
filteredData = filteredData.filter( ( item ) => { | ||
return _fields | ||
.filter( ( field ) => field.enableGlobalSearch ) | ||
.map( ( field ) => { | ||
return normalizeSearchInput( field.getValue( { item } ) ); | ||
} ) | ||
.some( ( field ) => field.includes( normalizedSearch ) ); | ||
} ); | ||
} | ||
|
||
if ( view.filters.length > 0 ) { | ||
view.filters.forEach( ( filter ) => { | ||
const field = _fields.find( | ||
( _field ) => _field.id === filter.field | ||
); | ||
if ( | ||
filter.operator === OPERATOR_IS_ANY && | ||
filter?.value?.length > 0 | ||
) { | ||
filteredData = filteredData.filter( ( item ) => { | ||
const fieldValue = field.getValue( { item } ); | ||
if ( Array.isArray( fieldValue ) ) { | ||
return filter.value.some( ( filterValue ) => | ||
fieldValue.includes( filterValue ) | ||
); | ||
} else if ( typeof fieldValue === 'string' ) { | ||
return filter.value.includes( fieldValue ); | ||
} | ||
return false; | ||
} ); | ||
} else if ( | ||
filter.operator === OPERATOR_IS_NONE && | ||
filter?.value?.length > 0 | ||
) { | ||
filteredData = filteredData.filter( ( item ) => { | ||
const fieldValue = field.getValue( { item } ); | ||
if ( Array.isArray( fieldValue ) ) { | ||
return ! filter.value.some( ( filterValue ) => | ||
fieldValue.includes( filterValue ) | ||
); | ||
} else if ( typeof fieldValue === 'string' ) { | ||
return ! filter.value.includes( fieldValue ); | ||
} | ||
return false; | ||
} ); | ||
} else if ( | ||
filter.operator === OPERATOR_IS_ALL && | ||
filter?.value?.length > 0 | ||
) { | ||
filteredData = filteredData.filter( ( item ) => { | ||
return filter.value.every( ( value ) => { | ||
return field.getValue( { item } ).includes( value ); | ||
} ); | ||
} ); | ||
} else if ( | ||
filter.operator === OPERATOR_IS_NOT_ALL && | ||
filter?.value?.length > 0 | ||
) { | ||
filteredData = filteredData.filter( ( item ) => { | ||
return filter.value.every( ( value ) => { | ||
return ! field.getValue( { item } ).includes( value ); | ||
} ); | ||
} ); | ||
} else if ( filter.operator === OPERATOR_IS ) { | ||
filteredData = filteredData.filter( ( item ) => { | ||
return filter.value === field.getValue( { item } ); | ||
} ); | ||
} else if ( filter.operator === OPERATOR_IS_NOT ) { | ||
filteredData = filteredData.filter( ( item ) => { | ||
return filter.value !== field.getValue( { item } ); | ||
} ); | ||
} | ||
} ); | ||
} | ||
|
||
// Handle sorting. | ||
if ( view.sort ) { | ||
const fieldId = view.sort.field; | ||
const fieldToSort = _fields.find( ( field ) => { | ||
return field.id === fieldId; | ||
} ); | ||
filteredData.sort( ( a, b ) => { | ||
const valueA = fieldToSort.getValue( { item: a } ) ?? ''; | ||
const valueB = fieldToSort.getValue( { item: b } ) ?? ''; | ||
return view.sort.direction === 'asc' | ||
? valueA.localeCompare( valueB ) | ||
: valueB.localeCompare( valueA ); | ||
} ); | ||
} | ||
|
||
// Handle pagination. | ||
const hasPagination = view.page && view.perPage; | ||
const start = hasPagination ? ( view.page - 1 ) * view.perPage : 0; | ||
const totalItems = filteredData?.length || 0; | ||
const totalPages = hasPagination | ||
? Math.ceil( totalItems / view.perPage ) | ||
: 1; | ||
filteredData = hasPagination | ||
? filteredData?.slice( start, start + view.perPage ) | ||
: filteredData; | ||
|
||
return { | ||
data: filteredData, | ||
paginationInfo: { | ||
totalItems, | ||
totalPages, | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
export { default as DataViews } from './dataviews'; | ||
export { sortByTextFields, getPaginationResults } from './utils'; | ||
export { VIEW_LAYOUTS } from './constants'; | ||
export { filterSortAndPaginate } from './filter-and-sort-data-view'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* Apply default values and normalize the fields config. | ||
* | ||
* @param {Object[]} fields Raw Fields. | ||
* @return {Object[]} Normalized fields. | ||
*/ | ||
export function normalizeFields( fields ) { | ||
return fields.map( ( field ) => { | ||
const getValue = field.getValue || ( ( { item } ) => item[ field.id ] ); | ||
|
||
return { | ||
...field, | ||
getValue, | ||
render: field.render || getValue, | ||
}; | ||
} ); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.