-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dataviews: Add some client side data handling utils #57488
Changes from 4 commits
706b378
5d60ac0
9bd1be1
ab5feff
a4c8fac
ea4b2ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export { default as DataViews } from './dataviews'; | ||
export { sortByTextFields, getPaginationResults } from './utils'; | ||
export { VIEW_LAYOUTS } from './constants'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/** | ||
* Helper util to sort data by text fields, when sorting is done client side. | ||
* | ||
* @param {Object} params Function params. | ||
* @param {Object[]} params.data Data to sort. | ||
* @param {Object} params.view Current view object. | ||
* @param {Object[]} params.fields Array of available fields. | ||
* @param {string[]} params.textFields Array of the field ids to sort. | ||
* | ||
* @return {Object[]} Sorted data. | ||
*/ | ||
export const sortByTextFields = ( { data, view, fields, textFields } ) => { | ||
const sortedData = [ ...data ]; | ||
const fieldId = view.sort.field; | ||
if ( textFields.includes( fieldId ) ) { | ||
const fieldToSort = fields.find( ( field ) => { | ||
return field.id === fieldId; | ||
} ); | ||
sortedData.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 ); | ||
} ); | ||
} | ||
return sortedData; | ||
}; | ||
|
||
/** | ||
* Helper util to get the paginated data and the paginateInfo needed, | ||
* when pagination is done client side. | ||
* | ||
* @param {Object} params Function params. | ||
* @param {Object[]} params.data Available data. | ||
* @param {Object} params.view Current view object. | ||
* | ||
* @return {Object} Paginated data and paginationInfo. | ||
*/ | ||
export function getPaginationResults( { data, view } ) { | ||
const start = ( view.page - 1 ) * view.perPage; | ||
const totalItems = data?.length || 0; | ||
data = data?.slice( start, start + view.perPage ); | ||
return { | ||
data, | ||
paginationInfo: { | ||
totalItems, | ||
totalPages: Math.ceil( totalItems / view.perPage ), | ||
}, | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,12 +10,22 @@ import { | |
} from '@wordpress/components'; | ||
import { getQueryArgs } from '@wordpress/url'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { useState, useMemo, useCallback, useId } from '@wordpress/element'; | ||
import { | ||
useState, | ||
useMemo, | ||
useCallback, | ||
useId, | ||
useEffect, | ||
} from '@wordpress/element'; | ||
import { | ||
BlockPreview, | ||
privateApis as blockEditorPrivateApis, | ||
} from '@wordpress/block-editor'; | ||
import { DataViews } from '@wordpress/dataviews'; | ||
import { | ||
DataViews, | ||
sortByTextFields, | ||
getPaginationResults, | ||
} from '@wordpress/dataviews'; | ||
import { | ||
Icon, | ||
header, | ||
|
@@ -24,6 +34,7 @@ import { | |
symbol, | ||
lockSmall, | ||
} from '@wordpress/icons'; | ||
import { usePrevious } from '@wordpress/compose'; | ||
|
||
/** | ||
* Internal dependencies | ||
|
@@ -192,6 +203,7 @@ export default function DataviewsPatterns() { | |
const [ view, setView ] = useState( DEFAULT_VIEW ); | ||
const isUncategorizedThemePatterns = | ||
type === PATTERN_TYPES.theme && categoryId === 'uncategorized'; | ||
const previousCategoryId = usePrevious( categoryId ); | ||
const { patterns, isResolving } = usePatterns( | ||
type, | ||
isUncategorizedThemePatterns ? '' : categoryId, | ||
|
@@ -229,7 +241,12 @@ export default function DataviewsPatterns() { | |
], | ||
[ view.type, categoryId ] | ||
); | ||
|
||
// Reset the page number when the category changes. | ||
useEffect( () => { | ||
if ( previousCategoryId !== categoryId && view.page !== 1 ) { | ||
setView( { ...view, page: 1 } ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now, these categories are built as separate pages, so for me we should reset the entire "view" object when changing categories. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I kind of disagree here because while what you say it's true, it feels like we have external filters in the sidebar and for example if I have sorted by title, I'd expect the other categories to be sorted too. No strong opinions for now though, I can update. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No strong opinion too. I don't like this effect though and I'm not sure how to avoid it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my mind this is just a filter that we will need to incorporate in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is also close to the "default views" of the pages data views, it's not really clear whether it's filters or just separate default views. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The "default views" are just presets, preconfigured settings for the view, so we still need a "category" filter in place. |
||
} | ||
}, [ categoryId, previousCategoryId, view ] ); | ||
const { data, paginationInfo } = useMemo( () => { | ||
if ( ! patterns ) { | ||
return { | ||
|
@@ -240,32 +257,18 @@ export default function DataviewsPatterns() { | |
let filteredData = [ ...patterns ]; | ||
// Handle sorting. | ||
if ( view.sort ) { | ||
const stringSortingFields = [ 'title' ]; | ||
const fieldId = view.sort.field; | ||
if ( stringSortingFields.includes( fieldId ) ) { | ||
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 ); | ||
} ); | ||
} | ||
filteredData = sortByTextFields( { | ||
data: filteredData, | ||
view, | ||
fields, | ||
textFields: [ 'title', 'author' ], | ||
} ); | ||
} | ||
// Handle pagination. | ||
const start = ( view.page - 1 ) * view.perPage; | ||
const totalItems = filteredData?.length || 0; | ||
filteredData = filteredData?.slice( start, start + view.perPage ); | ||
return { | ||
return getPaginationResults( { | ||
data: filteredData, | ||
paginationInfo: { | ||
totalItems, | ||
totalPages: Math.ceil( totalItems / view.perPage ), | ||
}, | ||
}; | ||
view, | ||
} ); | ||
}, [ patterns, view, fields ] ); | ||
|
||
const actions = useMemo( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,11 @@ import { | |
BlockPreview, | ||
privateApis as blockEditorPrivateApis, | ||
} from '@wordpress/block-editor'; | ||
import { DataViews } from '@wordpress/dataviews'; | ||
import { | ||
DataViews, | ||
sortByTextFields, | ||
getPaginationResults, | ||
} from '@wordpress/dataviews'; | ||
import { privateApis as routerPrivateApis } from '@wordpress/router'; | ||
|
||
/** | ||
|
@@ -252,10 +256,10 @@ export default function DataviewsTemplates() { | |
[ authors, view.type ] | ||
); | ||
|
||
const { shownTemplates, paginationInfo } = useMemo( () => { | ||
const { data, paginationInfo } = useMemo( () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this hook become a reusable hook that filters and sorts and everything based on "view", "allData" and "fields"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can explore this separately better because I think we need to advance the filters API more and see how we can abstract it best. Right now there is a lot of specific code regarding the filtering(see author in templates). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't seem specific at all for me. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course, eventually we shouldn't have much helpers.. |
||
if ( ! allTemplates ) { | ||
return { | ||
shownTemplates: EMPTY_ARRAY, | ||
data: EMPTY_ARRAY, | ||
paginationInfo: { totalItems: 0, totalPages: 0 }, | ||
}; | ||
} | ||
|
@@ -301,36 +305,18 @@ export default function DataviewsTemplates() { | |
|
||
// Handle sorting. | ||
if ( view.sort ) { | ||
const stringSortingFields = [ 'title', 'author' ]; | ||
const fieldId = view.sort.field; | ||
if ( stringSortingFields.includes( fieldId ) ) { | ||
const fieldToSort = fields.find( ( field ) => { | ||
return field.id === fieldId; | ||
} ); | ||
filteredTemplates.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 ); | ||
} ); | ||
} | ||
filteredTemplates = sortByTextFields( { | ||
data: filteredTemplates, | ||
view, | ||
fields, | ||
textFields: [ 'title', 'author' ], | ||
} ); | ||
} | ||
|
||
// Handle pagination. | ||
const start = ( view.page - 1 ) * view.perPage; | ||
const totalItems = filteredTemplates?.length || 0; | ||
filteredTemplates = filteredTemplates?.slice( | ||
start, | ||
start + view.perPage | ||
); | ||
return { | ||
shownTemplates: filteredTemplates, | ||
paginationInfo: { | ||
totalItems, | ||
totalPages: Math.ceil( totalItems / view.perPage ), | ||
}, | ||
}; | ||
return getPaginationResults( { | ||
data: filteredTemplates, | ||
view, | ||
} ); | ||
}, [ allTemplates, view, fields ] ); | ||
|
||
const resetTemplateAction = useResetTemplateAction(); | ||
|
@@ -381,7 +367,7 @@ export default function DataviewsTemplates() { | |
paginationInfo={ paginationInfo } | ||
fields={ fields } | ||
actions={ actions } | ||
data={ shownTemplates } | ||
data={ data } | ||
isLoading={ isLoadingData } | ||
view={ view } | ||
onChangeView={ onChangeView } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an unrelated fix for a bug that I observed here.