From 1a2a04e11a67edee16e0f5ca9f447281d4c7344d Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 15 Feb 2022 11:02:19 +0100 Subject: [PATCH 01/12] Update template part creation and replacement flows --- packages/block-editor/src/components/index.js | 1 + .../src/template-part/edit/index.js | 106 +++-- .../template-part/edit/placeholder/index.js | 152 +------ .../edit/selection-modal/index.js | 102 +++++ .../patterns-list.js} | 92 ++--- .../selection-modal/template-part-list.js | 123 ++++++ .../src/template-part/edit/selection/index.js | 37 -- .../edit/selection/template-part-previews.js | 372 ------------------ .../src/template-part/editor.scss | 93 ++--- 9 files changed, 362 insertions(+), 716 deletions(-) create mode 100644 packages/block-library/src/template-part/edit/selection-modal/index.js rename packages/block-library/src/template-part/edit/{placeholder/patterns-setup.js => selection-modal/patterns-list.js} (53%) create mode 100644 packages/block-library/src/template-part/edit/selection-modal/template-part-list.js delete mode 100644 packages/block-library/src/template-part/edit/selection/index.js delete mode 100644 packages/block-library/src/template-part/edit/selection/template-part-previews.js diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 87ef64deccc6f5..07b7d5c764c73d 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -145,6 +145,7 @@ export { default as WritingFlow } from './writing-flow'; export { default as useBlockDisplayInformation } from './use-block-display-information'; export { default as __unstableIframe } from './iframe'; export { default as __experimentalUseNoRecursiveRenders } from './use-no-recursive-renders'; +export { default as __experimentalBlockPatternsList } from './block-patterns-list'; /* * State Related Components diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index 9ee220f672cb63..744d31db9c7dad 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -15,19 +15,20 @@ import { store as blockEditorStore, } from '@wordpress/block-editor'; import { - Dropdown, ToolbarGroup, ToolbarButton, Spinner, + Modal, } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { store as coreStore } from '@wordpress/core-data'; +import { useState } from '@wordpress/element'; /** * Internal dependencies */ import TemplatePartPlaceholder from './placeholder'; -import TemplatePartSelection from './selection'; +import TemplatePartSelectionModal from './selection-modal'; import { TemplatePartAdvancedControls } from './advanced-controls'; import TemplatePartInnerBlocks from './inner-blocks'; import { createTemplatePartId } from './utils/create-template-part-id'; @@ -39,10 +40,13 @@ export default function TemplatePartEdit( { } ) { const { slug, theme, tagName, layout = {} } = attributes; const templatePartId = createTemplatePartId( theme, slug ); - const [ hasAlreadyRendered, RecursionProvider ] = useNoRecursiveRenders( templatePartId ); + const [ + isTemplatePartSelectionOpen, + setIsTemplatePartSelectionOpen, + ] = useState( false ); // Set the postId block attribute if it did not exist, // but wait until the inner blocks have loaded to allow @@ -53,15 +57,12 @@ export default function TemplatePartEdit( { isMissing, defaultWrapper, area, - enableSelection, - hasResolvedReplacements, + areaLabel, } = useSelect( ( select ) => { - const { - getEditedEntityRecord, - getEntityRecords, - hasFinishedResolution, - } = select( coreStore ); + const { getEditedEntityRecord, hasFinishedResolution } = select( + coreStore + ); const { getBlocks } = select( blockEditorStore ); const getEntityArgs = [ @@ -73,20 +74,6 @@ export default function TemplatePartEdit( { ? getEditedEntityRecord( ...getEntityArgs ) : null; const _area = entityRecord?.area || attributes.area; - - // Check whether other entities exist for switching/selection. - const availableReplacementArgs = [ - 'postType', - 'wp_template_part', - _area && 'uncategorized' !== _area && { area: _area }, - ]; - const matchingReplacements = getEntityRecords( - ...availableReplacementArgs - ); - const _enableSelection = templatePartId - ? matchingReplacements?.length > 1 - : matchingReplacements?.length > 0; - const hasResolvedEntity = templatePartId ? hasFinishedResolution( 'getEditedEntityRecord', @@ -97,7 +84,7 @@ export default function TemplatePartEdit( { // FIXME: @wordpress/block-library should not depend on @wordpress/editor. // Blocks can be loaded into a *non-post* block editor. // eslint-disable-next-line @wordpress/data-no-store-string-literals - const defaultWrapperElement = select( 'core/editor' ) + const areaObject = select( 'core/editor' ) .__experimentalGetDefaultTemplatePartAreas() .find( ( { area: value } ) => value === _area )?.area_tag; @@ -105,13 +92,9 @@ export default function TemplatePartEdit( { innerBlocks: getBlocks( clientId ), isResolved: hasResolvedEntity, isMissing: hasResolvedEntity && isEmpty( entityRecord ), - defaultWrapper: defaultWrapperElement || 'div', + defaultWrapper: areaObject?.area_tag ?? 'div', + areaLabel: areaObject?.label || __( 'Template Part' ), area: _area, - enableSelection: _enableSelection, - hasResolvedReplacements: hasFinishedResolution( - 'getEntityRecords', - availableReplacementArgs - ), }; }, [ templatePartId, clientId ] @@ -166,37 +149,22 @@ export default function TemplatePartEdit( { + setIsTemplatePartSelectionOpen( true ) + } /> ) } - { isEntityAvailable && enableSelection && ( + { isEntityAvailable && ( - ( - - { __( 'Replace' ) } - - ) } - renderContent={ ( { onClose } ) => ( - - ) } - /> + + setIsTemplatePartSelectionOpen( true ) + } + > + { __( 'Replace' ) } + ) } @@ -215,6 +183,30 @@ export default function TemplatePartEdit( { ) } + { isTemplatePartSelectionOpen && ( + + setIsTemplatePartSelectionOpen( false ) + } + > + + setIsTemplatePartSelectionOpen( false ) + } + /> + + ) } ); } diff --git a/packages/block-library/src/template-part/edit/placeholder/index.js b/packages/block-library/src/template-part/edit/placeholder/index.js index 64a730535c0f5b..fa3a04e2086c84 100644 --- a/packages/block-library/src/template-part/edit/placeholder/index.js +++ b/packages/block-library/src/template-part/edit/placeholder/index.js @@ -1,39 +1,19 @@ /** * External dependencies */ -import { find, kebabCase } from 'lodash'; +import { find } from 'lodash'; /** * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { useCallback, useState } from '@wordpress/element'; -import { useDispatch, useSelect } from '@wordpress/data'; -import { Placeholder, Dropdown, Button, Spinner } from '@wordpress/components'; -import { serialize } from '@wordpress/blocks'; -import { store as coreStore } from '@wordpress/core-data'; - -/** - * Internal dependencies - */ -import TemplatePartSelection from '../selection'; -import PatternsSetup from './patterns-setup'; - -const PLACEHOLDER_STEPS = { - initial: 1, - patterns: 2, -}; +import { useSelect } from '@wordpress/data'; +import { Placeholder, Button } from '@wordpress/components'; export default function TemplatePartPlaceholder( { area, - clientId, - setAttributes, - enableSelection, - hasResolvedReplacements, + onOpenSelectionModal, } ) { - const { saveEntityRecord } = useDispatch( coreStore ); - const [ step, setStep ] = useState( PLACEHOLDER_STEPS.initial ); - const { areaIcon, areaLabel } = useSelect( ( select ) => { // FIXME: @wordpress/block-library should not depend on @wordpress/editor. @@ -54,119 +34,19 @@ export default function TemplatePartPlaceholder( { [ area ] ); - const onCreate = useCallback( - async ( - startingBlocks = [], - title = __( 'Untitled Template Part' ) - ) => { - // If we have `area` set from block attributes, means an exposed - // block variation was inserted. So add this prop to the template - // part entity on creation. Afterwards remove `area` value from - // block attributes. - const record = { - title, - slug: kebabCase( title ), - content: serialize( startingBlocks ), - // `area` is filterable on the server and defaults to `UNCATEGORIZED` - // if provided value is not allowed. - area, - }; - const templatePart = await saveEntityRecord( - 'postType', - 'wp_template_part', - record - ); - setAttributes( { - slug: templatePart.slug, - theme: templatePart.theme, - area: undefined, - } ); - }, - [ setAttributes, area ] - ); - return ( - <> - { step === PLACEHOLDER_STEPS.initial && ( - - { ! hasResolvedReplacements ? ( - - ) : ( - ( - <> - { enableSelection && ( - - ) } - - - ) } - renderContent={ ( { onClose } ) => ( - - ) } - /> - ) } - - ) } - { step === PLACEHOLDER_STEPS.patterns && ( - - setStep( PLACEHOLDER_STEPS.initial ) - } - /> + + > + + ); } diff --git a/packages/block-library/src/template-part/edit/selection-modal/index.js b/packages/block-library/src/template-part/edit/selection-modal/index.js new file mode 100644 index 00000000000000..0a115a0ca4cd78 --- /dev/null +++ b/packages/block-library/src/template-part/edit/selection-modal/index.js @@ -0,0 +1,102 @@ +/** + * External dependencies + */ +import { kebabCase } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useCallback } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; +import { useDispatch } from '@wordpress/data'; +import { serialize } from '@wordpress/blocks'; +import { store as coreStore } from '@wordpress/core-data'; + +/** + * Internal dependencies + */ +import TemplatePartList from './template-part-list'; +import PatternsList from './patterns-list'; + +export default function TemplatePartSelectionModal( { + setAttributes, + onClose, + templatePartId = null, + area, + areaLabel, + clientId, +} ) { + const { createSuccessNotice } = useDispatch( noticesStore ); + const { saveEntityRecord } = useDispatch( coreStore ); + + const onTemplatePartSelect = useCallback( ( templatePart ) => { + setAttributes( { + slug: templatePart.slug, + theme: templatePart.theme, + area: undefined, + } ); + createSuccessNotice( + sprintf( + /* translators: %s: template part title. */ + __( 'Template Part "%s" inserted.' ), + templatePart.title?.rendered || templatePart.slug + ), + { + type: 'snackbar', + } + ); + onClose(); + }, [] ); + + const onBlockPatternSelect = async ( + startingBlocks = [], + title = __( 'Untitled Template Part' ) + ) => { + // If we have `area` set from block attributes, means an exposed + // block variation was inserted. So add this prop to the template + // part entity on creation. Afterwards remove `area` value from + // block attributes. + const record = { + title, + slug: kebabCase( title ), + content: serialize( startingBlocks ), + // `area` is filterable on the server and defaults to `UNCATEGORIZED` + // if provided value is not allowed. + area, + }; + const templatePart = await saveEntityRecord( + 'postType', + 'wp_template_part', + record + ); + setAttributes( { + slug: templatePart.slug, + theme: templatePart.theme, + area: undefined, + } ); + }; + + return ( +
+
+

{ __( 'Pick from the existing template parts' ) }

+ +
+ +
+

{ __( 'Pick from the existing patterns' ) }

+ +
+
+ ); +} diff --git a/packages/block-library/src/template-part/edit/placeholder/patterns-setup.js b/packages/block-library/src/template-part/edit/selection-modal/patterns-list.js similarity index 53% rename from packages/block-library/src/template-part/edit/placeholder/patterns-setup.js rename to packages/block-library/src/template-part/edit/selection-modal/patterns-list.js index bd28c4679fb313..781512efffa229 100644 --- a/packages/block-library/src/template-part/edit/placeholder/patterns-setup.js +++ b/packages/block-library/src/template-part/edit/selection-modal/patterns-list.js @@ -1,8 +1,11 @@ /** * WordPress dependencies */ -import { __experimentalBlockPatternSetup as BlockPatternSetup } from '@wordpress/block-editor'; -import { useEffect, useState } from '@wordpress/element'; +import { + __experimentalBlockPatternsList as BlockPatternsList, + store as blockEditorStore, +} from '@wordpress/block-editor'; +import { useState, useMemo } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { TextControl, @@ -10,55 +13,61 @@ import { FlexItem, Button, Modal, - Placeholder, } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; +import { useAsyncList } from '@wordpress/compose'; -export default function PatternsSetup( { +export default function PatternsList( { area, areaLabel, - areaIcon, clientId, - onCreate, - resetPlaceholder, + onSelect, } ) { const blockNameWithArea = area ? `core/template-part/${ area }` : 'core/template-part'; + const blockPatterns = useSelect( + ( select ) => { + const { + getBlockRootClientId, + __experimentalGetAllowedPatterns, + } = select( blockEditorStore ); + const rootClientId = getBlockRootClientId( clientId ); + return __experimentalGetAllowedPatterns( rootClientId ); + }, + [ clientId ] + ); + const filteredBlockPatterns = useMemo( () => { + return blockPatterns.filter( ( pattern ) => + pattern?.blockTypes?.some?.( + ( blockType ) => blockType === blockNameWithArea + ) + ); + }, [ blockNameWithArea, blockPatterns ] ); // Restructure onCreate to set the blocks on local state. // Add modal to confirm title and trigger onCreate. const [ title, setTitle ] = useState( __( 'Untitled Template Part' ) ); const [ startingBlocks, setStartingBlocks ] = useState( [] ); const [ isTitleStep, setIsTitleStep ] = useState( false ); - - const selectPattern = ( selectedPattern ) => { - setStartingBlocks( selectedPattern ); - setIsTitleStep( true ); - }; + const shownPatterns = useAsyncList( filteredBlockPatterns ); const submitForCreation = ( event ) => { event.preventDefault(); - onCreate( startingBlocks, title ); + onSelect( startingBlocks, title ); }; return ( <> - - } - onBlockPatternSelect={ selectPattern } - filterPatternsFn={ ( pattern ) => - pattern?.blockTypes?.some?.( - ( blockType ) => blockType === blockNameWithArea - ) - } + { + setStartingBlocks( blocks ); + setIsTitleStep( true ); + } } /> + { isTitleStep && ( setIsTitleStep( false ) } >
- - - + + + { showTitleModal && ( + setShowTitleModal( false ) } + onSubmit={ ( title ) => createFromBlocks( [], title ) } /> - - + ) } + ); } diff --git a/packages/block-library/src/template-part/edit/selection-modal/patterns-list.js b/packages/block-library/src/template-part/edit/selection-modal/patterns-list.js index 781512efffa229..ab6337f484c782 100644 --- a/packages/block-library/src/template-part/edit/selection-modal/patterns-list.js +++ b/packages/block-library/src/template-part/edit/selection-modal/patterns-list.js @@ -6,17 +6,14 @@ import { store as blockEditorStore, } from '@wordpress/block-editor'; import { useState, useMemo } from '@wordpress/element'; -import { __, sprintf } from '@wordpress/i18n'; -import { - TextControl, - Flex, - FlexItem, - Button, - Modal, -} from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { useAsyncList } from '@wordpress/compose'; +/** + * Internal dependencies + */ +import TitleModal from './title-modal'; + export default function PatternsList( { area, areaLabel, @@ -47,13 +44,11 @@ export default function PatternsList( { // Restructure onCreate to set the blocks on local state. // Add modal to confirm title and trigger onCreate. - const [ title, setTitle ] = useState( __( 'Untitled Template Part' ) ); const [ startingBlocks, setStartingBlocks ] = useState( [] ); const [ isTitleStep, setIsTitleStep ] = useState( false ); const shownPatterns = useAsyncList( filteredBlockPatterns ); - const submitForCreation = ( event ) => { - event.preventDefault(); + const onSubmitTitle = ( title ) => { onSelect( startingBlocks, title ); }; @@ -69,39 +64,11 @@ export default function PatternsList( { /> { isTitleStep && ( - setIsTitleStep( false ) } - > - - - - - - - - - + setIsTitleStep( false ) } + onSubmit={ onSubmitTitle } + /> ) } ); diff --git a/packages/block-library/src/template-part/edit/selection-modal/title-modal.js b/packages/block-library/src/template-part/edit/selection-modal/title-modal.js new file mode 100644 index 00000000000000..f5336e718e57ae --- /dev/null +++ b/packages/block-library/src/template-part/edit/selection-modal/title-modal.js @@ -0,0 +1,59 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; +import { + TextControl, + Flex, + FlexItem, + Button, + Modal, +} from '@wordpress/components'; + +export default function TitleModal( { areaLabel, onClose, onSubmit } ) { + // Restructure onCreate to set the blocks on local state. + // Add modal to confirm title and trigger onCreate. + const [ title, setTitle ] = useState( __( 'Untitled Template Part' ) ); + + const submitForCreation = ( event ) => { + event.preventDefault(); + onSubmit( title ); + }; + + return ( + +
+ + + + + + + +
+ ); +} diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index be1fcb76245966..c499d700c6ea8c 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -1,10 +1,14 @@ -.block-editor-template-part-placeholder__modal { +.block-editor-template-part__selection-modal { @include break-medium() { width: $break-medium - $grid-unit-20 * 2; } + + .components-modal__content { + padding: 0; + } } -.block-library-template-part__selection .block-editor-block-patterns-list { +.block-library-template-part__selection-content .block-editor-block-patterns-list { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-gap: $grid-unit-10; @@ -13,3 +17,20 @@ margin-bottom: 0; } } + +.block-library-template-part__selection-content { + padding: 0 $grid-unit-40 $grid-unit-20; +} + +.block-library-template-part__selection-footer { + position: sticky; + bottom: 0; + background: $white; + border-top: $border-width solid $gray-300; + padding: $grid-unit-20 $grid-unit-40; + display: flex; + flex-direction: row; + justify-content: flex-end; + width: 100%; + z-index: z-index(".components-modal__header"); +} From a33b0ee6940df4b53215d7f3db7434ce6cc8b061 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 15 Feb 2022 14:33:53 +0100 Subject: [PATCH 04/12] Fix bug --- packages/block-library/src/template-part/edit/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index 79bb1a67352ae0..7ce36ae9f16acf 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -86,7 +86,7 @@ export default function TemplatePartEdit( { // eslint-disable-next-line @wordpress/data-no-store-string-literals const areaObject = select( 'core/editor' ) .__experimentalGetDefaultTemplatePartAreas() - .find( ( { area: value } ) => value === _area )?.area_tag; + .find( ( { area: value } ) => value === _area ); return { innerBlocks: getBlocks( clientId ), From fb31d12e4b100c6f0838a1e36e5c353721b80e5a Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 17 Feb 2022 11:56:27 +0100 Subject: [PATCH 05/12] Simplify headings --- .../src/template-part/edit/selection-modal/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/template-part/edit/selection-modal/index.js b/packages/block-library/src/template-part/edit/selection-modal/index.js index 3e54f841ae3f7c..09d7b86ea80211 100644 --- a/packages/block-library/src/template-part/edit/selection-modal/index.js +++ b/packages/block-library/src/template-part/edit/selection-modal/index.js @@ -78,14 +78,13 @@ export default function TemplatePartSelectionModal( { theme: templatePart.theme, area: undefined, } ); - onClose(); }; return ( <>
-

{ __( 'Pick from the existing template parts' ) }

+

{ __( 'Existing template parts' ) }

-

{ __( 'Pick from the existing patterns' ) }

+

{ __( 'Patterns' ) }

Date: Thu, 17 Feb 2022 12:24:30 +0100 Subject: [PATCH 06/12] Extract hooks to fetch available template parts and patterns --- .../edit/selection-modal/index.js | 85 ++++++++++++----- .../edit/selection-modal/patterns-list.js | 75 --------------- .../selection-modal/template-part-list.js | 71 -------------- .../src/template-part/edit/utils/hooks.js | 92 +++++++++++++++++++ 4 files changed, 154 insertions(+), 169 deletions(-) delete mode 100644 packages/block-library/src/template-part/edit/selection-modal/patterns-list.js delete mode 100644 packages/block-library/src/template-part/edit/selection-modal/template-part-list.js create mode 100644 packages/block-library/src/template-part/edit/utils/hooks.js diff --git a/packages/block-library/src/template-part/edit/selection-modal/index.js b/packages/block-library/src/template-part/edit/selection-modal/index.js index 09d7b86ea80211..aecd9d4068aea0 100644 --- a/packages/block-library/src/template-part/edit/selection-modal/index.js +++ b/packages/block-library/src/template-part/edit/selection-modal/index.js @@ -6,20 +6,25 @@ import { kebabCase } from 'lodash'; /** * WordPress dependencies */ -import { useCallback, useState } from '@wordpress/element'; +import { useCallback, useState, useMemo } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { useDispatch } from '@wordpress/data'; -import { serialize } from '@wordpress/blocks'; +import { parse, serialize } from '@wordpress/blocks'; import { store as coreStore } from '@wordpress/core-data'; import { Button } from '@wordpress/components'; +import { useAsyncList } from '@wordpress/compose'; +import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; /** * Internal dependencies */ -import TemplatePartList from './template-part-list'; -import PatternsList from './patterns-list'; import TitleModal from './title-modal'; +import { + useAlternativeBlockPatterns, + useAlternativeTemplateParts, +} from '../utils/hooks'; +import { createTemplatePartId } from '../utils/create-template-part-id'; export default function TemplatePartSelectionModal( { setAttributes, @@ -29,9 +34,26 @@ export default function TemplatePartSelectionModal( { areaLabel, clientId, } ) { + const { templateParts } = useAlternativeTemplateParts( + area, + templatePartId + ); + // We can map template parts to block patters to reuse the BlockPatternsList UI + const templartPartsAsBlockPatterns = useMemo( () => { + return templateParts.map( ( templatePart ) => ( { + name: createTemplatePartId( templatePart.theme, templatePart.slug ), + title: templatePart.title.rendered, + blocks: parse( templatePart.content.raw ), + templatePart, + } ) ); + }, [ templateParts ] ); + const shownTemplateParts = useAsyncList( templartPartsAsBlockPatterns ); const { createSuccessNotice } = useDispatch( noticesStore ); const { saveEntityRecord } = useDispatch( coreStore ); const [ showTitleModal, setShowTitleModal ] = useState( false ); + const blockPatterns = useAlternativeBlockPatterns( area, clientId ); + const [ selectedBlocks, setSelectedBlocks ] = useState( [] ); + const shownBlockPatterns = useAsyncList( blockPatterns ); const onTemplatePartSelect = useCallback( ( templatePart ) => { setAttributes( { @@ -83,28 +105,42 @@ export default function TemplatePartSelectionModal( { return ( <>
-
-

{ __( 'Existing template parts' ) }

- -
+ { !! templartPartsAsBlockPatterns.length && ( +
+

{ __( 'Existing template parts' ) }

+ { + onTemplatePartSelect( pattern.templatePart ); + } } + /> +
+ ) } -
-

{ __( 'Patterns' ) }

- -
+ { !! blockPatterns.length && ( +
+

{ __( 'Patterns' ) }

+ { + setSelectedBlocks( blocks ); + setShowTitleModal( true ); + } } + /> +
+ ) }
-
@@ -113,7 +149,10 @@ export default function TemplatePartSelectionModal( { setShowTitleModal( false ) } - onSubmit={ ( title ) => createFromBlocks( [], title ) } + onSubmit={ ( title ) => { + createFromBlocks( selectedBlocks, title ); + onClose(); + } } /> ) } diff --git a/packages/block-library/src/template-part/edit/selection-modal/patterns-list.js b/packages/block-library/src/template-part/edit/selection-modal/patterns-list.js deleted file mode 100644 index ab6337f484c782..00000000000000 --- a/packages/block-library/src/template-part/edit/selection-modal/patterns-list.js +++ /dev/null @@ -1,75 +0,0 @@ -/** - * WordPress dependencies - */ -import { - __experimentalBlockPatternsList as BlockPatternsList, - store as blockEditorStore, -} from '@wordpress/block-editor'; -import { useState, useMemo } from '@wordpress/element'; -import { useSelect } from '@wordpress/data'; -import { useAsyncList } from '@wordpress/compose'; - -/** - * Internal dependencies - */ -import TitleModal from './title-modal'; - -export default function PatternsList( { - area, - areaLabel, - clientId, - onSelect, -} ) { - const blockNameWithArea = area - ? `core/template-part/${ area }` - : 'core/template-part'; - const blockPatterns = useSelect( - ( select ) => { - const { - getBlockRootClientId, - __experimentalGetAllowedPatterns, - } = select( blockEditorStore ); - const rootClientId = getBlockRootClientId( clientId ); - return __experimentalGetAllowedPatterns( rootClientId ); - }, - [ clientId ] - ); - const filteredBlockPatterns = useMemo( () => { - return blockPatterns.filter( ( pattern ) => - pattern?.blockTypes?.some?.( - ( blockType ) => blockType === blockNameWithArea - ) - ); - }, [ blockNameWithArea, blockPatterns ] ); - - // Restructure onCreate to set the blocks on local state. - // Add modal to confirm title and trigger onCreate. - const [ startingBlocks, setStartingBlocks ] = useState( [] ); - const [ isTitleStep, setIsTitleStep ] = useState( false ); - const shownPatterns = useAsyncList( filteredBlockPatterns ); - - const onSubmitTitle = ( title ) => { - onSelect( startingBlocks, title ); - }; - - return ( - <> - { - setStartingBlocks( blocks ); - setIsTitleStep( true ); - } } - /> - - { isTitleStep && ( - setIsTitleStep( false ) } - onSubmit={ onSubmitTitle } - /> - ) } - - ); -} diff --git a/packages/block-library/src/template-part/edit/selection-modal/template-part-list.js b/packages/block-library/src/template-part/edit/selection-modal/template-part-list.js deleted file mode 100644 index e28dcbc2079b19..00000000000000 --- a/packages/block-library/src/template-part/edit/selection-modal/template-part-list.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect } from '@wordpress/data'; -import { useMemo } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; -import { useAsyncList } from '@wordpress/compose'; -import { store as coreStore } from '@wordpress/core-data'; -import { parse } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import { createTemplatePartId } from '../utils/create-template-part-id'; - -export default function TemplatePartList( { area, templatePartId, onSelect } ) { - const templateParts = useSelect( ( select ) => { - return select( coreStore ).getEntityRecords( - 'postType', - 'wp_template_part', - { - per_page: -1, - } - ); - }, [] ); - - const templatePartsToShow = useMemo( () => { - if ( ! templateParts ) { - return []; - } - return ( - templateParts.filter( - ( templatePart ) => - createTemplatePartId( - templatePart.theme, - templatePart.slug - ) !== templatePartId && - ( ! area || - 'uncategorized' === area || - templatePart.area === area ) - ) || [] - ); - }, [ templateParts, area ] ); - - // We can map template parts to block patters to reuse the BlockPatternsList UI - const blockPatterns = useMemo( () => { - return templatePartsToShow.map( ( templatePart ) => ( { - name: createTemplatePartId( templatePart.theme, templatePart.slug ), - title: templatePart.title.rendered, - blocks: parse( templatePart.content.raw ), - templatePart, - } ) ); - }, [ templatePartsToShow ] ); - - const shownPatterns = useAsyncList( blockPatterns ); - - if ( ! templateParts || ! templateParts.length ) { - return __( 'There are no existing template parts to select.' ); - } - - return ( - { - onSelect( pattern.templatePart ); - } } - /> - ); -} diff --git a/packages/block-library/src/template-part/edit/utils/hooks.js b/packages/block-library/src/template-part/edit/utils/hooks.js new file mode 100644 index 00000000000000..93fcca723ccccd --- /dev/null +++ b/packages/block-library/src/template-part/edit/utils/hooks.js @@ -0,0 +1,92 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { store as blockEditorStore } from '@wordpress/block-editor'; +import { useMemo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { createTemplatePartId } from './create-template-part-id'; + +/** + * Retrieves the available template parts for the given area. + * + * @param {string} area Template part area. + * @param {string} excludedId Template part ID to exclude. + * + * @return {{ templateParts: Array, isResolving: boolean }} array of template parts. + */ +export function useAlternativeTemplateParts( area, excludedId ) { + const { templateParts, isResolving } = useSelect( ( select ) => { + const { getEntityRecords, isResolving: _isResolving } = select( + coreStore + ); + const query = { per_page: -1 }; + return { + templateParts: getEntityRecords( + 'postType', + 'wp_template_part', + query + ), + isLoading: _isResolving( 'getEntityRecords', [ + 'postType', + 'wp_template_part', + query, + ] ), + }; + }, [] ); + + const filteredTemplateParts = useMemo( () => { + if ( ! templateParts ) { + return []; + } + return ( + templateParts.filter( + ( templatePart ) => + createTemplatePartId( + templatePart.theme, + templatePart.slug + ) !== excludedId && + ( ! area || + 'uncategorized' === area || + templatePart.area === area ) + ) || [] + ); + }, [ templateParts, area ] ); + + return { + templateParts: filteredTemplateParts, + isResolving, + }; +} + +/** + * Retrieves the available block patterns for the given area. + * + * @param {string} area Template part area. + * @param {string} clientId Block Client ID. (The container of the block can impact allowed blocks). + * + * @return {Array} array of block patterns. + */ +export function useAlternativeBlockPatterns( area, clientId ) { + return useSelect( + ( select ) => { + const blockNameWithArea = area + ? `core/template-part/${ area }` + : 'core/template-part'; + const { + getBlockRootClientId, + __experimentalGetPatternsByBlockTypes, + } = select( blockEditorStore ); + const rootClientId = getBlockRootClientId( clientId ); + return __experimentalGetPatternsByBlockTypes( + blockNameWithArea, + rootClientId + ); + }, + [ area, clientId ] + ); +} From 4fbb34229be1aa080fac35c5c2c0d6182729466a Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 17 Feb 2022 12:54:49 +0100 Subject: [PATCH 07/12] Move the start blank button to the placeholder --- .../src/template-part/edit/index.js | 43 +++++----- .../src/template-part/edit/placeholder.js | 78 +++++++++++++++++++ .../template-part/edit/placeholder/index.js | 52 ------------- .../index.js => selection-modal.js} | 63 +++------------ .../edit/{selection-modal => }/title-modal.js | 0 .../src/template-part/edit/utils/hooks.js | 68 +++++++++++++++- .../src/template-part/editor.scss | 21 ----- 7 files changed, 176 insertions(+), 149 deletions(-) create mode 100644 packages/block-library/src/template-part/edit/placeholder.js delete mode 100644 packages/block-library/src/template-part/edit/placeholder/index.js rename packages/block-library/src/template-part/edit/{selection-modal/index.js => selection-modal.js} (67%) rename packages/block-library/src/template-part/edit/{selection-modal => }/title-modal.js (100%) diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index 7ce36ae9f16acf..b971586369e7b8 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -32,6 +32,11 @@ import TemplatePartSelectionModal from './selection-modal'; import { TemplatePartAdvancedControls } from './advanced-controls'; import TemplatePartInnerBlocks from './inner-blocks'; import { createTemplatePartId } from './utils/create-template-part-id'; +import { + useAlternativeBlockPatterns, + useAlternativeTemplateParts, + useTemplatePartArea, +} from './utils/hooks'; export default function TemplatePartEdit( { attributes, @@ -51,14 +56,7 @@ export default function TemplatePartEdit( { // Set the postId block attribute if it did not exist, // but wait until the inner blocks have loaded to allow // new edits to trigger this. - const { - isResolved, - innerBlocks, - isMissing, - defaultWrapper, - area, - areaLabel, - } = useSelect( + const { isResolved, innerBlocks, isMissing, area } = useSelect( ( select ) => { const { getEditedEntityRecord, hasFinishedResolution } = select( coreStore @@ -81,29 +79,26 @@ export default function TemplatePartEdit( { ) : false; - // FIXME: @wordpress/block-library should not depend on @wordpress/editor. - // Blocks can be loaded into a *non-post* block editor. - // eslint-disable-next-line @wordpress/data-no-store-string-literals - const areaObject = select( 'core/editor' ) - .__experimentalGetDefaultTemplatePartAreas() - .find( ( { area: value } ) => value === _area ); - return { innerBlocks: getBlocks( clientId ), isResolved: hasResolvedEntity, isMissing: hasResolvedEntity && isEmpty( entityRecord ), - defaultWrapper: areaObject?.area_tag ?? 'div', - areaLabel: areaObject?.label || __( 'Template Part' ), area: _area, }; }, [ templatePartId, clientId ] ); - + const { templateParts } = useAlternativeTemplateParts( + area, + templatePartId + ); + const blockPatterns = useAlternativeBlockPatterns( area, clientId ); + const hasReplacements = !! templateParts.length || !! blockPatterns.length; + const areaObject = useTemplatePartArea( area ); const blockProps = useBlockProps(); const isPlaceholder = ! slug; const isEntityAvailable = ! isPlaceholder && ! isMissing && isResolved; - const TagName = tagName || defaultWrapper; + const TagName = tagName || areaObject.tagName; // We don't want to render a missing state if we have any inner blocks. // A new template part is automatically created if we have any inner blocks but no entity. @@ -143,19 +138,22 @@ export default function TemplatePartEdit( { setAttributes={ setAttributes } isEntityAvailable={ isEntityAvailable } templatePartId={ templatePartId } - defaultWrapper={ defaultWrapper } + defaultWrapper={ areaObject.tagName } /> { isPlaceholder && ( setIsTemplatePartSelectionOpen( true ) } /> ) } - { isEntityAvailable && ( + { isEntityAvailable && hasReplacements && ( @@ -199,7 +197,6 @@ export default function TemplatePartEdit( { setIsTemplatePartSelectionOpen( false ) diff --git a/packages/block-library/src/template-part/edit/placeholder.js b/packages/block-library/src/template-part/edit/placeholder.js new file mode 100644 index 00000000000000..ff43ee5644ad7c --- /dev/null +++ b/packages/block-library/src/template-part/edit/placeholder.js @@ -0,0 +1,78 @@ +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { Placeholder, Button, Spinner } from '@wordpress/components'; +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { + useAlternativeBlockPatterns, + useAlternativeTemplateParts, + useCreateTemplatePartFromBlocks, + useTemplatePartArea, +} from './utils/hooks'; +import TitleModal from './title-modal'; + +export default function TemplatePartPlaceholder( { + area, + clientId, + templatePartId, + onOpenSelectionModal, + setAttributes, +} ) { + const { templateParts, isResolving } = useAlternativeTemplateParts( + area, + templatePartId + ); + const blockPatterns = useAlternativeBlockPatterns( area, clientId ); + const [ showTitleModal, setShowTitleModal ] = useState( false ); + const areaObject = useTemplatePartArea( area ); + const createFromBlocks = useCreateTemplatePartFromBlocks( + area, + setAttributes + ); + + return ( + + { isResolving && } + + { ! isResolving && + !! ( templateParts.length || blockPatterns.length ) && ( + + ) } + + { ! isResolving && ( + + ) } + { showTitleModal && ( + setShowTitleModal( false ) } + onSubmit={ ( title ) => { + createFromBlocks( [], title ); + } } + /> + ) } + + ); +} diff --git a/packages/block-library/src/template-part/edit/placeholder/index.js b/packages/block-library/src/template-part/edit/placeholder/index.js deleted file mode 100644 index fa3a04e2086c84..00000000000000 --- a/packages/block-library/src/template-part/edit/placeholder/index.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * External dependencies - */ -import { find } from 'lodash'; - -/** - * WordPress dependencies - */ -import { __, sprintf } from '@wordpress/i18n'; -import { useSelect } from '@wordpress/data'; -import { Placeholder, Button } from '@wordpress/components'; - -export default function TemplatePartPlaceholder( { - area, - onOpenSelectionModal, -} ) { - const { areaIcon, areaLabel } = useSelect( - ( select ) => { - // FIXME: @wordpress/block-library should not depend on @wordpress/editor. - // Blocks can be loaded into a *non-post* block editor. - // eslint-disable-next-line @wordpress/data-no-store-string-literals - const definedAreas = select( - 'core/editor' - ).__experimentalGetDefaultTemplatePartAreas(); - - const selectedArea = find( definedAreas, { area } ); - const defaultArea = find( definedAreas, { area: 'uncategorized' } ); - - return { - areaIcon: selectedArea?.icon || defaultArea?.icon, - areaLabel: selectedArea?.label || __( 'Template Part' ), - }; - }, - [ area ] - ); - - return ( - - - - ); -} diff --git a/packages/block-library/src/template-part/edit/selection-modal/index.js b/packages/block-library/src/template-part/edit/selection-modal.js similarity index 67% rename from packages/block-library/src/template-part/edit/selection-modal/index.js rename to packages/block-library/src/template-part/edit/selection-modal.js index aecd9d4068aea0..697d63ccd8a134 100644 --- a/packages/block-library/src/template-part/edit/selection-modal/index.js +++ b/packages/block-library/src/template-part/edit/selection-modal.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { kebabCase } from 'lodash'; - /** * WordPress dependencies */ @@ -10,9 +5,7 @@ import { useCallback, useState, useMemo } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { useDispatch } from '@wordpress/data'; -import { parse, serialize } from '@wordpress/blocks'; -import { store as coreStore } from '@wordpress/core-data'; -import { Button } from '@wordpress/components'; +import { parse } from '@wordpress/blocks'; import { useAsyncList } from '@wordpress/compose'; import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; @@ -23,17 +16,19 @@ import TitleModal from './title-modal'; import { useAlternativeBlockPatterns, useAlternativeTemplateParts, -} from '../utils/hooks'; -import { createTemplatePartId } from '../utils/create-template-part-id'; + useCreateTemplatePartFromBlocks, + useTemplatePartArea, +} from './utils/hooks'; +import { createTemplatePartId } from './utils/create-template-part-id'; export default function TemplatePartSelectionModal( { setAttributes, onClose, templatePartId = null, area, - areaLabel, clientId, } ) { + const areaObject = useTemplatePartArea( area ); const { templateParts } = useAlternativeTemplateParts( area, templatePartId @@ -49,7 +44,6 @@ export default function TemplatePartSelectionModal( { }, [ templateParts ] ); const shownTemplateParts = useAsyncList( templartPartsAsBlockPatterns ); const { createSuccessNotice } = useDispatch( noticesStore ); - const { saveEntityRecord } = useDispatch( coreStore ); const [ showTitleModal, setShowTitleModal ] = useState( false ); const blockPatterns = useAlternativeBlockPatterns( area, clientId ); const [ selectedBlocks, setSelectedBlocks ] = useState( [] ); @@ -74,33 +68,10 @@ export default function TemplatePartSelectionModal( { onClose(); }, [] ); - const createFromBlocks = async ( - blocks = [], - title = __( 'Untitled Template Part' ) - ) => { - // If we have `area` set from block attributes, means an exposed - // block variation was inserted. So add this prop to the template - // part entity on creation. Afterwards remove `area` value from - // block attributes. - const record = { - title, - slug: kebabCase( title ), - content: serialize( blocks ), - // `area` is filterable on the server and defaults to `UNCATEGORIZED` - // if provided value is not allowed. - area, - }; - const templatePart = await saveEntityRecord( - 'postType', - 'wp_template_part', - record - ); - setAttributes( { - slug: templatePart.slug, - theme: templatePart.theme, - area: undefined, - } ); - }; + const createFromBlocks = useCreateTemplatePartFromBlocks( + area, + setAttributes + ); return ( <> @@ -133,21 +104,9 @@ export default function TemplatePartSelectionModal( { ) }
-
- -
- { showTitleModal && ( setShowTitleModal( false ) } onSubmit={ ( title ) => { createFromBlocks( selectedBlocks, title ); diff --git a/packages/block-library/src/template-part/edit/selection-modal/title-modal.js b/packages/block-library/src/template-part/edit/title-modal.js similarity index 100% rename from packages/block-library/src/template-part/edit/selection-modal/title-modal.js rename to packages/block-library/src/template-part/edit/title-modal.js diff --git a/packages/block-library/src/template-part/edit/utils/hooks.js b/packages/block-library/src/template-part/edit/utils/hooks.js index 93fcca723ccccd..010e7f87eb80be 100644 --- a/packages/block-library/src/template-part/edit/utils/hooks.js +++ b/packages/block-library/src/template-part/edit/utils/hooks.js @@ -1,10 +1,17 @@ +/** + * External dependencies + */ +import { find, kebabCase } from 'lodash'; + /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { useMemo } from '@wordpress/element'; +import { serialize } from '@wordpress/blocks'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -90,3 +97,62 @@ export function useAlternativeBlockPatterns( area, clientId ) { [ area, clientId ] ); } + +export function useCreateTemplatePartFromBlocks( area, setAttributes ) { + const { saveEntityRecord } = useDispatch( coreStore ); + + return async ( blocks = [], title = __( 'Untitled Template Part' ) ) => { + // If we have `area` set from block attributes, means an exposed + // block variation was inserted. So add this prop to the template + // part entity on creation. Afterwards remove `area` value from + // block attributes. + const record = { + title, + slug: kebabCase( title ), + content: serialize( blocks ), + // `area` is filterable on the server and defaults to `UNCATEGORIZED` + // if provided value is not allowed. + area, + }; + const templatePart = await saveEntityRecord( + 'postType', + 'wp_template_part', + record + ); + setAttributes( { + slug: templatePart.slug, + theme: templatePart.theme, + area: undefined, + } ); + }; +} + +/** + * Retrieves the template part area object. + * + * @param {string} area Template part area identifier. + * + * @return {{icon: Object, label: string, tagName: string}} Template Part area. + */ +export function useTemplatePartArea( area ) { + return useSelect( + ( select ) => { + // FIXME: @wordpress/block-library should not depend on @wordpress/editor. + // Blocks can be loaded into a *non-post* block editor. + // eslint-disable-next-line @wordpress/data-no-store-string-literals + const definedAreas = select( + 'core/editor' + ).__experimentalGetDefaultTemplatePartAreas(); + + const selectedArea = find( definedAreas, { area } ); + const defaultArea = find( definedAreas, { area: 'uncategorized' } ); + + return { + icon: selectedArea?.icon || defaultArea?.icon, + label: selectedArea?.label || __( 'Template Part' ), + tagName: selectedArea?.area_tag ?? 'div', + }; + }, + [ area ] + ); +} diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index c499d700c6ea8c..abc5eebe3f3641 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -2,10 +2,6 @@ @include break-medium() { width: $break-medium - $grid-unit-20 * 2; } - - .components-modal__content { - padding: 0; - } } .block-library-template-part__selection-content .block-editor-block-patterns-list { @@ -17,20 +13,3 @@ margin-bottom: 0; } } - -.block-library-template-part__selection-content { - padding: 0 $grid-unit-40 $grid-unit-20; -} - -.block-library-template-part__selection-footer { - position: sticky; - bottom: 0; - background: $white; - border-top: $border-width solid $gray-300; - padding: $grid-unit-20 $grid-unit-40; - display: flex; - flex-direction: row; - justify-content: flex-end; - width: 100%; - z-index: z-index(".components-modal__header"); -} From a4e25b315f9a608c9e7be298375cfabadb02d74e Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 17 Feb 2022 13:16:35 +0100 Subject: [PATCH 08/12] Replace blocks when template part defined --- .../src/template-part/edit/index.js | 1 + .../src/template-part/edit/selection-modal.js | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index b971586369e7b8..35fa5f705e6388 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -195,6 +195,7 @@ export default function TemplatePartEdit( { } > { setAttributes( { @@ -96,8 +103,13 @@ export default function TemplatePartSelectionModal( { blockPatterns={ blockPatterns } shownPatterns={ shownBlockPatterns } onClickPattern={ ( _, blocks ) => { - setSelectedBlocks( blocks ); - setShowTitleModal( true ); + if ( isReplacingTemplatePartContent ) { + replaceInnerBlocks( clientId, blocks ); + onClose(); + } else { + setSelectedBlocks( blocks ); + setShowTitleModal( true ); + } } } />
From c11e04a070d8cf70e7062cb4fd99ac6e91340569 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 17 Feb 2022 15:01:09 +0100 Subject: [PATCH 09/12] Fix e2e tests --- .../specs/site-editor/template-part.test.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/e2e-tests/specs/site-editor/template-part.test.js b/packages/e2e-tests/specs/site-editor/template-part.test.js index 33158e21a77509..8b815d2c362128 100644 --- a/packages/e2e-tests/specs/site-editor/template-part.test.js +++ b/packages/e2e-tests/specs/site-editor/template-part.test.js @@ -260,10 +260,10 @@ describe( 'Template Part', () => { const savePostSelector = '.edit-site-save-button__button'; const templatePartSelector = '*[data-type="core/template-part"]'; const activatedTemplatePartSelector = `${ templatePartSelector }.block-editor-block-list__layout`; - const createNewButtonSelector = - '//button[contains(text(), "New template part")]'; + const startBlockButtonSelector = + '//button[contains(text(), "Start blank")]'; const chooseExistingButtonSelector = - '//button[contains(text(), "Choose existing")]'; + '//button[contains(text(), "Choose")]'; const confirmTitleButtonSelector = '.wp-block-template-part__placeholder-create-new__title-form .components-button.is-primary'; @@ -273,13 +273,10 @@ describe( 'Template Part', () => { // Create new template part. await insertBlock( 'Template Part' ); - await siteEditorCanvas.waitForXPath( - chooseExistingButtonSelector - ); - const [ createNewButton ] = await siteEditorCanvas.$x( - createNewButtonSelector + const startBlankButton = await siteEditorCanvas.waitForXPath( + startBlockButtonSelector ); - await createNewButton.click(); + await startBlankButton.click(); const confirmTitleButton = await page.waitForSelector( confirmTitleButtonSelector ); @@ -318,13 +315,16 @@ describe( 'Template Part', () => { chooseExistingButtonSelector ); await chooseExistingButton.click(); - await page.waitForSelector( - '.wp-block-template-part__selection-preview-container' - ); const preview = await page.waitForSelector( - '.wp-block-template-part__selection-preview-item[aria-label="Create New"]' + '.block-editor-block-patterns-list__item' ); await preview.click(); + + // Wait for the template parts to load properly. + await siteEditorCanvas.waitForSelector( + '[data-type="core/template-part"] > p:first-child' + ); + // We now have the same template part two times in the page, so check accordingly. const paragraphs = await siteEditorCanvas.$$eval( '[data-type="core/template-part"] > p:first-child', From 9992c51fe0b7bec53460cfff41eba9a1b58dbcea Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 18 Feb 2022 11:55:53 +0100 Subject: [PATCH 10/12] Use a two columns layout --- packages/block-library/src/template-part/editor.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index abc5eebe3f3641..f52f3bbfd44c82 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -6,7 +6,7 @@ .block-library-template-part__selection-content .block-editor-block-patterns-list { display: grid; - grid-template-columns: 1fr 1fr 1fr; + grid-template-columns: 1fr 1fr; grid-gap: $grid-unit-10; .block-editor-block-patterns-list__list-item { From 6ebdd9081170f0d065164ca3d7d8098b49174dc0 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 18 Feb 2022 12:00:09 +0100 Subject: [PATCH 11/12] Remove title when starting from patterns --- .../src/template-part/edit/selection-modal.js | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/packages/block-library/src/template-part/edit/selection-modal.js b/packages/block-library/src/template-part/edit/selection-modal.js index c1747011840810..a9e0ed68255ced 100644 --- a/packages/block-library/src/template-part/edit/selection-modal.js +++ b/packages/block-library/src/template-part/edit/selection-modal.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useCallback, useState, useMemo } from '@wordpress/element'; +import { useCallback, useMemo } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { useDispatch } from '@wordpress/data'; @@ -15,12 +15,10 @@ import { /** * Internal dependencies */ -import TitleModal from './title-modal'; import { useAlternativeBlockPatterns, useAlternativeTemplateParts, useCreateTemplatePartFromBlocks, - useTemplatePartArea, } from './utils/hooks'; import { createTemplatePartId } from './utils/create-template-part-id'; @@ -34,7 +32,6 @@ export default function TemplatePartSelectionModal( { // When the templatePartId is undefined, // it means the user is creating a new one from the placeholder. const isReplacingTemplatePartContent = !! templatePartId; - const areaObject = useTemplatePartArea( area ); const { templateParts } = useAlternativeTemplateParts( area, templatePartId @@ -50,9 +47,7 @@ export default function TemplatePartSelectionModal( { }, [ templateParts ] ); const shownTemplateParts = useAsyncList( templartPartsAsBlockPatterns ); const { createSuccessNotice } = useDispatch( noticesStore ); - const [ showTitleModal, setShowTitleModal ] = useState( false ); const blockPatterns = useAlternativeBlockPatterns( area, clientId ); - const [ selectedBlocks, setSelectedBlocks ] = useState( [] ); const shownBlockPatterns = useAsyncList( blockPatterns ); const { replaceInnerBlocks } = useDispatch( blockEditorStore ); @@ -102,30 +97,19 @@ export default function TemplatePartSelectionModal( { { + onClickPattern={ ( pattern, blocks ) => { if ( isReplacingTemplatePartContent ) { replaceInnerBlocks( clientId, blocks ); - onClose(); } else { - setSelectedBlocks( blocks ); - setShowTitleModal( true ); + createFromBlocks( blocks, pattern.title ); } + + onClose(); } } />
) } - - { showTitleModal && ( - setShowTitleModal( false ) } - onSubmit={ ( title ) => { - createFromBlocks( selectedBlocks, title ); - onClose(); - } } - /> - ) } ); } From cb5c7b92630a6c2c160cc463bb4b0af690ebccad Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 18 Feb 2022 13:45:55 +0100 Subject: [PATCH 12/12] Consistent modal height --- packages/block-library/src/template-part/editor.scss | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index f52f3bbfd44c82..1e7c7608860f09 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -1,7 +1,16 @@ .block-editor-template-part__selection-modal { + // To keep modal dimensions consistent as subsections are navigated, width + // and height are used instead of max-(width/height). + @include break-small() { + width: calc(100% - #{ $grid-unit-20 * 2 }); + height: calc(100% - #{ $header-height * 2 }); + } @include break-medium() { width: $break-medium - $grid-unit-20 * 2; } + @include break-large() { + height: 70%; + } } .block-library-template-part__selection-content .block-editor-block-patterns-list {