diff --git a/packages/edit-site/src/components/page-patterns/index.js b/packages/edit-site/src/components/page-patterns/index.js index 0a6a4469cef4e7..e9114e53184ccf 100644 --- a/packages/edit-site/src/components/page-patterns/index.js +++ b/packages/edit-site/src/components/page-patterns/index.js @@ -30,6 +30,8 @@ import { usePrevious } from '@wordpress/compose'; import { useEntityRecords } from '@wordpress/core-data'; import { privateApis as editorPrivateApis } from '@wordpress/editor'; import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { parse } from '@wordpress/blocks'; +import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies @@ -53,6 +55,7 @@ import PatternsHeader from './header'; import { useLink } from '../routes/link'; import { useAddedBy } from '../page-templates/hooks'; import { useEditPostAction } from '../dataviews-actions'; +import { defaultGetTitle } from './search-items'; const { ExperimentalBlockEditorProvider, useGlobalStyle } = unlock( blockEditorPrivateApis @@ -116,14 +119,21 @@ function Preview( { item, viewType } ) { const descriptionId = useId(); const isUserPattern = item.type === PATTERN_TYPES.user; const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; - const isEmpty = ! item.blocks?.length; - const [ backgroundColor ] = useGlobalStyle( 'color.background' ); const { onClick } = useLink( { postType: item.type, - postId: isUserPattern ? item.id : item.name, + postId: isUserPattern || isTemplatePart ? item.id : item.name, canvas: 'edit', } ); + const blocks = useMemo( () => { + return ( + item.blocks ?? + parse( item.content.raw, { + __unstableSkipMigrationLogs: true, + } ) + ); + }, [ item?.content?.raw, item.blocks ] ); + const isEmpty = ! blocks?.length; return (
@@ -187,11 +197,13 @@ function Author( { item, viewType } ) { function Title( { item } ) { const isUserPattern = item.type === PATTERN_TYPES.user; + const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; const { onClick } = useLink( { postType: item.type, - postId: isUserPattern ? item.id : item.name, + postId: isUserPattern || isTemplatePart ? item.id : item.name, canvas: 'edit', } ); + const title = decodeEntities( defaultGetTitle( item ) ); return ( { item.type === PATTERN_TYPES.theme ? ( - item.title + title ) : ( ) } @@ -321,7 +333,7 @@ export default function DataviewsPatterns() { _fields.push( { header: __( 'Author' ), id: 'author', - getValue: ( { item } ) => item.templatePart.author_text, + getValue: ( { item } ) => item.author_text, render: ( { item } ) => { return ; }, @@ -406,7 +418,7 @@ export default function DataviewsPatterns() { fields={ fields } actions={ actions } data={ data || EMPTY_ARRAY } - getItemId={ ( item ) => item.name } + getItemId={ ( item ) => item.name ?? item.id } isLoading={ isResolving } view={ view } onChangeView={ onChangeView } diff --git a/packages/edit-site/src/components/page-patterns/search-items.js b/packages/edit-site/src/components/page-patterns/search-items.js index 8aa4b349f48d7c..64565d951e833f 100644 --- a/packages/edit-site/src/components/page-patterns/search-items.js +++ b/packages/edit-site/src/components/page-patterns/search-items.js @@ -20,11 +20,14 @@ import { PATTERN_DEFAULT_CATEGORY, PATTERN_USER_CATEGORY, PATTERN_TYPES, + TEMPLATE_PART_POST_TYPE, } from '../../utils/constants'; // Default search helpers. -const defaultGetName = ( item ) => item.name || ''; -const defaultGetTitle = ( item ) => item.title; +const defaultGetName = ( item ) => + item.type !== TEMPLATE_PART_POST_TYPE ? item.name || '' : ''; +export const defaultGetTitle = ( item ) => + typeof item.title === 'string' ? item.title : item.title.rendered; const defaultGetDescription = ( item ) => item.description || ''; const defaultGetKeywords = ( item ) => item.keywords || []; const defaultHasCategory = () => false; diff --git a/packages/edit-site/src/components/page-patterns/use-patterns.js b/packages/edit-site/src/components/page-patterns/use-patterns.js index 32377edcd96842..dc5a92b560537d 100644 --- a/packages/edit-site/src/components/page-patterns/use-patterns.js +++ b/packages/edit-site/src/components/page-patterns/use-patterns.js @@ -5,7 +5,6 @@ import { parse } from '@wordpress/blocks'; import { useSelect, createSelector } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import { store as editorStore } from '@wordpress/editor'; -import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies @@ -16,7 +15,6 @@ import { PATTERN_TYPES, PATTERN_SYNC_TYPES, TEMPLATE_PART_POST_TYPE, - TEMPLATE_ORIGINS, TEMPLATE_PART_AREA_DEFAULT_CATEGORY, } from '../../utils/constants'; import { unlock } from '../../lock-unlock'; @@ -25,38 +23,16 @@ import { store as editSiteStore } from '../../store'; const EMPTY_PATTERN_LIST = []; -const createTemplatePartId = ( theme, slug ) => - theme && slug ? theme + '//' + slug : null; - -const templatePartToPattern = ( templatePart ) => ( { - blocks: parse( templatePart.content.raw, { - __unstableSkipMigrationLogs: true, - } ), - categories: [ templatePart.area ], - description: templatePart.description || '', - isCustom: templatePart.source === TEMPLATE_ORIGINS.custom, - keywords: templatePart.keywords || [], - id: createTemplatePartId( templatePart.theme, templatePart.slug ), - name: createTemplatePartId( templatePart.theme, templatePart.slug ), - title: decodeEntities( templatePart.title.rendered ), - type: templatePart.type, - _links: templatePart._links, - templatePart, -} ); - -const selectTemplatePartsAsPatterns = createSelector( +const selectTemplateParts = createSelector( ( select, categoryId, search = '' ) => { const { getEntityRecords, isResolving: isResolvingSelector } = select( coreStore ); const { __experimentalGetDefaultTemplatePartAreas } = select( editorStore ); const query = { per_page: -1 }; - const rawTemplateParts = + const templateParts = getEntityRecords( 'postType', TEMPLATE_PART_POST_TYPE, query ) ?? EMPTY_PATTERN_LIST; - const templateParts = rawTemplateParts.map( ( templatePart ) => - templatePartToPattern( templatePart ) - ); // In the case where a custom template part area has been removed we need // the current list of areas to cross check against so orphaned template @@ -66,12 +42,12 @@ const selectTemplatePartsAsPatterns = createSelector( const templatePartHasCategory = ( item, category ) => { if ( category !== TEMPLATE_PART_AREA_DEFAULT_CATEGORY ) { - return item.templatePart.area === category; + return item.area === category; } return ( - item.templatePart.area === category || - ! templatePartAreas.includes( item.templatePart.area ) + item.area === category || + ! templatePartAreas.includes( item.area ) ); }; @@ -298,11 +274,7 @@ export const usePatterns = ( return useSelect( ( select ) => { if ( postType === TEMPLATE_PART_POST_TYPE ) { - return selectTemplatePartsAsPatterns( - select, - categoryId, - search - ); + return selectTemplateParts( select, categoryId, search ); } else if ( postType === PATTERN_TYPES.user && !! categoryId ) { const appliedCategory = categoryId === 'uncategorized' ? '' : categoryId; diff --git a/packages/editor/src/components/post-actions/actions.js b/packages/editor/src/components/post-actions/actions.js index 0783478126a1eb..89ac77a6945c4d 100644 --- a/packages/editor/src/components/post-actions/actions.js +++ b/packages/editor/src/components/post-actions/actions.js @@ -10,6 +10,7 @@ import { __, _n, sprintf, _x } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { useMemo, useState } from '@wordpress/element'; import { privateApis as patternsPrivateApis } from '@wordpress/patterns'; +import { parse } from '@wordpress/blocks'; import { Button, @@ -52,17 +53,17 @@ function isTemplateRemovable( template ) { // than the one returned from the endpoint. This is why we need to check for // two props whether is custom or has a theme file. return ( - [ template.source, template.templatePart?.source ].includes( - TEMPLATE_ORIGINS.custom - ) && - ! template.has_theme_file && - ! template.templatePart?.has_theme_file + template?.source === TEMPLATE_ORIGINS.custom && + ! template?.has_theme_file ); } const canDeleteOrReset = ( item ) => { const isTemplatePart = item.type === TEMPLATE_PART_POST_TYPE; const isUserPattern = item.type === PATTERN_TYPES.user; - return isUserPattern || ( isTemplatePart && item.isCustom ); + return ( + isUserPattern || + ( isTemplatePart && item.source === TEMPLATE_ORIGINS.custom ) + ); }; function getItemTitle( item ) { @@ -630,19 +631,13 @@ const renamePostAction = { // two props whether is custom or has a theme file. const isCustomPattern = isUserPattern || - ( isTemplatePart && - ( post.isCustom || post.source === TEMPLATE_ORIGINS.custom ) ); - const hasThemeFile = - isTemplatePart && - ( post.templatePart?.has_theme_file || post.has_theme_file ); + ( isTemplatePart && post.source === TEMPLATE_ORIGINS.custom ); + const hasThemeFile = post?.has_theme_file; return isCustomPattern && ! hasThemeFile; }, RenderModal: ( { items, closeModal, onActionPerformed } ) => { const [ item ] = items; - const originalTitle = decodeEntities( - typeof item.title === 'string' ? item.title : item.title.rendered - ); - const [ title, setTitle ] = useState( () => originalTitle ); + const [ title, setTitle ] = useState( () => getItemTitle( item ) ); const { editEntityRecord, saveEditedEntityRecord } = useDispatch( coreStore ); const { createSuccessNotice, createErrorNotice } = @@ -879,7 +874,7 @@ const isTemplatePartRevertable = ( item ) => { if ( ! item ) { return false; } - const hasThemeFile = item.templatePart?.has_theme_file; + const hasThemeFile = item?.has_theme_file; return canDeleteOrReset( item ) && hasThemeFile; }; @@ -1031,13 +1026,21 @@ export const duplicateTemplatePartAction = { modalHeader: _x( 'Duplicate template part', 'action label' ), RenderModal: ( { items, closeModal } ) => { const [ item ] = items; + const blocks = useMemo( () => { + return ( + item.blocks ?? + parse( item.content.raw, { + __unstableSkipMigrationLogs: true, + } ) + ); + }, [ item?.content?.raw, item.blocks ] ); const { createSuccessNotice } = useDispatch( noticesStore ); function onTemplatePartSuccess() { createSuccessNotice( sprintf( // translators: %s: The new template part's title e.g. 'Call to action (copy)'. __( '"%s" duplicated.' ), - item.title + getItemTitle( item ) ), { type: 'snackbar', id: 'edit-site-patterns-success' } ); @@ -1045,12 +1048,12 @@ export const duplicateTemplatePartAction = { } return ( async ( { registry } ) => { - const isResetting = items.every( - ( item ) => - !! item && - ( item.has_theme_file || - ( item.templatePart && item.templatePart.has_theme_file ) ) - ); + const isResetting = items.every( ( item ) => item?.has_theme_file ); const promiseResult = await Promise.allSettled( items.map( ( item ) => { diff --git a/test/e2e/specs/site-editor/site-editor-url-navigation.spec.js b/test/e2e/specs/site-editor/site-editor-url-navigation.spec.js index 337c26315c8a3f..f26fb8e13b8c3c 100644 --- a/test/e2e/specs/site-editor/site-editor-url-navigation.spec.js +++ b/test/e2e/specs/site-editor/site-editor-url-navigation.spec.js @@ -81,7 +81,7 @@ test.describe( 'Site editor url navigation', () => { .getByRole( 'region', { name: 'Patterns content', } ) - .getByLabel( 'header', { exact: true } ) + .getByText( 'header', { exact: true } ) .click(); await expect( page.getByRole( 'region', { name: 'Editor content' } )