From 69236dcd43e16a71568d693e244cb8263d8710c0 Mon Sep 17 00:00:00 2001 From: Bernie Reiter <96308+ockham@users.noreply.github.com> Date: Fri, 7 Jul 2023 10:54:29 +0200 Subject: [PATCH 01/31] Post and Comment Template blocks: Change render_block_context priority to 1 (#52364) --- packages/block-library/src/comment-template/index.php | 7 +++++-- packages/block-library/src/post-template/index.php | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/block-library/src/comment-template/index.php b/packages/block-library/src/comment-template/index.php index 3a553e802de0e..bb1cfa474e4c3 100644 --- a/packages/block-library/src/comment-template/index.php +++ b/packages/block-library/src/comment-template/index.php @@ -35,8 +35,11 @@ function block_core_comment_template_render_comments( $comments, $block ) { * We set commentId context through the `render_block_context` filter so * that dynamically inserted blocks (at `render_block` filter stage) * will also receive that context. + * + * Use an early priority to so that other 'render_block_context' filters + * have access to the values. */ - add_filter( 'render_block_context', $filter_block_context ); + add_filter( 'render_block_context', $filter_block_context, 1 ); /* * We construct a new WP_Block instance from the parsed block so that @@ -44,7 +47,7 @@ function block_core_comment_template_render_comments( $comments, $block ) { */ $block_content = ( new WP_Block( $block->parsed_block ) )->render( array( 'dynamic' => false ) ); - remove_filter( 'render_block_context', $filter_block_context ); + remove_filter( 'render_block_context', $filter_block_context, 1 ); $children = $comment->get_children(); diff --git a/packages/block-library/src/post-template/index.php b/packages/block-library/src/post-template/index.php index 3c023c80ed263..b1499d845f39a 100644 --- a/packages/block-library/src/post-template/index.php +++ b/packages/block-library/src/post-template/index.php @@ -97,11 +97,13 @@ function render_block_core_post_template( $attributes, $content, $block ) { $context['postId'] = $post_id; return $context; }; - add_filter( 'render_block_context', $filter_block_context ); + + // Use an early priority to so that other 'render_block_context' filters have access to the values. + add_filter( 'render_block_context', $filter_block_context, 1 ); // Render the inner blocks of the Post Template block with `dynamic` set to `false` to prevent calling // `render_callback` and ensure that no wrapper markup is included. $block_content = ( new WP_Block( $block_instance ) )->render( array( 'dynamic' => false ) ); - remove_filter( 'render_block_context', $filter_block_context ); + remove_filter( 'render_block_context', $filter_block_context, 1 ); // Wrap the render inner blocks in a `li` element with the appropriate post classes. $post_classes = implode( ' ', get_post_class( 'wp-block-post' ) ); From 8dc04dd9b97e244b230a5ac12ba06098792e99c6 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Fri, 7 Jul 2023 22:09:38 +0200 Subject: [PATCH 02/31] Footnotes: fix lingering format boundary attr (#52439) --- packages/rich-text/src/to-tree.js | 2 +- test/e2e/specs/editor/various/footnotes.spec.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/rich-text/src/to-tree.js b/packages/rich-text/src/to-tree.js index 805b539053a57..b390954b79672 100644 --- a/packages/rich-text/src/to-tree.js +++ b/packages/rich-text/src/to-tree.js @@ -60,7 +60,7 @@ function fromFormat( { let elementAttributes = {}; - if ( boundaryClass ) { + if ( boundaryClass && isEditableTree ) { elementAttributes[ 'data-rich-text-format-boundary' ] = 'true'; } diff --git a/test/e2e/specs/editor/various/footnotes.spec.js b/test/e2e/specs/editor/various/footnotes.spec.js index 63480bd11521c..e65eee6d3d08e 100644 --- a/test/e2e/specs/editor/various/footnotes.spec.js +++ b/test/e2e/specs/editor/various/footnotes.spec.js @@ -48,7 +48,7 @@ test.describe( 'Footnotes', () => { { name: 'core/paragraph', attributes: { - content: `second paragraph*`, + content: `second paragraph*`, }, }, { @@ -72,13 +72,13 @@ test.describe( 'Footnotes', () => { { name: 'core/paragraph', attributes: { - content: `first paragraph*`, + content: `first paragraph*`, }, }, { name: 'core/paragraph', attributes: { - content: `second paragraph*`, + content: `second paragraph*`, }, }, { @@ -106,13 +106,13 @@ test.describe( 'Footnotes', () => { { name: 'core/paragraph', attributes: { - content: `second paragraph*`, + content: `second paragraph*`, }, }, { name: 'core/paragraph', attributes: { - content: `first paragraph*`, + content: `first paragraph*`, }, }, { @@ -138,7 +138,7 @@ test.describe( 'Footnotes', () => { { name: 'core/paragraph', attributes: { - content: `second paragraph*`, + content: `second paragraph*`, }, }, { From 59234d3c1dd92fd16af57920e13a80ec89f26880 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Sat, 8 Jul 2023 03:51:13 +0900 Subject: [PATCH 03/31] Footnotes: Fix incorrect anchor position in Firefox (#52425) --- packages/block-library/src/footnotes/style.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/footnotes/style.scss b/packages/block-library/src/footnotes/style.scss index 4debba0560f17..ad49bc1cf2911 100644 --- a/packages/block-library/src/footnotes/style.scss +++ b/packages/block-library/src/footnotes/style.scss @@ -7,7 +7,8 @@ vertical-align: super; font-size: smaller; counter-increment: footnotes; - display: inline-block; + display: inline-flex; + text-decoration: none; text-indent: -9999999px; } From c0e0230e761bdc743de49a431eebaf01ea22120a Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Fri, 7 Jul 2023 17:33:32 +0200 Subject: [PATCH 04/31] Scope CSS rules for the wp admin reset to js support only. (#52376) --- packages/edit-post/src/style.scss | 8 +------- packages/edit-site/src/style.scss | 22 ++++++++++------------ packages/edit-widgets/src/style.scss | 10 ++-------- 3 files changed, 13 insertions(+), 27 deletions(-) diff --git a/packages/edit-post/src/style.scss b/packages/edit-post/src/style.scss index d33a5ae22c0fd..fac5deef18f71 100644 --- a/packages/edit-post/src/style.scss +++ b/packages/edit-post/src/style.scss @@ -41,13 +41,7 @@ } } -// In order to use mix-blend-mode, this element needs to have an explicitly set background-color -// We scope it to .wp-toolbar to be wp-admin only, to prevent bleed into other implementations -html.wp-toolbar { - background: $white; -} - -body.block-editor-page { +body.js.block-editor-page { @include wp-admin-reset( ".block-editor" ); } diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index 5a7f96433518a..a6309ae2a895c 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -47,27 +47,20 @@ @import "./components/resizable-frame/style.scss"; @import "./hooks/push-changes-to-global-styles/style.scss"; -html #wpadminbar { +body.js #wpadminbar { display: none; } -html #wpbody { +body.js #wpbody { padding-top: 0; } -// In order to use mix-blend-mode, this element needs to have an explicitly set background-color. -// We scope it to .wp-toolbar to be wp-admin only, to prevent bleed into other implementations. -html.wp-toolbar { - background: $white; - padding-top: 0; -} - -body.appearance_page_gutenberg-template-parts, -body.site-editor-php { +body.js.appearance_page_gutenberg-template-parts, +body.js.site-editor-php { @include wp-admin-reset(".edit-site"); } -body.site-editor-php { +body.js.site-editor-php { background: $gray-900; } @@ -91,6 +84,11 @@ body.site-editor-php { top: 0; } + .no-js & { + min-height: 0; + position: static; + } + .interface-interface-skeleton { top: 0; } diff --git a/packages/edit-widgets/src/style.scss b/packages/edit-widgets/src/style.scss index 2e78903d72c71..ae850c3bb78fe 100644 --- a/packages/edit-widgets/src/style.scss +++ b/packages/edit-widgets/src/style.scss @@ -11,14 +11,8 @@ @import "./components/widget-areas-block-editor-content/style.scss"; @import "./components/secondary-sidebar/style.scss"; -// In order to use mix-blend-mode, this element needs to have an explicitly set background-color -// We scope it to .wp-toolbar to be wp-admin only, to prevent bleed into other implementations -html.wp-toolbar { - background: $white; -} - -body.appearance_page_gutenberg-widgets, -body.widgets-php { +body.js.appearance_page_gutenberg-widgets, +body.js.widgets-php { @include wp-admin-reset( ".blocks-widgets-container" ); } From eb8ef9f17d5fba6743a6970260557753b6c1e55d Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Fri, 7 Jul 2023 15:44:10 +0200 Subject: [PATCH 05/31] Fix: Patterns & template parts: remove "apply globally" option from block settings (#52160) * Advanced styles panel: add an early return * Update index.js * Minor styling changes * Use array of features --------- Co-authored-by: George Mamadashvili --- .../src/hooks/push-changes-to-global-styles/index.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/hooks/push-changes-to-global-styles/index.js b/packages/edit-site/src/hooks/push-changes-to-global-styles/index.js index d82f3a86847da..9b4730a7bb1fc 100644 --- a/packages/edit-site/src/hooks/push-changes-to-global-styles/index.js +++ b/packages/edit-site/src/hooks/push-changes-to-global-styles/index.js @@ -18,6 +18,7 @@ import { __, sprintf } from '@wordpress/i18n'; import { __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, getBlockType, + hasBlockSupport, } from '@wordpress/blocks'; import { useContext, useMemo, useCallback } from '@wordpress/element'; import { useDispatch } from '@wordpress/data'; @@ -93,6 +94,8 @@ const STYLE_PATH_TO_PRESET_BLOCK_ATTRIBUTE = { 'typography.fontFamily': 'fontFamily', }; +const SUPPORTED_STYLES = [ 'border', 'color', 'spacing', 'typography' ]; + function useChangesToPush( name, attributes ) { const supports = useSupportedStyles( name ); @@ -212,10 +215,14 @@ function PushChangesToGlobalStylesControl( { const withPushChangesToGlobalStyles = createHigherOrderComponent( ( BlockEdit ) => ( props ) => { const blockEditingMode = useBlockEditingMode(); + const supportsStyles = SUPPORTED_STYLES.some( ( feature ) => + hasBlockSupport( props.name, feature ) + ); + return ( <> - { blockEditingMode === 'default' && ( + { blockEditingMode === 'default' && supportsStyles && ( From 8b10e6a251066d2222897b8912dcd06328037197 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Fri, 7 Jul 2023 16:32:15 +0300 Subject: [PATCH 06/31] make the body of the editor minimmum viewport height so that smaller contents maintain clickability (#52406) --- packages/edit-site/src/components/block-editor/editor-canvas.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/block-editor/editor-canvas.js b/packages/edit-site/src/components/block-editor/editor-canvas.js index 34c23cc699bc2..a8bbe75e0261b 100644 --- a/packages/edit-site/src/components/block-editor/editor-canvas.js +++ b/packages/edit-site/src/components/block-editor/editor-canvas.js @@ -88,7 +88,7 @@ function EditorCanvas( { enableResizing, settings, children, ...props } ) { enableResizing ? 'min-height:0!important;' : '' }}body{position:relative; ${ canvasMode === 'view' - ? 'cursor: pointer; height: 100vh' + ? 'cursor: pointer; min-height: 100vh;' : '' }}}` } From 3e5bc998202daa0c75a5ee83734c16e47566e30a Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 7 Jul 2023 16:40:48 +1000 Subject: [PATCH 07/31] Patterns: Add renaming, duplication, and deletion options (#52270) --- .../components/create-pattern-modal/index.js | 7 +- .../page-patterns/duplicate-menu-item.js | 196 ++++++++++++++++++ .../src/components/page-patterns/grid-item.js | 156 +++++++++----- .../page-patterns/rename-menu-item.js | 115 ++++++++++ .../src/components/page-patterns/style.scss | 4 + .../components/page-patterns/use-patterns.js | 2 + 6 files changed, 421 insertions(+), 59 deletions(-) create mode 100644 packages/edit-site/src/components/page-patterns/duplicate-menu-item.js create mode 100644 packages/edit-site/src/components/page-patterns/rename-menu-item.js diff --git a/packages/edit-site/src/components/create-pattern-modal/index.js b/packages/edit-site/src/components/create-pattern-modal/index.js index 46d734b86fdd1..753dccfb961dd 100644 --- a/packages/edit-site/src/components/create-pattern-modal/index.js +++ b/packages/edit-site/src/components/create-pattern-modal/index.js @@ -14,6 +14,7 @@ import { __ } from '@wordpress/i18n'; import { useState } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; import { useDispatch } from '@wordpress/data'; +import { serialize } from '@wordpress/blocks'; /** * Internal dependencies @@ -21,9 +22,11 @@ import { useDispatch } from '@wordpress/data'; import { SYNC_TYPES, USER_PATTERN_CATEGORY } from '../page-patterns/utils'; export default function CreatePatternModal( { + blocks = [], closeModal, onCreate, onError, + title, } ) { const [ name, setName ] = useState( '' ); const [ syncType, setSyncType ] = useState( SYNC_TYPES.unsynced ); @@ -52,7 +55,7 @@ export default function CreatePatternModal( { 'wp_block', { title: name || __( 'Untitled Pattern' ), - content: '', + content: blocks?.length ? serialize( blocks ) : '', status: 'publish', meta: syncType === SYNC_TYPES.unsynced @@ -76,7 +79,7 @@ export default function CreatePatternModal( { return ( diff --git a/packages/edit-site/src/components/page-patterns/duplicate-menu-item.js b/packages/edit-site/src/components/page-patterns/duplicate-menu-item.js new file mode 100644 index 0000000000000..d2c14d15f341b --- /dev/null +++ b/packages/edit-site/src/components/page-patterns/duplicate-menu-item.js @@ -0,0 +1,196 @@ +/** + * WordPress dependencies + */ +import { MenuItem } from '@wordpress/components'; +import { store as coreStore } from '@wordpress/core-data'; +import { useDispatch } from '@wordpress/data'; +import { __, sprintf } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; + +/** + * Internal dependencies + */ +import { + TEMPLATE_PARTS, + PATTERNS, + SYNC_TYPES, + USER_PATTERNS, + USER_PATTERN_CATEGORY, +} from './utils'; +import { + useExistingTemplateParts, + getUniqueTemplatePartTitle, + getCleanTemplatePartSlug, +} from '../../utils/template-part-create'; +import { unlock } from '../../lock-unlock'; + +const { useHistory } = unlock( routerPrivateApis ); + +function getPatternMeta( item ) { + if ( item.type === PATTERNS ) { + return { wp_pattern_sync_status: SYNC_TYPES.unsynced }; + } + + const syncStatus = item.reusableBlock.wp_pattern_sync_status; + const isUnsynced = syncStatus === SYNC_TYPES.unsynced; + + return { + ...item.reusableBlock.meta, + wp_pattern_sync_status: isUnsynced ? syncStatus : undefined, + }; +} + +export default function DuplicateMenuItem( { + categoryId, + item, + label = __( 'Duplicate' ), + onClose, +} ) { + const { saveEntityRecord } = useDispatch( coreStore ); + const { createErrorNotice, createSuccessNotice } = + useDispatch( noticesStore ); + + const history = useHistory(); + const existingTemplateParts = useExistingTemplateParts(); + + async function createTemplatePart() { + try { + const copiedTitle = sprintf( + /* translators: %s: Existing template part title */ + __( '%s (Copy)' ), + item.title + ); + const title = getUniqueTemplatePartTitle( + copiedTitle, + existingTemplateParts + ); + const slug = getCleanTemplatePartSlug( title ); + const { area, content } = item.templatePart; + + const result = await saveEntityRecord( + 'postType', + 'wp_template_part', + { slug, title, content, area }, + { throwOnError: true } + ); + + createSuccessNotice( + sprintf( + // translators: %s: The new template part's title e.g. 'Call to action (copy)'. + __( '"%s" created.' ), + title + ), + { + type: 'snackbar', + id: 'edit-site-patterns-success', + actions: [ + { + label: __( 'Edit' ), + onClick: () => + history.push( { + postType: TEMPLATE_PARTS, + postId: result?.id, + categoryType: TEMPLATE_PARTS, + categoryId, + } ), + }, + ], + } + ); + + onClose(); + } catch ( error ) { + const errorMessage = + error.message && error.code !== 'unknown_error' + ? error.message + : __( + 'An error occurred while creating the template part.' + ); + + createErrorNotice( errorMessage, { + type: 'snackbar', + id: 'edit-site-patterns-error', + } ); + onClose(); + } + } + + async function createPattern() { + try { + const isThemePattern = item.type === PATTERNS; + const title = sprintf( + /* translators: %s: Existing pattern title */ + __( '%s (Copy)' ), + item.title + ); + + const result = await saveEntityRecord( + 'postType', + 'wp_block', + { + content: isThemePattern + ? item.content + : item.reusableBlock.content, + meta: getPatternMeta( item ), + status: 'publish', + title, + }, + { throwOnError: true } + ); + + const actionLabel = isThemePattern + ? __( 'View my patterns' ) + : __( 'Edit' ); + + const newLocation = isThemePattern + ? { + categoryType: USER_PATTERNS, + categoryId: USER_PATTERN_CATEGORY, + path: '/patterns', + } + : { + categoryType: USER_PATTERNS, + categoryId: USER_PATTERN_CATEGORY, + postType: USER_PATTERNS, + postId: result?.id, + }; + + createSuccessNotice( + sprintf( + // translators: %s: The new pattern's title e.g. 'Call to action (copy)'. + __( '"%s" added to my patterns.' ), + title + ), + { + type: 'snackbar', + id: 'edit-site-patterns-success', + actions: [ + { + label: actionLabel, + onClick: () => history.push( newLocation ), + }, + ], + } + ); + + onClose(); + } catch ( error ) { + const errorMessage = + error.message && error.code !== 'unknown_error' + ? error.message + : __( 'An error occurred while creating the pattern.' ); + + createErrorNotice( errorMessage, { + type: 'snackbar', + id: 'edit-site-patterns-error', + } ); + onClose(); + } + } + + const createItem = + item.type === TEMPLATE_PARTS ? createTemplatePart : createPattern; + + return { label }; +} diff --git a/packages/edit-site/src/components/page-patterns/grid-item.js b/packages/edit-site/src/components/page-patterns/grid-item.js index 7db14e1d37788..71356ff4cf540 100644 --- a/packages/edit-site/src/components/page-patterns/grid-item.js +++ b/packages/edit-site/src/components/page-patterns/grid-item.js @@ -25,7 +25,7 @@ import { Icon, header, footer, - symbolFilled, + symbolFilled as uncategorized, moreHorizontal, lockSmall, } from '@wordpress/icons'; @@ -36,23 +36,31 @@ import { DELETE, BACKSPACE } from '@wordpress/keycodes'; /** * Internal dependencies */ -import { PATTERNS, USER_PATTERNS } from './utils'; +import RenameMenuItem from './rename-menu-item'; +import DuplicateMenuItem from './duplicate-menu-item'; +import { PATTERNS, TEMPLATE_PARTS, USER_PATTERNS } from './utils'; +import { store as editSiteStore } from '../../store'; import { useLink } from '../routes/link'; -const THEME_PATTERN_TOOLTIP = __( 'Theme patterns cannot be edited.' ); +const templatePartIcons = { header, footer, uncategorized }; export default function GridItem( { categoryId, composite, icon, item } ) { const descriptionId = useId(); const [ isDeleteDialogOpen, setIsDeleteDialogOpen ] = useState( false ); + const { removeTemplate } = useDispatch( editSiteStore ); const { __experimentalDeleteReusableBlock } = useDispatch( reusableBlocksStore ); const { createErrorNotice, createSuccessNotice } = useDispatch( noticesStore ); + const isUserPattern = item.type === USER_PATTERNS; + const isNonUserPattern = item.type === PATTERNS; + const isTemplatePart = item.type === TEMPLATE_PARTS; + const { onClick } = useLink( { postType: item.type, - postId: item.type === USER_PATTERNS ? item.id : item.name, + postId: isUserPattern ? item.id : item.name, categoryId, categoryType: item.type, } ); @@ -68,27 +76,41 @@ export default function GridItem( { categoryId, composite, icon, item } ) { 'is-placeholder': isEmpty, } ); const previewClassNames = classnames( 'edit-site-patterns__preview', { - 'is-inactive': item.type === PATTERNS, + 'is-inactive': isNonUserPattern, } ); const deletePattern = async () => { try { await __experimentalDeleteReusableBlock( item.id ); - createSuccessNotice( __( 'Pattern successfully deleted.' ), { - type: 'snackbar', - } ); + createSuccessNotice( + sprintf( + // translators: %s: The pattern's title e.g. 'Call to action'. + __( '"%s" deleted.' ), + item.title + ), + { type: 'snackbar', id: 'edit-site-patterns-success' } + ); } catch ( error ) { const errorMessage = error.message && error.code !== 'unknown_error' ? error.message : __( 'An error occurred while deleting the pattern.' ); - createErrorNotice( errorMessage, { type: 'snackbar' } ); + createErrorNotice( errorMessage, { + type: 'snackbar', + id: 'edit-site-patterns-error', + } ); } }; + const deleteItem = () => + isTemplatePart ? removeTemplate( item ) : deletePattern(); - const isUserPattern = item.type === USER_PATTERNS; + // Only custom patterns or custom template parts can be renamed or deleted. + const isCustomPattern = + isUserPattern || ( isTemplatePart && item.isCustom ); + const hasThemeFile = isTemplatePart && item.templatePart.has_theme_file; const ariaDescriptions = []; - if ( isUserPattern ) { + + if ( isCustomPattern ) { // User patterns don't have descriptions, but can be edited and deleted, so include some help text. ariaDescriptions.push( __( 'Press Enter to edit, or Delete to delete the pattern.' ) @@ -96,19 +118,24 @@ export default function GridItem( { categoryId, composite, icon, item } ) { } else if ( item.description ) { ariaDescriptions.push( item.description ); } - if ( item.type === PATTERNS ) { - ariaDescriptions.push( THEME_PATTERN_TOOLTIP ); - } - let itemIcon = icon; - if ( categoryId === 'header' ) { - itemIcon = header; - } else if ( categoryId === 'footer' ) { - itemIcon = footer; - } else if ( categoryId === 'uncategorized' ) { - itemIcon = symbolFilled; + if ( isNonUserPattern ) { + ariaDescriptions.push( __( 'Theme patterns cannot be edited.' ) ); } + const itemIcon = templatePartIcons[ categoryId ] + ? templatePartIcons[ categoryId ] + : icon; + + const confirmButtonText = hasThemeFile ? __( 'Clear' ) : __( 'Delete' ); + const confirmPrompt = hasThemeFile + ? __( 'Are you sure you want to clear these customizations?' ) + : sprintf( + // translators: %s: The pattern or template part's title e.g. 'Call to action'. + __( 'Are you sure you want to delete "%s"?' ), + item.title + ); + return ( <>
@@ -118,7 +145,7 @@ export default function GridItem( { categoryId, composite, icon, item } ) { as="div" { ...composite } onClick={ item.type !== PATTERNS ? onClick : undefined } - onKeyDown={ isUserPattern ? onKeyDown : undefined } + onKeyDown={ isCustomPattern ? onKeyDown : undefined } aria-label={ item.title } aria-describedby={ ariaDescriptions.length @@ -175,58 +202,73 @@ export default function GridItem( { categoryId, composite, icon, item } ) { ) } > - + ) } - { item.type === USER_PATTERNS && ( - - { () => ( - + + { ( { onClose } ) => ( + + { isCustomPattern && ! hasThemeFile && ( + + ) } + + { isCustomPattern && ( setIsDeleteDialogOpen( true ) } > - { __( 'Delete' ) } + { hasThemeFile + ? __( 'Clear customizations' ) + : __( 'Delete' ) } - - ) } - - ) } + ) } + + ) } +
{ isDeleteDialogOpen && ( setIsDeleteDialogOpen( false ) } > - { __( 'Are you sure you want to delete this pattern?' ) } + { confirmPrompt } ) } diff --git a/packages/edit-site/src/components/page-patterns/rename-menu-item.js b/packages/edit-site/src/components/page-patterns/rename-menu-item.js new file mode 100644 index 0000000000000..938023a62cefd --- /dev/null +++ b/packages/edit-site/src/components/page-patterns/rename-menu-item.js @@ -0,0 +1,115 @@ +/** + * WordPress dependencies + */ +import { + Button, + MenuItem, + Modal, + TextControl, + __experimentalHStack as HStack, + __experimentalVStack as VStack, +} from '@wordpress/components'; +import { store as coreStore } from '@wordpress/core-data'; +import { useDispatch } from '@wordpress/data'; +import { useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; + +/** + * Internal dependencies + */ +import { TEMPLATE_PARTS } from './utils'; + +export default function RenameMenuItem( { item, onClose } ) { + const [ title, setTitle ] = useState( () => item.title ); + const [ isModalOpen, setIsModalOpen ] = useState( false ); + + const { editEntityRecord, saveEditedEntityRecord } = + useDispatch( coreStore ); + const { createSuccessNotice, createErrorNotice } = + useDispatch( noticesStore ); + + if ( item.type === TEMPLATE_PARTS && ! item.isCustom ) { + return null; + } + + async function onRename( event ) { + event.preventDefault(); + + try { + await editEntityRecord( 'postType', item.type, item.id, { title } ); + + // Update state before saving rerenders the list. + setTitle( '' ); + setIsModalOpen( false ); + onClose(); + + // Persist edited entity. + await saveEditedEntityRecord( 'postType', item.type, item.id, { + throwOnError: true, + } ); + + createSuccessNotice( __( 'Entity renamed.' ), { + type: 'snackbar', + } ); + } catch ( error ) { + const errorMessage = + error.message && error.code !== 'unknown_error' + ? error.message + : __( 'An error occurred while renaming the entity.' ); + + createErrorNotice( errorMessage, { type: 'snackbar' } ); + } + } + + return ( + <> + { + setIsModalOpen( true ); + setTitle( item.title ); + } } + > + { __( 'Rename' ) } + + { isModalOpen && ( + { + setIsModalOpen( false ); + onClose(); + } } + overlayClassName="edit-site-list__rename_modal" + > +
+ + + + + + + + + +
+
+ ) } + + ); +} diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index fdf0aea3431f6..0c7660035fb3e 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -101,6 +101,10 @@ .edit-site-patterns__pattern-lock-icon { display: inline-flex; + + svg { + fill: currentcolor; + } } } 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 a394aabf572c4..295d1eee8e410 100644 --- a/packages/edit-site/src/components/page-patterns/use-patterns.js +++ b/packages/edit-site/src/components/page-patterns/use-patterns.js @@ -31,7 +31,9 @@ const templatePartToPattern = ( templatePart ) => ( { blocks: parse( templatePart.content.raw ), categories: [ templatePart.area ], description: templatePart.description || '', + isCustom: templatePart.source === 'custom', keywords: templatePart.keywords || [], + id: createTemplatePartId( templatePart.theme, templatePart.slug ), name: createTemplatePartId( templatePart.theme, templatePart.slug ), title: templatePart.title.rendered, type: templatePart.type, From cfcf5ff64a9cbe7c9bd275d35a187b68c321e803 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 7 Jul 2023 19:01:21 +1000 Subject: [PATCH 08/31] Patterns: Update manage pattern links to go to site editor if available (#52403) --- packages/edit-post/src/plugins/index.js | 40 ++++++++++--- .../reusable-blocks-manage-button.js | 59 +++++++++++-------- 2 files changed, 67 insertions(+), 32 deletions(-) diff --git a/packages/edit-post/src/plugins/index.js b/packages/edit-post/src/plugins/index.js index e3bd1b2dd72bd..1cd03debbf7a7 100644 --- a/packages/edit-post/src/plugins/index.js +++ b/packages/edit-post/src/plugins/index.js @@ -2,6 +2,9 @@ * WordPress dependencies */ import { MenuItem, VisuallyHidden } from '@wordpress/components'; +import { store as coreStore } from '@wordpress/core-data'; +import { store as editorStore } from '@wordpress/editor'; +import { useSelect } from '@wordpress/data'; import { external } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; import { registerPlugin } from '@wordpress/plugins'; @@ -15,6 +18,34 @@ import KeyboardShortcutsHelpMenuItem from './keyboard-shortcuts-help-menu-item'; import ToolsMoreMenuGroup from '../components/header/tools-more-menu-group'; import WelcomeGuideMenuItem from './welcome-guide-menu-item'; +function ManagePatternsMenuItem() { + const url = useSelect( ( select ) => { + const { canUser } = select( coreStore ); + const { getEditorSettings } = select( editorStore ); + + const isBlockTheme = getEditorSettings().__unstableIsBlockBasedTheme; + const defaultUrl = addQueryArgs( 'edit.php', { + post_type: 'wp_block', + } ); + const patternsUrl = addQueryArgs( 'site-editor.php', { + path: '/patterns', + } ); + + // The site editor and templates both check whether the user has + // edit_theme_options capabilities. We can leverage that here and not + // display the manage patterns link if the user can't access it. + return canUser( 'read', 'templates' ) && isBlockTheme + ? patternsUrl + : defaultUrl; + }, [] ); + + return ( + + { __( 'Manage Patterns' ) } + + ); +} + registerPlugin( 'edit-post', { render() { return ( @@ -22,14 +53,7 @@ registerPlugin( 'edit-post', { { ( { onClose } ) => ( <> - - { __( 'Manage Patterns' ) } - + diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js index 6f33905888511..e3bbef8bf7738 100644 --- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js +++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js @@ -18,28 +18,41 @@ import { store as coreStore } from '@wordpress/core-data'; import { store as reusableBlocksStore } from '../../store'; function ReusableBlocksManageButton( { clientId } ) { - const { canRemove, isVisible, innerBlockCount } = useSelect( - ( select ) => { - const { getBlock, canRemoveBlock, getBlockCount } = - select( blockEditorStore ); - const { canUser } = select( coreStore ); - const reusableBlock = getBlock( clientId ); + const { canRemove, isVisible, innerBlockCount, managePatternsUrl } = + useSelect( + ( select ) => { + const { getBlock, canRemoveBlock, getBlockCount, getSettings } = + select( blockEditorStore ); + const { canUser } = select( coreStore ); + const reusableBlock = getBlock( clientId ); + const isBlockTheme = getSettings().__unstableIsBlockBasedTheme; - return { - canRemove: canRemoveBlock( clientId ), - isVisible: - !! reusableBlock && - isReusableBlock( reusableBlock ) && - !! canUser( - 'update', - 'blocks', - reusableBlock.attributes.ref - ), - innerBlockCount: getBlockCount( clientId ), - }; - }, - [ clientId ] - ); + return { + canRemove: canRemoveBlock( clientId ), + isVisible: + !! reusableBlock && + isReusableBlock( reusableBlock ) && + !! canUser( + 'update', + 'blocks', + reusableBlock.attributes.ref + ), + innerBlockCount: getBlockCount( clientId ), + // The site editor and templates both check whether the user + // has edit_theme_options capabilities. We can leverage that here + // and omit the manage patterns link if the user can't access it. + managePatternsUrl: + isBlockTheme && canUser( 'read', 'templates' ) + ? addQueryArgs( 'site-editor.php', { + path: '/patterns', + } ) + : addQueryArgs( 'edit.php', { + post_type: 'wp_block', + } ), + }; + }, + [ clientId ] + ); const { __experimentalConvertBlockToStatic: convertBlockToStatic } = useDispatch( reusableBlocksStore ); @@ -50,9 +63,7 @@ function ReusableBlocksManageButton( { clientId } ) { return ( - + { __( 'Manage Patterns' ) } { canRemove && ( From 509fcf32c4bb7634bc3f3e373d21c192aed72330 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Mon, 10 Jul 2023 08:58:59 +0800 Subject: [PATCH 09/31] [Patterns] Separate sync status into a filter control (#52303) Co-authored-by: Saxon Fletcher Co-authored-by: Glen Davies --- .../src/components/page-patterns/grid-item.js | 22 +- .../src/components/page-patterns/grid.js | 56 ++-- .../src/components/page-patterns/header.js | 69 +++++ .../src/components/page-patterns/index.js | 7 +- .../components/page-patterns/patterns-list.js | 136 +++++---- .../src/components/page-patterns/style.scss | 82 ++++-- .../components/page-patterns/use-patterns.js | 258 +++++++----------- .../use-my-patterns.js | 13 +- 8 files changed, 372 insertions(+), 271 deletions(-) create mode 100644 packages/edit-site/src/components/page-patterns/header.js diff --git a/packages/edit-site/src/components/page-patterns/grid-item.js b/packages/edit-site/src/components/page-patterns/grid-item.js index 71356ff4cf540..e7ca2d3ced53e 100644 --- a/packages/edit-site/src/components/page-patterns/grid-item.js +++ b/packages/edit-site/src/components/page-patterns/grid-item.js @@ -19,13 +19,14 @@ import { Flex, } from '@wordpress/components'; import { useDispatch } from '@wordpress/data'; -import { useState, useId } from '@wordpress/element'; +import { useState, useId, memo } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import { Icon, header, footer, symbolFilled as uncategorized, + symbol, moreHorizontal, lockSmall, } from '@wordpress/icons'; @@ -38,13 +39,13 @@ import { DELETE, BACKSPACE } from '@wordpress/keycodes'; */ import RenameMenuItem from './rename-menu-item'; import DuplicateMenuItem from './duplicate-menu-item'; -import { PATTERNS, TEMPLATE_PARTS, USER_PATTERNS } from './utils'; +import { PATTERNS, TEMPLATE_PARTS, USER_PATTERNS, SYNC_TYPES } from './utils'; import { store as editSiteStore } from '../../store'; import { useLink } from '../routes/link'; const templatePartIcons = { header, footer, uncategorized }; -export default function GridItem( { categoryId, composite, icon, item } ) { +function GridItem( { categoryId, item, ...props } ) { const descriptionId = useId(); const [ isDeleteDialogOpen, setIsDeleteDialogOpen ] = useState( false ); @@ -123,9 +124,9 @@ export default function GridItem( { categoryId, composite, icon, item } ) { ariaDescriptions.push( __( 'Theme patterns cannot be edited.' ) ); } - const itemIcon = templatePartIcons[ categoryId ] - ? templatePartIcons[ categoryId ] - : icon; + const itemIcon = + templatePartIcons[ categoryId ] || + ( item.syncStatus === SYNC_TYPES.full ? symbol : undefined ); const confirmButtonText = hasThemeFile ? __( 'Clear' ) : __( 'Delete' ); const confirmPrompt = hasThemeFile @@ -143,7 +144,10 @@ export default function GridItem( { categoryId, composite, icon, item } ) { className={ previewClassNames } role="option" as="div" - { ...composite } + // Even though still incomplete, passing ids helps performance. + // @see https://reakit.io/docs/composite/#performance. + id={ `edit-site-patterns-${ item.name }` } + { ...props } onClick={ item.type !== PATTERNS ? onClick : undefined } onKeyDown={ isCustomPattern ? onKeyDown : undefined } aria-label={ item.title } @@ -181,7 +185,7 @@ export default function GridItem( { categoryId, composite, icon, item } ) { spacing={ 3 } className="edit-site-patterns__pattern-title" > - { icon && ( + { itemIcon && ( ); } + +export default memo( GridItem ); diff --git a/packages/edit-site/src/components/page-patterns/grid.js b/packages/edit-site/src/components/page-patterns/grid.js index 3f6e5fd01f72f..b9b7053c112ce 100644 --- a/packages/edit-site/src/components/page-patterns/grid.js +++ b/packages/edit-site/src/components/page-patterns/grid.js @@ -4,36 +4,56 @@ import { __unstableComposite as Composite, __unstableUseCompositeState as useCompositeState, + __experimentalText as Text, } from '@wordpress/components'; +import { useRef } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; /** * Internal dependencies */ import GridItem from './grid-item'; -export default function Grid( { categoryId, label, icon, items } ) { - const composite = useCompositeState( { orientation: 'vertical' } ); +const PAGE_SIZE = 100; + +export default function Grid( { categoryId, items, ...props } ) { + const composite = useCompositeState( { wrap: true } ); + const gridRef = useRef(); if ( ! items?.length ) { return null; } + const list = items.slice( 0, PAGE_SIZE ); + const restLength = items.length - PAGE_SIZE; + return ( - - { items.map( ( item ) => ( - - ) ) } - + <> + + { list.map( ( item ) => ( + + ) ) } + + { restLength > 0 && ( + + { sprintf( + /* translators: %d: number of patterns */ + __( '+ %d more patterns discoverable by searching' ), + restLength + ) } + + ) } + ); } diff --git a/packages/edit-site/src/components/page-patterns/header.js b/packages/edit-site/src/components/page-patterns/header.js new file mode 100644 index 0000000000000..1237b85d6c978 --- /dev/null +++ b/packages/edit-site/src/components/page-patterns/header.js @@ -0,0 +1,69 @@ +/** + * WordPress dependencies + */ +import { + __experimentalVStack as VStack, + __experimentalHeading as Heading, + __experimentalText as Text, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { store as editorStore } from '@wordpress/editor'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import usePatternCategories from '../sidebar-navigation-screen-patterns/use-pattern-categories'; +import { + USER_PATTERN_CATEGORY, + USER_PATTERNS, + TEMPLATE_PARTS, + PATTERNS, +} from './utils'; + +export default function PatternsHeader( { + categoryId, + type, + titleId, + descriptionId, +} ) { + const { patternCategories } = usePatternCategories(); + const templatePartAreas = useSelect( + ( select ) => + select( editorStore ).__experimentalGetDefaultTemplatePartAreas(), + [] + ); + + let title, description; + if ( categoryId === USER_PATTERN_CATEGORY && type === USER_PATTERNS ) { + title = __( 'My Patterns' ); + description = ''; + } else if ( type === TEMPLATE_PARTS ) { + const templatePartArea = templatePartAreas.find( + ( area ) => area.area === categoryId + ); + title = templatePartArea?.label; + description = templatePartArea?.description; + } else if ( type === PATTERNS ) { + const patternCategory = patternCategories.find( + ( category ) => category.name === categoryId + ); + title = patternCategory?.label; + description = patternCategory?.description; + } + + if ( ! title ) return null; + + return ( + + + { title } + + { description ? ( + + { description } + + ) : null } + + ); +} diff --git a/packages/edit-site/src/components/page-patterns/index.js b/packages/edit-site/src/components/page-patterns/index.js index 961ed51f39e5d..d90fc74844244 100644 --- a/packages/edit-site/src/components/page-patterns/index.js +++ b/packages/edit-site/src/components/page-patterns/index.js @@ -32,7 +32,12 @@ export default function PagePatterns() { title={ __( 'Patterns content' ) } hideTitleFromUI > - + ); diff --git a/packages/edit-site/src/components/page-patterns/patterns-list.js b/packages/edit-site/src/components/page-patterns/patterns-list.js index 545ffdb044275..bc2a18bf39456 100644 --- a/packages/edit-site/src/components/page-patterns/patterns-list.js +++ b/packages/edit-site/src/components/page-patterns/patterns-list.js @@ -1,51 +1,87 @@ /** * WordPress dependencies */ - +import { useState, useDeferredValue, useId } from '@wordpress/element'; import { SearchControl, - __experimentalHeading as Heading, - __experimentalText as Text, __experimentalVStack as VStack, Flex, FlexBlock, + __experimentalToggleGroupControl as ToggleGroupControl, + __experimentalToggleGroupControlOption as ToggleGroupControlOption, + __experimentalHeading as Heading, + __experimentalText as Text, } from '@wordpress/components'; import { __, isRTL } from '@wordpress/i18n'; -import { symbol, chevronLeft, chevronRight } from '@wordpress/icons'; +import { chevronLeft, chevronRight } from '@wordpress/icons'; import { privateApis as routerPrivateApis } from '@wordpress/router'; -import { useViewportMatch } from '@wordpress/compose'; +import { useViewportMatch, useAsyncList } from '@wordpress/compose'; /** * Internal dependencies */ +import PatternsHeader from './header'; import Grid from './grid'; import NoPatterns from './no-patterns'; import usePatterns from './use-patterns'; import SidebarButton from '../sidebar-button'; import useDebouncedInput from '../../utils/use-debounced-input'; import { unlock } from '../../lock-unlock'; +import { SYNC_TYPES, USER_PATTERN_CATEGORY } from './utils'; const { useLocation, useHistory } = unlock( routerPrivateApis ); +const SYNC_FILTERS = { + all: __( 'All' ), + [ SYNC_TYPES.full ]: __( 'Synced' ), + [ SYNC_TYPES.unsynced ]: __( 'Standard' ), +}; + +const SYNC_DESCRIPTIONS = { + all: '', + [ SYNC_TYPES.full ]: __( + 'Patterns that are kept in sync across your site.' + ), + [ SYNC_TYPES.unsynced ]: __( + 'Patterns that can be changed freely without affecting your site.' + ), +}; + export default function PatternsList( { categoryId, type } ) { const location = useLocation(); const history = useHistory(); const isMobileViewport = useViewportMatch( 'medium', '<' ); const [ filterValue, setFilterValue, delayedFilterValue ] = useDebouncedInput( '' ); + const deferredFilterValue = useDeferredValue( delayedFilterValue ); - const [ patterns, isResolving ] = usePatterns( - type, - categoryId, - delayedFilterValue - ); + const [ syncFilter, setSyncFilter ] = useState( 'all' ); + const deferredSyncedFilter = useDeferredValue( syncFilter ); + const { patterns, isResolving } = usePatterns( type, categoryId, { + search: deferredFilterValue, + syncStatus: + deferredSyncedFilter === 'all' ? undefined : deferredSyncedFilter, + } ); - const { syncedPatterns, unsyncedPatterns } = patterns; - const hasPatterns = !! syncedPatterns.length || !! unsyncedPatterns.length; + const id = useId(); + const titleId = `${ id }-title`; + const descriptionId = `${ id }-description`; + + const hasPatterns = patterns.length; + const title = SYNC_FILTERS[ syncFilter ]; + const description = SYNC_DESCRIPTIONS[ syncFilter ]; + const shownPatterns = useAsyncList( patterns ); return ( - + + + { isMobileViewport && ( ) } - + setFilterValue( value ) } @@ -71,42 +107,48 @@ export default function PatternsList( { categoryId, type } ) { __nextHasNoMarginBottom /> + { categoryId === USER_PATTERN_CATEGORY && ( + setSyncFilter( value ) } + __nextHasNoMarginBottom + > + { Object.entries( SYNC_FILTERS ).map( + ( [ key, label ] ) => ( + + ) + ) } + + ) } - { isResolving && __( 'Loading' ) } - { ! isResolving && !! syncedPatterns.length && ( - <> - - { __( 'Synced' ) } - - { __( - 'Patterns that are kept in sync across the site' - ) } + { syncFilter !== 'all' && ( + + + { title } + + { description ? ( + + { description } - - - + ) : null } + ) } - { ! isResolving && !! unsyncedPatterns.length && ( - <> - - { __( 'Standard' ) } - - { __( - 'Patterns that can be changed freely without affecting the site' - ) } - - - - + { hasPatterns && ( + ) } { ! isResolving && ! hasPatterns && } diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index 0c7660035fb3e..aafa778feae3e 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -1,6 +1,13 @@ .edit-site-patterns { - background: rgba(0, 0, 0, 0.05); + background: rgba(0, 0, 0, 0.15); margin: $header-height 0 0; + .components-base-control { + width: 100%; + @include break-medium { + width: auto; + } + } + .components-text { color: $gray-600; } @@ -12,25 +19,63 @@ @include break-medium { margin: 0; } -} -.edit-site-patterns__grid { - column-gap: $grid-unit-30; - @include break-large() { - column-count: 2; + .edit-site-patterns__search-block { + min-width: fit-content; + flex-grow: 1; } + // The increased specificity here is to overcome component styles + // without relying on internal component class names. + .edit-site-patterns__search { + input[type="search"] { + height: $button-size-next-default-40px; + background: $gray-800; + color: $gray-200; + + &:focus { + background: $gray-800; + } + } + + svg { + fill: $gray-600; + } + } + + .edit-site-patterns__sync-status-filter { + background: $gray-800; + border: none; + height: $button-size-next-default-40px; + min-width: max-content; + width: 100%; + max-width: 100%; + + @include break-medium { + width: 300px; + } + } + .edit-site-patterns__sync-status-filter-option:active { + background: $gray-700; + color: $gray-100; + } +} + +.edit-site-patterns__grid { + display: grid; + grid-template-columns: 1fr; + gap: $grid-unit-40; // Small top padding required to avoid cutting off the visible outline // when hovering items. padding-top: $border-width-focus-fallback; margin-bottom: $grid-unit-40; - + @include break-large { + grid-template-columns: 1fr 1fr; + } .edit-site-patterns__pattern { break-inside: avoid-column; display: flex; flex-direction: column; - margin-bottom: $grid-unit-60; - .edit-site-patterns__preview { border-radius: $radius-block-ui; cursor: pointer; @@ -68,26 +113,13 @@ } .edit-site-patterns__preview { - flex: 1; + flex: 0 1 auto; margin-bottom: $grid-unit-20; } } -// The increased specificity here is to overcome component styles -// without relying on internal component class names. -.edit-site-patterns__search { - &#{&} input[type="search"] { - background: $gray-800; - color: $gray-200; - - &:focus { - background: $gray-800; - } - } - - svg { - fill: $gray-600; - } +.edit-site-patterns__load-more { + align-self: center; } .edit-site-patterns__pattern-title { 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 295d1eee8e410..0bcc52c85cb62 100644 --- a/packages/edit-site/src/components/page-patterns/use-patterns.js +++ b/packages/edit-site/src/components/page-patterns/use-patterns.js @@ -4,7 +4,6 @@ import { parse } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; -import { useMemo } from '@wordpress/element'; /** * Internal dependencies @@ -15,7 +14,6 @@ import { SYNC_TYPES, TEMPLATE_PARTS, USER_PATTERNS, - USER_PATTERN_CATEGORY, filterOutDuplicatesByName, } from './utils'; import { unlock } from '../../lock-unlock'; @@ -43,106 +41,64 @@ const templatePartToPattern = ( templatePart ) => ( { const templatePartHasCategory = ( item, category ) => item.templatePart.area === category; -const useTemplatePartsAsPatterns = ( - categoryId, - postType = TEMPLATE_PARTS, - filterValue = '' +const selectTemplatePartsAsPatterns = ( + select, + { categoryId, search = '' } = {} ) => { - const { templateParts, isResolving } = useSelect( - ( select ) => { - if ( postType !== TEMPLATE_PARTS ) { - return { - templateParts: EMPTY_PATTERN_LIST, - isResolving: false, - }; - } - - const { getEntityRecords, isResolving: _isResolving } = - select( coreStore ); - const query = { per_page: -1 }; - const rawTemplateParts = getEntityRecords( - 'postType', - postType, - query - ); - const partsAsPatterns = rawTemplateParts?.map( ( templatePart ) => - templatePartToPattern( templatePart ) - ); - - return { - templateParts: partsAsPatterns, - isResolving: _isResolving( 'getEntityRecords', [ - 'postType', - 'wp_template_part', - query, - ] ), - }; - }, - [ postType ] + const { getEntityRecords, getIsResolving } = select( coreStore ); + const query = { per_page: -1 }; + const rawTemplateParts = + getEntityRecords( 'postType', TEMPLATE_PARTS, query ) ?? + EMPTY_PATTERN_LIST; + const templateParts = rawTemplateParts.map( ( templatePart ) => + templatePartToPattern( templatePart ) ); - const filteredTemplateParts = useMemo( () => { - if ( ! templateParts ) { - return EMPTY_PATTERN_LIST; - } + const isResolving = getIsResolving( 'getEntityRecords', [ + 'postType', + 'wp_template_part', + query, + ] ); - return searchItems( templateParts, filterValue, { - categoryId, - hasCategory: templatePartHasCategory, - } ); - }, [ templateParts, filterValue, categoryId ] ); + const patterns = searchItems( templateParts, search, { + categoryId, + hasCategory: templatePartHasCategory, + } ); - return { templateParts: filteredTemplateParts, isResolving }; + return { patterns, isResolving }; }; -const useThemePatterns = ( - categoryId, - postType = PATTERNS, - filterValue = '' -) => { - const blockPatterns = useSelect( ( select ) => { - const { getSettings } = unlock( select( editSiteStore ) ); - const settings = getSettings(); - return ( - settings.__experimentalAdditionalBlockPatterns ?? - settings.__experimentalBlockPatterns - ); +const selectThemePatterns = ( select, { categoryId, search = '' } = {} ) => { + const { getSettings } = unlock( select( editSiteStore ) ); + const settings = getSettings(); + const blockPatterns = + settings.__experimentalAdditionalBlockPatterns ?? + settings.__experimentalBlockPatterns; + + const restBlockPatterns = select( coreStore ).getBlockPatterns(); + + let patterns = [ + ...( blockPatterns || [] ), + ...( restBlockPatterns || [] ), + ] + .filter( + ( pattern ) => ! CORE_PATTERN_SOURCES.includes( pattern.source ) + ) + .filter( filterOutDuplicatesByName ) + .map( ( pattern ) => ( { + ...pattern, + keywords: pattern.keywords || [], + type: 'pattern', + blocks: parse( pattern.content ), + } ) ); + + patterns = searchItems( patterns, search, { + categoryId, + hasCategory: ( item, currentCategory ) => + item.categories?.includes( currentCategory ), } ); - const restBlockPatterns = useSelect( ( select ) => - select( coreStore ).getBlockPatterns() - ); - - const patterns = useMemo( - () => - [ ...( blockPatterns || [] ), ...( restBlockPatterns || [] ) ] - .filter( - ( pattern ) => - ! CORE_PATTERN_SOURCES.includes( pattern.source ) - ) - .filter( filterOutDuplicatesByName ) - .map( ( pattern ) => ( { - ...pattern, - keywords: pattern.keywords || [], - type: 'pattern', - blocks: parse( pattern.content ), - } ) ), - [ blockPatterns, restBlockPatterns ] - ); - - const filteredPatterns = useMemo( () => { - if ( postType !== PATTERNS ) { - return EMPTY_PATTERN_LIST; - } - - return searchItems( patterns, filterValue, { - categoryId, - hasCategory: ( item, currentCategory ) => - item.categories?.includes( currentCategory ), - } ); - }, [ patterns, filterValue, categoryId, postType ] ); - - return filteredPatterns; + return { patterns, isResolving: false }; }; const reusableBlockToPattern = ( reusableBlock ) => ( { @@ -156,88 +112,58 @@ const reusableBlockToPattern = ( reusableBlock ) => ( { reusableBlock, } ); -const useUserPatterns = ( - categoryId, - categoryType = PATTERNS, - filterValue = '' -) => { - const postType = categoryType === PATTERNS ? USER_PATTERNS : categoryType; - const unfilteredPatterns = useSelect( - ( select ) => { - if ( - postType !== USER_PATTERNS || - categoryId !== USER_PATTERN_CATEGORY - ) { - return EMPTY_PATTERN_LIST; - } +const selectUserPatterns = ( select, { search = '', syncStatus } = {} ) => { + const { getEntityRecords, getIsResolving } = select( coreStore ); - const { getEntityRecords } = select( coreStore ); - const records = getEntityRecords( 'postType', postType, { - per_page: -1, - } ); + const query = { per_page: -1 }; + const records = getEntityRecords( 'postType', USER_PATTERNS, query ); - if ( ! records ) { - return EMPTY_PATTERN_LIST; - } + let patterns = records + ? records.map( ( record ) => reusableBlockToPattern( record ) ) + : EMPTY_PATTERN_LIST; + const isResolving = getIsResolving( 'getEntityRecords', [ + 'postType', + USER_PATTERNS, + query, + ] ); - return records.map( ( record ) => - reusableBlockToPattern( record ) - ); - }, - [ postType, categoryId ] - ); + if ( syncStatus ) { + patterns = patterns.filter( + ( pattern ) => pattern.syncStatus === syncStatus + ); + } - const filteredPatterns = useMemo( () => { - if ( ! unfilteredPatterns.length ) { - return EMPTY_PATTERN_LIST; - } - - return searchItems( unfilteredPatterns, filterValue, { - // We exit user pattern retrieval early if we aren't in the - // catch-all category for user created patterns, so it has - // to be in the category. - hasCategory: () => true, - } ); - }, [ unfilteredPatterns, filterValue ] ); - - const patterns = { syncedPatterns: [], unsyncedPatterns: [] }; - - filteredPatterns.forEach( ( pattern ) => { - if ( pattern.syncStatus === SYNC_TYPES.full ) { - patterns.syncedPatterns.push( pattern ); - } else { - patterns.unsyncedPatterns.push( pattern ); - } + patterns = searchItems( patterns, search, { + // We exit user pattern retrieval early if we aren't in the + // catch-all category for user created patterns, so it has + // to be in the category. + hasCategory: () => true, } ); - return patterns; + return { patterns, isResolving }; }; -export const usePatterns = ( categoryType, categoryId, filterValue ) => { - const blockPatterns = useThemePatterns( - categoryId, - categoryType, - filterValue - ); - - const { syncedPatterns = [], unsyncedPatterns = [] } = useUserPatterns( - categoryId, - categoryType, - filterValue - ); - - const { templateParts, isResolving } = useTemplatePartsAsPatterns( - categoryId, - categoryType, - filterValue +export const usePatterns = ( + categoryType, + categoryId, + { search = '', syncStatus } +) => { + return useSelect( + ( select ) => { + if ( categoryType === TEMPLATE_PARTS ) { + return selectTemplatePartsAsPatterns( select, { + categoryId, + search, + } ); + } else if ( categoryType === PATTERNS ) { + return selectThemePatterns( select, { categoryId, search } ); + } else if ( categoryType === USER_PATTERNS ) { + return selectUserPatterns( select, { search, syncStatus } ); + } + return { patterns: EMPTY_PATTERN_LIST, isResolving: false }; + }, + [ categoryId, categoryType, search, syncStatus ] ); - - const patterns = { - syncedPatterns: [ ...templateParts, ...syncedPatterns ], - unsyncedPatterns: [ ...blockPatterns, ...unsyncedPatterns ], - }; - - return [ patterns, isResolving ]; }; export default usePatterns; diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-my-patterns.js b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-my-patterns.js index e3d5cc297164a..37f0b0f8a4e06 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-my-patterns.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-my-patterns.js @@ -6,18 +6,19 @@ import { useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; export default function useMyPatterns() { - const myPatterns = useSelect( ( select ) => - select( coreStore ).getEntityRecords( 'postType', 'wp_block', { - per_page: -1, - } ) + const myPatternsCount = useSelect( + ( select ) => + select( coreStore ).getEntityRecords( 'postType', 'wp_block', { + per_page: -1, + } )?.length ?? 0 ); return { myPatterns: { - count: myPatterns?.length || 0, + count: myPatternsCount, name: 'my-patterns', label: __( 'My patterns' ), }, - hasPatterns: !! myPatterns?.length, + hasPatterns: myPatternsCount > 0, }; } From 1219af65bcca93c93dc1aff0941472ddb4c36637 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 10 Jul 2023 14:25:24 +0900 Subject: [PATCH 10/31] Patterns: Add missing decoding entities processing in Patterns and Template/Parts pages (#52449) --- .../src/components/page-patterns/use-patterns.js | 3 ++- .../src/components/template-actions/index.js | 3 ++- .../components/template-actions/rename-menu-item.js | 13 ++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) 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 0bcc52c85cb62..ea2b8ac976fea 100644 --- a/packages/edit-site/src/components/page-patterns/use-patterns.js +++ b/packages/edit-site/src/components/page-patterns/use-patterns.js @@ -4,6 +4,7 @@ import { parse } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; +import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies @@ -33,7 +34,7 @@ const templatePartToPattern = ( templatePart ) => ( { keywords: templatePart.keywords || [], id: createTemplatePartId( templatePart.theme, templatePart.slug ), name: createTemplatePartId( templatePart.theme, templatePart.slug ), - title: templatePart.title.rendered, + title: decodeEntities( templatePart.title.rendered ), type: templatePart.type, templatePart, } ); diff --git a/packages/edit-site/src/components/template-actions/index.js b/packages/edit-site/src/components/template-actions/index.js index b4618dcae966d..6e744135df67f 100644 --- a/packages/edit-site/src/components/template-actions/index.js +++ b/packages/edit-site/src/components/template-actions/index.js @@ -13,6 +13,7 @@ import { } from '@wordpress/components'; import { moreVertical } from '@wordpress/icons'; import { store as noticesStore } from '@wordpress/notices'; +import { decodeEntities } from '@wordpress/html-entities'; /** * Internal dependencies @@ -58,7 +59,7 @@ export default function TemplateActions( { sprintf( /* translators: The template/part's name. */ __( '"%s" reverted.' ), - template.title.rendered + decodeEntities( template.title.rendered ) ), { type: 'snackbar', diff --git a/packages/edit-site/src/components/template-actions/rename-menu-item.js b/packages/edit-site/src/components/template-actions/rename-menu-item.js index dec7f0bc8bf7d..79285047ef04a 100644 --- a/packages/edit-site/src/components/template-actions/rename-menu-item.js +++ b/packages/edit-site/src/components/template-actions/rename-menu-item.js @@ -14,9 +14,13 @@ import { } from '@wordpress/components'; import { store as coreStore } from '@wordpress/core-data'; import { store as noticesStore } from '@wordpress/notices'; +import { decodeEntities } from '@wordpress/html-entities'; export default function RenameMenuItem( { template, onClose } ) { - const [ title, setTitle ] = useState( () => template.title.rendered ); + const [ title, setTitle ] = useState( + decodeEntities( template.title.rendered ) + ); + const [ isModalOpen, setIsModalOpen ] = useState( false ); const { editEntityRecord, saveEditedEntityRecord } = @@ -64,12 +68,7 @@ export default function RenameMenuItem( { template, onClose } ) { return ( <> - { - setIsModalOpen( true ); - setTitle( template.title.rendered ); - } } - > + setIsModalOpen( true ) }> { __( 'Rename' ) } { isModalOpen && ( From 0d3432fdb5d2bf7584d273a37259ea973dd4796e Mon Sep 17 00:00:00 2001 From: James Koster Date: Mon, 10 Jul 2023 07:01:47 +0100 Subject: [PATCH 11/31] Fix document title icon appearance (#52424) --- .../components/header-edit-mode/document-actions/style.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss b/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss index 6281887b13738..d26bbdaf28ff6 100644 --- a/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss +++ b/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss @@ -40,6 +40,10 @@ overflow: hidden; grid-column: 2 / 3; + .block-editor-block-icon { + min-width: $grid-unit-30; + } + h1 { white-space: nowrap; overflow: hidden; From 80a84f850dbba1193cb296f43ca4a910b1a7e836 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Mon, 10 Jul 2023 01:51:12 -0400 Subject: [PATCH 12/31] Quote block: Add transform to paragraph (#51809) * Add ungroup transform as transform to p * Lint * Update test and snapshot. --------- Co-authored-by: Juan Aldasoro --- .../test/__snapshots__/transforms.native.js.snap | 10 ++++++++++ .../src/quote/test/transforms.native.js | 6 +++++- packages/block-library/src/quote/transforms.js | 13 +++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap b/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap index 5b5df918f2bee..65d87d5b0d7bd 100644 --- a/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap +++ b/packages/block-library/src/quote/test/__snapshots__/transforms.native.js.snap @@ -22,6 +22,16 @@ exports[`Quote block transforms to Group block 1`] = ` " `; +exports[`Quote block transforms to Paragraph block 1`] = ` +" +

"This will make running your own blog a viable alternative again."

+ + + +

— Adrian Zumbrunnen

+" +`; + exports[`Quote block transforms to Pullquote block 1`] = ` "

"This will make running your own blog a viable alternative again."

— Adrian Zumbrunnen
diff --git a/packages/block-library/src/quote/test/transforms.native.js b/packages/block-library/src/quote/test/transforms.native.js index 46c4eb2b6f972..25030e0a018d4 100644 --- a/packages/block-library/src/quote/test/transforms.native.js +++ b/packages/block-library/src/quote/test/transforms.native.js @@ -21,7 +21,11 @@ const initialHtml = ` `; const transformsWithInnerBlocks = [ 'Columns', 'Group' ]; -const blockTransforms = [ 'Pullquote', ...transformsWithInnerBlocks ]; +const blockTransforms = [ + 'Pullquote', + 'Paragraph', + ...transformsWithInnerBlocks, +]; setupCoreBlocks(); diff --git a/packages/block-library/src/quote/transforms.js b/packages/block-library/src/quote/transforms.js index d4cd77177bf03..4e153a6399029 100644 --- a/packages/block-library/src/quote/transforms.js +++ b/packages/block-library/src/quote/transforms.js @@ -109,6 +109,19 @@ const transforms = { } ); }, }, + { + type: 'block', + blocks: [ 'core/paragraph' ], + transform: ( { citation }, innerBlocks ) => + citation + ? [ + ...innerBlocks, + createBlock( 'core/paragraph', { + content: citation, + } ), + ] + : innerBlocks, + }, { type: 'block', blocks: [ 'core/group' ], From 34b374b135f5f578680dcf0c7490bdeb3eb616ca Mon Sep 17 00:00:00 2001 From: Saxon Fletcher Date: Mon, 10 Jul 2023 11:06:38 +1000 Subject: [PATCH 13/31] remove sidebar group descriptions (#52453) --- .../components/sidebar-navigation-screen-patterns/index.js | 6 ------ .../sidebar-navigation-screen-patterns/style.scss | 3 --- 2 files changed, 9 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js index f200382f96311..d3fc15358027b 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js @@ -35,7 +35,6 @@ function TemplatePartGroup( { areas, currentArea, currentType } ) { <>
{ __( 'Template parts' ) } -

{ __( 'Synced patterns for use in template building.' ) }

{ Object.entries( areas ).map( @@ -64,11 +63,6 @@ function ThemePatternsGroup( { categories, currentCategory, currentType } ) { <>
{ __( 'Theme patterns' ) } -

- { __( - 'For insertion into documents where they can then be customized.' - ) } -

{ categories.map( ( category ) => ( diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss index 65790b5e86216..6a6fbc009e0aa 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss @@ -1,8 +1,5 @@ .edit-site-sidebar-navigation-screen-patterns__group { margin-bottom: $grid-unit-40; - padding-bottom: $grid-unit-30; - border-bottom: 1px solid $gray-800; - &:last-of-type, &:first-of-type { border-bottom: 0; From b8faaa8469ca9333d6b3fb81c5e700c132bc566f Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Mon, 10 Jul 2023 18:13:20 +1000 Subject: [PATCH 14/31] Patterns: alternative grid layout to improve keyboard accessibility (#52357) --- .../src/components/page-patterns/grid-item.js | 239 ++++++++---------- .../src/components/page-patterns/grid.js | 13 +- .../src/components/page-patterns/style.scss | 20 +- 3 files changed, 128 insertions(+), 144 deletions(-) diff --git a/packages/edit-site/src/components/page-patterns/grid-item.js b/packages/edit-site/src/components/page-patterns/grid-item.js index e7ca2d3ced53e..b78c58f5a2572 100644 --- a/packages/edit-site/src/components/page-patterns/grid-item.js +++ b/packages/edit-site/src/components/page-patterns/grid-item.js @@ -12,9 +12,7 @@ import { DropdownMenu, MenuGroup, MenuItem, - __experimentalHeading as Heading, __experimentalHStack as HStack, - __unstableCompositeItem as CompositeItem, Tooltip, Flex, } from '@wordpress/components'; @@ -32,7 +30,6 @@ import { } from '@wordpress/icons'; import { store as noticesStore } from '@wordpress/notices'; import { store as reusableBlocksStore } from '@wordpress/reusable-blocks'; -import { DELETE, BACKSPACE } from '@wordpress/keycodes'; /** * Internal dependencies @@ -66,12 +63,6 @@ function GridItem( { categoryId, item, ...props } ) { categoryType: item.type, } ); - const onKeyDown = ( event ) => { - if ( DELETE === event.keyCode || BACKSPACE === event.keyCode ) { - setIsDeleteDialogOpen( true ); - } - }; - const isEmpty = ! item.blocks?.length; const patternClassNames = classnames( 'edit-site-patterns__pattern', { 'is-placeholder': isEmpty, @@ -138,134 +129,120 @@ function GridItem( { categoryId, item, ...props } ) { ); return ( - <> -
- - `${ descriptionId }-${ index }` - ) - .join( ' ' ) - : undefined - } +
  • + + { ariaDescriptions.map( ( ariaDescription, index ) => ( + + ) ) } + + + { ( { onClose } ) => ( + + { isCustomPattern && ! hasThemeFile && ( + - { isCustomPattern && ( - - setIsDeleteDialogOpen( true ) - } - > - { hasThemeFile - ? __( 'Clear customizations' ) - : __( 'Delete' ) } - - ) } - - ) } - - -
  • + ) } + + { isCustomPattern && ( + + setIsDeleteDialogOpen( true ) + } + > + { hasThemeFile + ? __( 'Clear customizations' ) + : __( 'Delete' ) } + + ) } + + ) } + + + { isDeleteDialogOpen && ( ) } - + ); } diff --git a/packages/edit-site/src/components/page-patterns/grid.js b/packages/edit-site/src/components/page-patterns/grid.js index b9b7053c112ce..1902b36982c14 100644 --- a/packages/edit-site/src/components/page-patterns/grid.js +++ b/packages/edit-site/src/components/page-patterns/grid.js @@ -1,11 +1,7 @@ /** * WordPress dependencies */ -import { - __unstableComposite as Composite, - __unstableUseCompositeState as useCompositeState, - __experimentalText as Text, -} from '@wordpress/components'; +import { __experimentalText as Text } from '@wordpress/components'; import { useRef } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; @@ -17,7 +13,6 @@ import GridItem from './grid-item'; const PAGE_SIZE = 100; export default function Grid( { categoryId, items, ...props } ) { - const composite = useCompositeState( { wrap: true } ); const gridRef = useRef(); if ( ! items?.length ) { @@ -29,8 +24,7 @@ export default function Grid( { categoryId, items, ...props } ) { return ( <> - ) ) } - + { restLength > 0 && ( { sprintf( diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index aafa778feae3e..c0a999599251e 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -61,6 +61,12 @@ } } +.edit-site-patterns__section-header { + .screen-reader-shortcut:focus { + top: 0; + } +} + .edit-site-patterns__grid { display: grid; grid-template-columns: 1fr; @@ -77,13 +83,17 @@ display: flex; flex-direction: column; .edit-site-patterns__preview { - border-radius: $radius-block-ui; + box-shadow: none; + border: none; + padding: 0; + background-color: unset; + box-sizing: border-box; + border-radius: 4px; cursor: pointer; overflow: hidden; &:focus { - box-shadow: inset 0 0 0 2px $white, 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); - + box-shadow: inset 0 0 0 0 $white, 0 0 0 2px var(--wp-admin-theme-color); // Windows High Contrast mode will show this outline, but not the box-shadow. outline: 2px solid transparent; } @@ -91,6 +101,10 @@ &.is-inactive { cursor: default; } + &.is-inactive:focus { + box-shadow: 0 0 0 var(--wp-admin-border-width-focus) $gray-800; + opacity: 0.8; + } } .edit-site-patterns__footer, From 0fd4e209f6228dfc8e0bb96b333f696b8dea5e5b Mon Sep 17 00:00:00 2001 From: Saxon Fletcher Date: Mon, 10 Jul 2023 15:36:52 +1000 Subject: [PATCH 15/31] add sync tooltip (#52458) --- .../src/components/page-patterns/grid-item.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/edit-site/src/components/page-patterns/grid-item.js b/packages/edit-site/src/components/page-patterns/grid-item.js index b78c58f5a2572..0b1f6dc1fdf8d 100644 --- a/packages/edit-site/src/components/page-patterns/grid-item.js +++ b/packages/edit-site/src/components/page-patterns/grid-item.js @@ -173,10 +173,19 @@ function GridItem( { categoryId, item, ...props } ) { className="edit-site-patterns__pattern-title" > { itemIcon && ( - + + + + + ) } { item.title } From 3b9c88d4672c765c632d402e74df098b92eea2f2 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 5 Jul 2023 11:24:05 +1000 Subject: [PATCH 16/31] Patterns: Update section heading levels (#52273) --- packages/edit-site/src/components/page-patterns/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index c0a999599251e..d6f0b897d0cf2 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -137,7 +137,7 @@ } .edit-site-patterns__pattern-title { - color: $gray-600; + color: $gray-200; .edit-site-patterns__pattern-icon { border-radius: $grid-unit-05; From 78d8766d9aa6623ca610985715e4d7880b237695 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Tue, 11 Jul 2023 08:56:13 +0900 Subject: [PATCH 17/31] Ensure that the unsaved title is not persisted when reopening the modal (#52473) --- .../template-actions/rename-menu-item.js | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/edit-site/src/components/template-actions/rename-menu-item.js b/packages/edit-site/src/components/template-actions/rename-menu-item.js index 79285047ef04a..51959cfe579a1 100644 --- a/packages/edit-site/src/components/template-actions/rename-menu-item.js +++ b/packages/edit-site/src/components/template-actions/rename-menu-item.js @@ -17,10 +17,8 @@ import { store as noticesStore } from '@wordpress/notices'; import { decodeEntities } from '@wordpress/html-entities'; export default function RenameMenuItem( { template, onClose } ) { - const [ title, setTitle ] = useState( - decodeEntities( template.title.rendered ) - ); - + const title = decodeEntities( template.title.rendered ); + const [ editedTitle, setEditedTitle ] = useState( title ); const [ isModalOpen, setIsModalOpen ] = useState( false ); const { editEntityRecord, saveEditedEntityRecord } = @@ -37,11 +35,11 @@ export default function RenameMenuItem( { template, onClose } ) { try { await editEntityRecord( 'postType', template.type, template.id, { - title, + title: editedTitle, } ); // Update state before saving rerenders the list. - setTitle( '' ); + setEditedTitle( '' ); setIsModalOpen( false ); onClose(); @@ -68,7 +66,12 @@ export default function RenameMenuItem( { template, onClose } ) { return ( <> - setIsModalOpen( true ) }> + { + setIsModalOpen( true ); + setEditedTitle( title ); + } } + > { __( 'Rename' ) } { isModalOpen && ( @@ -84,8 +87,8 @@ export default function RenameMenuItem( { template, onClose } ) { From 730f7f1acb1b4bc6ab32b4eeb6c542ad57264ce6 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Mon, 10 Jul 2023 21:05:46 +0200 Subject: [PATCH 18/31] Iframe: avoid asset parsing & fix script localisation (#52405) * Iframe: avoid asset parsing & fix script localisation * Add e2e test for script localisation --- .../src/components/iframe/index.js | 63 +++++-------------- .../plugins/iframed-enqueue-block-assets.php | 13 ++++ .../iframed-enqueue-block-assets/script.js | 3 + .../iframed-equeue-block-assets.test.js | 7 +++ 4 files changed, 39 insertions(+), 47 deletions(-) create mode 100644 packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index 4d4521eae3581..afe403af0aa39 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -11,7 +11,6 @@ import { createPortal, forwardRef, useMemo, - useReducer, useEffect, } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; @@ -78,29 +77,6 @@ function bubbleEvents( doc ) { } } -function useParsedAssets( html ) { - return useMemo( () => { - const doc = document.implementation.createHTMLDocument( '' ); - doc.body.innerHTML = html; - return Array.from( doc.body.children ); - }, [ html ] ); -} - -async function loadScript( head, { id, src } ) { - return new Promise( ( resolve, reject ) => { - const script = head.ownerDocument.createElement( 'script' ); - script.id = id; - if ( src ) { - script.src = src; - script.onload = () => resolve(); - script.onerror = () => reject(); - } else { - resolve(); - } - head.appendChild( script ); - } ); -} - function Iframe( { contentRef, children, @@ -112,21 +88,22 @@ function Iframe( { forwardedRef: ref, ...props } ) { - const assets = useSelect( + const { styles = '', scripts = '' } = useSelect( ( select ) => select( blockEditorStore ).getSettings().__unstableResolvedAssets, [] ); - const [ , forceRender ] = useReducer( () => ( {} ) ); const [ iframeDocument, setIframeDocument ] = useState(); const [ bodyClasses, setBodyClasses ] = useState( [] ); const compatStyles = useCompatibilityStyles(); - const scripts = useParsedAssets( assets?.scripts ); const clearerRef = useBlockSelectionClearer(); const [ before, writingFlowRef, after ] = useWritingFlow(); const [ contentResizeListener, { height: contentHeight } ] = useResizeObserver(); const setRef = useRefEffect( ( node ) => { + node._load = () => { + setIframeDocument( node.contentDocument ); + }; let iFrameDocument; // Prevent the default browser action for files dropped outside of dropzones. function preventFileDropDefault( event ) { @@ -138,7 +115,6 @@ function Iframe( { iFrameDocument = contentDocument; bubbleEvents( contentDocument ); - setIframeDocument( contentDocument ); clearerRef( documentElement ); // Ideally ALL classes that are added through get_body_class should @@ -154,7 +130,6 @@ function Iframe( { ); contentDocument.dir = ownerDocument.dir; - documentElement.removeChild( contentDocument.body ); for ( const compatStyle of compatStyles ) { if ( contentDocument.getElementById( compatStyle.id ) ) { @@ -199,35 +174,29 @@ function Iframe( { }; }, [] ); - const headRef = useRefEffect( ( element ) => { - scripts - .reduce( - ( promise, script ) => - promise.then( () => loadScript( element, script ) ), - Promise.resolve() - ) - .finally( () => { - // When script are loaded, re-render blocks to allow them - // to initialise. - forceRender(); - } ); - }, [] ); const disabledRef = useDisabled( { isDisabled: ! readonly } ); const bodyRef = useMergeRefs( [ contentRef, clearerRef, writingFlowRef, disabledRef, - headRef, ] ); // Correct doctype is required to enable rendering in standards // mode. Also preload the styles to avoid a flash of unstyled // content. - const html = - '' + - '' + - ( assets?.styles ?? '' ); + const html = ` + + + + + ${ styles } + ${ scripts } + + + + +`; const [ src, cleanup ] = useMemo( () => { const _src = URL.createObjectURL( diff --git a/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php b/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php index ad98354dd45dc..3f24a6e25cfcb 100644 --- a/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php +++ b/packages/e2e-tests/plugins/iframed-enqueue-block-assets.php @@ -17,5 +17,18 @@ static function() { filemtime( plugin_dir_path( __FILE__ ) . 'iframed-enqueue-block-assets/style.css' ) ); wp_add_inline_style( 'iframed-enqueue-block-assets', 'body{padding:20px!important}' ); + wp_enqueue_script( + 'iframed-enqueue-block-assets-script', + plugin_dir_url( __FILE__ ) . 'iframed-enqueue-block-assets/script.js', + array(), + filemtime( plugin_dir_path( __FILE__ ) . 'iframed-enqueue-block-assets/script.js' ) + ); + wp_localize_script( + 'iframed-enqueue-block-assets-script', + 'iframedEnqueueBlockAssetsL10n', + array( + 'test' => 'Iframed Enqueue Block Assets!', + ) + ); } ); diff --git a/packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js b/packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js new file mode 100644 index 0000000000000..f0eddd65c70eb --- /dev/null +++ b/packages/e2e-tests/plugins/iframed-enqueue-block-assets/script.js @@ -0,0 +1,3 @@ +window.addEventListener( 'load', () => { + document.body.dataset.iframedEnqueueBlockAssetsL10n = window.iframedEnqueueBlockAssetsL10n.test; +} ); diff --git a/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js b/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js index c1bd26fe1c761..c29af593abb12 100644 --- a/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js +++ b/packages/e2e-tests/specs/editor/plugins/iframed-equeue-block-assets.test.js @@ -32,6 +32,7 @@ describe( 'iframed inline styles', () => { } ); it( 'should load styles added through enqueue_block_assets', async () => { + await page.waitForSelector( 'iframe[name="editor-canvas"]' ); // Check stylesheet. expect( await getComputedStyle( canvas(), 'body', 'background-color' ) @@ -40,5 +41,11 @@ describe( 'iframed inline styles', () => { expect( await getComputedStyle( canvas(), 'body', 'padding' ) ).toBe( '20px' ); + + expect( + await canvas().evaluate( () => ( { ...document.body.dataset } ) ) + ).toEqual( { + iframedEnqueueBlockAssetsL10n: 'Iframed Enqueue Block Assets!', + } ); } ); } ); From 93a06edab15c3153741c0c513acc9e385a0b0357 Mon Sep 17 00:00:00 2001 From: James Koster Date: Tue, 11 Jul 2023 00:01:43 +0100 Subject: [PATCH 19/31] Update descriptions (#52468) --- .../edit-site/src/components/page-patterns/patterns-list.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/page-patterns/patterns-list.js b/packages/edit-site/src/components/page-patterns/patterns-list.js index bc2a18bf39456..7bf2a9d506584 100644 --- a/packages/edit-site/src/components/page-patterns/patterns-list.js +++ b/packages/edit-site/src/components/page-patterns/patterns-list.js @@ -40,10 +40,10 @@ const SYNC_FILTERS = { const SYNC_DESCRIPTIONS = { all: '', [ SYNC_TYPES.full ]: __( - 'Patterns that are kept in sync across your site.' + 'Patterns that are kept in sync across the site.' ), [ SYNC_TYPES.unsynced ]: __( - 'Patterns that can be changed freely without affecting your site.' + 'Patterns that can be changed freely without affecting the site.' ), }; From c98e64dcd58ae8c1a96671512d40d2242d61c5e6 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Mon, 10 Jul 2023 17:22:34 +0200 Subject: [PATCH 20/31] Footnotes: show in inserter and placehold (#52445) * Footnotes: show in inserter and placehold * Fix placeholder block membrane; fix copy; add icon, label --------- Co-authored-by: Miguel Fonseca <150562+mcsf@users.noreply.github.com> --- docs/reference-guides/core-blocks.md | 2 +- .../block-library/src/footnotes/block.json | 1 - packages/block-library/src/footnotes/edit.js | 23 +++++++++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 73e4186fce9b6..628225f536e45 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -275,7 +275,7 @@ Add a link to a downloadable file. ([Source](https://github.com/WordPress/gutenb - **Name:** core/footnotes - **Category:** text -- **Supports:** ~~html~~, ~~inserter~~, ~~multiple~~, ~~reusable~~ +- **Supports:** ~~html~~, ~~multiple~~, ~~reusable~~ - **Attributes:** ## Classic diff --git a/packages/block-library/src/footnotes/block.json b/packages/block-library/src/footnotes/block.json index 0ab992009d123..e021e9c5225da 100644 --- a/packages/block-library/src/footnotes/block.json +++ b/packages/block-library/src/footnotes/block.json @@ -11,7 +11,6 @@ "supports": { "html": false, "multiple": false, - "inserter": false, "reusable": false }, "style": "wp-block-footnotes" diff --git a/packages/block-library/src/footnotes/edit.js b/packages/block-library/src/footnotes/edit.js index e90a7f82be94a..fdfe7a94039af 100644 --- a/packages/block-library/src/footnotes/edit.js +++ b/packages/block-library/src/footnotes/edit.js @@ -1,8 +1,11 @@ /** * WordPress dependencies */ -import { RichText, useBlockProps } from '@wordpress/block-editor'; +import { BlockIcon, RichText, useBlockProps } from '@wordpress/block-editor'; import { useEntityProp } from '@wordpress/core-data'; +import { __ } from '@wordpress/i18n'; +import { Placeholder } from '@wordpress/components'; +import { formatListNumbered as icon } from '@wordpress/icons'; export default function FootnotesEdit( { context: { postType, postId } } ) { const [ meta, updateMeta ] = useEntityProp( @@ -12,8 +15,24 @@ export default function FootnotesEdit( { context: { postType, postId } } ) { postId ); const footnotes = meta?.footnotes ? JSON.parse( meta.footnotes ) : []; + const blockProps = useBlockProps(); + + if ( ! footnotes.length ) { + return ( +
    + } + label={ __( 'Footnotes' ) } + instructions={ __( + 'Footnotes found in blocks within this document will be displayed here.' + ) } + /> +
    + ); + } + return ( -
      +
        { footnotes.map( ( { id, content } ) => (
      1. Date: Mon, 10 Jul 2023 11:47:20 +0100 Subject: [PATCH 21/31] Fix: Focus loss on navigation link label editing on Firefox. (#52428) --- .../block-library/src/navigation-link/edit.js | 104 ++++++++++-------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index b91015313f403..96d6f16926a66 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -181,6 +181,10 @@ export default function NavigationLinkEdit( { const itemLabelPlaceholder = __( 'Add label…' ); const ref = useRef(); + // Change the label using inspector causes rich text to change focus on firefox. + // This is a workaround to keep the focus on the label field when label filed is focused we don't render the rich text. + const [ isLabelFieldFocused, setIsLabelFieldFocused ] = useState( false ); + const { innerBlocks, isAtMaxNesting, @@ -424,6 +428,8 @@ export default function NavigationLinkEdit( { } } label={ __( 'Label' ) } autoComplete="off" + onFocus={ () => setIsLabelFieldFocused( true ) } + onBlur={ () => setIsLabelFieldFocused( false ) } /> ) : ( <> - { ! isInvalid && ! isDraft && ( - <> - - setAttributes( { - label: labelValue, - } ) - } - onMerge={ mergeBlocks } - onReplace={ onReplace } - __unstableOnSplitAtEnd={ () => - insertBlocksAfter( - createBlock( - 'core/navigation-link' + { ! isInvalid && + ! isDraft && + ! isLabelFieldFocused && ( + <> + + setAttributes( { + label: labelValue, + } ) + } + onMerge={ mergeBlocks } + onReplace={ onReplace } + __unstableOnSplitAtEnd={ () => + insertBlocksAfter( + createBlock( + 'core/navigation-link' + ) ) - ) - } - aria-label={ __( - 'Navigation link text' - ) } - placeholder={ itemLabelPlaceholder } - withoutInteractiveFormatting - allowedFormats={ [ - 'core/bold', - 'core/italic', - 'core/image', - 'core/strikethrough', - ] } - onClick={ () => { - if ( ! url ) { - setIsLinkOpen( true ); } - } } - /> - { description && ( - - { description } - - ) } - - ) } - { ( isInvalid || isDraft ) && ( + aria-label={ __( + 'Navigation link text' + ) } + placeholder={ itemLabelPlaceholder } + withoutInteractiveFormatting + allowedFormats={ [ + 'core/bold', + 'core/italic', + 'core/image', + 'core/strikethrough', + ] } + onClick={ () => { + if ( ! url ) { + setIsLinkOpen( true ); + } + } } + /> + { description && ( + + { description } + + ) } + + ) } + { ( isInvalid || + isDraft || + isLabelFieldFocused ) && (
        From f87efd4e4643c4f2cb96b0690731902ada5e1117 Mon Sep 17 00:00:00 2001 From: James Koster Date: Mon, 10 Jul 2023 11:43:59 +0100 Subject: [PATCH 22/31] Update tooltip (#52465) --- .../edit-site/src/components/sidebar-navigation-screen/index.js | 2 +- packages/edit-site/src/components/site-hub/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/index.js b/packages/edit-site/src/components/sidebar-navigation-screen/index.js index 4c0d9d82f02cb..919e7e92d721d 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen/index.js @@ -78,7 +78,7 @@ export default function SidebarNavigationScreen( { icon={ icon } label={ ! isPreviewingTheme() - ? __( 'Go back to the Dashboard' ) + ? __( 'Go to the Dashboard' ) : __( 'Go back to the theme showcase' ) } href={ diff --git a/packages/edit-site/src/components/site-hub/index.js b/packages/edit-site/src/components/site-hub/index.js index d4f68943c46ae..897924a92a12b 100644 --- a/packages/edit-site/src/components/site-hub/index.js +++ b/packages/edit-site/src/components/site-hub/index.js @@ -57,7 +57,7 @@ const SiteHub = forwardRef( ( props, ref ) => { const siteIconButtonProps = isBackToDashboardButton ? { href: dashboardLink, - label: __( 'Go back to the Dashboard' ), + label: __( 'Go to the Dashboard' ), } : { href: dashboardLink, // We need to keep the `href` here so the component doesn't remount as a ` + + ) } { item.type === PATTERNS && ( Date: Tue, 11 Jul 2023 13:59:27 +1000 Subject: [PATCH 30/31] remove status icon (#52457) --- .../status-label.js | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js index bcfc540b1f841..f864d48de3383 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js @@ -9,42 +9,10 @@ import classnames from 'classnames'; import { __, sprintf } from '@wordpress/i18n'; import { dateI18n, getDate, humanTimeDiff } from '@wordpress/date'; import { createInterpolateElement } from '@wordpress/element'; -import { Path, SVG } from '@wordpress/primitives'; - -const publishedIcon = ( - - - -); - -const draftIcon = ( - - - -); - -const pendingIcon = ( - - - -); export default function StatusLabel( { status, date, short } ) { const relateToNow = humanTimeDiff( date ); let statusLabel = status; - let statusIcon = pendingIcon; switch ( status ) { case 'publish': statusLabel = date @@ -57,7 +25,6 @@ export default function StatusLabel( { status, date, short } ) { { time:
        ); } From 7f83e9ac9c4e68acf6096752e353763e6eaf44e1 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Tue, 11 Jul 2023 13:35:29 +1000 Subject: [PATCH 31/31] Rename block theme activation nonce variable. (#52398) --- lib/compat/wordpress-6.3/theme-previews.php | 2 +- packages/edit-site/src/utils/use-activate-theme.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compat/wordpress-6.3/theme-previews.php b/lib/compat/wordpress-6.3/theme-previews.php index eab05c5824b1f..f663e7a9783d6 100644 --- a/lib/compat/wordpress-6.3/theme-previews.php +++ b/lib/compat/wordpress-6.3/theme-previews.php @@ -107,7 +107,7 @@ function block_theme_activate_nonce() { $nonce_handle = 'switch-theme_' . gutenberg_get_theme_preview_path(); ?>