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

Add template replace flow to template inspector #54609

Merged
merged 35 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
40299b2
Add a modal to allow template switching
scruffian Sep 19, 2023
d67576e
fetch template info
scruffian Sep 20, 2023
49301d5
Allow switching to different patterns
scruffian Sep 21, 2023
5d0c438
Allow switching to different patterns
scruffian Sep 21, 2023
0e5bdaa
Add columns
scruffian Sep 21, 2023
accc523
move availble templates to the actions
scruffian Sep 21, 2023
1947768
filter for the correct templates
scruffian Sep 21, 2023
7e5e8c9
create the right data structure in the use select
scruffian Sep 21, 2023
ac58a31
move to a hook
scruffian Sep 21, 2023
e1dc6be
inject theme attribute into pattern again
scruffian Sep 21, 2023
540dfbc
put the overlay over the top of the dropdown
scruffian Sep 21, 2023
7c9d71a
fix the pattern to templates hook
draganescu Sep 25, 2023
a00363f
set the template on click
draganescu Sep 25, 2023
c78d255
Also set the blocks
scruffian Sep 26, 2023
9b76d30
remove calls to set template with the current template, since setting…
draganescu Sep 26, 2023
c3a13a4
serialize blocks so that we have correctly processed template parts
scruffian Sep 26, 2023
5787636
remove duplicated code
scruffian Sep 26, 2023
59cbfab
Remove unnecessary mapping
scruffian Sep 26, 2023
3c513a3
refactor
scruffian Sep 26, 2023
a3763ea
memoize the patterns
scruffian Sep 26, 2023
af09641
combine the useSelect
scruffian Sep 26, 2023
083478b
Update packages/edit-site/src/components/sidebar-edit-mode/page-panel…
scruffian Sep 26, 2023
ca5935a
Fix ESLint error
Mamaduka Sep 26, 2023
e7226e4
Only show the button is there is more than 1 pattern
scruffian Sep 26, 2023
f5ef5d7
Copy update
scruffian Sep 26, 2023
59c7c24
Move the hook to a subdir
scruffian Sep 27, 2023
3f1fd65
check that there are patterns
scruffian Sep 27, 2023
dd80e09
move the check
scruffian Sep 27, 2023
4b4e835
remove useCallback
scruffian Sep 27, 2023
6c54a97
change condition to show the button
scruffian Sep 27, 2023
0b1af45
change condition
scruffian Sep 27, 2023
f7a087b
move to use editEntityRecord
scruffian Sep 27, 2023
7c7fdfd
combine filters
scruffian Sep 27, 2023
3521d12
add comments
scruffian Sep 27, 2023
03e26f3
Update packages/edit-site/src/components/sidebar-edit-mode/template-p…
scruffian Sep 27, 2023
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
1 change: 1 addition & 0 deletions packages/base-styles/_z-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ $z-layers: (
".block-editor-block-rename-modal": 1000001,
".edit-site-list__rename-modal": 1000001,
".edit-site-swap-template-modal": 1000001,
".edit-site-template-panel__replace-template-modal": 1000001,

// Note: The ConfirmDialog component's z-index is being set to 1000001 in packages/components/src/confirm-dialog/styles.ts
// because it uses emotion and not sass. We need it to render on top its parent popover.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { useMemo } from '@wordpress/element';
import { store as coreStore } from '@wordpress/core-data';
import { parse } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../../store';
import { PATTERN_CORE_SOURCES, PATTERN_TYPES } from '../../../utils/constants';
import { unlock } from '../../../lock-unlock';

function injectThemeAttributeInBlockTemplateContent(
block,
currentThemeStylesheet
) {
block.innerBlocks = block.innerBlocks.map( ( innerBlock ) => {
return injectThemeAttributeInBlockTemplateContent(
innerBlock,
currentThemeStylesheet
);
} );

if (
block.name === 'core/template-part' &&
block.attributes.theme === undefined
) {
block.attributes.theme = currentThemeStylesheet;
}
return block;
}

function preparePatterns( patterns, template, currentThemeStylesheet ) {
// Filter out duplicates.
const filterOutDuplicatesByName = ( currentItem, index, items ) =>
index === items.findIndex( ( item ) => currentItem.name === item.name );

// Filter out core patterns.
const filterOutCorePatterns = ( pattern ) =>
! PATTERN_CORE_SOURCES.includes( pattern.source );

// Filter only the patterns that are compatible with the current template.
const filterCompatiblePatterns = ( pattern ) =>
pattern.templateTypes?.includes( template.slug );

return patterns
.filter(
filterOutCorePatterns &&
filterOutDuplicatesByName &&
filterCompatiblePatterns
)
.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 ) {
const { blockPatterns, restBlockPatterns, currentThemeStylesheet } =
useSelect( ( select ) => {
const { getSettings } = unlock( select( editSiteStore ) );
const settings = getSettings();

return {
blockPatterns:
settings.__experimentalAdditionalBlockPatterns ??
settings.__experimentalBlockPatterns,
restBlockPatterns: select( coreStore ).getBlockPatterns(),
currentThemeStylesheet:
select( coreStore ).getCurrentTheme().stylesheet,
};
}, [] );

return useMemo( () => {
const mergedPatterns = [
...( blockPatterns || [] ),
...( restBlockPatterns || [] ),
];
return preparePatterns(
mergedPatterns,
template,
currentThemeStylesheet
);
}, [ blockPatterns, restBlockPatterns, template, currentThemeStylesheet ] );
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This file is a bit confusing to me. It's unfortunate that we have to do all this work in order to get the available list of patterns for a given template.

  • Isn't there a canonical way to retrieve block patterns?
  • Do we really need the "inject" stuff?

We need to have more clarity in terms of APIs there. How should a developer know that he has to do all of this in order to make use of patterns?

cc @ellatrix @jsnajdr as you both worked on patterns before as well.

Copy link
Member

Choose a reason for hiding this comment

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

There is the getAllPatterns selector in core/block-editor store that could replace most of this useAvailablePatterns hook. But it has one major difference: it also loads all the reusable blocks, aka user patterns, using the getReusableBlocks selector. But the Site Editor is apparently never interested in user patterns. Does it even make sense to use user patterns in Site Editor? Or are they intended only for the Post Editor.

If we have a version of getAllPatterns that doesn't return user patterns, we could use it at all four places in Site Editor where the code that gathers patterns from various locations is repeated.

Copy link
Member

Choose a reason for hiding this comment

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

Do we really need the "inject" stuff?

Seems to me it was needed at the time when this PR was merged -- it was adding a theme attribute to all core/template-part blocks that didn't have a theme attribute. But then, one month later, @gziolo merged #55965 which added the theme fallback directly in the core/template-part block.

Today, I believe injectThemeAttributeInBlockTemplateContent could be removed. From more than one place.

Copy link
Member

Choose a reason for hiding this comment

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

We would have to perform some extensive testing, but hopefully, injecting a theme attribute on the template level should no longer be necessary.

Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useState } from '@wordpress/element';
import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor';
import { MenuItem, Modal } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { store as coreStore } from '@wordpress/core-data';
import { useAsyncList } from '@wordpress/compose';
import { serialize } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../../store';

export default function ReplaceTemplateButton( {
onClick,
availableTemplates,
} ) {
const { editEntityRecord } = useDispatch( coreStore );
const [ showModal, setShowModal ] = useState( false );
const onClose = () => {
setShowModal( false );
};

const { postId, postType } = useSelect( ( select ) => {
return {
postId: select( editSiteStore ).getEditedPostId(),
postType: select( editSiteStore ).getEditedPostType(),
};
}, [] );

const onTemplateSelect = async ( selectedTemplate ) => {
onClose(); // Close the template suggestions modal first.
onClick();
await editEntityRecord( 'postType', postType, postId, {
blocks: selectedTemplate.blocks,
content: serialize( selectedTemplate.blocks ),
} );
};

if ( ! availableTemplates.length || availableTemplates.length < 1 ) {
return null;
}

return (
<>
<MenuItem
info={ __(
'Replace the contents of this template with another.'
) }
onClick={ () => setShowModal( true ) }
>
{ __( 'Replace template' ) }
</MenuItem>

{ showModal && (
<Modal
title={ __( 'Choose a template' ) }
onRequestClose={ onClose }
overlayClassName="edit-site-template-panel__replace-template-modal"
isFullScreen
>
<div className="edit-site-template-panel__replace-template-modal__content">
<TemplatesList
availableTemplates={ availableTemplates }
onSelect={ onTemplateSelect }
/>
</div>
</Modal>
) }
</>
);
}

function TemplatesList( { availableTemplates, onSelect } ) {
const shownTemplates = useAsyncList( availableTemplates );

return (
<BlockPatternsList
label={ __( 'Templates' ) }
blockPatterns={ availableTemplates }
shownPatterns={ shownTemplates }
onClickPattern={ onSelect }
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,21 @@ h3.edit-site-template-card__template-areas-title {
font-weight: 500;
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-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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ import { moreVertical } from '@wordpress/icons';
*/
import { store as editSiteStore } from '../../../store';
import isTemplateRevertable from '../../../utils/is-template-revertable';
import ReplaceTemplateButton from './replace-template-button';
import { useAvailablePatterns } from './hooks';

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

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

return (
<DropdownMenu
icon={ moreVertical }
Expand All @@ -27,17 +35,24 @@ export default function Actions( { template } ) {
>
{ ( { onClose } ) => (
<MenuGroup>
<MenuItem
info={ __(
'Use the template as supplied by the theme.'
) }
onClick={ () => {
revertTemplate( template );
onClose();
} }
>
{ __( 'Clear customizations' ) }
</MenuItem>
{ isRevertable && (
<MenuItem
info={ __(
'Use the template as supplied by the theme.'
) }
onClick={ () => {
revertTemplate( template );
onClose();
} }
>
{ __( 'Clear customizations' ) }
</MenuItem>
) }
<ReplaceTemplateButton
availableTemplates={ availablePatterns }
template={ template }
onClick={ onClose }
/>
</MenuGroup>
) }
</DropdownMenu>
Expand Down
Loading