Skip to content
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

Template inspector: Surface related patterns #55091

Merged
merged 2 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,14 @@ function injectThemeAttributeInBlockTemplateContent(
return block;
}

function preparePatterns( patterns, template, currentThemeStylesheet ) {
/**
* Filter all patterns and return only the ones that are compatible with the current template.
*
* @param {Array} patterns An array of patterns.
* @param {Object} template The current template.
* @return {Array} Array of patterns that are compatible with the current template.
*/
function filterPatterns( patterns, template ) {
// Filter out duplicates.
const filterOutDuplicatesByName = ( currentItem, index, items ) =>
index === items.findIndex( ( item ) => currentItem.name === item.name );
Expand All @@ -45,30 +52,33 @@ function preparePatterns( patterns, template, currentThemeStylesheet ) {
const filterOutExcludedPatternSources = ( pattern ) =>
! EXCLUDED_PATTERN_SOURCES.includes( pattern.source );

// Filter only the patterns that are compatible with the current template.
// Looks for patterns that have the same template type as the current template,
// or have a block type that matches the current template area.
const filterCompatiblePatterns = ( pattern ) =>
pattern.templateTypes?.includes( template.slug );
pattern.templateTypes?.includes( template.slug ) ||
pattern.blockTypes?.includes( 'core/template-part/' + template.area );

return patterns
.filter(
( pattern, index, items ) =>
filterOutExcludedPatternSources( pattern ) &&
filterOutDuplicatesByName( pattern, index, items ) &&
filterCompatiblePatterns( pattern )
)
.map( ( pattern ) => ( {
...pattern,
keywords: pattern.keywords || [],
type: PATTERN_TYPES.theme,
blocks: parse( pattern.content, {
__unstableSkipMigrationLogs: true,
} ).map( ( block ) =>
injectThemeAttributeInBlockTemplateContent(
block,
currentThemeStylesheet
)
),
} ) );
return patterns.filter(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also filter out the currently used pattern? Can we if we don't know which one it is?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's possible to determine the pattern used after it has been applied as there is no information available about the pattern used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can save the slug in the metadata like we do in #59251 - then we will have a way to know.

filterOutDuplicatesByName &&
filterOutExcludedPatternSources &&
filterCompatiblePatterns
);
}

function preparePatterns( patterns, template, currentThemeStylesheet ) {
return patterns.map( ( pattern ) => ( {
...pattern,
keywords: pattern.keywords || [],
type: PATTERN_TYPES.theme,
blocks: parse( pattern.content, {
__unstableSkipMigrationLogs: true,
} ).map( ( block ) =>
injectThemeAttributeInBlockTemplateContent(
block,
currentThemeStylesheet
)
),
} ) );
}

export function useAvailablePatterns( template ) {
Expand All @@ -92,8 +102,9 @@ export function useAvailablePatterns( template ) {
...( blockPatterns || [] ),
...( restBlockPatterns || [] ),
];
const filteredPatterns = filterPatterns( mergedPatterns, template );
return preparePatterns(
mergedPatterns,
filteredPatterns,
template,
currentThemeStylesheet
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { PanelBody } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { PanelBody, PanelRow } from '@wordpress/components';
import {
PageAttributesPanel,
PostDiscussionPanel,
Expand All @@ -15,6 +15,10 @@ import {
import { store as coreStore } from '@wordpress/core-data';
import { decodeEntities } from '@wordpress/html-entities';
import { navigation, symbol } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import { useAsyncList } from '@wordpress/compose';
import { serialize } from '@wordpress/blocks';
import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor';

/**
* Internal dependencies
Expand All @@ -23,36 +27,71 @@ import { store as editSiteStore } from '../../../store';
import TemplateActions from './template-actions';
import TemplateAreas from './template-areas';
import SidebarCard from '../sidebar-card';
import { useAvailablePatterns } from './hooks';
import { TEMPLATE_PART_POST_TYPE } from '../../../utils/constants';

const CARD_ICONS = {
wp_block: symbol,
wp_navigation: navigation,
};

function TemplatesList( { availableTemplates, onSelect } ) {
const shownTemplates = useAsyncList( availableTemplates );
if ( ! availableTemplates || availableTemplates?.length < 2 ) {
return null;
}

return (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, to help with performance, in the other places that we list patterns we have added client-side pagination, eg. https://github.com/WordPress/gutenberg/blob/trunk/packages/edit-site/src/components/page-patterns/patterns-list.js#L210, you may want to think about also adding it here, but could be a follow up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When displaying patterns for a particular template or a part of it, we anticipate that we won't have to show too many patterns. However, if this does become a problem, we can revisit the idea of adding pagination in the future.

<BlockPatternsList
label={ __( 'Templates' ) }
blockPatterns={ availableTemplates }
shownPatterns={ shownTemplates }
onClickPattern={ onSelect }
showTitlesAsTooltip={ true }
/>
);
}

export default function TemplatePanel() {
const { title, description, icon, record } = useSelect( ( select ) => {
const { getEditedPostType, getEditedPostId } = select( editSiteStore );
const { getEditedEntityRecord } = select( coreStore );
const { __experimentalGetTemplateInfo: getTemplateInfo } =
select( editorStore );
const { title, description, icon, record, postType, postId } = useSelect(
( select ) => {
const { getEditedPostType, getEditedPostId } =
select( editSiteStore );
const { getEditedEntityRecord } = select( coreStore );
const { __experimentalGetTemplateInfo: getTemplateInfo } =
select( editorStore );

const type = getEditedPostType();
const _postId = getEditedPostId();
const _record = getEditedEntityRecord( 'postType', type, _postId );
const info = getTemplateInfo( _record );

const type = getEditedPostType();
const postId = getEditedPostId();
const _record = getEditedEntityRecord( 'postType', type, postId );
const info = getTemplateInfo( _record );
return {
title: info.title,
description: info.description,
icon: info.icon,
record: _record,
postType: type,
postId: _postId,
};
},
[]
);

return {
title: info.title,
description: info.description,
icon: info.icon,
record: _record,
};
}, [] );
const availablePatterns = useAvailablePatterns( record );
const { editEntityRecord } = useDispatch( coreStore );

if ( ! title && ! description ) {
return null;
}

const onTemplateSelect = async ( selectedTemplate ) => {
await editEntityRecord( 'postType', postType, postId, {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since record is already an entity can we edit it directly?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot directly mutate a record as that way the store update would not propagate. Instead, we need to call editEntityRecord as we are doing currently.

blocks: selectedTemplate.blocks,
content: serialize( selectedTemplate.blocks ),
} );
};

return (
<>
<PanelBody>
Expand All @@ -66,6 +105,22 @@ export default function TemplatePanel() {
<TemplateAreas />
</SidebarCard>
</PanelBody>
<PanelBody
title={ __( 'Transform into:' ) }
initialOpen={ postType === TEMPLATE_PART_POST_TYPE }
>
<PanelRow>
<p>
{ __(
'Choose a predefined pattern to switch up the look of your template.' // TODO - make this dynamic?
) }
</p>
</PanelRow>
<TemplatesList
availableTemplates={ availablePatterns }
onSelect={ onTemplateSelect }
/>
</PanelBody>
<PostLastRevisionPanel />
<PostTaxonomiesPanel />
<PostFeaturedImagePanel />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,11 @@ h3.edit-site-template-card__template-areas-title {
margin: 0 0 $grid-unit-10;
}


.edit-site-template-panel__replace-template-modal {
z-index: z-index(".edit-site-template-panel__replace-template-modal");
.edit-site-template-card__templates-list {
margin-top: $grid-unit-20;
}

.edit-site-template-panel__replace-template-modal__content {
column-count: 2;
column-gap: $grid-unit-30;

@include break-medium() {
column-count: 3;
}

@include break-wide() {
column-count: 4;
}
.edit-site-template-panel .block-editor-block-preview__container {
border-radius: 2px;
box-shadow: none;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,12 @@ import { moreVertical } from '@wordpress/icons';
*/
import { store as editSiteStore } from '../../../store';
import isTemplateRevertable from '../../../utils/is-template-revertable';
import ReplaceTemplateButton from './replace-template-button';
scruffian marked this conversation as resolved.
Show resolved Hide resolved
import { useAvailablePatterns } from './hooks';

export default function Actions( { template } ) {
const availablePatterns = useAvailablePatterns( template );
const { revertTemplate } = useDispatch( editSiteStore );
const isRevertable = isTemplateRevertable( template );

if (
! isRevertable &&
( ! availablePatterns.length || availablePatterns.length < 1 )
) {
if ( ! isRevertable ) {
return null;
}

Expand All @@ -48,11 +42,6 @@ export default function Actions( { template } ) {
{ __( 'Clear customizations' ) }
</MenuItem>
) }
<ReplaceTemplateButton
availableTemplates={ availablePatterns }
template={ template }
onClick={ onClose }
/>
</MenuGroup>
) }
</DropdownMenu>
Expand Down
Loading