From aec7269e0133185f3d6750940dd9c254316731cd Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 12 Dec 2024 11:13:26 +0100 Subject: [PATCH 1/9] Add getEditedEntityRecords selector --- docs/reference-guides/data/data-core.md | 15 ++++++++ packages/core-data/README.md | 15 ++++++++ packages/core-data/src/selectors.ts | 51 +++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/docs/reference-guides/data/data-core.md b/docs/reference-guides/data/data-core.md index 199c29cd67dd2..eda4499f9e78b 100644 --- a/docs/reference-guides/data/data-core.md +++ b/docs/reference-guides/data/data-core.md @@ -279,6 +279,21 @@ _Returns_ - `ET.Updatable< EntityRecord > | false`: The entity record, merged with its edits. +### getEditedEntityRecords + +Returns a list of entity records, merged with their edits. + +_Parameters_ + +- _state_ `State`: State tree. +- _kind_ `string`: Entity kind. +- _name_ `string`: Entity name. +- _recordIds_ `EntityRecordKey[]`: Record IDs. + +_Returns_ + +- `Array< ET.Updatable< EntityRecord > | false >`: The list of entity records, merged with their edits. + ### getEmbedPreview Returns the embed preview for the given URL. diff --git a/packages/core-data/README.md b/packages/core-data/README.md index 9549e6742d8cd..bf66993d16b78 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -501,6 +501,21 @@ _Returns_ - `ET.Updatable< EntityRecord > | false`: The entity record, merged with its edits. +### getEditedEntityRecords + +Returns a list of entity records, merged with their edits. + +_Parameters_ + +- _state_ `State`: State tree. +- _kind_ `string`: Entity kind. +- _name_ `string`: Entity name. +- _recordIds_ `EntityRecordKey[]`: Record IDs. + +_Returns_ + +- `Array< ET.Updatable< EntityRecord > | false >`: The list of entity records, merged with their edits. + ### getEmbedPreview Returns the embed preview for the given URL. diff --git a/packages/core-data/src/selectors.ts b/packages/core-data/src/selectors.ts index 7f4b0d3884646..d4ed44d6e0988 100644 --- a/packages/core-data/src/selectors.ts +++ b/packages/core-data/src/selectors.ts @@ -891,6 +891,57 @@ export const getEditedEntityRecord = createSelector( } ); +/** + * Returns a list of entity records, merged with their edits. + * + * @param state State tree. + * @param kind Entity kind. + * @param name Entity name. + * @param recordIds Record IDs. + * + * @return The list of entity records, merged with their edits. + */ +export const getEditedEntityRecords = createSelector( + < EntityRecord extends ET.EntityRecord< any > >( + state: State, + kind: string, + name: string, + recordIds: EntityRecordKey[] + ): Array< ET.Updatable< EntityRecord > | false > => { + return recordIds.map( ( recordId ) => + getEditedEntityRecord( state, kind, name, recordId ) + ); + }, + ( + state: State, + kind: string, + name: string, + recordIds: EntityRecordKey[], + query?: GetRecordsHttpQuery + ) => { + const context = query?.context ?? 'default'; + return [ + state.entities.config, + ...recordIds.map( + ( recordId ) => + state.entities.records?.[ kind ]?.[ name ]?.queriedData + .items[ context ]?.[ recordId ] + ), + ...recordIds.map( + ( recordId ) => + state.entities.records?.[ kind ]?.[ name ]?.queriedData + .itemIsComplete[ context ]?.[ recordId ] + ), + ...recordIds.map( + ( recordId ) => + state.entities.records?.[ kind ]?.[ name ]?.edits?.[ + recordId + ] + ), + ]; + } +); + /** * Returns true if the specified entity record is autosaving, and false otherwise. * From 2840862fefe1a11192f90cbc5d36501277d6e763 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 12 Dec 2024 11:13:57 +0100 Subject: [PATCH 2/9] Remove map usage from useSelect --- packages/editor/src/components/post-actions/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/editor/src/components/post-actions/index.js b/packages/editor/src/components/post-actions/index.js index 4b541d52d429c..f89b6bb83477b 100644 --- a/packages/editor/src/components/post-actions/index.js +++ b/packages/editor/src/components/post-actions/index.js @@ -23,14 +23,14 @@ const { Menu, kebabCase } = unlock( componentsPrivateApis ); function useEditedEntityRecordsWithPermissions( postType, postIds ) { const { items, permissions } = useSelect( ( select ) => { - const { getEditedEntityRecord, getEntityRecordPermissions } = + const { getEditedEntityRecords, getEntityRecordsPermissions } = unlock( select( coreStore ) ); return { - items: postIds.map( ( postId ) => - getEditedEntityRecord( 'postType', postType, postId ) - ), - permissions: postIds.map( ( postId ) => - getEntityRecordPermissions( 'postType', postType, postId ) + items: getEditedEntityRecords( 'postType', postType, postIds ), + permissions: getEntityRecordsPermissions( + 'postType', + postType, + postIds ), }; }, From 5762893120bf1e248a53ca4057ced32a1effb427 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 12 Dec 2024 11:14:33 +0100 Subject: [PATCH 3/9] Remove filter usage from template useSelect --- .../src/fields/template/template-edit.tsx | 79 ++++++++++--------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/packages/fields/src/fields/template/template-edit.tsx b/packages/fields/src/fields/template/template-edit.tsx index c17364568a457..da0048f675ca3 100644 --- a/packages/fields/src/fields/template/template-edit.tsx +++ b/packages/fields/src/fields/template/template-edit.tsx @@ -39,52 +39,49 @@ export const TemplateEdit = ( { typeof data.id === 'number' ? data.id : parseInt( data.id, 10 ); const slug = data.slug; - const { availableTemplates, templates } = useSelect( + const { templates, allowSwitchingTemplate } = useSelect( ( select ) => { - const allTemplates = - select( coreStore ).getEntityRecords< WpTemplate >( - 'postType', - 'wp_template', - { - per_page: -1, - post_type: postType, - } - ) ?? []; + const allTemplates = select( + coreStore + ).getEntityRecords< WpTemplate >( 'postType', 'wp_template', { + per_page: -1, + post_type: postType, + } ); const { getHomePage, getPostsPageId } = unlock( select( coreStore ) ); - const isPostsPage = getPostsPageId() === +postId; + const isPostsPage = +getPostsPageId() === postId; const isFrontPage = - postType === 'page' && getHomePage()?.postId === +postId; - - const allowSwitchingTemplate = ! isPostsPage && ! isFrontPage; + postType === 'page' && +getHomePage()?.postId === postId; return { templates: allTemplates, - availableTemplates: allowSwitchingTemplate - ? allTemplates.filter( - ( template ) => - template.is_custom && - template.slug !== data.template && - !! template.content.raw // Skip empty templates. - ) - : [], + allowSwitchingTemplate: ! isPostsPage && ! isFrontPage, }; }, - [ data.template, postId, postType ] + [ postId, postType ] ); const templatesAsPatterns = useMemo( () => - availableTemplates.map( ( template ) => ( { - name: template.slug, - blocks: parse( template.content.raw ), - title: decodeEntities( template.title.rendered ), - id: template.id, - } ) ), - [ availableTemplates ] + allowSwitchingTemplate + ? ( templates ?? [] ) + .filter( + ( template ) => + template.is_custom && + template.slug !== data.template && + !! template.content.raw // Skip empty templates. + ) + .map( ( template ) => ( { + name: template.slug, + blocks: parse( template.content.raw ), + title: decodeEntities( template.title.rendered ), + id: template.id, + } ) ) + : [], + [ templates, allowSwitchingTemplate, data.template ] ); const shownTemplates = useAsyncList( templatesAsPatterns ); @@ -151,6 +148,10 @@ export const TemplateEdit = ( { variant="tertiary" size="compact" onClick={ onToggle } + accessibleWhenDisabled + disabled={ + value === '' && ! templatesAsPatterns.length + } > { currentTemplate ? getItemTitle( currentTemplate ) @@ -159,14 +160,16 @@ export const TemplateEdit = ( { ) } renderContent={ ( { onToggle } ) => ( - { - setShowModal( true ); - onToggle(); - } } - > - { __( 'Swap template' ) } - + { templatesAsPatterns.length > 0 && ( + { + setShowModal( true ); + onToggle(); + } } + > + { __( 'Swap template' ) } + + ) } { // The default template in a post is indicated by an empty string value !== '' && ( From 6972c7969594019ce7ac61ba0a598fb4f0572156 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 12 Dec 2024 15:09:49 +0100 Subject: [PATCH 4/9] Update hook to use correct ids for permissions --- .../src/components/post-actions/index.js | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/editor/src/components/post-actions/index.js b/packages/editor/src/components/post-actions/index.js index f89b6bb83477b..784fd42b1b50a 100644 --- a/packages/editor/src/components/post-actions/index.js +++ b/packages/editor/src/components/post-actions/index.js @@ -21,27 +21,47 @@ import { usePostActions } from './actions'; const { Menu, kebabCase } = unlock( componentsPrivateApis ); function useEditedEntityRecordsWithPermissions( postType, postIds ) { - const { items, permissions } = useSelect( + const entityConfig = useSelect( + ( select ) => + select( coreStore ).getEntityConfig( 'postType', postType ), + [ postType ] + ); + const { items } = useSelect( ( select ) => { - const { getEditedEntityRecords, getEntityRecordsPermissions } = - unlock( select( coreStore ) ); + const { getEditedEntityRecords } = unlock( select( coreStore ) ); return { items: getEditedEntityRecords( 'postType', postType, postIds ), - permissions: getEntityRecordsPermissions( - 'postType', - postType, - postIds - ), }; }, [ postIds, postType ] ); + const ids = useMemo( + () => + items?.map( + // @ts-ignore + ( record ) => record[ entityConfig?.key ?? 'id' ] + ) ?? [], + [ items, entityConfig?.key ] + ); + + const permissions = useSelect( + ( select ) => { + const { getEntityRecordsPermissions } = unlock( + select( coreStore ) + ); + return getEntityRecordsPermissions( 'postType', postType, ids ); + }, + [ ids, postType ] + ); + return useMemo( () => { - return items.map( ( item, index ) => ( { - ...item, - permissions: permissions[ index ], - } ) ); + return ( + items?.map( ( item, index ) => ( { + ...item, + permissions: permissions[ index ], + } ) ) ?? [] + ); }, [ items, permissions ] ); } From fce61de88c8d02d3a01f933f162a586a3dec0e66 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 12 Dec 2024 15:10:18 +0100 Subject: [PATCH 5/9] Compare using strings for template id --- packages/fields/src/fields/template/template-edit.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/fields/src/fields/template/template-edit.tsx b/packages/fields/src/fields/template/template-edit.tsx index da0048f675ca3..20fc05c0a4a10 100644 --- a/packages/fields/src/fields/template/template-edit.tsx +++ b/packages/fields/src/fields/template/template-edit.tsx @@ -52,9 +52,10 @@ export const TemplateEdit = ( { select( coreStore ) ); - const isPostsPage = +getPostsPageId() === postId; + const isPostsPage = getPostsPageId() === postId.toString(); const isFrontPage = - postType === 'page' && +getHomePage()?.postId === postId; + postType === 'page' && + getHomePage()?.postId === postId.toString(); return { templates: allTemplates, From e4cd460ee05bb514677af29f12a1492721960843 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Thu, 12 Dec 2024 17:19:25 +0100 Subject: [PATCH 6/9] Move new selector to private --- docs/reference-guides/data/data-core.md | 15 ------ packages/core-data/README.md | 15 ------ packages/core-data/src/private-selectors.ts | 60 ++++++++++++++++++++- packages/core-data/src/selectors.ts | 53 +----------------- 4 files changed, 60 insertions(+), 83 deletions(-) diff --git a/docs/reference-guides/data/data-core.md b/docs/reference-guides/data/data-core.md index eda4499f9e78b..199c29cd67dd2 100644 --- a/docs/reference-guides/data/data-core.md +++ b/docs/reference-guides/data/data-core.md @@ -279,21 +279,6 @@ _Returns_ - `ET.Updatable< EntityRecord > | false`: The entity record, merged with its edits. -### getEditedEntityRecords - -Returns a list of entity records, merged with their edits. - -_Parameters_ - -- _state_ `State`: State tree. -- _kind_ `string`: Entity kind. -- _name_ `string`: Entity name. -- _recordIds_ `EntityRecordKey[]`: Record IDs. - -_Returns_ - -- `Array< ET.Updatable< EntityRecord > | false >`: The list of entity records, merged with their edits. - ### getEmbedPreview Returns the embed preview for the given URL. diff --git a/packages/core-data/README.md b/packages/core-data/README.md index bf66993d16b78..9549e6742d8cd 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -501,21 +501,6 @@ _Returns_ - `ET.Updatable< EntityRecord > | false`: The entity record, merged with its edits. -### getEditedEntityRecords - -Returns a list of entity records, merged with their edits. - -_Parameters_ - -- _state_ `State`: State tree. -- _kind_ `string`: Entity kind. -- _name_ `string`: Entity name. -- _recordIds_ `EntityRecordKey[]`: Record IDs. - -_Returns_ - -- `Array< ET.Updatable< EntityRecord > | false >`: The list of entity records, merged with their edits. - ### getEmbedPreview Returns the embed preview for the given URL. diff --git a/packages/core-data/src/private-selectors.ts b/packages/core-data/src/private-selectors.ts index 0d4a28ad174a1..31c06f4f2073d 100644 --- a/packages/core-data/src/private-selectors.ts +++ b/packages/core-data/src/private-selectors.ts @@ -6,9 +6,16 @@ import { createSelector, createRegistrySelector } from '@wordpress/data'; /** * Internal dependencies */ -import { getDefaultTemplateId, getEntityRecord, type State } from './selectors'; +import { + getDefaultTemplateId, + getEditedEntityRecord, + getEntityRecord, + type State, + type GetRecordsHttpQuery, +} from './selectors'; import { STORE_NAME } from './name'; import { unlock } from './lock-unlock'; +import type * as ET from './entity-types'; type EntityRecordKey = string | number; @@ -257,3 +264,54 @@ export const getTemplateId = createRegistrySelector( } ); } ); + +/** + * Returns a list of entity records, merged with their edits. + * + * @param state State tree. + * @param kind Entity kind. + * @param name Entity name. + * @param recordIds Record IDs. + * + * @return The list of entity records, merged with their edits. + */ +export const getEditedEntityRecords = createSelector( + < EntityRecord extends ET.EntityRecord< any > >( + state: State, + kind: string, + name: string, + recordIds: EntityRecordKey[] + ): Array< ET.Updatable< EntityRecord > | false > => { + return recordIds.map( ( recordId ) => + getEditedEntityRecord( state, kind, name, recordId ) + ); + }, + ( + state: State, + kind: string, + name: string, + recordIds: EntityRecordKey[], + query?: GetRecordsHttpQuery + ) => { + const context = query?.context ?? 'default'; + return [ + state.entities.config, + ...recordIds.map( + ( recordId ) => + state.entities.records?.[ kind ]?.[ name ]?.queriedData + .items[ context ]?.[ recordId ] + ), + ...recordIds.map( + ( recordId ) => + state.entities.records?.[ kind ]?.[ name ]?.queriedData + .itemIsComplete[ context ]?.[ recordId ] + ), + ...recordIds.map( + ( recordId ) => + state.entities.records?.[ kind ]?.[ name ]?.edits?.[ + recordId + ] + ), + ]; + } +); diff --git a/packages/core-data/src/selectors.ts b/packages/core-data/src/selectors.ts index d4ed44d6e0988..c31ebc0425464 100644 --- a/packages/core-data/src/selectors.ts +++ b/packages/core-data/src/selectors.ts @@ -113,7 +113,7 @@ type Optional< T > = T | undefined; /** * HTTP Query parameters sent with the API request to fetch the entity records. */ -type GetRecordsHttpQuery = Record< string, any >; +export type GetRecordsHttpQuery = Record< string, any >; /** * Arguments for EntityRecord selectors. @@ -891,57 +891,6 @@ export const getEditedEntityRecord = createSelector( } ); -/** - * Returns a list of entity records, merged with their edits. - * - * @param state State tree. - * @param kind Entity kind. - * @param name Entity name. - * @param recordIds Record IDs. - * - * @return The list of entity records, merged with their edits. - */ -export const getEditedEntityRecords = createSelector( - < EntityRecord extends ET.EntityRecord< any > >( - state: State, - kind: string, - name: string, - recordIds: EntityRecordKey[] - ): Array< ET.Updatable< EntityRecord > | false > => { - return recordIds.map( ( recordId ) => - getEditedEntityRecord( state, kind, name, recordId ) - ); - }, - ( - state: State, - kind: string, - name: string, - recordIds: EntityRecordKey[], - query?: GetRecordsHttpQuery - ) => { - const context = query?.context ?? 'default'; - return [ - state.entities.config, - ...recordIds.map( - ( recordId ) => - state.entities.records?.[ kind ]?.[ name ]?.queriedData - .items[ context ]?.[ recordId ] - ), - ...recordIds.map( - ( recordId ) => - state.entities.records?.[ kind ]?.[ name ]?.queriedData - .itemIsComplete[ context ]?.[ recordId ] - ), - ...recordIds.map( - ( recordId ) => - state.entities.records?.[ kind ]?.[ name ]?.edits?.[ - recordId - ] - ), - ]; - } -); - /** * Returns true if the specified entity record is autosaving, and false otherwise. * From 9e4ceb6906d4a0702f97c0b9ec223914f684c6ca Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Fri, 13 Dec 2024 12:59:10 +0100 Subject: [PATCH 7/9] Move getEntityConfig inside of other useSelect --- .../src/components/post-actions/index.js | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/editor/src/components/post-actions/index.js b/packages/editor/src/components/post-actions/index.js index 784fd42b1b50a..deb621c9d058e 100644 --- a/packages/editor/src/components/post-actions/index.js +++ b/packages/editor/src/components/post-actions/index.js @@ -21,16 +21,19 @@ import { usePostActions } from './actions'; const { Menu, kebabCase } = unlock( componentsPrivateApis ); function useEditedEntityRecordsWithPermissions( postType, postIds ) { - const entityConfig = useSelect( - ( select ) => - select( coreStore ).getEntityConfig( 'postType', postType ), - [ postType ] - ); - const { items } = useSelect( + const { items, entityConfig } = useSelect( ( select ) => { + const { getEntityConfig } = select( coreStore ); const { getEditedEntityRecords } = unlock( select( coreStore ) ); + const entityRecords = getEditedEntityRecords( + 'postType', + postType, + postIds + ); + return { - items: getEditedEntityRecords( 'postType', postType, postIds ), + items: entityRecords, + entityConfig: getEntityConfig( 'postType', postType ), }; }, [ postIds, postType ] @@ -38,10 +41,8 @@ function useEditedEntityRecordsWithPermissions( postType, postIds ) { const ids = useMemo( () => - items?.map( - // @ts-ignore - ( record ) => record[ entityConfig?.key ?? 'id' ] - ) ?? [], + items?.map( ( record ) => record[ entityConfig?.key ?? 'id' ] ) ?? + [], [ items, entityConfig?.key ] ); @@ -59,7 +60,7 @@ function useEditedEntityRecordsWithPermissions( postType, postIds ) { return ( items?.map( ( item, index ) => ( { ...item, - permissions: permissions[ index ], + permissions: permissions ? permissions[ index ] : undefined, } ) ) ?? [] ); }, [ items, permissions ] ); From ba6868a3a10c615c20c1bc53f9c53f74db49f024 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Tue, 17 Dec 2024 11:22:42 +0100 Subject: [PATCH 8/9] Update post/page related functions to return ID as number --- packages/core-data/src/private-selectors.ts | 8 ++++++-- packages/fields/src/fields/template/template-edit.tsx | 5 ++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/core-data/src/private-selectors.ts b/packages/core-data/src/private-selectors.ts index 31c06f4f2073d..22288c17e10a3 100644 --- a/packages/core-data/src/private-selectors.ts +++ b/packages/core-data/src/private-selectors.ts @@ -119,7 +119,7 @@ export function getRegisteredPostMeta( state: State, postType: string ) { return state.registeredPostMeta?.[ postType ] ?? {}; } -function normalizePageId( value: number | string | undefined ): string | null { +function normalizePageId( value: number | string | undefined ): number | null { if ( ! value || ! [ 'number', 'string' ].includes( typeof value ) ) { return null; } @@ -129,7 +129,11 @@ function normalizePageId( value: number | string | undefined ): string | null { return null; } - return value.toString(); + if ( typeof value === 'string' ) { + return parseInt( value, 10 ); + } + + return value; } interface SiteData { diff --git a/packages/fields/src/fields/template/template-edit.tsx b/packages/fields/src/fields/template/template-edit.tsx index 20fc05c0a4a10..3cf07b406874a 100644 --- a/packages/fields/src/fields/template/template-edit.tsx +++ b/packages/fields/src/fields/template/template-edit.tsx @@ -52,10 +52,9 @@ export const TemplateEdit = ( { select( coreStore ) ); - const isPostsPage = getPostsPageId() === postId.toString(); + const isPostsPage = getPostsPageId() === postId; const isFrontPage = - postType === 'page' && - getHomePage()?.postId === postId.toString(); + postType === 'page' && getHomePage()?.postId === postId; return { templates: allTemplates, From 9c55bef36e3190e74e5864f0b3a56989b12f7509 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Tue, 17 Dec 2024 11:40:45 +0100 Subject: [PATCH 9/9] Handle post ids as numbers --- packages/edit-site/src/components/post-edit/index.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/post-edit/index.js b/packages/edit-site/src/components/post-edit/index.js index 97484c89ed670..afcd814d43b7d 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -33,7 +33,14 @@ const fieldsWithBulkEditSupport = [ ]; function PostEditForm( { postType, postId } ) { - const ids = useMemo( () => postId.split( ',' ), [ postId ] ); + const ids = useMemo( + () => + postId + .split( ',' ) + .map( ( id ) => parseInt( id, 10 ) ) + .filter( Number ), + [ postId ] + ); const { record, hasFinishedResolution } = useSelect( ( select ) => { const args = [ 'postType', postType, ids[ 0 ] ];