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' } )