From 83821963b9df639f62c10920607ac4a5859dfd96 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 12 Jul 2023 04:57:50 +0400 Subject: [PATCH 01/28] Site Editor: Restore quick inserter 'Browse all' button (#52529) * Site Editor: Restore quick inserter 'Browse all' button * Remove leftover comment --- .../src/components/block-editor/index.js | 4 ---- .../block-editor/use-site-editor-settings.js | 24 +++++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/edit-site/src/components/block-editor/index.js b/packages/edit-site/src/components/block-editor/index.js index fcd28948ccbb3..e2d9e2680b263 100644 --- a/packages/edit-site/src/components/block-editor/index.js +++ b/packages/edit-site/src/components/block-editor/index.js @@ -1,7 +1,3 @@ -/** - * External dependencies - */ - /** * WordPress dependencies */ diff --git a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js index 4241c7f55cb67..064fdfe6119d3 100644 --- a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js +++ b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; import { store as coreStore } from '@wordpress/core-data'; /** @@ -12,15 +12,19 @@ import { unlock } from '../../lock-unlock'; import inserterMediaCategories from './inserter-media-categories'; export default function useSiteEditorSettings( templateType ) { - const { storedSettings, canvasMode } = useSelect( ( select ) => { - const { getSettings, getCanvasMode } = unlock( - select( editSiteStore ) - ); - return { - storedSettings: getSettings(), - canvasMode: getCanvasMode(), - }; - }, [] ); + const { setIsInserterOpened } = useDispatch( editSiteStore ); + const { storedSettings, canvasMode } = useSelect( + ( select ) => { + const { getSettings, getCanvasMode } = unlock( + select( editSiteStore ) + ); + return { + storedSettings: getSettings( setIsInserterOpened ), + canvasMode: getCanvasMode(), + }; + }, + [ setIsInserterOpened ] + ); const settingsBlockPatterns = storedSettings.__experimentalAdditionalBlockPatterns ?? // WP 6.0 From 90fbe99516db8c8aefcf8cd690470e279c93f86f Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Wed, 12 Jul 2023 12:35:19 +1200 Subject: [PATCH 02/28] Patterns: update the title of Pattern block in the block inspector card (#52010) --- docs/reference-guides/core-blocks.md | 2 +- .../use-block-display-information/index.js | 17 ++++++++++++----- packages/block-library/src/block/block.json | 2 +- .../editor/various/reusable-blocks.test.js | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 628225f536e45..2bf81ebf2cc96 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -38,7 +38,7 @@ Add a user’s avatar. ([Source](https://github.com/WordPress/gutenberg/tree/tru ## Pattern -Create and save content to reuse across your site. Update the block, and the changes apply everywhere it’s used. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/block)) +Create and save content to reuse across your site. Update the pattern, and the changes apply everywhere it’s used. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/block)) - **Name:** core/block - **Category:** reusable diff --git a/packages/block-editor/src/components/use-block-display-information/index.js b/packages/block-editor/src/components/use-block-display-information/index.js index 87909cea45f63..1cff9da4bc04a 100644 --- a/packages/block-editor/src/components/use-block-display-information/index.js +++ b/packages/block-editor/src/components/use-block-display-information/index.js @@ -67,8 +67,11 @@ export default function useBlockDisplayInformation( clientId ) { return useSelect( ( select ) => { if ( ! clientId ) return null; - const { getBlockName, getBlockAttributes } = - select( blockEditorStore ); + const { + getBlockName, + getBlockAttributes, + __experimentalGetReusableBlockTitle, + } = select( blockEditorStore ); const { getBlockType, getActiveBlockVariation } = select( blocksStore ); const blockName = getBlockName( clientId ); @@ -76,12 +79,16 @@ export default function useBlockDisplayInformation( clientId ) { if ( ! blockType ) return null; const attributes = getBlockAttributes( clientId ); const match = getActiveBlockVariation( blockName, attributes ); - const isSynced = - isReusableBlock( blockType ) || isTemplatePart( blockType ); + const isReusable = isReusableBlock( blockType ); + const resusableTitle = isReusable + ? __experimentalGetReusableBlockTitle( attributes.ref ) + : undefined; + const title = resusableTitle || blockType.title; + const isSynced = isReusable || isTemplatePart( blockType ); const positionLabel = getPositionTypeLabel( attributes ); const blockTypeInfo = { isSynced, - title: blockType.title, + title, icon: blockType.icon, description: blockType.description, anchor: attributes?.anchor, diff --git a/packages/block-library/src/block/block.json b/packages/block-library/src/block/block.json index 5846e7ead0c9b..01d858f592834 100644 --- a/packages/block-library/src/block/block.json +++ b/packages/block-library/src/block/block.json @@ -4,7 +4,7 @@ "name": "core/block", "title": "Pattern", "category": "reusable", - "description": "Create and save content to reuse across your site. Update the block, and the changes apply everywhere it’s used.", + "description": "Create and save content to reuse across your site. Update the pattern, and the changes apply everywhere it’s used.", "textdomain": "default", "attributes": { "ref": { diff --git a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js index 0a18c75528930..2f237822b1ccc 100644 --- a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js @@ -352,7 +352,7 @@ describe( 'Reusable blocks', () => { expect( reusableBlockWithParagraph ).toBeTruthy(); // Convert back to regular blocks. - await clickBlockToolbarButton( 'Select Pattern' ); + await clickBlockToolbarButton( 'Select Edited block' ); await clickBlockToolbarButton( 'Detach pattern' ); await page.waitForXPath( selector, { hidden: true, From 3324c2aa05fd6e0ea17232c6ccb99c451e21a7a5 Mon Sep 17 00:00:00 2001 From: Ramon Date: Mon, 10 Jul 2023 11:32:54 +1000 Subject: [PATCH 03/28] Site Editor Pages: load the appropriate template if posts page set (#52266) * This commit: - links the posts page to the homepage template when a post page is set - abstracts logic to get page item props * The Posts Page resolves to display the Home or Index template only. Adding a check to skip the Front Page * Showing homepage settings for posts pages that are set as the post page in reading settings * Post pages that have been set to display posts will redirect to first the home template, then the index template. The fallback is the post id of the page. * Reverted refactor of packages/edit-site/src/components/sidebar-navigation-screen-page/index.js Will do it in a follow up --- .../sidebar-navigation-screen-pages/index.js | 68 +++++++++++-------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js index 567bf91aca69f..331221dde7985 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pages/index.js @@ -58,13 +58,16 @@ export default function SidebarNavigationScreenPages() { templates?.find( ( template ) => template.slug === 'home' ) || templates?.find( ( template ) => template.slug === 'index' ); + const getPostsPageTemplate = () => + templates?.find( ( template ) => template.slug === 'home' ) || + templates?.find( ( template ) => template.slug === 'index' ); + const pagesAndTemplates = pages?.concat( dynamicPageTemplates, [ homeTemplate, ] ); const { frontPage, postsPage } = useSelect( ( select ) => { const { getEntityRecord } = select( coreStore ); - const siteSettings = getEntityRecord( 'root', 'site' ); return { frontPage: siteSettings?.page_on_front, @@ -106,6 +109,27 @@ export default function SidebarNavigationScreenPages() { setShowAddPage( false ); }; + const getPageProps = ( id ) => { + let itemIcon = page; + const postsPageTemplateId = + postsPage && postsPage === id ? getPostsPageTemplate()?.id : null; + + switch ( id ) { + case frontPage: + itemIcon = home; + break; + case postsPage: + itemIcon = verse; + break; + } + + return { + icon: itemIcon, + postType: postsPageTemplateId ? 'wp_template' : 'page', + postId: postsPageTemplateId || id, + }; + }; + return ( <> { showAddPage && ( @@ -152,34 +176,20 @@ export default function SidebarNavigationScreenPages() { ) } - { reorderedPages?.map( ( item ) => { - let itemIcon; - switch ( item.id ) { - case frontPage: - itemIcon = home; - break; - case postsPage: - itemIcon = verse; - break; - default: - itemIcon = page; - } - return ( - - - { decodeEntities( - item?.title?.rendered || - __( '(no title)' ) - ) } - - - ); - } ) } + { reorderedPages?.map( ( { id, title } ) => ( + + + { decodeEntities( + title?.rendered || + __( '(no title)' ) + ) } + + + ) ) } ) } From 70fb0f910b1630744ebf1bd63ebb2023877dcbd5 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca <150562+mcsf@users.noreply.github.com> Date: Tue, 11 Jul 2023 14:20:21 +0100 Subject: [PATCH 04/28] Allow editing existing footnote from formats toolbar (#52506) --- .../block-library/src/footnotes/format.js | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/packages/block-library/src/footnotes/format.js b/packages/block-library/src/footnotes/format.js index 40de6a132ea99..eb700787d02ee 100644 --- a/packages/block-library/src/footnotes/format.js +++ b/packages/block-library/src/footnotes/format.js @@ -40,24 +40,30 @@ export const format = { } = useSelect( blockEditorStore ); const { selectionChange, insertBlock } = useDispatch( blockEditorStore ); + function onClick() { registry.batch( () => { - const id = createId(); - const newValue = insertObject( - value, - { - type: formatName, - attributes: { - 'data-fn': id, + let id; + if ( isObjectActive ) { + const object = value.replacements[ value.start ]; + id = object?.attributes?.[ 'data-fn' ]; + } else { + id = createId(); + const newValue = insertObject( + value, + { + type: formatName, + attributes: { + 'data-fn': id, + }, + innerHTML: `*`, }, - innerHTML: `*`, - }, - value.end, - value.end - ); - newValue.start = newValue.end - 1; - - onChange( newValue ); + value.end, + value.end + ); + newValue.start = newValue.end - 1; + onChange( newValue ); + } // BFS search to find the first footnote block. let fnBlock = null; From 5f767043acf5fb98e33d6fc703158d26f6b01491 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 11 Jul 2023 17:18:34 +0400 Subject: [PATCH 05/28] Block Editor: Display variation icon in the 'BlockDraggable' component (#52502) --- .../src/components/block-draggable/index.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index 1e4048c90ab61..0b8f3c2d87f52 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -22,16 +22,25 @@ const BlockDraggable = ( { } ) => { const { srcRootClientId, isDraggable, icon } = useSelect( ( select ) => { - const { canMoveBlocks, getBlockRootClientId, getBlockName } = - select( blockEditorStore ); - const { getBlockType } = select( blocksStore ); + const { + canMoveBlocks, + getBlockRootClientId, + getBlockName, + getBlockAttributes, + } = select( blockEditorStore ); + const { getBlockType, getActiveBlockVariation } = + select( blocksStore ); const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); const blockName = getBlockName( clientIds[ 0 ] ); + const variation = getActiveBlockVariation( + blockName, + getBlockAttributes( clientIds[ 0 ] ) + ); return { srcRootClientId: rootClientId, isDraggable: canMoveBlocks( clientIds, rootClientId ), - icon: getBlockType( blockName )?.icon, + icon: variation?.icon || getBlockType( blockName )?.icon, }; }, [ clientIds ] From 18ce87a5672a1c942fdeb18d256e7a50e2290180 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Tue, 11 Jul 2023 21:39:52 +1200 Subject: [PATCH 06/28] Patterns: add option to set sync status when adding from wp-admin patterns list (#52352) * Show a modal to set sync status if adding pattern from pattern list page * Make sure the modal loads if post settings panel not open * don't load modal component at all if not new post * Simplify the sync status so undefined always = synced * Update packages/editor/src/components/post-sync-status/index.js --------- Co-authored-by: Ramon --- .../edit-post/src/components/layout/index.js | 2 + packages/editor/src/components/index.js | 5 +- .../src/components/post-sync-status/index.js | 105 +++++++++++++++++- 3 files changed, 105 insertions(+), 7 deletions(-) diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 15bc017900daa..b7aa8bcd025af 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -13,6 +13,7 @@ import { EditorNotices, EditorKeyboardShortcutsRegister, EditorSnackbars, + PostSyncStatusModal, store as editorStore, } from '@wordpress/editor'; import { useSelect, useDispatch } from '@wordpress/data'; @@ -291,6 +292,7 @@ function Layout( { styles } ) { + diff --git a/packages/editor/src/components/index.js b/packages/editor/src/components/index.js index b037397cfdd4c..aa20f9d6ae9fa 100644 --- a/packages/editor/src/components/index.js +++ b/packages/editor/src/components/index.js @@ -51,7 +51,10 @@ export { default as PostSlugCheck } from './post-slug/check'; export { default as PostSticky } from './post-sticky'; export { default as PostStickyCheck } from './post-sticky/check'; export { default as PostSwitchToDraftButton } from './post-switch-to-draft-button'; -export { default as PostSyncStatus } from './post-sync-status'; +export { + default as PostSyncStatus, + PostSyncStatusModal, +} from './post-sync-status'; export { default as PostTaxonomies } from './post-taxonomies'; export { FlatTermSelector as PostTaxonomiesFlatTermSelector } from './post-taxonomies/flat-term-selector'; export { HierarchicalTermSelector as PostTaxonomiesHierarchicalTermSelector } from './post-taxonomies/hierarchical-term-selector'; diff --git a/packages/editor/src/components/post-sync-status/index.js b/packages/editor/src/components/post-sync-status/index.js index 0600ece953173..8219ef7c0e4f5 100644 --- a/packages/editor/src/components/post-sync-status/index.js +++ b/packages/editor/src/components/post-sync-status/index.js @@ -1,9 +1,18 @@ /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useSelect, useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; -import { PanelRow } from '@wordpress/components'; +import { + PanelRow, + Modal, + Button, + __experimentalHStack as HStack, + __experimentalVStack as VStack, + ToggleControl, +} from '@wordpress/components'; +import { useEffect, useState } from '@wordpress/element'; +import { ReusableBlocksRenameHint } from '@wordpress/block-editor'; /** * Internal dependencies @@ -11,24 +20,108 @@ import { PanelRow } from '@wordpress/components'; import { store as editorStore } from '../../store'; export default function PostSyncStatus() { - const { syncStatus, postType } = useSelect( ( select ) => { + const { syncStatus, postType, meta } = useSelect( ( select ) => { const { getEditedPostAttribute } = select( editorStore ); return { syncStatus: getEditedPostAttribute( 'wp_pattern_sync_status' ), + meta: getEditedPostAttribute( 'meta' ), postType: getEditedPostAttribute( 'type' ), }; - }, [] ); + } ); + if ( postType !== 'wp_block' ) { return null; } - const isFullySynced = ! syncStatus; + // When the post is first created, the top level wp_pattern_sync_status is not set so get meta value instead. + const currentSyncStatus = + meta?.wp_pattern_sync_status === 'unsynced' ? 'unsynced' : syncStatus; return ( { __( 'Sync status' ) }
- { isFullySynced ? __( 'Fully synced' ) : __( 'Not synced' ) } + { currentSyncStatus === 'unsynced' + ? __( 'Not synced' ) + : __( 'Fully synced' ) }
); } + +export function PostSyncStatusModal() { + const { editPost } = useDispatch( editorStore ); + const [ isModalOpen, setIsModalOpen ] = useState( false ); + const [ syncType, setSyncType ] = useState( undefined ); + + const { postType, isNewPost } = useSelect( ( select ) => { + const { getEditedPostAttribute, isCleanNewPost } = + select( editorStore ); + return { + postType: getEditedPostAttribute( 'type' ), + isNewPost: isCleanNewPost(), + }; + }, [] ); + + useEffect( () => { + if ( isNewPost && postType === 'wp_block' ) { + setIsModalOpen( true ); + } + // We only want the modal to open when the page is first loaded. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [] ); + + const setSyncStatus = () => { + editPost( { + meta: { + wp_pattern_sync_status: syncType, + }, + } ); + }; + + if ( postType !== 'wp_block' || ! isNewPost ) { + return null; + } + + return ( + <> + { isModalOpen && ( + { + setIsModalOpen( false ); + } } + overlayClassName="reusable-blocks-menu-items__convert-modal" + > +
{ + event.preventDefault(); + setIsModalOpen( false ); + setSyncStatus(); + } } + > + + + { + setSyncType( + ! syncType ? 'unsynced' : undefined + ); + } } + /> + + + + +
+
+ ) } + + ); +} From ee99b2a416d5de71924820bf546b5593911414e5 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Fri, 23 Jun 2023 06:13:30 -0400 Subject: [PATCH 07/28] Revise LinkControl suggestions UI to use MenuItem (#50978) * Use "link" instead of "URL" for URL_TYPE * Use MenuItem for search create button * Use sentence case for "Create page" * Use a MenuGroup for search results * Use MenuItem for search item * Refactoring styles (WIP) * Preserve whitespace in results text * Reinstate result item information including permalink * Remove debugging CSS code * Reinstate CSS to control size of rich previews favicon * Remove other commented out CSS code * Reinstate selected styles * Remove more redundant CSS * Add some basic results hover/focus styling. Needs improving * Improve icon alignment * Update tests to handle wording changes * Remove inconsistent hover/focus style MenuItem already has hover/focus styles * Reinstate is-selected visual state * Update test to make sense in context of #51011 See https://github.com/WordPress/gutenberg/pull/51011 * Fix locator for result text --------- Co-authored-by: Dave Smith --- .../src/components/link-control/constants.js | 2 +- .../link-control/search-create-button.js | 34 ++---- .../components/link-control/search-item.js | 64 ++++------ .../components/link-control/search-results.js | 94 ++++++++------- .../src/components/link-control/style.scss | 113 +++--------------- .../src/components/link-control/test/index.js | 13 +- packages/format-library/src/link/inline.js | 2 +- .../specs/editor/blocks/navigation.spec.js | 4 +- 8 files changed, 106 insertions(+), 220 deletions(-) diff --git a/packages/block-editor/src/components/link-control/constants.js b/packages/block-editor/src/components/link-control/constants.js index eaf07aea73703..e70ff7b04c747 100644 --- a/packages/block-editor/src/components/link-control/constants.js +++ b/packages/block-editor/src/components/link-control/constants.js @@ -8,7 +8,7 @@ import { __ } from '@wordpress/i18n'; // order to handle it as a unique case. export const CREATE_TYPE = '__CREATE__'; export const TEL_TYPE = 'tel'; -export const URL_TYPE = 'URL'; +export const URL_TYPE = 'link'; export const MAILTO_TYPE = 'mailto'; export const INTERNAL_TYPE = 'internal'; diff --git a/packages/block-editor/src/components/link-control/search-create-button.js b/packages/block-editor/src/components/link-control/search-create-button.js index 1786b516df5c0..6a53fd36cf893 100644 --- a/packages/block-editor/src/components/link-control/search-create-button.js +++ b/packages/block-editor/src/components/link-control/search-create-button.js @@ -1,21 +1,15 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - /** * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { Button } from '@wordpress/components'; +import { MenuItem } from '@wordpress/components'; import { createInterpolateElement } from '@wordpress/element'; -import { Icon, plus } from '@wordpress/icons'; +import { plus } from '@wordpress/icons'; export const LinkControlSearchCreate = ( { searchTerm, onClick, itemProps, - isSelected, buttonText, } ) => { if ( ! searchTerm ) { @@ -40,27 +34,15 @@ export const LinkControlSearchCreate = ( { } return ( - + { text } + ); }; diff --git a/packages/block-editor/src/components/link-control/search-item.js b/packages/block-editor/src/components/link-control/search-item.js index 250b28596f4f3..976bb4420cb0c 100644 --- a/packages/block-editor/src/components/link-control/search-item.js +++ b/packages/block-editor/src/components/link-control/search-item.js @@ -1,14 +1,8 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - /** * WordPress dependencies */ -import { safeDecodeURI, filterURLForDisplay } from '@wordpress/url'; import { __ } from '@wordpress/i18n'; -import { Button, TextHighlight } from '@wordpress/components'; +import { MenuItem, TextHighlight } from '@wordpress/components'; import { Icon, globe, @@ -19,6 +13,7 @@ import { file, } from '@wordpress/icons'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; +import { safeDecodeURI, filterURLForDisplay } from '@wordpress/url'; const ICONS_MAP = { post: postList, @@ -52,50 +47,33 @@ function SearchItemIcon( { isURL, suggestion } ) { export const LinkControlSearchItem = ( { itemProps, suggestion, - isSelected = false, + searchTerm, onClick, isURL = false, - searchTerm = '', shouldShowType = false, } ) => { + const info = isURL + ? __( 'Press ENTER to add this link' ) + : filterURLForDisplay( safeDecodeURI( suggestion?.url ) ); + return ( - + + ); }; diff --git a/packages/block-editor/src/components/link-control/search-results.js b/packages/block-editor/src/components/link-control/search-results.js index 9d7ee7ca41abd..71e258c769bf1 100644 --- a/packages/block-editor/src/components/link-control/search-results.js +++ b/packages/block-editor/src/components/link-control/search-results.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { VisuallyHidden } from '@wordpress/components'; +import { VisuallyHidden, MenuGroup } from '@wordpress/components'; /** * External dependencies @@ -72,59 +72,61 @@ export default function LinkControlSearchResults( { className={ resultsListClasses } aria-labelledby={ searchResultsLabelId } > - { suggestions.map( ( suggestion, index ) => { - if ( - shouldShowCreateSuggestion && - CREATE_TYPE === suggestion.type - ) { + + { suggestions.map( ( suggestion, index ) => { + if ( + shouldShowCreateSuggestion && + CREATE_TYPE === suggestion.type + ) { + return ( + + handleSuggestionClick( suggestion ) + } + // Intentionally only using `type` here as + // the constant is enough to uniquely + // identify the single "CREATE" suggestion. + key={ suggestion.type } + itemProps={ buildSuggestionItemProps( + suggestion, + index + ) } + isSelected={ index === selectedSuggestion } + /> + ); + } + + // If we're not handling "Create" suggestions above then + // we don't want them in the main results so exit early. + if ( CREATE_TYPE === suggestion.type ) { + return null; + } + return ( - - handleSuggestionClick( suggestion ) - } - // Intentionally only using `type` here as - // the constant is enough to uniquely - // identify the single "CREATE" suggestion. - key={ suggestion.type } + { + handleSuggestionClick( suggestion ); + } } isSelected={ index === selectedSuggestion } + isURL={ LINK_ENTRY_TYPES.includes( + suggestion.type + ) } + searchTerm={ currentInputValue } + shouldShowType={ shouldShowSuggestionsTypes } + isFrontPage={ suggestion?.isFrontPage } /> ); - } - - // If we're not handling "Create" suggestions above then - // we don't want them in the main results so exit early. - if ( CREATE_TYPE === suggestion.type ) { - return null; - } - - return ( - { - handleSuggestionClick( suggestion ); - } } - isSelected={ index === selectedSuggestion } - isURL={ LINK_ENTRY_TYPES.includes( - suggestion.type - ) } - searchTerm={ currentInputValue } - shouldShowType={ shouldShowSuggestionsTypes } - isFrontPage={ suggestion?.isFrontPage } - /> - ); - } ) } + } ) } + ); diff --git a/packages/block-editor/src/components/link-control/style.scss b/packages/block-editor/src/components/link-control/style.scss index eb9769c2d299b..8775dffbedc6e 100644 --- a/packages/block-editor/src/components/link-control/style.scss +++ b/packages/block-editor/src/components/link-control/style.scss @@ -41,6 +41,7 @@ $preview-image-height: 140px; // Provides positioning context for reset button. Without this then when an // error notice is displayed the input's reset button is incorrectly positioned. .block-editor-link-control__search-input-wrapper { + margin-bottom: $grid-unit-10; position: relative; } @@ -87,36 +88,9 @@ $preview-image-height: 140px; order: 20; } -.block-editor-link-control__search-results-wrapper { - position: relative; - - &::before, - &::after { - content: ""; - position: absolute; - left: -1px; - right: $grid-unit-20; // avoid overlaying scrollbars - display: block; - pointer-events: none; - z-index: 100; - } - - &::before { - height: $grid-unit-20 * 0.5; - top: 0; - bottom: auto; - } - - &::after { - height: $grid-unit-20; - bottom: 0; - top: auto; - } -} - .block-editor-link-control__search-results { - margin: 0; - padding: $grid-unit-20 * 0.5 $grid-unit-20 $grid-unit-20 * 0.5; + margin-top: -$grid-unit-20; + padding: $grid-unit-10; max-height: 200px; overflow-y: auto; // allow results list to scroll @@ -126,39 +100,28 @@ $preview-image-height: 140px; } .block-editor-link-control__search-item { - position: relative; - display: flex; - align-items: flex-start; // when link text is very long it is important this indicator remains visible and thus should be aligned top. - font-size: $default-font-size; - cursor: pointer; - background: $white; - width: 100%; - border: none; - text-align: left; - padding: $grid-unit-15 $grid-unit-20; - border-radius: 2px; - height: auto; - &:hover, - &:focus { - background-color: $gray-100; + &.components-button.components-menu-item__button { + height: auto; + text-align: left; + } - .block-editor-link-control__search-item-type { - background: $white; - } + .components-menu-item__item { + overflow: hidden; + text-overflow: ellipsis; + // Inline block required to preserve white space + // between `` elements and text nodes. + display: inline-block; } - // The added specificity is needed to override. - &:focus:not(:disabled) { - box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color) inset; + .components-menu-item__shortcut { + color: $gray-700; + text-transform: capitalize; + white-space: nowrap; // tags shouldn't go over two lines. } - &.is-selected { + &[aria-selected] { background: $gray-100; - - .block-editor-link-control__search-item-type { - background: $white; - } } &.is-current { @@ -198,7 +161,6 @@ $preview-image-height: 140px; .block-editor-link-control__search-item-icon { position: relative; - top: 0.2em; margin-right: $grid-unit-10; max-height: 24px; flex-shrink: 0; @@ -217,18 +179,6 @@ $preview-image-height: 140px; max-height: 32px; } - .block-editor-link-control__search-item-info, - .block-editor-link-control__search-item-title { - overflow: hidden; - text-overflow: ellipsis; - - .components-external-link__icon { - position: absolute; - right: 0; - margin-top: 0; - } - } - .block-editor-link-control__search-item-title { display: block; margin-bottom: 0.2em; @@ -250,28 +200,6 @@ $preview-image-height: 140px; } } - .block-editor-link-control__search-item-info { - display: block; - color: $gray-700; - font-size: 0.9em; - line-height: 1.3; - } - - .block-editor-link-control__search-item-error-notice { - font-style: italic; - font-size: 1.1em; - } - - .block-editor-link-control__search-item-type { - display: block; - padding: 3px 6px; - margin-left: auto; - font-size: 0.9em; - background-color: $gray-100; - border-radius: 2px; - white-space: nowrap; // tags shouldn't go over two lines. - } - .block-editor-link-control__search-item-description { padding-top: 12px; margin: 0; @@ -411,11 +339,6 @@ $preview-image-height: 140px; } } -// Specificity override -.block-editor-link-control__search-results div[role="menu"] > .block-editor-link-control__search-item.block-editor-link-control__search-item { - padding: 10px; -} - .block-editor-link-control__drawer { display: flex; // allow for ordering. order: 30; diff --git a/packages/block-editor/src/components/link-control/test/index.js b/packages/block-editor/src/components/link-control/test/index.js index 20b72363391ee..82bb82a0cfc51 100644 --- a/packages/block-editor/src/components/link-control/test/index.js +++ b/packages/block-editor/src/components/link-control/test/index.js @@ -478,16 +478,16 @@ describe( 'Searching for a link', () => { // The fallback URL suggestion should not be shown when input is not URL-like. expect( searchResultElements[ searchResultElements.length - 1 ] - ).not.toHaveTextContent( 'URL' ); + ).not.toHaveTextContent( 'Press ENTER to add this link' ); } ); it.each( [ - [ 'https://wordpress.org', 'URL' ], - [ 'http://wordpress.org', 'URL' ], - [ 'www.wordpress.org', 'URL' ], - [ 'wordpress.org', 'URL' ], - [ 'ftp://wordpress.org', 'URL' ], + [ 'https://wordpress.org', 'link' ], + [ 'http://wordpress.org', 'link' ], + [ 'www.wordpress.org', 'link' ], + [ 'wordpress.org', 'link' ], + [ 'ftp://wordpress.org', 'link' ], [ 'mailto:hello@wordpress.org', 'mailto' ], [ 'tel:123456789', 'tel' ], [ '#internal', 'internal' ], @@ -667,7 +667,6 @@ describe( 'Manual link entry', () => { expect( searchResultElements ).toBeVisible(); expect( searchResultElements ).toHaveTextContent( searchTerm ); - expect( searchResultElements ).toHaveTextContent( 'URL' ); expect( searchResultElements ).toHaveTextContent( 'Press ENTER to add this link' ); diff --git a/packages/format-library/src/link/inline.js b/packages/format-library/src/link/inline.js index 064ed81943de1..384fa4c653e53 100644 --- a/packages/format-library/src/link/inline.js +++ b/packages/format-library/src/link/inline.js @@ -243,7 +243,7 @@ function InlineLinkUI( { return createInterpolateElement( sprintf( /* translators: %s: search term. */ - __( 'Create Page: %s' ), + __( 'Create page: %s' ), searchTerm ), { mark: } diff --git a/test/e2e/specs/editor/blocks/navigation.spec.js b/test/e2e/specs/editor/blocks/navigation.spec.js index 8f45e8ddcf7e3..b7632530ab150 100644 --- a/test/e2e/specs/editor/blocks/navigation.spec.js +++ b/test/e2e/specs/editor/blocks/navigation.spec.js @@ -1300,7 +1300,9 @@ class LinkControl { await expect( result ).toBeVisible(); return result - .locator( '.block-editor-link-control__search-item-title' ) // this is the only way to get the label text without the URL. + .locator( + '.components-menu-item__info-wrapper .components-menu-item__item' + ) // this is the only way to get the label text without the URL. .innerText(); } } From ca85f26a49891528394fb4a794a0ae6779cf4861 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Tue, 11 Jul 2023 14:43:47 -0400 Subject: [PATCH 08/28] Fix LinkControl mark highlight to bold (#52517) --- .../block-editor/src/components/link-control/style.scss | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/link-control/style.scss b/packages/block-editor/src/components/link-control/style.scss index 8775dffbedc6e..f76f2cfab4930 100644 --- a/packages/block-editor/src/components/link-control/style.scss +++ b/packages/block-editor/src/components/link-control/style.scss @@ -112,6 +112,12 @@ $preview-image-height: 140px; // Inline block required to preserve white space // between `` elements and text nodes. display: inline-block; + + mark { + font-weight: 600; + color: inherit; + background-color: transparent; + } } .components-menu-item__shortcut { @@ -186,7 +192,7 @@ $preview-image-height: 140px; position: relative; mark { - font-weight: 700; + font-weight: 600; color: inherit; background-color: transparent; } From 605f510c4932805342f12c72a7e77fb0c7c3b82e Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Thu, 13 Jul 2023 10:12:18 +1000 Subject: [PATCH 09/28] Add 'reusable' keyword to Pattern blocks (#52543) --- packages/block-editor/src/store/selectors.js | 2 +- packages/block-editor/src/store/test/selectors.js | 2 +- packages/block-library/src/block/block.json | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 4cca99535a8e5..b6d030293c815 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2018,7 +2018,7 @@ export const getInserterItems = createSelector( title: reusableBlock.title.raw, icon, category: 'reusable', - keywords: [], + keywords: [ 'reusable' ], isDisabled: false, utility: 1, // Deprecated. frecency, diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js index 270bceaad447f..fc4db2c41f8a3 100644 --- a/packages/block-editor/src/store/test/selectors.js +++ b/packages/block-editor/src/store/test/selectors.js @@ -3351,7 +3351,7 @@ describe( 'selectors', () => { id: 'core/block/1', initialAttributes: { ref: 1 }, isDisabled: false, - keywords: [], + keywords: [ 'reusable' ], name: 'core/block', syncStatus: undefined, title: 'Reusable Block 1', diff --git a/packages/block-library/src/block/block.json b/packages/block-library/src/block/block.json index 01d858f592834..d6630177fc685 100644 --- a/packages/block-library/src/block/block.json +++ b/packages/block-library/src/block/block.json @@ -5,6 +5,7 @@ "title": "Pattern", "category": "reusable", "description": "Create and save content to reuse across your site. Update the pattern, and the changes apply everywhere it’s used.", + "keywords": [ "reusable" ], "textdomain": "default", "attributes": { "ref": { From 9140547b04f52b9e5440d9c36034245eeb92c094 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Thu, 13 Jul 2023 09:41:29 +1000 Subject: [PATCH 10/28] Fix missing Add Template Part button in Template Parts page (#52542) --- .../add-new-template-part.js | 57 +++++++++++++++++++ .../components/page-template-parts/index.js | 25 +------- 2 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 packages/edit-site/src/components/page-template-parts/add-new-template-part.js diff --git a/packages/edit-site/src/components/page-template-parts/add-new-template-part.js b/packages/edit-site/src/components/page-template-parts/add-new-template-part.js new file mode 100644 index 0000000000000..d2b52a88701fd --- /dev/null +++ b/packages/edit-site/src/components/page-template-parts/add-new-template-part.js @@ -0,0 +1,57 @@ +/** + * WordPress dependencies + */ +import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { useSelect } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { useState } from '@wordpress/element'; +import { Button } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; +import { store as editSiteStore } from '../../store'; +import CreateTemplatePartModal from '../create-template-part-modal'; + +const { useHistory } = unlock( routerPrivateApis ); + +export default function AddNewTemplatePart() { + const { canCreate, postType } = useSelect( ( select ) => { + const { supportsTemplatePartsMode } = + select( editSiteStore ).getSettings(); + return { + canCreate: ! supportsTemplatePartsMode, + postType: select( coreStore ).getPostType( 'wp_template_part' ), + }; + }, [] ); + const [ isModalOpen, setIsModalOpen ] = useState( false ); + const history = useHistory(); + + if ( ! canCreate || ! postType ) { + return null; + } + + return ( + <> + + { isModalOpen && ( + setIsModalOpen( false ) } + blocks={ [] } + onCreate={ ( templatePart ) => { + setIsModalOpen( false ); + history.push( { + postId: templatePart.id, + postType: 'wp_template_part', + canvas: 'edit', + } ); + } } + onError={ () => setIsModalOpen( false ) } + /> + ) } + + ); +} diff --git a/packages/edit-site/src/components/page-template-parts/index.js b/packages/edit-site/src/components/page-template-parts/index.js index 7e9c8cb6dd6e1..24b042ed92001 100644 --- a/packages/edit-site/src/components/page-template-parts/index.js +++ b/packages/edit-site/src/components/page-template-parts/index.js @@ -7,8 +7,7 @@ import { __experimentalVStack as VStack, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useSelect } from '@wordpress/data'; -import { store as coreStore, useEntityRecords } from '@wordpress/core-data'; +import { useEntityRecords } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; /** @@ -19,8 +18,7 @@ import Table from '../table'; import Link from '../routes/link'; import AddedBy from '../list/added-by'; import TemplateActions from '../template-actions'; -import AddNewTemplate from '../add-new-template'; -import { store as editSiteStore } from '../../store'; +import AddNewTemplatePart from './add-new-template-part'; export default function PageTemplateParts() { const { records: templateParts } = useEntityRecords( @@ -31,15 +29,6 @@ export default function PageTemplateParts() { } ); - const { canCreate } = useSelect( ( select ) => { - const { supportsTemplatePartsMode } = - select( editSiteStore ).getSettings(); - return { - postType: select( coreStore ).getPostType( 'wp_template_part' ), - canCreate: ! supportsTemplatePartsMode, - }; - } ); - const columns = [ { header: __( 'Template Part' ), @@ -87,15 +76,7 @@ export default function PageTemplateParts() { return ( - ) - } + actions={ } > { templateParts && ( From ecc1eb8a105cc1212be2fcf23418ae982f5ede40 Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Wed, 12 Jul 2023 15:54:09 -0400 Subject: [PATCH 11/28] Tweak copy for the reusable block rename hint (#52581) --- .../src/components/inserter/reusable-block-rename-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js b/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js index 09861d9b97f1c..d4702eb137283 100644 --- a/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js +++ b/packages/block-editor/src/components/inserter/reusable-block-rename-hint.js @@ -29,7 +29,7 @@ export default function ReusableBlocksRenameHint() {
{ __( - 'Reusable blocks are now called patterns. A synced pattern will behave in exactly the same way as a reusable block.' + 'Reusable blocks are now synced patterns. A synced pattern will behave in exactly the same way as a reusable block.' ) }
); }, + migrate: migrateTag, }; // Deprecation for blocks that renders fixed background as backgroud from the main block container. @@ -465,6 +466,7 @@ const v10 = { ); }, + migrate: migrateTag, }; // Deprecation for blocks with `minHeightUnit` set but no `minHeight`. From 4df10308883c2510329163549736f32db8eefc0e Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Thu, 13 Jul 2023 21:18:18 +0900 Subject: [PATCH 22/28] ResizableFrame: Make keyboard accessible (#52443) * ResizableFrame: Make keyboard accessible * Fix outline in Safari * Use proper CSS modifier * Add aria-label to button * Keep handle enlarged when resizing (Safari) * Add back visually hidden help text * Don't switch to edit mode * Make the handle a role="separator" * Revert to `button` * Switch description text to `div hidden` * Prevent keydown event default when right/left arrow * Change minimum frame width to 320px * Mention shift key in description text * Only render resize handle when in View mode --- .../src/components/resizable-frame/index.js | 131 +++++++++++++----- .../src/components/resizable-frame/style.scss | 18 +-- 2 files changed, 109 insertions(+), 40 deletions(-) diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index a13881e7905ff..1bb9315a07e38 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -9,9 +9,12 @@ import classnames from 'classnames'; import { useState, useRef, useEffect } from '@wordpress/element'; import { ResizableBox, + Tooltip, __unstableMotion as motion, } from '@wordpress/components'; -import { useDispatch } from '@wordpress/data'; +import { useInstanceId } from '@wordpress/compose'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -33,7 +36,7 @@ const HANDLE_STYLES_OVERRIDE = { }; // The minimum width of the frame (in px) while resizing. -const FRAME_MIN_WIDTH = 340; +const FRAME_MIN_WIDTH = 320; // The reference width of the frame (in px) used to calculate the aspect ratio. const FRAME_REFERENCE_WIDTH = 1300; // 9 : 19.5 is the target aspect ratio enforced (when possible) while resizing. @@ -42,6 +45,8 @@ const FRAME_TARGET_ASPECT_RATIO = 9 / 19.5; // viewport's edge. If the frame is resized to be closer to the viewport's edge // than this distance, then "canvas mode" will be enabled. const SNAP_TO_EDIT_CANVAS_MODE_THRESHOLD = 200; +// Default size for the `frameSize` state. +const INITIAL_FRAME_SIZE = { width: '100%', height: '100%' }; function calculateNewHeight( width, initialAspectRatio ) { const lerp = ( a, b, amount ) => { @@ -78,22 +83,27 @@ function ResizableFrame( { oversizedClassName, innerContentStyle, } ) { - const [ frameSize, setFrameSize ] = useState( { - width: '100%', - height: '100%', - } ); + const [ frameSize, setFrameSize ] = useState( INITIAL_FRAME_SIZE ); // The width of the resizable frame when a new resize gesture starts. const [ startingWidth, setStartingWidth ] = useState(); const [ isResizing, setIsResizing ] = useState( false ); - const [ isHovering, setIsHovering ] = useState( false ); + const [ shouldShowHandle, setShouldShowHandle ] = useState( false ); const [ isOversized, setIsOversized ] = useState( false ); const [ resizeRatio, setResizeRatio ] = useState( 1 ); + const canvasMode = useSelect( + ( select ) => unlock( select( editSiteStore ) ).getCanvasMode(), + [] + ); const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); const initialAspectRatioRef = useRef( null ); // The width of the resizable frame on initial render. const initialComputedWidthRef = useRef( null ); const FRAME_TRANSITION = { type: 'tween', duration: isResizing ? 0 : 0.5 }; const frameRef = useRef( null ); + const resizableHandleHelpId = useInstanceId( + ResizableFrame, + 'edit-site-resizable-frame-handle-help' + ); // Remember frame dimensions on initial render. useEffect( () => { @@ -154,13 +164,40 @@ function ResizableFrame( { if ( remainingWidth > SNAP_TO_EDIT_CANVAS_MODE_THRESHOLD ) { // Reset the initial aspect ratio if the frame is resized slightly // above the sidebar but not far enough to trigger full screen. - setFrameSize( { width: '100%', height: '100%' } ); + setFrameSize( INITIAL_FRAME_SIZE ); } else { // Trigger full screen if the frame is resized far enough to the left. setCanvasMode( 'edit' ); } }; + // Handle resize by arrow keys + const handleResizableHandleKeyDown = ( event ) => { + if ( ! [ 'ArrowLeft', 'ArrowRight' ].includes( event.key ) ) { + return; + } + + event.preventDefault(); + + const step = 20 * ( event.shiftKey ? 5 : 1 ); + const delta = step * ( event.key === 'ArrowLeft' ? 1 : -1 ); + const newWidth = Math.min( + Math.max( + FRAME_MIN_WIDTH, + frameRef.current.resizable.offsetWidth + delta + ), + initialComputedWidthRef.current + ); + + setFrameSize( { + width: newWidth, + height: calculateNewHeight( + newWidth, + initialAspectRatioRef.current + ), + } ); + }; + const frameAnimationVariants = { default: { flexGrow: 0, @@ -173,16 +210,26 @@ function ResizableFrame( { }; const resizeHandleVariants = { - default: { + hidden: { + opacity: 0, + left: 0, + }, + visible: { opacity: 1, left: -16, }, - resizing: { + active: { opacity: 1, left: -16, scaleY: 1.3, }, }; + const currentResizeHandleVariant = ( () => { + if ( isResizing ) { + return 'active'; + } + return shouldShowHandle ? 'visible' : 'hidden'; + } )(); return ( setIsHovering( true ) } - onMouseOut={ () => setIsHovering( false ) } + onFocus={ () => setShouldShowHandle( true ) } + onBlur={ () => setShouldShowHandle( false ) } + onMouseOver={ () => setShouldShowHandle( true ) } + onMouseOut={ () => setShouldShowHandle( false ) } handleComponent={ { - left: - isHovering || isResizing ? ( - - ) : null, + left: canvasMode === 'view' && ( + <> + + { /* Disable reason: role="separator" does in fact support aria-valuenow */ } + { /* eslint-disable-next-line jsx-a11y/role-supports-aria-props */ } + + + + + ), } } onResizeStart={ handleResizeStart } onResize={ handleResize } diff --git a/packages/edit-site/src/components/resizable-frame/style.scss b/packages/edit-site/src/components/resizable-frame/style.scss index b1da6e1c39399..596304be8d6b9 100644 --- a/packages/edit-site/src/components/resizable-frame/style.scss +++ b/packages/edit-site/src/components/resizable-frame/style.scss @@ -47,11 +47,13 @@ .edit-site-resizable-frame__handle { align-items: center; background-color: rgba($gray-700, 0.4); + border: 0; border-radius: $grid-unit-05; cursor: col-resize; display: flex; height: $grid-unit-80; justify-content: flex-end; + padding: 0; position: absolute; top: calc(50% - #{$grid-unit-40}); width: $grid-unit-05; @@ -73,16 +75,14 @@ width: $grid-unit-40; } - &:hover, - .is-resizing & { - background-color: var(--wp-admin-theme-color); + &:focus-visible { + // Works with Windows high contrast mode while also hiding weird outline in Safari. + outline: 2px solid transparent; } - .edit-site-resizable-frame__handle-label { - background: var(--wp-admin-theme-color); - border-radius: 2px; - color: #fff; - margin-right: $grid-unit-10; - padding: 4px 8px; + &:hover, + &:focus, + &.is-resizing { + background-color: var(--wp-admin-theme-color); } } From 95363abd83221019e80f609a1a69f86030682bb5 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Thu, 13 Jul 2023 15:54:46 +0300 Subject: [PATCH 23/28] Fix importing classic menus (#52573) * use the same create hook for classic import * Remove redundant arg to hook --------- Co-authored-by: Dave Smith --- packages/block-library/src/navigation/edit/index.js | 2 +- .../edit/use-convert-classic-menu-to-block-menu.js | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js index 7ec990d5b7389..87078999b0c9b 100644 --- a/packages/block-library/src/navigation/edit/index.js +++ b/packages/block-library/src/navigation/edit/index.js @@ -196,7 +196,7 @@ function Navigation( { convert: convertClassicMenu, status: classicMenuConversionStatus, error: classicMenuConversionError, - } = useConvertClassicToBlockMenu( clientId ); + } = useConvertClassicToBlockMenu( createNavigationMenu ); const isConvertingClassicMenu = classicMenuConversionStatus === CLASSIC_MENU_CONVERSION_PENDING; diff --git a/packages/block-library/src/navigation/edit/use-convert-classic-menu-to-block-menu.js b/packages/block-library/src/navigation/edit/use-convert-classic-menu-to-block-menu.js index 70f9a6ff4bfea..405663726cee8 100644 --- a/packages/block-library/src/navigation/edit/use-convert-classic-menu-to-block-menu.js +++ b/packages/block-library/src/navigation/edit/use-convert-classic-menu-to-block-menu.js @@ -9,7 +9,6 @@ import { __, sprintf } from '@wordpress/i18n'; /** * Internal dependencies */ -import useCreateNavigationMenu from './use-create-navigation-menu'; import menuItemsToBlocks from '../menu-items-to-blocks'; export const CLASSIC_MENU_CONVERSION_SUCCESS = 'success'; @@ -21,15 +20,7 @@ export const CLASSIC_MENU_CONVERSION_IDLE = 'idle'; // do not import the same classic menu twice. let classicMenuBeingConvertedId = null; -function useConvertClassicToBlockMenu( clientId ) { - /* - * The wp_navigation post is created as a draft so the changes on the frontend and - * the site editor are not permanent without a save interaction done by the user. - */ - const { create: createNavigationMenu } = useCreateNavigationMenu( - clientId, - 'draft' - ); +function useConvertClassicToBlockMenu( createNavigationMenu ) { const registry = useRegistry(); const { editEntityRecord } = useDispatch( coreStore ); From 97afb2642505560fbef6932faa50cf45bb6972a3 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 13 Jul 2023 15:35:46 +0400 Subject: [PATCH 24/28] Site Editor: Fix navigation menu sidebar actions order and label (#52592) --- .../sidebar-navigation-screen-navigation-menu/more-menu.js | 1 + .../single-navigation-menu.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/more-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/more-menu.js index 08c17304ca38b..4de10b8377da9 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/more-menu.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/more-menu.js @@ -33,6 +33,7 @@ export default function ScreenNavigationMoreMenu( props ) { <> diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js index 6b6fc8587228f..f52204f3a1843 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js @@ -23,13 +23,13 @@ export default function SingleNavigationMenu( { - + } title={ decodeEntities( menuTitle ) } From f37e6a3a485020d1964bb4f3fea51af1f7872abc Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 13 Jul 2023 11:33:47 +0400 Subject: [PATCH 25/28] Avoid errors in Dimension visualizers when switching between iframed and non-iframed editors (#52588) --- packages/block-editor/src/hooks/margin.js | 5 ++++- packages/block-editor/src/hooks/padding.js | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/hooks/margin.js b/packages/block-editor/src/hooks/margin.js index bdf5cabea6a9d..8f723b3f8c97d 100644 --- a/packages/block-editor/src/hooks/margin.js +++ b/packages/block-editor/src/hooks/margin.js @@ -23,7 +23,10 @@ export function MarginVisualizer( { clientId, attributes, forceShow } ) { const margin = attributes?.style?.spacing?.margin; useEffect( () => { - if ( ! blockElement ) { + if ( + ! blockElement || + null === blockElement.ownerDocument.defaultView + ) { return; } diff --git a/packages/block-editor/src/hooks/padding.js b/packages/block-editor/src/hooks/padding.js index f451f2cb4262e..b6e4e50e30f9c 100644 --- a/packages/block-editor/src/hooks/padding.js +++ b/packages/block-editor/src/hooks/padding.js @@ -23,7 +23,10 @@ export function PaddingVisualizer( { clientId, attributes, forceShow } ) { const padding = attributes?.style?.spacing?.padding; useEffect( () => { - if ( ! blockElement ) { + if ( + ! blockElement || + null === blockElement.ownerDocument.defaultView + ) { return; } From 56a380676edc7ab3b7e26f69dca7fdbf83c8b965 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Thu, 13 Jul 2023 19:31:42 +1200 Subject: [PATCH 26/28] Patterns: Add client side pagination to patterns list (#52538) Co-authored-by: Saxon Fletcher --- .../src/components/page-patterns/grid.js | 117 +++++++++++++++--- .../components/page-patterns/patterns-list.js | 7 +- .../src/components/page-patterns/style.scss | 22 +++- 3 files changed, 125 insertions(+), 21 deletions(-) diff --git a/packages/edit-site/src/components/page-patterns/grid.js b/packages/edit-site/src/components/page-patterns/grid.js index 1902b36982c14..47bcdc8a1f768 100644 --- a/packages/edit-site/src/components/page-patterns/grid.js +++ b/packages/edit-site/src/components/page-patterns/grid.js @@ -1,26 +1,115 @@ /** * WordPress dependencies */ -import { __experimentalText as Text } from '@wordpress/components'; -import { useRef } from '@wordpress/element'; -import { __, sprintf } from '@wordpress/i18n'; +import { + __experimentalHStack as HStack, + __experimentalText as Text, + Button, +} from '@wordpress/components'; +import { useRef, useState, useMemo } from '@wordpress/element'; +import { __, _x, _n, sprintf } from '@wordpress/i18n'; +import { useAsyncList } from '@wordpress/compose'; /** * Internal dependencies */ import GridItem from './grid-item'; -const PAGE_SIZE = 100; +const PAGE_SIZE = 20; + +function Pagination( { currentPage, numPages, changePage, totalItems } ) { + return ( + + + { + // translators: %s: Total number of patterns. + sprintf( + // translators: %s: Total number of patterns. + _n( '%s item', '%s items', totalItems ), + totalItems + ) + } + + + + + + + { sprintf( + // translators: %1$s: Current page number, %2$s: Total number of pages. + _x( '%1$s of %2$s', 'paging' ), + currentPage, + numPages + ) } + + + + + + + ); +} export default function Grid( { categoryId, items, ...props } ) { + const [ currentPage, setCurrentPage ] = useState( 1 ); const gridRef = useRef(); + const totalItems = items.length; + const pageIndex = currentPage - 1; - if ( ! items?.length ) { + const list = useMemo( + () => + items.slice( + pageIndex * PAGE_SIZE, + pageIndex * PAGE_SIZE + PAGE_SIZE + ), + [ pageIndex, items ] + ); + + const asyncList = useAsyncList( list, { step: 10 } ); + + if ( ! list?.length ) { return null; } - const list = items.slice( 0, PAGE_SIZE ); - const restLength = items.length - PAGE_SIZE; + const numPages = Math.ceil( items.length / PAGE_SIZE ); + const changePage = ( page ) => { + const scrollContainer = document.querySelector( '.edit-site-patterns' ); + scrollContainer?.scrollTo( 0, 0 ); + + setCurrentPage( page ); + }; return ( <> @@ -30,7 +119,7 @@ export default function Grid( { categoryId, items, ...props } ) { { ...props } ref={ gridRef } > - { list.map( ( item ) => ( + { asyncList.map( ( item ) => ( ) ) } - { restLength > 0 && ( - - { sprintf( - /* translators: %d: number of patterns */ - __( '+ %d more patterns discoverable by searching' ), - restLength - ) } - + { numPages > 1 && ( + ) } ); 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 7bf2a9d506584..f4fefed4a2d3e 100644 --- a/packages/edit-site/src/components/page-patterns/patterns-list.js +++ b/packages/edit-site/src/components/page-patterns/patterns-list.js @@ -15,7 +15,7 @@ import { import { __, isRTL } from '@wordpress/i18n'; import { chevronLeft, chevronRight } from '@wordpress/icons'; import { privateApis as routerPrivateApis } from '@wordpress/router'; -import { useViewportMatch, useAsyncList } from '@wordpress/compose'; +import { useViewportMatch } from '@wordpress/compose'; /** * Internal dependencies @@ -70,7 +70,6 @@ export default function PatternsList( { categoryId, type } ) { const hasPatterns = patterns.length; const title = SYNC_FILTERS[ syncFilter ]; const description = SYNC_DESCRIPTIONS[ syncFilter ]; - const shownPatterns = useAsyncList( patterns ); return ( @@ -132,7 +131,7 @@ export default function PatternsList( { categoryId, type } ) { { syncFilter !== 'all' && ( - + { title } { description ? ( @@ -145,7 +144,7 @@ export default function PatternsList( { categoryId, type } ) { { hasPatterns && ( diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index 79731999f46ef..d1fbedb141f70 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -1,6 +1,8 @@ .edit-site-patterns { - background: rgba(0, 0, 0, 0.15); + border: 1px solid $gray-800; + background: none; margin: $header-height 0 0; + border-radius: 0; .components-base-control { width: 100%; @include break-medium { @@ -59,6 +61,23 @@ background: $gray-700; color: $gray-100; } + + .edit-site-patterns__grid-pagination { + width: fit-content; + .components-button.is-tertiary { + width: $button-size-compact; + height: $button-size-compact; + color: $gray-100; + background-color: $gray-800; + &:disabled { + color: $gray-600; + background: none; + } + &:hover:not(:disabled) { + background-color: $gray-700; + } + } + } } .edit-site-patterns__section-header { @@ -74,6 +93,7 @@ // Small top padding required to avoid cutting off the visible outline // when hovering items. padding-top: $border-width-focus-fallback; + margin-top: 0; margin-bottom: $grid-unit-40; @include break-large { grid-template-columns: 1fr 1fr; From bd02332698db04cc77dfc8d23d7d955863e564ff Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Thu, 13 Jul 2023 16:38:51 +1000 Subject: [PATCH 27/28] Site Editor: Make sidebar back button go *back* instead of *up* if possible (#52456) * Navigator: Add replace option to goTo() and goToParent() * Site Editor: Make sidebar back button go back instead of up if possible --- packages/components/CHANGELOG.md | 4 + .../navigator-provider/component.tsx | 53 ++++--- packages/components/src/navigator/types.ts | 5 +- .../sidebar-navigation-screen/index.js | 19 ++- .../use-sync-path-with-url.js | 139 +++++++++--------- 5 files changed, 124 insertions(+), 96 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index bdf9ef752d98d..c6e48d108ae50 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancements + +- `Navigator`: Add `replace` option to `navigator.goTo()` and `navigator.goToParent()` ([#52456](https://github.com/WordPress/gutenberg/pull/52456)). + ### Bug Fix - `UnitControl`: Fix crash when certain units are used ([#52211](https://github.com/WordPress/gutenberg/pull/52211)). diff --git a/packages/components/src/navigator/navigator-provider/component.tsx b/packages/components/src/navigator/navigator-provider/component.tsx index 4c98db5b1a4d1..9bcffad6bc54b 100644 --- a/packages/components/src/navigator/navigator-provider/component.tsx +++ b/packages/components/src/navigator/navigator-provider/component.tsx @@ -148,6 +148,7 @@ function UnconnectedNavigatorProvider( focusTargetSelector, isBack = false, skipFocus = false, + replace = false, ...restOptions } = options; @@ -172,34 +173,38 @@ function UnconnectedNavigatorProvider( skipFocus, }; - if ( prevLocationHistory.length < 1 ) { - return [ newLocation ]; + if ( prevLocationHistory.length === 0 ) { + return replace ? [] : [ newLocation ]; } - return [ - ...prevLocationHistory.slice( - prevLocationHistory.length > MAX_HISTORY_LENGTH - 1 - ? 1 - : 0, - -1 - ), - // Assign `focusTargetSelector` to the previous location in history - // (the one we just navigated from). - { - ...prevLocationHistory[ - prevLocationHistory.length - 1 - ], - focusTargetSelector, - }, - newLocation, - ]; + const newLocationHistory = prevLocationHistory.slice( + prevLocationHistory.length > MAX_HISTORY_LENGTH - 1 ? 1 : 0, + -1 + ); + + if ( ! replace ) { + newLocationHistory.push( + // Assign `focusTargetSelector` to the previous location in history + // (the one we just navigated from). + { + ...prevLocationHistory[ + prevLocationHistory.length - 1 + ], + focusTargetSelector, + } + ); + } + + newLocationHistory.push( newLocation ); + + return newLocationHistory; } ); }, [ goBack ] ); - const goToParent: NavigatorContextType[ 'goToParent' ] = - useCallback( () => { + const goToParent: NavigatorContextType[ 'goToParent' ] = useCallback( + ( options = {} ) => { const currentPath = currentLocationHistory.current[ currentLocationHistory.current.length - 1 @@ -214,8 +219,10 @@ function UnconnectedNavigatorProvider( if ( parentPath === undefined ) { return; } - goTo( parentPath, { isBack: true } ); - }, [ goTo ] ); + goTo( parentPath, { ...options, isBack: true } ); + }, + [ goTo ] + ); const navigatorContextValue: NavigatorContextType = useMemo( () => ( { diff --git a/packages/components/src/navigator/types.ts b/packages/components/src/navigator/types.ts index e638084e8376d..557f8074fd42e 100644 --- a/packages/components/src/navigator/types.ts +++ b/packages/components/src/navigator/types.ts @@ -14,8 +14,11 @@ export type NavigateOptions = { focusTargetSelector?: string; isBack?: boolean; skipFocus?: boolean; + replace?: boolean; }; +export type NavigateToParentOptions = Omit< NavigateOptions, 'isBack' >; + export type NavigatorLocation = NavigateOptions & { isInitial?: boolean; path?: string; @@ -28,7 +31,7 @@ export type Navigator = { params: MatchParams; goTo: ( path: string, options?: NavigateOptions ) => void; goBack: () => void; - goToParent: () => void; + goToParent: ( options?: NavigateToParentOptions ) => void; }; export type NavigatorContext = Navigator & { 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 919e7e92d721d..32367c32b71e9 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen/index.js @@ -4,7 +4,6 @@ import { __experimentalHStack as HStack, __experimentalHeading as Heading, - __experimentalNavigatorToParentButton as NavigatorToParentButton, __experimentalUseNavigator as useNavigator, __experimentalVStack as VStack, } from '@wordpress/components'; @@ -41,7 +40,7 @@ export default function SidebarNavigationScreen( { }; }, [] ); const { getTheme } = useSelect( coreStore ); - const { goTo } = useNavigator(); + const navigator = useNavigator(); const theme = getTheme( currentlyPreviewingTheme() ); const icon = isRTL() ? chevronRight : chevronLeft; @@ -58,16 +57,24 @@ export default function SidebarNavigationScreen( { className="edit-site-sidebar-navigation-screen__title-icon" > { ! isRoot && ! backPath && ( - { + if ( navigator.location.isInitial ) { + navigator.goToParent( { replace: true } ); + } else { + navigator.goBack(); + } + } } + icon={ icon } label={ __( 'Back' ) } showTooltip={ false } /> ) } { ! isRoot && backPath && ( goTo( backPath, { isBack: true } ) } + onClick={ () => + navigator.goTo( backPath, { isBack: true } ) + } icon={ icon } label={ __( 'Back' ) } showTooltip={ false } diff --git a/packages/edit-site/src/components/sync-state-with-url/use-sync-path-with-url.js b/packages/edit-site/src/components/sync-state-with-url/use-sync-path-with-url.js index 45f7eb1b044c1..86928c1920a94 100644 --- a/packages/edit-site/src/components/sync-state-with-url/use-sync-path-with-url.js +++ b/packages/edit-site/src/components/sync-state-with-url/use-sync-path-with-url.js @@ -36,6 +36,12 @@ export function getPathFromURL( urlParams ) { return path; } +function isSubset( subset, superset ) { + return Object.entries( subset ).every( ( [ key, value ] ) => { + return superset[ key ] === value; + } ); +} + export default function useSyncPathWithURL() { const history = useHistory(); const { params: urlParams } = useLocation(); @@ -44,76 +50,77 @@ export default function useSyncPathWithURL() { params: navigatorParams, goTo, } = useNavigator(); - const currentUrlParams = useRef( urlParams ); - const currentPath = useRef( navigatorLocation.path ); const isMounting = useRef( true ); - useEffect( () => { - // The navigatorParams are only initially filled properly when the - // navigator screens mount. so we ignore the first synchronisation. - if ( isMounting.current ) { - isMounting.current = false; - return; - } - - function updateUrlParams( newUrlParams ) { - if ( - Object.entries( newUrlParams ).every( ( [ key, value ] ) => { - return currentUrlParams.current[ key ] === value; - } ) - ) { + useEffect( + () => { + // The navigatorParams are only initially filled properly when the + // navigator screens mount. so we ignore the first synchronisation. + if ( isMounting.current ) { + isMounting.current = false; return; } - const updatedParams = { - ...currentUrlParams.current, - ...newUrlParams, - }; - currentUrlParams.current = updatedParams; - history.push( updatedParams ); - } - if ( navigatorParams?.postType && navigatorParams?.postId ) { - updateUrlParams( { - postType: navigatorParams?.postType, - postId: navigatorParams?.postId, - path: undefined, - } ); - } else if ( - navigatorLocation.path.startsWith( '/page/' ) && - navigatorParams?.postId - ) { - updateUrlParams( { - postType: 'page', - postId: navigatorParams?.postId, - path: undefined, - } ); - } else if ( navigatorLocation.path === '/patterns' ) { - updateUrlParams( { - postType: undefined, - postId: undefined, - canvas: undefined, - path: navigatorLocation.path, - } ); - } else { - updateUrlParams( { - postType: undefined, - postId: undefined, - categoryType: undefined, - categoryId: undefined, - path: - navigatorLocation.path === '/' - ? undefined - : navigatorLocation.path, - } ); - } - }, [ navigatorLocation?.path, navigatorParams, history ] ); + function updateUrlParams( newUrlParams ) { + if ( isSubset( newUrlParams, urlParams ) ) { + return; + } + const updatedParams = { + ...urlParams, + ...newUrlParams, + }; + history.push( updatedParams ); + } - useEffect( () => { - currentUrlParams.current = urlParams; - const path = getPathFromURL( urlParams ); - if ( currentPath.current !== path ) { - currentPath.current = path; - goTo( path ); - } - }, [ urlParams, goTo ] ); + if ( navigatorParams?.postType && navigatorParams?.postId ) { + updateUrlParams( { + postType: navigatorParams?.postType, + postId: navigatorParams?.postId, + path: undefined, + } ); + } else if ( + navigatorLocation.path.startsWith( '/page/' ) && + navigatorParams?.postId + ) { + updateUrlParams( { + postType: 'page', + postId: navigatorParams?.postId, + path: undefined, + } ); + } else if ( navigatorLocation.path === '/patterns' ) { + updateUrlParams( { + postType: undefined, + postId: undefined, + canvas: undefined, + path: navigatorLocation.path, + } ); + } else { + updateUrlParams( { + postType: undefined, + postId: undefined, + categoryType: undefined, + categoryId: undefined, + path: + navigatorLocation.path === '/' + ? undefined + : navigatorLocation.path, + } ); + } + }, + // Trigger only when navigator changes to prevent infinite loops. + // eslint-disable-next-line react-hooks/exhaustive-deps + [ navigatorLocation?.path, navigatorParams ] + ); + + useEffect( + () => { + const path = getPathFromURL( urlParams ); + if ( navigatorLocation.path !== path ) { + goTo( path ); + } + }, + // Trigger only when URL changes to prevent infinite loops. + // eslint-disable-next-line react-hooks/exhaustive-deps + [ urlParams ] + ); } From ebbd07824807ff71907d45d732d37036ea21070e Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Thu, 13 Jul 2023 21:40:32 -0400 Subject: [PATCH 28/28] Adapt template part hint copy (#52527) * Try "panel" instead of "page" * Update template-part-hint.js --- .../sidebar-navigation-screen-main/template-part-hint.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js b/packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js index 8fbe74f81bb4d..c6f270465b86f 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js @@ -28,9 +28,7 @@ export default function TemplatePartHint() { setPreference( 'core', PREFERENCE_NAME, false ); } } > - { __( - 'Looking for template parts? You can now find them in the new "Patterns" page.' - ) } + { __( 'Looking for template parts? Find them in "Patterns".' ) } ); }