Skip to content

Commit

Permalink
Add template replace flow to template inspector (#54609)
Browse files Browse the repository at this point in the history
* Add a modal to allow template switching

* fetch template info

* Allow switching to different patterns

* Allow switching to different patterns

* Add columns

* move availble templates to the actions

* filter for the correct templates

* create the right data structure in the use select

* move to a hook

* inject theme attribute into pattern again

* put the overlay over the top of the dropdown

* fix the pattern to templates hook

* set the template on click

* Also set the blocks

* remove calls to set template with the current template, since setting blocks correctly updates the content in the editor

* serialize blocks so that we have correctly processed template parts

* remove duplicated code

* Remove unnecessary mapping

* refactor

* memoize the patterns

* combine the useSelect

* Update packages/edit-site/src/components/sidebar-edit-mode/page-panels/hooks.js

Co-authored-by: Andrei Draganescu <me@andreidraganescu.info>

* Fix ESLint error

* Only show the button is there is more than 1 pattern

* Copy update

* Move the hook to a subdir

* check that there are patterns

* move the check

* remove useCallback

* change condition to show the button

* change condition

* move to use editEntityRecord

* combine filters

* add comments

* Update packages/edit-site/src/components/sidebar-edit-mode/template-panel/replace-template-button.js

Co-authored-by: Andrei Draganescu <me@andreidraganescu.info>

---------

Co-authored-by: Andrei Draganescu <andrei.draganescu@automattic.com>
Co-authored-by: Andrei Draganescu <me@andreidraganescu.info>
Co-authored-by: George Mamadashvili <georgemamadashvili@gmail.com>
  • Loading branch information
4 people authored Oct 3, 2023
1 parent 8b264bf commit 39b57b0
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 12 deletions.
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 ] );
}
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

1 comment on commit 39b57b0

@github-actions
Copy link

Choose a reason for hiding this comment

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

Flaky tests detected in 39b57b0.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/6397951748
📝 Reported issues:

Please sign in to comment.