From 132c8e4ddfce5b74829dc319c84c980ba71b1d33 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Mon, 24 Jul 2023 08:39:03 +0200 Subject: [PATCH 01/61] Preformatted: Add spacing support (#45196) * Preformatted: Add spacing support --- docs/reference-guides/core-blocks.md | 2 +- packages/block-library/src/preformatted/block.json | 4 ++++ packages/block-library/src/preformatted/style.scss | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index d0a6ca7d356b2..62604319e9aa0 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -646,7 +646,7 @@ Add text that respects your spacing and tabs, and also allows styling. ([Source] - **Name:** core/preformatted - **Category:** text -- **Supports:** anchor, color (background, gradients, text), typography (fontSize, lineHeight) +- **Supports:** anchor, color (background, gradients, text), spacing (margin, padding), typography (fontSize, lineHeight) - **Attributes:** content ## Pullquote diff --git a/packages/block-library/src/preformatted/block.json b/packages/block-library/src/preformatted/block.json index f781cb1125efa..ec6ea839385eb 100644 --- a/packages/block-library/src/preformatted/block.json +++ b/packages/block-library/src/preformatted/block.json @@ -25,6 +25,10 @@ "text": true } }, + "spacing": { + "padding": true, + "margin": true + }, "typography": { "fontSize": true, "lineHeight": true, diff --git a/packages/block-library/src/preformatted/style.scss b/packages/block-library/src/preformatted/style.scss index 71e60ffe4ea52..783fee74d4f4f 100644 --- a/packages/block-library/src/preformatted/style.scss +++ b/packages/block-library/src/preformatted/style.scss @@ -1,7 +1,10 @@ .wp-block-preformatted { + // This block has customizable padding, border-box makes that more predictable. + box-sizing: border-box; white-space: pre-wrap; } -.wp-block-preformatted.has-background { +// Add low specificity default padding when a background is used. +:where(.wp-block-preformatted.has-background) { padding: $block-bg-padding--v $block-bg-padding--h; } From f49587d9736e21a57ec4a69ce900b28f1e38256c Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Mon, 24 Jul 2023 16:44:25 +1000 Subject: [PATCH 02/61] Distraction Free: Fix notices in site editor (#52867) --- .../src/components/keyboard-shortcuts/edit-mode.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js b/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js index 6bc2d59ec9e97..49efa11368a7d 100644 --- a/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js +++ b/packages/edit-site/src/components/keyboard-shortcuts/edit-mode.js @@ -33,10 +33,8 @@ function KeyboardShortcutsEditMode() { ); const { redo, undo } = useDispatch( coreStore ); const { - isFeatureActive, setIsListViewOpened, switchEditorMode, - toggleFeature, setIsInserterOpened, closeGeneralSidebar, } = useDispatch( editSiteStore ); @@ -47,7 +45,8 @@ function KeyboardShortcutsEditMode() { const { getBlockName, getSelectedBlockClientId, getBlockAttributes } = useSelect( blockEditorStore ); - const { set: setPreference } = useDispatch( preferencesStore ); + const { get: getPreference } = useSelect( preferencesStore ); + const { set: setPreference, toggle } = useDispatch( preferencesStore ); const { createInfoNotice } = useDispatch( noticesStore ); const toggleDistractionFree = () => { @@ -135,9 +134,9 @@ function KeyboardShortcutsEditMode() { useShortcut( 'core/edit-site/toggle-distraction-free', () => { toggleDistractionFree(); - toggleFeature( 'distractionFree' ); + toggle( 'core/edit-site', 'distractionFree' ); createInfoNotice( - isFeatureActive( 'distractionFree' ) + getPreference( 'core/edit-site', 'distractionFree' ) ? __( 'Distraction free mode turned on.' ) : __( 'Distraction free mode turned off.' ), { From 30520b77d2acffd8770e1309f4a7f10cf9881d6e Mon Sep 17 00:00:00 2001 From: Ramon Date: Mon, 24 Jul 2023 16:59:29 +1000 Subject: [PATCH 03/61] Global styles revisions: display text if no revisions are found (#52865) * If somehow a user lands on the revisions panel when there are no revisions, show some helpful text rather than a loading spinner. Also, add an E2E test! * Updated unit tests to reflect resolver logic changes * Use existing string * Only open edit view when testing the revisions panel itself --- docs/reference-guides/data/data-core.md | 2 +- packages/core-data/README.md | 2 +- packages/core-data/src/selectors.ts | 2 +- .../global-styles/screen-revisions/index.js | 134 ++++++++++-------- .../test/use-global-styles-revisions.js | 15 +- .../use-global-styles-revisions.js | 113 ++++++++------- .../user-global-styles-revisions.spec.js | 21 ++- 7 files changed, 176 insertions(+), 113 deletions(-) diff --git a/docs/reference-guides/data/data-core.md b/docs/reference-guides/data/data-core.md index a66c0991e3d27..f2bc3374f9e72 100644 --- a/docs/reference-guides/data/data-core.md +++ b/docs/reference-guides/data/data-core.md @@ -136,7 +136,7 @@ _Parameters_ _Returns_ -- `Object | null`: The current global styles. +- `Array< object > | null`: The current global styles. ### getCurrentUser diff --git a/packages/core-data/README.md b/packages/core-data/README.md index 63e6e28db08d5..c778b724149ef 100644 --- a/packages/core-data/README.md +++ b/packages/core-data/README.md @@ -313,7 +313,7 @@ _Parameters_ _Returns_ -- `Object | null`: The current global styles. +- `Array< object > | null`: The current global styles. ### getCurrentUser diff --git a/packages/core-data/src/selectors.ts b/packages/core-data/src/selectors.ts index 142d24a9d2b8d..377134ab7c9a3 100644 --- a/packages/core-data/src/selectors.ts +++ b/packages/core-data/src/selectors.ts @@ -1266,7 +1266,7 @@ export function getBlockPatternCategories( state: State ): Array< any > { */ export function getCurrentThemeGlobalStylesRevisions( state: State -): Object | null { +): Array< object > | null { const currentGlobalStylesId = __experimentalGetCurrentGlobalStylesId( state ); diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/index.js b/packages/edit-site/src/components/global-styles/screen-revisions/index.js index b21c14418cbee..aa8bb1f377f29 100644 --- a/packages/edit-site/src/components/global-styles/screen-revisions/index.js +++ b/packages/edit-site/src/components/global-styles/screen-revisions/index.js @@ -7,6 +7,7 @@ import { __experimentalUseNavigator as useNavigator, __experimentalConfirmDialog as ConfirmDialog, Spinner, + __experimentalSpacer as Spacer, } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { useContext, useState, useEffect } from '@wordpress/element'; @@ -89,6 +90,7 @@ function ScreenRevisions() { const isLoadButtonEnabled = !! globalStylesRevision?.id && ! areGlobalStyleConfigsEqual( globalStylesRevision, userConfig ); + const shouldShowRevisions = ! isLoading && revisions.length; return ( <> @@ -101,68 +103,84 @@ function ScreenRevisions() { { isLoading && ( ) } - { ! isLoading && ( - - ) } -
- - { isLoadButtonEnabled && ( - - + + ) } +
+ { isLoadingRevisionWithUnsavedChanges && ( + + restoreRevision( globalStylesRevision ) + } + onCancel={ () => + setIsLoadingRevisionWithUnsavedChanges( false ) } - onClick={ () => { - if ( hasUnsavedChanges ) { - setIsLoadingRevisionWithUnsavedChanges( - true - ); - } else { - restoreRevision( globalStylesRevision ); - } - } } > - { __( 'Apply' ) } - - - ) } - - { isLoadingRevisionWithUnsavedChanges && ( - +

+ { __( + 'Loading this revision will discard all unsaved changes.' + ) } +

+

+ { __( + 'Do you want to replace your unsaved changes in the editor?' + ) } +

+ +
) } - isOpen={ isLoadingRevisionWithUnsavedChanges } - confirmButtonText={ __( ' Discard unsaved changes' ) } - onConfirm={ () => restoreRevision( globalStylesRevision ) } - onCancel={ () => - setIsLoadingRevisionWithUnsavedChanges( false ) + + ) : ( + + { + // Adding an existing translation here in case these changes are shipped to WordPress 6.3. + // Later we could update to something better, e.g., "There are currently no style revisions.". + __( 'No results found.' ) } - > - <> -

- { __( - 'Loading this revision will discard all unsaved changes.' - ) } -

-

- { __( - 'Do you want to replace your unsaved changes in the editor?' - ) } -

- -
+ ) } ); diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/test/use-global-styles-revisions.js b/packages/edit-site/src/components/global-styles/screen-revisions/test/use-global-styles-revisions.js index 0b7d086c1120f..8f618a4897edc 100644 --- a/packages/edit-site/src/components/global-styles/screen-revisions/test/use-global-styles-revisions.js +++ b/packages/edit-site/src/components/global-styles/screen-revisions/test/use-global-styles-revisions.js @@ -49,6 +49,7 @@ describe( 'useGlobalStylesRevisions', () => { styles: {}, }, ], + isLoadingGlobalStylesRevisions: false, }; it( 'returns loaded revisions with no unsaved changes', () => { @@ -117,11 +118,23 @@ describe( 'useGlobalStylesRevisions', () => { const { result } = renderHook( () => useGlobalStylesRevisions() ); const { revisions, isLoading, hasUnsavedChanges } = result.current; - expect( isLoading ).toBe( true ); + expect( isLoading ).toBe( false ); expect( hasUnsavedChanges ).toBe( false ); expect( revisions ).toEqual( [] ); } ); + it( 'returns loading status when resolving global revisions', () => { + useSelect.mockImplementation( () => ( { + ...selectValue, + isLoadingGlobalStylesRevisions: true, + } ) ); + + const { result } = renderHook( () => useGlobalStylesRevisions() ); + const { isLoading } = result.current; + + expect( isLoading ).toBe( true ); + } ); + it( 'returns empty revisions when authors are not yet available', () => { useSelect.mockImplementation( () => ( { ...selectValue, diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js b/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js index 5aee31f1ff99a..bc82a5f2b1293 100644 --- a/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js +++ b/packages/edit-site/src/components/global-styles/screen-revisions/use-global-styles-revisions.js @@ -21,34 +21,40 @@ const EMPTY_ARRAY = []; const { GlobalStylesContext } = unlock( blockEditorPrivateApis ); export default function useGlobalStylesRevisions() { const { user: userConfig } = useContext( GlobalStylesContext ); - const { authors, currentUser, isDirty, revisions } = useSelect( - ( select ) => { - const { - __experimentalGetDirtyEntityRecords, - getCurrentUser, - getUsers, - getCurrentThemeGlobalStylesRevisions, - } = select( coreStore ); - const dirtyEntityRecords = __experimentalGetDirtyEntityRecords(); - const _currentUser = getCurrentUser(); - const _isDirty = dirtyEntityRecords.length > 0; - const globalStylesRevisions = - getCurrentThemeGlobalStylesRevisions() || EMPTY_ARRAY; - const _authors = - getUsers( SITE_EDITOR_AUTHORS_QUERY ) || EMPTY_ARRAY; + const { + authors, + currentUser, + isDirty, + revisions, + isLoadingGlobalStylesRevisions, + } = useSelect( ( select ) => { + const { + __experimentalGetDirtyEntityRecords, + getCurrentUser, + getUsers, + getCurrentThemeGlobalStylesRevisions, + isResolving, + } = select( coreStore ); + const dirtyEntityRecords = __experimentalGetDirtyEntityRecords(); + const _currentUser = getCurrentUser(); + const _isDirty = dirtyEntityRecords.length > 0; + const globalStylesRevisions = + getCurrentThemeGlobalStylesRevisions() || EMPTY_ARRAY; + const _authors = getUsers( SITE_EDITOR_AUTHORS_QUERY ) || EMPTY_ARRAY; - return { - authors: _authors, - currentUser: _currentUser, - isDirty: _isDirty, - revisions: globalStylesRevisions, - }; - }, - [] - ); + return { + authors: _authors, + currentUser: _currentUser, + isDirty: _isDirty, + revisions: globalStylesRevisions, + isLoadingGlobalStylesRevisions: isResolving( + 'getCurrentThemeGlobalStylesRevisions' + ), + }; + }, [] ); return useMemo( () => { let _modifiedRevisions = []; - if ( ! authors.length || ! revisions.length ) { + if ( ! authors.length || isLoadingGlobalStylesRevisions ) { return { revisions: _modifiedRevisions, hasUnsavedChanges: isDirty, @@ -66,31 +72,33 @@ export default function useGlobalStylesRevisions() { }; } ); - // Flags the most current saved revision. - if ( _modifiedRevisions[ 0 ].id !== 'unsaved' ) { - _modifiedRevisions[ 0 ].isLatest = true; - } + if ( _modifiedRevisions.length ) { + // Flags the most current saved revision. + if ( _modifiedRevisions[ 0 ].id !== 'unsaved' ) { + _modifiedRevisions[ 0 ].isLatest = true; + } - // Adds an item for unsaved changes. - if ( - isDirty && - userConfig && - Object.keys( userConfig ).length > 0 && - currentUser - ) { - const unsavedRevision = { - id: 'unsaved', - styles: userConfig?.styles, - settings: userConfig?.settings, - behaviors: userConfig?.behaviors, - author: { - name: currentUser?.name, - avatar_urls: currentUser?.avatar_urls, - }, - modified: new Date(), - }; + // Adds an item for unsaved changes. + if ( + isDirty && + userConfig && + Object.keys( userConfig ).length > 0 && + currentUser + ) { + const unsavedRevision = { + id: 'unsaved', + styles: userConfig?.styles, + settings: userConfig?.settings, + behaviors: userConfig?.behaviors, + author: { + name: currentUser?.name, + avatar_urls: currentUser?.avatar_urls, + }, + modified: new Date(), + }; - _modifiedRevisions.unshift( unsavedRevision ); + _modifiedRevisions.unshift( unsavedRevision ); + } } return { @@ -98,5 +106,12 @@ export default function useGlobalStylesRevisions() { hasUnsavedChanges: isDirty, isLoading: false, }; - }, [ isDirty, revisions, currentUser, authors, userConfig ] ); + }, [ + isDirty, + revisions, + currentUser, + authors, + userConfig, + isLoadingGlobalStylesRevisions, + ] ); } diff --git a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js index 4ab61111df419..cb90ebe5edf25 100644 --- a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js +++ b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js @@ -22,9 +22,24 @@ test.describe( 'Global styles revisions', () => { await requestUtils.activateTheme( 'twentytwentyone' ); } ); - test.beforeEach( async ( { admin, editor } ) => { + test.beforeEach( async ( { admin } ) => { await admin.visitSiteEditor(); - await editor.canvas.click( 'body' ); + } ); + + test( 'should display no revisions message if landing via command center', async ( { + page, + } ) => { + await page + .getByRole( 'button', { name: 'Open command palette' } ) + .focus(); + await page.keyboard.press( 'Meta+k' ); + await page.keyboard.type( 'styles revisions' ); + await page + .getByRole( 'option', { name: 'Open styles revisions' } ) + .click(); + await expect( + page.getByTestId( 'global-styles-no-revisions' ) + ).toHaveText( 'No results found.' ); } ); test( 'should display revisions UI when there is more than 1 revision', async ( { @@ -32,6 +47,7 @@ test.describe( 'Global styles revisions', () => { editor, userGlobalStylesRevisions, } ) => { + await editor.canvas.click( 'body' ); const currentRevisions = await userGlobalStylesRevisions.getGlobalStylesRevisions(); await userGlobalStylesRevisions.openStylesPanel(); @@ -78,6 +94,7 @@ test.describe( 'Global styles revisions', () => { editor, userGlobalStylesRevisions, } ) => { + await editor.canvas.click( 'body' ); await userGlobalStylesRevisions.openStylesPanel(); await page.getByRole( 'button', { name: 'Colors styles' } ).click(); await page From 81b58d30a8685872fd5ac361bb75760d50318e1d Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 24 Jul 2023 12:16:14 +0400 Subject: [PATCH 04/61] Update e2e tests that use code editor (#52788) --- test/e2e/specs/editor/blocks/list.spec.js | 14 ++++++++------ .../editor/various/content-only-lock.spec.js | 15 ++++++++------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/test/e2e/specs/editor/blocks/list.spec.js b/test/e2e/specs/editor/blocks/list.spec.js index c8be29ac2a64e..41c846a975077 100644 --- a/test/e2e/specs/editor/blocks/list.spec.js +++ b/test/e2e/specs/editor/blocks/list.spec.js @@ -1241,17 +1241,19 @@ test.describe( 'List (@firefox)', () => { test( 'can be created by pasting an empty list (-firefox)', async ( { editor, + page, pageUtils, } ) => { // Open code editor await pageUtils.pressKeys( 'secondary+M' ); // Emulates CTRL+Shift+Alt + M => toggle code editor - // Paste empty list block - pageUtils.setClipboardData( { - plainText: - '\n
\n', - } ); - await pageUtils.pressKeys( 'primary+v' ); + // Add empty list block + await page.getByPlaceholder( 'Start writing with text or HTML' ) + .fill( ` +
    +
  • +
+` ); // Go back to normal editor await pageUtils.pressKeys( 'secondary+M' ); // Emulates CTRL+Shift+Alt + M => toggle code editor diff --git a/test/e2e/specs/editor/various/content-only-lock.spec.js b/test/e2e/specs/editor/various/content-only-lock.spec.js index 962fe0a627909..d6ea152d65f3f 100644 --- a/test/e2e/specs/editor/various/content-only-lock.spec.js +++ b/test/e2e/specs/editor/various/content-only-lock.spec.js @@ -15,13 +15,14 @@ test.describe( 'Content-only lock', () => { } ) => { // Add content only locked block in the code editor await pageUtils.pressKeys( 'secondary+M' ); // Emulates CTRL+Shift+Alt + M => toggle code editor - await page.click( '.editor-post-text-editor' ); - await page.keyboard - .type( ` -
-

Hello

-
- ` ); + + await page.getByPlaceholder( 'Start writing with text or HTML' ) + .fill( ` +
+

Hello

+
+` ); + await pageUtils.pressKeys( 'secondary+M' ); await page.waitForSelector( 'iframe[name="editor-canvas"]' ); await editor.canvas.click( 'role=document[name="Paragraph block"i]' ); From dad8f92cf74ea1398ff84e68b81df4328877a7ca Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Mon, 24 Jul 2023 19:32:36 +1000 Subject: [PATCH 05/61] Distraction Free: Add missing command in site editor (#52868) --- .../hooks/commands/use-edit-mode-commands.js | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js index 3bd1b561a3ab1..2c66a8f2d9f09 100644 --- a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js +++ b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js @@ -19,6 +19,7 @@ import { useCommandLoader } from '@wordpress/commands'; import { privateApis as routerPrivateApis } from '@wordpress/router'; import { store as preferencesStore } from '@wordpress/preferences'; import { store as interfaceStore } from '@wordpress/interface'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -137,8 +138,13 @@ function useManipulateDocumentCommands() { } function useEditUICommands() { - const { openGeneralSidebar, closeGeneralSidebar, switchEditorMode } = - useDispatch( editSiteStore ); + const { + openGeneralSidebar, + closeGeneralSidebar, + setIsInserterOpened, + setIsListViewOpened, + switchEditorMode, + } = useDispatch( editSiteStore ); const { canvasMode, editorMode, activeSidebar } = useSelect( ( select ) => ( { canvasMode: unlock( select( editSiteStore ) ).getCanvasMode(), @@ -150,7 +156,9 @@ function useEditUICommands() { [] ); const { openModal } = useDispatch( interfaceStore ); - const { toggle } = useDispatch( preferencesStore ); + const { get: getPreference } = useSelect( preferencesStore ); + const { set: setPreference, toggle } = useDispatch( preferencesStore ); + const { createInfoNotice } = useDispatch( noticesStore ); if ( canvasMode !== 'edit' ) { return { isLoading: false, commands: [] }; @@ -196,6 +204,29 @@ function useEditUICommands() { }, } ); + commands.push( { + name: 'core/toggle-distraction-free', + label: __( 'Toggle distraction free' ), + icon: cog, + callback: ( { close } ) => { + setPreference( 'core/edit-site', 'fixedToolbar', false ); + setIsInserterOpened( false ); + setIsListViewOpened( false ); + closeGeneralSidebar(); + toggle( 'core/edit-site', 'distractionFree' ); + createInfoNotice( + getPreference( 'core/edit-site', 'distractionFree' ) + ? __( 'Distraction free mode turned on.' ) + : __( 'Distraction free mode turned off.' ), + { + id: 'core/edit-site/distraction-free-mode/notice', + type: 'snackbar', + } + ); + close(); + }, + } ); + commands.push( { name: 'core/toggle-top-toolbar', label: __( 'Toggle top toolbar' ), From 54f716ad2119057b4be048a0555112d60362e6d5 Mon Sep 17 00:00:00 2001 From: Ramon Date: Mon, 24 Jul 2023 19:36:22 +1000 Subject: [PATCH 06/61] Updating function name copy (#52869) --- .../sidebar-navigation-screen-global-styles/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js index 1e9200bf0af01..c885ca97ba789 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js @@ -37,10 +37,10 @@ export function SidebarNavigationItemGlobalStyles( props ) { const { setCanvasMode } = unlock( useDispatch( editSiteStore ) ); const { createNotice } = useDispatch( noticesStore ); const { set: setPreference } = useDispatch( preferencesStore ); - const { get: getPrefference } = useSelect( preferencesStore ); + const { get: getPreference } = useSelect( preferencesStore ); const turnOffDistractionFreeMode = useCallback( () => { - const isDistractionFree = getPrefference( + const isDistractionFree = getPreference( editSiteStore.name, 'distractionFree' ); @@ -52,7 +52,7 @@ export function SidebarNavigationItemGlobalStyles( props ) { isDismissible: true, type: 'snackbar', } ); - }, [ createNotice, setPreference, getPrefference ] ); + }, [ createNotice, setPreference, getPreference ] ); const hasGlobalStyleVariations = useSelect( ( select ) => !! select( @@ -170,7 +170,7 @@ export default function SidebarNavigationScreenGlobalStyles() { ); const { createNotice } = useDispatch( noticesStore ); const { set: setPreference } = useDispatch( preferencesStore ); - const { get: getPrefference } = useSelect( preferencesStore ); + const { get: getPreference } = useSelect( preferencesStore ); const { isViewMode, isStyleBookOpened } = useSelect( ( select ) => { const { getCanvasMode, getEditorCanvasContainerView } = unlock( select( editSiteStore ) @@ -182,7 +182,7 @@ export default function SidebarNavigationScreenGlobalStyles() { }, [] ); const turnOffDistractionFreeMode = useCallback( () => { - const isDistractionFree = getPrefference( + const isDistractionFree = getPreference( editSiteStore.name, 'distractionFree' ); @@ -194,7 +194,7 @@ export default function SidebarNavigationScreenGlobalStyles() { isDismissible: true, type: 'snackbar', } ); - }, [ createNotice, setPreference, getPrefference ] ); + }, [ createNotice, setPreference, getPreference ] ); const openGlobalStyles = useCallback( async () => { turnOffDistractionFreeMode(); From cdd4f8b71bbdbd275c43b3607131e12fe7fada50 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Mon, 24 Jul 2023 11:42:42 +0200 Subject: [PATCH 07/61] Interactivity API: Move Store's data encoding to the `echo` call (#51974) * Move `json_enconde` to the `echo` call inside `render` * Escape tags and ampersands in WP_Interactivity_Store output * Fix expected and add missing commas --- .../class-wp-interactivity-store.php | 16 ++++------------ .../class-wp-interactivity-store-test.php | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/experimental/interactivity-api/class-wp-interactivity-store.php b/lib/experimental/interactivity-api/class-wp-interactivity-store.php index 8b43dbb6d9e24..0dd8aae5406fc 100644 --- a/lib/experimental/interactivity-api/class-wp-interactivity-store.php +++ b/lib/experimental/interactivity-api/class-wp-interactivity-store.php @@ -47,16 +47,6 @@ static function merge_data( $data ) { self::$store = array_replace_recursive( self::$store, $data ); } - /** - * Serialize store data to JSON. - * - * @return string|false Serialized JSON data. - */ - static function serialize() { - // TODO: Escape? - return wp_json_encode( self::$store ); - } - /** * Reset the store data. */ @@ -71,7 +61,9 @@ static function render() { if ( empty( self::$store ) ) { return; } - $store = self::serialize(); - echo ""; + echo sprintf( + '', + wp_json_encode( self::$store, JSON_HEX_TAG | JSON_HEX_AMP ) + ); } } diff --git a/phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php b/phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php index 84286457f2612..22205289b20be 100644 --- a/phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php +++ b/phpunit/experimental/interactivity-api/class-wp-interactivity-store-test.php @@ -165,4 +165,22 @@ public function test_store_should_be_correctly_rendered() { $rendered ); } + + public function test_store_should_also_escape_tags_and_amps() { + WP_Interactivity_Store::merge_data( + array( + 'state' => array( + 'amps' => 'http://site.test/?foo=1&baz=2&bar=3', + 'tags' => 'Do not do this: + +## Unreleased + +### Enhancement + +- Moves the `example` property into block.json by leveraging changes to create-block to now support `example`. [#52801](https://github.com/WordPress/gutenberg/pull/52801) diff --git a/packages/create-block-interactive-template/block-templates/index.js.mustache b/packages/create-block-interactive-template/block-templates/index.js.mustache index cf40358217c76..5279d80f5754c 100644 --- a/packages/create-block-interactive-template/block-templates/index.js.mustache +++ b/packages/create-block-interactive-template/block-templates/index.js.mustache @@ -28,14 +28,6 @@ import metadata from './block.json'; * @see https://developer.wordpress.org/block-editor/developers/block-api/#registering-a-block */ registerBlockType( metadata.name, { - /** - * Used to construct a preview for the block to be shown in the block inserter. - */ - example: { - attributes: { - message: '{{title}}', - }, - }, /** * @see ./edit.js */ diff --git a/packages/create-block-interactive-template/block-templates/render.php.mustache b/packages/create-block-interactive-template/block-templates/render.php.mustache index ba791783e6ede..c458473d565e0 100644 --- a/packages/create-block-interactive-template/block-templates/render.php.mustache +++ b/packages/create-block-interactive-template/block-templates/render.php.mustache @@ -1,6 +1,13 @@ {{#isBasicVariant}} " + aria-controls="p-" > - +

-{{/isBasicVariant}} \ No newline at end of file +{{/isBasicVariant}} diff --git a/packages/create-block-interactive-template/index.js b/packages/create-block-interactive-template/index.js index a52475ed7a79e..5717b3e709723 100644 --- a/packages/create-block-interactive-template/index.js +++ b/packages/create-block-interactive-template/index.js @@ -15,6 +15,7 @@ module.exports = { }, render: 'file:./render.php', viewScript: 'file:./view.js', + example: {}, }, variants: { basic: {}, From f41c8ea14627391cbd1fa64f1abe80b67c739f39 Mon Sep 17 00:00:00 2001 From: James Koster Date: Mon, 24 Jul 2023 18:11:42 +0100 Subject: [PATCH 13/61] Display command palette keyboard shortcut in tooltip when hovering/focusing icon button (#52841) --- packages/edit-site/src/components/site-hub/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/edit-site/src/components/site-hub/index.js b/packages/edit-site/src/components/site-hub/index.js index 888ad3032a34f..b5e14f0e7cd4b 100644 --- a/packages/edit-site/src/components/site-hub/index.js +++ b/packages/edit-site/src/components/site-hub/index.js @@ -21,6 +21,7 @@ import { decodeEntities } from '@wordpress/html-entities'; import { forwardRef } from '@wordpress/element'; import { search, external } from '@wordpress/icons'; import { store as commandsStore } from '@wordpress/commands'; +import { displayShortcut } from '@wordpress/keycodes'; /** * Internal dependencies @@ -192,6 +193,7 @@ const SiteHub = forwardRef( ( { isTransparent, ...restProps }, ref ) => { icon={ search } onClick={ () => openCommandCenter() } label={ __( 'Open command palette' ) } + shortcut={ displayShortcut.primary( 'k' ) } /> ) } From b2309e8207ee048b0e0e0be272a114501ba6c776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor?= <27339341+priethor@users.noreply.github.com> Date: Mon, 24 Jul 2023 19:39:14 +0200 Subject: [PATCH 14/61] Changelog automation: update to work with consolidated a11y labels (#52896) * Update changelog automation to work with consolidated a11y labels * Update test fixtures --- bin/plugin/commands/changelog.js | 4 ---- bin/plugin/commands/test/fixtures/pull-requests.json | 12 ++++++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/bin/plugin/commands/changelog.js b/bin/plugin/commands/changelog.js index 69be77ac66991..c9e6d039596c5 100644 --- a/bin/plugin/commands/changelog.js +++ b/bin/plugin/commands/changelog.js @@ -126,10 +126,6 @@ const LABEL_FEATURE_MAPPING = { 'REST API Interaction': 'REST API', 'New Block': 'Block Library', 'Accessibility (a11y)': 'Accessibility', - '[a11y] Color Contrast': 'Accessibility', - '[a11y] Keyboard & Focus': 'Accessibility', - '[a11y] Labelling': 'Accessibility', - '[a11y] Zooming': 'Accessibility', '[Package] E2E Tests': 'Testing', '[Package] E2E Test Utils': 'Testing', 'Automated Testing': 'Testing', diff --git a/bin/plugin/commands/test/fixtures/pull-requests.json b/bin/plugin/commands/test/fixtures/pull-requests.json index 34f3c06c5d854..eccb2257a2c1f 100644 --- a/bin/plugin/commands/test/fixtures/pull-requests.json +++ b/bin/plugin/commands/test/fixtures/pull-requests.json @@ -5747,13 +5747,13 @@ "description": "/packages/components" }, { - "id": 1344464662, - "node_id": "MDU6TGFiZWwxMzQ0NDY0NjYy", - "url": "https://api.github.com/repos/WordPress/gutenberg/labels/[a11y]%20Keyboard%20&%20Focus", - "name": "[a11y] Keyboard & Focus", - "color": "efde5d", + "id": 546517042, + "node_id": "MDU6TGFiZWw1NDY1MTcwNDI=", + "url": "https://api.github.com/repos/WordPress/gutenberg/labels/Accessibility%20(a11y)", + "name": "Accessibility (a11y)", + "color": "655104", "default": false, - "description": "" + "description": "Changes that impact accessibility and need corresponding review (e.g. markup changes)." } ], "state": "closed", From e05e0380903f216858bffa54f0356dfb66cddd71 Mon Sep 17 00:00:00 2001 From: Ramon Date: Tue, 25 Jul 2023 09:50:27 +1000 Subject: [PATCH 15/61] Backporting changes from https://github.com/WordPress/wordpress-develop/pull/4891 (#52878) --- .../wordpress-6.3/navigation-fallback.php | 30 ++++++++++++------- ...st-navigation-fallback-controller-test.php | 2 +- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/compat/wordpress-6.3/navigation-fallback.php b/lib/compat/wordpress-6.3/navigation-fallback.php index 5619e3204627a..5cc84f4a1c848 100644 --- a/lib/compat/wordpress-6.3/navigation-fallback.php +++ b/lib/compat/wordpress-6.3/navigation-fallback.php @@ -13,28 +13,36 @@ * Navigation Fallback REST endpoint. * * The endpoint may embed the full Navigation Menu object into the - * response as the `self` link. By default the Posts Controller - * will only exposes a limited subset of fields but the editor requires - * additional fields to be available in order to utilise the menu. + * response as the `self` link. By default, the Posts Controller + * will only expose a limited subset of fields but the editor requires + * additional fields to be available in order to utilize the menu. * * @param array $schema the schema for the `wp_navigation` post. * @return array the modified schema. */ -function gutenberg_add_fields_to_navigation_fallback_embeded_links( $schema ) { +function gutenberg_add_fields_to_navigation_fallback_embedded_links( $schema ) { // Expose top level fields. $schema['properties']['status']['context'] = array_merge( $schema['properties']['status']['context'], array( 'embed' ) ); $schema['properties']['content']['context'] = array_merge( $schema['properties']['content']['context'], array( 'embed' ) ); - // Expose sub properties of content field. - // These aren't exposed by the posts controller by default, see: - // https://github.com/WordPress/wordpress-develop/blob/5c3c6258e468c67ba00bbd13db29994f1a57a52a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L2425. + /* + * Exposes sub properties of content field. + * These sub properties aren't exposed by the posts controller by default, + * for requests where context is `embed`. + * + * @see WP_REST_Posts_Controller::get_item_schema() + */ $schema['properties']['content']['properties']['raw']['context'] = array_merge( $schema['properties']['content']['properties']['raw']['context'], array( 'embed' ) ); $schema['properties']['content']['properties']['rendered']['context'] = array_merge( $schema['properties']['content']['properties']['rendered']['context'], array( 'embed' ) ); $schema['properties']['content']['properties']['block_version']['context'] = array_merge( $schema['properties']['content']['properties']['block_version']['context'], array( 'embed' ) ); - // Expose sub properties of title field. - // These aren't exposed by the posts controller by default, see: - // https://github.com/WordPress/wordpress-develop/blob/5c3c6258e468c67ba00bbd13db29994f1a57a52a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L2401. + /* + * Exposes sub properties of title field. + * These sub properties aren't exposed by the posts controller by default, + * for requests where context is `embed`. + * + * @see WP_REST_Posts_Controller::get_item_schema() + */ $schema['properties']['title']['properties']['raw']['context'] = array_merge( $schema['properties']['title']['properties']['raw']['context'], array( 'embed' ) ); return $schema; @@ -42,5 +50,5 @@ function gutenberg_add_fields_to_navigation_fallback_embeded_links( $schema ) { add_filter( 'rest_wp_navigation_item_schema', - 'gutenberg_add_fields_to_navigation_fallback_embeded_links' + 'gutenberg_add_fields_to_navigation_fallback_embedded_links' ); diff --git a/phpunit/class-gutenberg-rest-navigation-fallback-controller-test.php b/phpunit/class-gutenberg-rest-navigation-fallback-controller-test.php index 70d84cb716951..d6df0c3c504ee 100644 --- a/phpunit/class-gutenberg-rest-navigation-fallback-controller-test.php +++ b/phpunit/class-gutenberg-rest-navigation-fallback-controller-test.php @@ -165,7 +165,7 @@ public function test_prepare_item() { * to the embedded Navigation Post, when the navigation fallback endpoint * is called with the `_embed` param. * - * @covers wp_add_fields_to_navigation_fallback_embeded_links + * @covers wp_add_fields_to_navigation_fallback_embedded_links */ public function test_embedded_navigation_post_contains_required_fields() { // First we'll use the navigation fallback to get a link to the navigation endpoint. From be21ed4dd2a193a8f93b278e1f0a159d2f68b13b Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Mon, 24 Jul 2023 17:40:50 -0700 Subject: [PATCH 16/61] Return focus more from focus return hook (#52710) --- packages/compose/README.md | 2 +- .../compose/src/hooks/use-focus-return/index.js | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/compose/README.md b/packages/compose/README.md index 0a3fed45bce8f..62ebdef6d798e 100644 --- a/packages/compose/README.md +++ b/packages/compose/README.md @@ -323,7 +323,7 @@ _Returns_ ### useFocusReturn -When opening modals/sidebars/dialogs, the focus must move to the opened area and return to the previously focused element when closed. The current hook implements the returning behavior. +Adds the unmount behavior of returning focus to the element which had it previously as is expected for roles like menus or dialogs. _Usage_ diff --git a/packages/compose/src/hooks/use-focus-return/index.js b/packages/compose/src/hooks/use-focus-return/index.js index 66751b7028d32..2cd93b279cd31 100644 --- a/packages/compose/src/hooks/use-focus-return/index.js +++ b/packages/compose/src/hooks/use-focus-return/index.js @@ -3,11 +3,12 @@ */ import { useRef, useEffect, useCallback } from '@wordpress/element'; +/** @type {Element|null} */ +let origin = null; + /** - * When opening modals/sidebars/dialogs, the focus - * must move to the opened area and return to the - * previously focused element when closed. - * The current hook implements the returning behavior. + * Adds the unmount behavior of returning focus to the element which had it + * previously as is expected for roles like menus or dialogs. * * @param {() => void} [onFocusReturn] Overrides the default return behavior. * @return {import('react').RefCallback} Element Ref. @@ -54,6 +55,7 @@ function useFocusReturn( onFocusReturn ) { ); if ( ref.current?.isConnected && ! isFocused ) { + origin ??= focusedBeforeMount.current; return; } @@ -64,10 +66,13 @@ function useFocusReturn( onFocusReturn ) { if ( onFocusReturnRef.current ) { onFocusReturnRef.current(); } else { - /** @type {null | HTMLElement} */ ( - focusedBeforeMount.current + /** @type {null|HTMLElement} */ ( + ! focusedBeforeMount.current.isConnected + ? origin + : focusedBeforeMount.current )?.focus(); } + origin = null; } }, [] ); } From 78b70adade306dc25f409f8e687ea79e8a3582d1 Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Tue, 25 Jul 2023 05:22:33 +0200 Subject: [PATCH 17/61] Disambiguate "Import" button string. (#52907) * Disambiguate "Import" button string. * Add _x to import --------- Co-authored-by: Andrew Serong <14988353+andrewserong@users.noreply.github.com> --- .../block-library/src/template-part/edit/import-controls.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/template-part/edit/import-controls.js b/packages/block-library/src/template-part/edit/import-controls.js index f629691535a32..c8f4fbc2e647d 100644 --- a/packages/block-library/src/template-part/edit/import-controls.js +++ b/packages/block-library/src/template-part/edit/import-controls.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { __, sprintf } from '@wordpress/i18n'; +import { __, _x, sprintf } from '@wordpress/i18n'; import { useMemo, useState } from '@wordpress/element'; import { useDispatch, useSelect, useRegistry } from '@wordpress/data'; import { @@ -163,7 +163,7 @@ export function TemplatePartImportControls( { area, setAttributes } ) { isBusy={ isBusy } aria-disabled={ isBusy || ! selectedSidebar } > - { __( 'Import' ) } + { _x( 'Import', 'button label' ) } From b0f3b9550800f4562b58993c5407e8cfc7355126 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Tue, 25 Jul 2023 06:04:45 +0200 Subject: [PATCH 18/61] My patterns page: Increase color contrast for the toggle group (#52678) * Update style.scss * Update style.scss --- packages/edit-site/src/components/page-patterns/style.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index ae35bcb86137b..106474f4d8f70 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -68,6 +68,9 @@ width: 300px; } } + .edit-site-patterns__sync-status-filter-option:not([aria-checked="true"]) { + color: $gray-600; + } .edit-site-patterns__sync-status-filter-option:active { background: $gray-700; color: $gray-100; From 4228be85532b9d5fda6e582a9648c3f4e895c280 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Tue, 25 Jul 2023 14:16:05 +1000 Subject: [PATCH 19/61] Check if object exists before accessing its properties. (#52870) * Check if object exists before accessing its properties. * Name anonymous functions * Update names --- .../block-library/src/footnotes/index.php | 236 +++++++++--------- 1 file changed, 116 insertions(+), 120 deletions(-) diff --git a/packages/block-library/src/footnotes/index.php b/packages/block-library/src/footnotes/index.php index ca9aca60abfb6..9815033820406 100644 --- a/packages/block-library/src/footnotes/index.php +++ b/packages/block-library/src/footnotes/index.php @@ -83,136 +83,132 @@ function register_block_core_footnotes() { } add_action( 'init', 'register_block_core_footnotes' ); -add_action( - 'wp_after_insert_post', - /** - * Saves the footnotes meta value to the revision. - * - * @since 6.3.0 - * - * @param int $revision_id The revision ID. - */ - static function( $revision_id ) { - $post_id = wp_is_post_revision( $revision_id ); - - if ( $post_id ) { +/** + * Saves the footnotes meta value to the revision. + * + * @since 6.3.0 + * + * @param int $revision_id The revision ID. + */ +function wp_save_footnotes_meta( $revision_id ) { + $post_id = wp_is_post_revision( $revision_id ); + + if ( $post_id ) { + $footnotes = get_post_meta( $post_id, 'footnotes', true ); + + if ( $footnotes ) { + // Can't use update_post_meta() because it doesn't allow revisions. + update_metadata( 'post', $revision_id, 'footnotes', $footnotes ); + } + } +} +add_action( 'wp_after_insert_post', 'wp_save_footnotes_meta' ); + +/** + * Keeps track of the revision ID for "rest_after_insert_{$post_type}". + * + * @since 6.3.0 + * + * @global int $wp_temporary_footnote_revision_id The footnote revision ID. + * + * @param int $revision_id The revision ID. + */ +function wp_keep_footnotes_revision_id( $revision_id ) { + global $wp_temporary_footnote_revision_id; + $wp_temporary_footnote_revision_id = $revision_id; +} +add_action( '_wp_put_post_revision', 'wp_keep_footnotes_revision_id' ); + +/** + * This is a specific fix for the REST API. The REST API doesn't update + * the post and post meta in one go (through `meta_input`). While it + * does fix the `wp_after_insert_post` hook to be called correctly after + * updating meta, it does NOT fix hooks such as post_updated and + * save_post, which are normally also fired after post meta is updated + * in `wp_insert_post()`. Unfortunately, `wp_save_post_revision` is + * added to the `post_updated` action, which means the meta is not + * available at the time, so we have to add it afterwards through the + * `"rest_after_insert_{$post_type}"` action. + * + * @since 6.3.0 + * + * @global int $wp_temporary_footnote_revision_id The footnote revision ID. + * + * @param WP_Post $post The post object. + */ +function wp_add_footnotes_revisions_to_post_meta( $post ) { + global $wp_temporary_footnote_revision_id; + + if ( $wp_temporary_footnote_revision_id ) { + $revision = get_post( $wp_temporary_footnote_revision_id ); + + if ( ! $revision ) { + return; + } + + $post_id = $revision->post_parent; + + // Just making sure we're updating the right revision. + if ( $post->ID === $post_id ) { $footnotes = get_post_meta( $post_id, 'footnotes', true ); if ( $footnotes ) { // Can't use update_post_meta() because it doesn't allow revisions. - update_metadata( 'post', $revision_id, 'footnotes', $footnotes ); + update_metadata( 'post', $wp_temporary_footnote_revision_id, 'footnotes', $footnotes ); } } } -); - -add_action( - '_wp_put_post_revision', - /** - * Keeps track of the revision ID for "rest_after_insert_{$post_type}". - * - * @param int $revision_id The revision ID. - */ - static function( $revision_id ) { - global $_gutenberg_revision_id; - $_gutenberg_revision_id = $revision_id; - } -); +} foreach ( array( 'post', 'page' ) as $post_type ) { - add_action( - "rest_after_insert_{$post_type}", - /** - * This is a specific fix for the REST API. The REST API doesn't update - * the post and post meta in one go (through `meta_input`). While it - * does fix the `wp_after_insert_post` hook to be called correctly after - * updating meta, it does NOT fix hooks such as post_updated and - * save_post, which are normally also fired after post meta is updated - * in `wp_insert_post()`. Unfortunately, `wp_save_post_revision` is - * added to the `post_updated` action, which means the meta is not - * available at the time, so we have to add it afterwards through the - * `"rest_after_insert_{$post_type}"` action. - * - * @since 6.3.0 - * - * @param WP_Post $post The post object. - */ - static function( $post ) { - global $_gutenberg_revision_id; - - if ( $_gutenberg_revision_id ) { - $revision = get_post( $_gutenberg_revision_id ); - $post_id = $revision->post_parent; - - // Just making sure we're updating the right revision. - if ( $post->ID === $post_id ) { - $footnotes = get_post_meta( $post_id, 'footnotes', true ); - - if ( $footnotes ) { - // Can't use update_post_meta() because it doesn't allow revisions. - update_metadata( 'post', $_gutenberg_revision_id, 'footnotes', $footnotes ); - } - } - } - } - ); + add_action( "rest_after_insert_{$post_type}", 'wp_add_footnotes_revisions_to_post_meta' ); } -add_action( - 'wp_restore_post_revision', - /** - * Restores the footnotes meta value from the revision. - * - * @since 6.3.0 - * - * @param int $post_id The post ID. - * @param int $revision_id The revision ID. - */ - static function( $post_id, $revision_id ) { - $footnotes = get_post_meta( $revision_id, 'footnotes', true ); +/** + * Restores the footnotes meta value from the revision. + * + * @since 6.3.0 + * + * @param int $post_id The post ID. + * @param int $revision_id The revision ID. + */ +function wp_restore_footnotes_from_revision( $post_id, $revision_id ) { + $footnotes = get_post_meta( $revision_id, 'footnotes', true ); - if ( $footnotes ) { - update_post_meta( $post_id, 'footnotes', $footnotes ); - } else { - delete_post_meta( $post_id, 'footnotes' ); - } - }, - 10, - 2 -); - -add_filter( - '_wp_post_revision_fields', - /** - * Adds the footnotes field to the revision. - * - * @since 6.3.0 - * - * @param array $fields The revision fields. - * @return array The revision fields. - */ - static function( $fields ) { - $fields['footnotes'] = __( 'Footnotes' ); - return $fields; + if ( $footnotes ) { + update_post_meta( $post_id, 'footnotes', $footnotes ); + } else { + delete_post_meta( $post_id, 'footnotes' ); } -); - -add_filter( - 'wp_post_revision_field_footnotes', - /** - * Gets the footnotes field from the revision. - * - * @since 6.3.0 - * - * @param string $revision_field The field value, but $revision->$field - * (footnotes) does not exist. - * @param string $field The field name, in this case "footnotes". - * @param object $revision The revision object to compare against. - * @return string The field value. - */ - static function( $revision_field, $field, $revision ) { - return get_metadata( 'post', $revision->ID, $field, true ); - }, - 10, - 3 -); +} +add_action( 'wp_restore_post_revision', 'wp_restore_footnotes_from_revision', 10, 2 ); + +/** + * Adds the footnotes field to the revision. + * + * @since 6.3.0 + * + * @param array $fields The revision fields. + * @return array The revision fields. + */ +function wp_add_footnotes_to_revision( $fields ) { + $fields['footnotes'] = __( 'Footnotes' ); + return $fields; +} +add_filter( '_wp_post_revision_fields', 'wp_add_footnotes_to_revision' ); + +/** + * Gets the footnotes field from the revision. + * + * @since 6.3.0 + * + * @param string $revision_field The field value, but $revision->$field + * (footnotes) does not exist. + * @param string $field The field name, in this case "footnotes". + * @param object $revision The revision object to compare against. + * @return string The field value. + */ +function wp_get_footnotes_from_revision( $revision_field, $field, $revision ) { + return get_metadata( 'post', $revision->ID, $field, true ); +} +add_filter( 'wp_post_revision_field_footnotes', 'wp_get_footnotes_from_revision', 10, 3 ); From b1278c6072eaa363590590d47628d30893356ecc Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Tue, 25 Jul 2023 14:21:26 +1000 Subject: [PATCH 20/61] Sidebar: Restore Back buton 'go to parent' functionality (#52910) --- .../leaf-more-menu.js | 42 ++++++++++++------- .../sidebar-navigation-screen-pages/index.js | 13 ++++-- .../sidebar-navigation-screen/index.js | 28 ++++++------- 3 files changed, 49 insertions(+), 34 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js index d714ecb43b15d..14d78405b3abe 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js @@ -23,10 +23,12 @@ import { currentlyPreviewingTheme, } from '../../utils/is-previewing-theme'; import { unlock } from '../../lock-unlock'; +import { getPathFromURL } from '../sync-state-with-url/use-sync-path-with-url'; -const { useHistory } = unlock( routerPrivateApis ); +const { useLocation, useHistory } = unlock( routerPrivateApis ); export default function LeafMoreMenu( props ) { + const location = useLocation(); const history = useHistory(); const { block } = props; const { clientId } = block; @@ -63,22 +65,32 @@ export default function LeafMoreMenu( props ) { attributes.type && history ) { - history.push( { - postType: attributes.type, - postId: attributes.id, - ...( isPreviewingTheme() && { - wp_theme_preview: currentlyPreviewingTheme(), - } ), - } ); + history.push( + { + postType: attributes.type, + postId: attributes.id, + ...( isPreviewingTheme() && { + wp_theme_preview: currentlyPreviewingTheme(), + } ), + }, + { + backPath: getPathFromURL( location.params ), + } + ); } if ( name === 'core/page-list-item' && attributes.id && history ) { - history.push( { - postType: 'page', - postId: attributes.id, - ...( isPreviewingTheme() && { - wp_theme_preview: currentlyPreviewingTheme(), - } ), - } ); + history.push( + { + postType: 'page', + postId: attributes.id, + ...( isPreviewingTheme() && { + wp_theme_preview: currentlyPreviewingTheme(), + } ), + }, + { + backPath: getPathFromURL( location.params ), + } + ); } }, [ history ] 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 331221dde7985..ff6466f98ab5f 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 @@ -28,10 +28,15 @@ import { unlock } from '../../lock-unlock'; const { useHistory } = unlock( routerPrivateApis ); const PageItem = ( { postType = 'page', postId, ...props } ) => { - const linkInfo = useLink( { - postType, - postId, - } ); + const linkInfo = useLink( + { + postType, + postId, + }, + { + backPath: '/page', + } + ); return ; }; 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 32367c32b71e9..d26cc4c7d62a0 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen/index.js @@ -11,6 +11,7 @@ import { isRTL, __, sprintf } from '@wordpress/i18n'; import { chevronRight, chevronLeft } from '@wordpress/icons'; import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; /** * Internal dependencies @@ -23,6 +24,8 @@ import { currentlyPreviewingTheme, } from '../../utils/is-previewing-theme'; +const { useLocation } = unlock( routerPrivateApis ); + export default function SidebarNavigationScreen( { isRoot, title, @@ -31,7 +34,7 @@ export default function SidebarNavigationScreen( { content, footer, description, - backPath, + backPath: backPathProp, } ) { const { dashboardLink } = useSelect( ( select ) => { const { getSettings } = unlock( select( editSiteStore ) ); @@ -40,6 +43,7 @@ export default function SidebarNavigationScreen( { }; }, [] ); const { getTheme } = useSelect( coreStore ); + const location = useLocation(); const navigator = useNavigator(); const theme = getTheme( currentlyPreviewingTheme() ); const icon = isRTL() ? chevronRight : chevronLeft; @@ -56,13 +60,17 @@ export default function SidebarNavigationScreen( { alignment="flex-start" className="edit-site-sidebar-navigation-screen__title-icon" > - { ! isRoot && ! backPath && ( + { ! isRoot && ( { - if ( navigator.location.isInitial ) { - navigator.goToParent( { replace: true } ); + const backPath = + backPathProp ?? location.state?.backPath; + if ( backPath ) { + navigator.goTo( backPath, { + isBack: true, + } ); } else { - navigator.goBack(); + navigator.goToParent(); } } } icon={ icon } @@ -70,16 +78,6 @@ export default function SidebarNavigationScreen( { showTooltip={ false } /> ) } - { ! isRoot && backPath && ( - - navigator.goTo( backPath, { isBack: true } ) - } - icon={ icon } - label={ __( 'Back' ) } - showTooltip={ false } - /> - ) } { isRoot && ( Date: Tue, 25 Jul 2023 16:46:56 +1200 Subject: [PATCH 21/61] Patterns: Allow inserting of unsynced patterns from quick inserter (#52866) --- .../block-editor/src/autocompleters/block.js | 27 ++++++++++++++----- .../src/components/block-list/block.js | 7 ++++- .../inserter/hooks/use-patterns-state.js | 2 +- .../inserter/reusable-blocks-tab.js | 5 +++- packages/block-editor/src/store/selectors.js | 14 ++-------- 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/packages/block-editor/src/autocompleters/block.js b/packages/block-editor/src/autocompleters/block.js index 4d189e52b7cdd..bc06c9de5aaaf 100644 --- a/packages/block-editor/src/autocompleters/block.js +++ b/packages/block-editor/src/autocompleters/block.js @@ -5,6 +5,7 @@ import { useSelect } from '@wordpress/data'; import { createBlock, createBlocksFromInnerBlocksTemplate, + parse, } from '@wordpress/blocks'; import { useMemo } from '@wordpress/element'; @@ -116,14 +117,28 @@ function createBlockCompleter() { return ! ( /\S/.test( before ) || /\S/.test( after ) ); }, getOptionCompletion( inserterItem ) { - const { name, initialAttributes, innerBlocks } = inserterItem; + const { + name, + initialAttributes, + innerBlocks, + syncStatus, + content, + } = inserterItem; + return { action: 'replace', - value: createBlock( - name, - initialAttributes, - createBlocksFromInnerBlocksTemplate( innerBlocks ) - ), + value: + syncStatus === 'unsynced' + ? parse( content, { + __unstableSkipMigrationLogs: true, + } ) + : createBlock( + name, + initialAttributes, + createBlocksFromInnerBlocksTemplate( + innerBlocks + ) + ), }; }, }; diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index aa1a40ef48057..a2acb3c7b53be 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -505,9 +505,14 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => { ) { __unstableMarkLastChangeAsPersistent(); } + //Unsynced patterns are nested in an array so we need to flatten them. + const replacementBlocks = + blocks?.length === 1 && Array.isArray( blocks[ 0 ] ) + ? blocks[ 0 ] + : blocks; replaceBlocks( [ ownProps.clientId ], - blocks, + replacementBlocks, indexToSelect, initialPosition ); diff --git a/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js b/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js index 6d6333587b7d0..c0b8d7ff92512 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js +++ b/packages/block-editor/src/components/inserter/hooks/use-patterns-state.js @@ -15,7 +15,7 @@ import { store as blockEditorStore } from '../../../store'; const CUSTOM_CATEGORY = { name: 'custom', label: __( 'My patterns' ), - description: __( 'Custom patterns add by site users' ), + description: __( 'Custom patterns added by site users' ), }; /** diff --git a/packages/block-editor/src/components/inserter/reusable-blocks-tab.js b/packages/block-editor/src/components/inserter/reusable-blocks-tab.js index 08cd8d57ba0d0..920b9f56384d4 100644 --- a/packages/block-editor/src/components/inserter/reusable-blocks-tab.js +++ b/packages/block-editor/src/components/inserter/reusable-blocks-tab.js @@ -22,7 +22,10 @@ function ReusableBlocksList( { onHover, onInsert, rootClientId } ) { ); const filteredItems = useMemo( () => { - return items.filter( ( { category } ) => category === 'reusable' ); + return items.filter( + ( { category, syncStatus } ) => + category === 'reusable' && syncStatus !== 'unsynced' + ); }, [ items ] ); if ( filteredItems.length === 0 ) { diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index c3d8847b03239..e459e536c9091 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2023,6 +2023,7 @@ export const getInserterItems = createSelector( utility: 1, // Deprecated. frecency, content: reusableBlock.content.raw, + syncStatus: reusableBlock.wp_pattern_sync_status, }; }; @@ -2031,18 +2032,7 @@ export const getInserterItems = createSelector( 'core/block', rootClientId ) - ? getReusableBlocks( state ) - .filter( - ( reusableBlock ) => - // Reusable blocks that are fully synced should have no sync status set - // for backwards compat between patterns and old reusable blocks, but - // some in release 16.1 may have had sync status inadvertantly set to - // 'fully' if created in the site editor. - reusableBlock.wp_pattern_sync_status === 'fully' || - reusableBlock.wp_pattern_sync_status === '' || - ! reusableBlock.wp_pattern_sync_status - ) - .map( buildReusableBlockInserterItem ) + ? getReusableBlocks( state ).map( buildReusableBlockInserterItem ) : []; const buildBlockTypeInserterItem = buildBlockTypeItem( state, { From 75638ad6cf12995b477ba5b8cb084fd2700d4def Mon Sep 17 00:00:00 2001 From: Lena Morita Date: Tue, 25 Jul 2023 13:48:12 +0900 Subject: [PATCH 22/61] ResizableFrame: Account for window resizing (#52697) * ResizableFrame: Account for window resizing * Don't memoize --------- Co-authored-by: Robert Anderson --- .../edit-site/src/components/layout/index.js | 6 ++++ .../src/components/resizable-frame/index.js | 35 ++++++------------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/packages/edit-site/src/components/layout/index.js b/packages/edit-site/src/components/layout/index.js index 17c660b94f003..1a1c2f3a3830a 100644 --- a/packages/edit-site/src/components/layout/index.js +++ b/packages/edit-site/src/components/layout/index.js @@ -340,6 +340,12 @@ export default function Layout() { ! isEditorLoading } isFullWidth={ isEditing } + defaultSize={ { + width: + canvasSize.width - + 24 /* $canvas-padding */, + height: canvasSize.height, + } } isOversized={ isResizableFrameOversized } diff --git a/packages/edit-site/src/components/resizable-frame/index.js b/packages/edit-site/src/components/resizable-frame/index.js index 2b98e46a71532..ee5c98da09033 100644 --- a/packages/edit-site/src/components/resizable-frame/index.js +++ b/packages/edit-site/src/components/resizable-frame/index.js @@ -6,7 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { useState, useRef, useEffect } from '@wordpress/element'; +import { useState, useRef } from '@wordpress/element'; import { ResizableBox, Tooltip, @@ -82,6 +82,8 @@ function ResizableFrame( { setIsOversized, isReady, children, + /** The default (unresized) width/height of the frame, based on the space availalbe in the viewport. */ + defaultSize, innerContentStyle, } ) { const [ frameSize, setFrameSize ] = useState( INITIAL_FRAME_SIZE ); @@ -95,22 +97,13 @@ function ResizableFrame( { [] ); 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( () => { - const { offsetWidth, offsetHeight } = frameRef.current.resizable; - initialComputedWidthRef.current = offsetWidth; - initialAspectRatioRef.current = offsetWidth / offsetHeight; - }, [] ); + const defaultAspectRatio = defaultSize.width / defaultSize.height; const handleResizeStart = ( _event, _direction, ref ) => { // Remember the starting width so we don't have to get `ref.offsetWidth` on @@ -126,7 +119,7 @@ function ResizableFrame( { const maxDoubledDelta = delta.width < 0 // is shrinking ? deltaAbs - : ( initialComputedWidthRef.current - startingWidth ) / 2; + : ( defaultSize.width - startingWidth ) / 2; const deltaToDouble = Math.min( deltaAbs, maxDoubledDelta ); const doubleSegment = deltaAbs === 0 ? 0 : deltaToDouble / deltaAbs; const singleSegment = 1 - doubleSegment; @@ -135,17 +128,14 @@ function ResizableFrame( { const updatedWidth = startingWidth + delta.width; - setIsOversized( updatedWidth > initialComputedWidthRef.current ); + setIsOversized( updatedWidth > defaultSize.width ); // Width will be controlled by the library (via `resizeRatio`), // so we only need to update the height. setFrameSize( { height: isOversized ? '100%' - : calculateNewHeight( - updatedWidth, - initialAspectRatioRef.current - ), + : calculateNewHeight( updatedWidth, defaultAspectRatio ), } ); }; @@ -186,15 +176,12 @@ function ResizableFrame( { FRAME_MIN_WIDTH, frameRef.current.resizable.offsetWidth + delta ), - initialComputedWidthRef.current + defaultSize.width ); setFrameSize( { width: newWidth, - height: calculateNewHeight( - newWidth, - initialAspectRatioRef.current - ), + height: calculateNewHeight( newWidth, defaultAspectRatio ), } ); }; @@ -291,9 +278,7 @@ function ResizableFrame( { undefined } aria-valuemin={ FRAME_MIN_WIDTH } - aria-valuemax={ - initialComputedWidthRef.current - } + aria-valuemax={ defaultSize.width } onKeyDown={ handleResizableHandleKeyDown } initial="hidden" exit="hidden" From 5582673dd915e63fdb6f6184c3a891914ddffe13 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Tue, 25 Jul 2023 15:16:03 +1000 Subject: [PATCH 23/61] Distraction Free: Fix conflict with showListViewByDefault preference (#52914) --- packages/edit-post/src/index.js | 6 +++++- packages/edit-site/src/store/private-actions.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index 116dea17a5c04..fc5ce8c948e2a 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -65,7 +65,11 @@ export function initializeEditor( dispatch( blocksStore ).__experimentalReapplyBlockTypeFilters(); // Check if the block list view should be open by default. - if ( select( editPostStore ).isFeatureActive( 'showListViewByDefault' ) ) { + // If `distractionFree` mode is enabled, the block list view should not be open. + if ( + select( editPostStore ).isFeatureActive( 'showListViewByDefault' ) && + ! select( editPostStore ).isFeatureActive( 'distractionFree' ) + ) { dispatch( editPostStore ).setIsListViewOpened( true ); } diff --git a/packages/edit-site/src/store/private-actions.js b/packages/edit-site/src/store/private-actions.js index 1b97959277760..f3dd4c10cec43 100644 --- a/packages/edit-site/src/store/private-actions.js +++ b/packages/edit-site/src/store/private-actions.js @@ -18,11 +18,15 @@ export const setCanvasMode = mode, } ); // Check if the block list view should be open by default. + // If `distractionFree` mode is enabled, the block list view should not be open. if ( mode === 'edit' && registry .select( preferencesStore ) - .get( 'core/edit-site', 'showListViewByDefault' ) + .get( 'core/edit-site', 'showListViewByDefault' ) && + ! registry + .select( preferencesStore ) + .get( 'core/edit-site', 'distractionFree' ) ) { dispatch.setIsListViewOpened( true ); } From 5a9a8e3bed76469950449c4828f9275ebbe56b1e Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 25 Jul 2023 09:41:37 +0400 Subject: [PATCH 24/61] Site Editor: Don't navigate to the patterns in Template Parts mode (#52884) * Site Editor: Don't navigate to the patterns in Template Parts mode * Remove leftovers * No need for check in patterns navigation sidebar * Restore only condition --- .../src/components/add-new-pattern/index.js | 2 ++ .../index.js | 8 ------ .../index.js | 10 +++++++ .../index.js | 10 +------ .../specs/site-editor/hybrid-theme.spec.js | 27 ------------------- 5 files changed, 13 insertions(+), 44 deletions(-) diff --git a/packages/edit-site/src/components/add-new-pattern/index.js b/packages/edit-site/src/components/add-new-pattern/index.js index 3879498834f42..e7d97aad5feb4 100644 --- a/packages/edit-site/src/components/add-new-pattern/index.js +++ b/packages/edit-site/src/components/add-new-pattern/index.js @@ -65,6 +65,8 @@ export default function AddNewPattern() { }, ]; + // Remove condition when command palette issues are resolved. + // See: https://github.com/WordPress/gutenberg/issues/52154. if ( ! isTemplatePartsMode ) { controls.push( { icon: symbolFilled, 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 86155c166d583..f25a13552c424 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 @@ -10,7 +10,6 @@ import { __experimentalHeading as Heading, } from '@wordpress/components'; import { useViewportMatch } from '@wordpress/compose'; -import { useSelect } from '@wordpress/data'; import { getTemplatePartIcon } from '@wordpress/editor'; import { __, sprintf } from '@wordpress/i18n'; import { getQueryArgs } from '@wordpress/url'; @@ -24,7 +23,6 @@ import SidebarNavigationItem from '../sidebar-navigation-item'; import SidebarNavigationScreen from '../sidebar-navigation-screen'; import CategoryItem from './category-item'; import { DEFAULT_CATEGORY, DEFAULT_TYPE } from '../page-patterns/utils'; -import { store as editSiteStore } from '../../store'; import { useLink } from '../routes/link'; import usePatternCategories from './use-pattern-categories'; import useMyPatterns from './use-my-patterns'; @@ -108,11 +106,6 @@ export default function SidebarNavigationScreenPatterns() { const { patternCategories, hasPatterns } = usePatternCategories(); const { myPatterns } = useMyPatterns(); - const isTemplatePartsMode = useSelect( ( select ) => { - const settings = select( editSiteStore ).getSettings(); - return !! settings.supportsTemplatePartsMode; - }, [] ); - const templatePartsLink = useLink( { path: '/wp_template_part/all' } ); const footer = ! isMobileViewport ? ( @@ -131,7 +124,6 @@ export default function SidebarNavigationScreenPatterns() { return ( { + const settings = select( editSiteStore ).getSettings(); + + return !! settings.supportsTemplatePartsMode; + }, [] ); + return ( { @@ -31,11 +29,6 @@ const TemplateItem = ( { postType, postId, ...props } ) => { export default function SidebarNavigationScreenTemplates() { const isMobileViewport = useViewportMatch( 'medium', '<' ); - const isTemplatePartsMode = useSelect( ( select ) => { - const settings = select( editSiteStore ).getSettings(); - - return !! settings.supportsTemplatePartsMode; - }, [] ); const { records: templates, isResolving: isLoading } = useEntityRecords( 'postType', @@ -51,10 +44,9 @@ export default function SidebarNavigationScreenTemplates() { ); const browseAllLink = useLink( { path: '/wp_template/all' } ); - const canCreate = ! isMobileViewport && ! isTemplatePartsMode; + const canCreate = ! isMobileViewport; return ( { await requestUtils.activateTheme( 'twentytwentyone' ); } ); - test( 'should not show the Add Template Part button', async ( { - admin, - page, - } ) => { - await admin.visitAdminPage( - '/site-editor.php', - 'postType=wp_template_part&path=/wp_template_part/all' - ); - - await expect( - page.locator( 'role=button[name="Add New Template Part"i]' ) - ).toBeHidden(); - - // Back to Patterns page. - await page.click( - 'role=region[name="Navigation"i] >> role=button[name="Back"i]' - ); - - await page.click( - 'role=region[name="Navigation"i] >> role=button[name="Create pattern"i]' - ); - - await expect( - page.locator( 'role=menuitem[name="Create template part"i]' ) - ).toBeHidden(); - } ); - test( 'can access template parts list page', async ( { admin, page } ) => { await admin.visitAdminPage( 'site-editor.php', From b417786e36d784d8bd3df5df25bea039070873ea Mon Sep 17 00:00:00 2001 From: Hiroshi Urabe Date: Tue, 25 Jul 2023 15:02:45 +0900 Subject: [PATCH 25/61] PreventDefault when isComposing is true. apply patch from t-hamano. (#52844) see: https://github.com/WordPress/gutenberg/issues/52821#issuecomment-1645523477 --- .../commands/src/components/command-menu.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/commands/src/components/command-menu.js b/packages/commands/src/components/command-menu.js index b4a828f34303d..aa77925763007 100644 --- a/packages/commands/src/components/command-menu.js +++ b/packages/commands/src/components/command-menu.js @@ -201,6 +201,20 @@ export function CommandMenu() { if ( ! isOpen ) { return false; } + + const onKeyDown = ( event ) => { + if ( + // Ignore keydowns from IMEs + event.nativeEvent.isComposing || + // Workaround for Mac Safari where the final Enter/Backspace of an IME composition + // is `isComposing=false`, even though it's technically still part of the composition. + // These can only be detected by keyCode. + event.keyCode === 229 + ) { + event.preventDefault(); + } + }; + const isLoading = Object.values( loaders ).some( Boolean ); return ( @@ -211,7 +225,10 @@ export function CommandMenu() { __experimentalHideHeader >
- +
Date: Tue, 25 Jul 2023 10:06:35 +0400 Subject: [PATCH 26/61] Site Editor: Open template parts in view canvas view mode (#52916) --- packages/edit-site/src/components/page-template-parts/index.js | 1 - 1 file changed, 1 deletion(-) 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 4eaea0944b5fd..e03d726a57525 100644 --- a/packages/edit-site/src/components/page-template-parts/index.js +++ b/packages/edit-site/src/components/page-template-parts/index.js @@ -39,7 +39,6 @@ export default function PageTemplateParts() { params={ { postId: templatePart.id, postType: templatePart.type, - canvas: 'edit', } } state={ { backPath: '/wp_template_part/all' } } > From 3535bb89d53c2120806a6a530e4b36b43b4a0736 Mon Sep 17 00:00:00 2001 From: Bernie Reiter <96308+ockham@users.noreply.github.com> Date: Tue, 25 Jul 2023 11:52:39 +0200 Subject: [PATCH 27/61] Template Part Block: Use `get_block_file_template` for rendering (#52892) Change the Template Part block's render method (`render_block_core_template_part`) to use `get_block_file_template` rather than a number of lower-level functions. Higher-level functions such as [`get_block_file_template`](https://developer.wordpress.org/reference/functions/get_block_file_template/) apply filters (such as the eponymous [`get_block_file_template`](https://developer.wordpress.org/reference/hooks/get_block_file_template/)) that otherwise aren't invoked. Furthermore, it can be argued that if a high-level function is available to what otherwise requires calling a number of lower-level functions, it's preferable to use the high-level function (unless there are side effects and/or performance issues, of course) . --- packages/block-library/src/template-part/index.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/block-library/src/template-part/index.php b/packages/block-library/src/template-part/index.php index 1066aa0141915..cb4c2228b3e0c 100644 --- a/packages/block-library/src/template-part/index.php +++ b/packages/block-library/src/template-part/index.php @@ -67,14 +67,11 @@ function render_block_core_template_part( $attributes ) { // Else, if the template part was provided by the active theme, // render the corresponding file content. if ( 0 === validate_file( $attributes['slug'] ) ) { - $block_template_file = _get_block_template_file( 'wp_template_part', $attributes['slug'] ); - if ( $block_template_file ) { - $template_part_file_path = $block_template_file['path']; - $content = (string) file_get_contents( $template_part_file_path ); - $content = '' !== $content ? _inject_theme_attribute_in_block_template_content( $content ) : ''; - if ( isset( $block_template_file['area'] ) ) { - $area = $block_template_file['area']; - } + $block_template = get_block_file_template( $template_part_id, 'wp_template_part' ); + + $content = $block_template->content; + if ( isset( $block_template->area ) ) { + $area = $block_template->area; } } From d18d70b8830af66e644feddb09deda62e396a95f Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 25 Jul 2023 15:04:50 +0400 Subject: [PATCH 28/61] Verse: Enable the line breaks (#52928) This reverts commit 50722cc8f8b11c3b754d86fde849e16325e471fb. --- packages/block-library/src/verse/edit.js | 5 ----- packages/block-library/src/verse/test/edit.native.js | 6 ++---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/block-library/src/verse/edit.js b/packages/block-library/src/verse/edit.js index 4ff910c8c2fa0..1ca846fb848b5 100644 --- a/packages/block-library/src/verse/edit.js +++ b/packages/block-library/src/verse/edit.js @@ -13,7 +13,6 @@ import { AlignmentToolbar, useBlockProps, } from '@wordpress/block-editor'; -import { createBlock, getDefaultBlockName } from '@wordpress/blocks'; export default function VerseEdit( { attributes, @@ -21,7 +20,6 @@ export default function VerseEdit( { mergeBlocks, onRemove, style, - insertBlocksAfter, } ) { const { textAlign, content } = attributes; const blockProps = useBlockProps( { @@ -58,9 +56,6 @@ export default function VerseEdit( { textAlign={ textAlign } { ...blockProps } __unstablePastePlainText - __unstableOnSplitAtEnd={ () => - insertBlocksAfter( createBlock( getDefaultBlockName() ) ) - } /> ); diff --git a/packages/block-library/src/verse/test/edit.native.js b/packages/block-library/src/verse/test/edit.native.js index aa7cedbbbfd46..bbdacbb90366a 100644 --- a/packages/block-library/src/verse/test/edit.native.js +++ b/packages/block-library/src/verse/test/edit.native.js @@ -64,15 +64,13 @@ describe( 'Verse block', () => { const verseTextInput = await screen.findByPlaceholderText( 'Write verse…' ); - typeInRichText( verseTextInput, 'A great statement.Again', { - finalSelectionStart: 18, - finalSelectionEnd: 18, - } ); + typeInRichText( verseTextInput, 'A great statement.' ); fireEvent( verseTextInput, 'onKeyDown', { nativeEvent: {}, preventDefault() {}, keyCode: ENTER, } ); + typeInRichText( verseTextInput, 'Again' ); // Assert expect( getEditorHtml() ).toMatchInlineSnapshot( ` From cc639910ef6229c0f89cd417e883c044eb6921c1 Mon Sep 17 00:00:00 2001 From: Joen A <1204802+jasmussen@users.noreply.github.com> Date: Tue, 25 Jul 2023 15:43:14 +0200 Subject: [PATCH 29/61] Command Palette: Remove double border on results pages. (#52873) * Command Palette: Remove double border on results pages. * Restore the rule but tweak it. * Use variables. --- packages/commands/src/components/style.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/commands/src/components/style.scss b/packages/commands/src/components/style.scss index 11114cce856ba..b4545b6ee3844 100644 --- a/packages/commands/src/components/style.scss +++ b/packages/commands/src/components/style.scss @@ -116,8 +116,8 @@ position: relative; } - [cmdk-group]:has([cmdk-group-items]:not(:empty)) + [cmdk-group]:has([cmdk-group-items]:not(:empty)) { - border-top: 1px solid $gray-200; + [cmdk-group]:has([cmdk-group-items]:not(:empty)):not([hidden]) + [cmdk-group]:has([cmdk-group-items]:not(:empty)) { + border-top: $border-width solid $gray-200; } } From 563402f9209b88e41880984160868c57926c8c8f Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 25 Jul 2023 17:53:38 +0400 Subject: [PATCH 30/61] Site Editor: Use the correct icon for Patterns in sidebar card (#52931) --- .../sidebar-edit-mode/template-panel/index.js | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js b/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js index 2fcdcbb2c7e1b..a76626d247af9 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/template-panel/index.js @@ -7,7 +7,7 @@ import { store as editorStore } from '@wordpress/editor'; import { store as coreStore } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; import { __ } from '@wordpress/i18n'; -import { navigation as navigationIcon } from '@wordpress/icons'; +import { navigation, symbol } from '@wordpress/icons'; /** * Internal dependencies @@ -18,11 +18,13 @@ import TemplateAreas from './template-areas'; import LastRevision from './last-revision'; import SidebarCard from '../sidebar-card'; +const CARD_ICONS = { + wp_block: symbol, + wp_navigation: navigation, +}; + export default function TemplatePanel() { - const { - info: { title, description, icon }, - record, - } = useSelect( ( select ) => { + const { title, description, icon, record } = useSelect( ( select ) => { const { getEditedPostType, getEditedPostId } = select( editSiteStore ); const { getEditedEntityRecord } = select( coreStore ); const { __experimentalGetTemplateInfo: getTemplateInfo } = @@ -31,10 +33,14 @@ export default function TemplatePanel() { const postType = getEditedPostType(); const postId = getEditedPostId(); const _record = getEditedEntityRecord( 'postType', postType, postId ); + const info = getTemplateInfo( _record ); - const info = _record ? getTemplateInfo( _record ) : {}; - - return { info, record: _record }; + return { + title: info.title, + description: info.description, + icon: info.icon, + record: _record, + }; }, [] ); if ( ! title && ! description ) { @@ -46,9 +52,7 @@ export default function TemplatePanel() { } > From eead5314c342c9dab3c191e3968993fb620b720d Mon Sep 17 00:00:00 2001 From: James Koster Date: Tue, 25 Jul 2023 15:24:50 +0100 Subject: [PATCH 31/61] Patterns grid: Switch to three column layout on `huge` screens (#52927) --- packages/edit-site/src/components/page-patterns/style.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index 106474f4d8f70..99c4c4c04757e 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -131,6 +131,9 @@ @include break-large { grid-template-columns: 1fr 1fr; } + @include break-huge { + grid-template-columns: 1fr 1fr 1fr; + } .edit-site-patterns__pattern { break-inside: avoid-column; display: flex; From 775c751c5f2b63ccc45648232696cec6d90b4440 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Tue, 25 Jul 2023 15:48:51 +0100 Subject: [PATCH 32/61] Navigation Sidebar: Fetch the blocks from the content when trying to load navigations (#52899) * Parse blocks from the entity * move parsing lower for performance * Update packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js Co-authored-by: Dave Smith --------- Co-authored-by: Dave Smith --- .../use-navigation-menu-content.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js index 73d0905f4001a..0a5de0a6274d4 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-navigation-menu-content.js @@ -1,3 +1,8 @@ +/** + * WordPress dependencies + */ +import { parse } from '@wordpress/blocks'; + /** * Internal dependencies */ @@ -54,9 +59,14 @@ export default function useNavigationMenuContent( postType, postId ) { return; } + const blocks = + record?.content && typeof record.content !== 'function' + ? parse( record.content ) + : []; + const navigationBlocks = getBlocksOfTypeFromBlocks( 'core/navigation', - record?.blocks + blocks ); if ( ! navigationBlocks.length ) { From f6834cc8e3570b7d41eaa762bbea909fa9efc1db Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Tue, 25 Jul 2023 23:52:47 +0900 Subject: [PATCH 33/61] ColorPalette, BorderControl: Don't hyphenate HEX value in `aria-label` (#52932) * ColorPalette, BorderControl: Don't hyphenate HEX value in `aria-label` * Update changelog * Update changelog --- .../test/__snapshots__/control.js.snap | 2 +- packages/components/CHANGELOG.md | 4 ++++ .../border-control-dropdown/component.tsx | 18 +++++++----------- .../src/border-control/test/index.js | 12 ++++++------ .../components/src/color-palette/index.tsx | 4 ++-- .../test/__snapshots__/index.tsx.snap | 2 +- .../src/color-palette/test/index.tsx | 6 +----- 7 files changed, 22 insertions(+), 26 deletions(-) diff --git a/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap b/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap index 93ff7de880717..039d35937294b 100644 --- a/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap +++ b/packages/block-editor/src/components/color-palette/test/__snapshots__/control.js.snap @@ -172,7 +172,7 @@ exports[`ColorPaletteControl matches the snapshot 1`] = ` + { isShown && ( + setIsShown( false ) }> +

Modal content

+
+ ) } +
+ ); + }; + render( ); + + const opener = screen.getByRole( 'button' ); + await user.click( opener ); + const modalFrame = screen.getByRole( 'dialog' ); + expect( modalFrame ).toHaveFocus(); + + // Disable reason: No semantic query can reach the overlay. + // eslint-disable-next-line testing-library/no-node-access + await user.click( modalFrame.parentElement! ); + expect( opener ).toHaveFocus(); + } ); } ); From 6cf6643284ef8b5116e286ad07f260ddfadb0909 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 25 Jul 2023 15:10:15 -0700 Subject: [PATCH 38/61] Use defer loading strategy for frontend view scripts (#52536) * Load interactivity API scripts with defer loading strategy * Load comment-reply script with defer loading strategy * Load search view script with defer loading strategy * Defer the view scripts for the Navigation and File blocks * Use DCA event instead of window load event for Navigation viewScripts * Optimize submenu-on-click view script * Fully leverage event delegation to allow async loading strategy. * Keep track of whether a submenu is open to short-circuit event handlers. * Use passive event listeners. * Optimize modal view script * Fully leverage event delegation to allow async loading strategy. * Remove event listener for anchor clicks in modal when modal is closed. * Use passive event listeners. * Utilize asset file for wp-block--search-view * Conditionally enqueue navigation view scripts only if needed * Update Navigation block to remove/add view scripts in the same way the File block does * Update Search block to include view script in same way as File block and Navigation block * Refactor Search block view script * Adopt asynchronous loading strategy. * Use event delegation. * Collapse expanded blocks when tabbing out of expanded Search block. * Only attach keydown/keyup event handlers while Search block is expanded. * Ensure search button's aria-label is translated. * Ensure Search button's type is restored to 'button' instead of deleting type (since no type is same as 'submit'). * Use passive event listeners. * Use more DOM properties instead of attributes * Use more precise reflected terminology * Remove resolved TODO comment * Use event delegation to open MicroModal * Remove excessive type check * Fix closing submenus when there are multiple Navigation blocks on a page * Clarify block.json comment * Add core merge note for comment-reply script strategy * Use defer strategy instead of async for Navigation and Search blocks * Defer all block view scripts * Ensure interactivity scripts remain in footer in WP<6.3 * Remove defer from comment-reply for now Co-authored-by: Felix Arntz --------- Co-authored-by: Felix Arntz --- lib/blocks.php | 34 +++ .../interactivity-api/scripts.php | 24 +- packages/block-library/src/file/index.php | 2 +- .../block-library/src/navigation/index.php | 40 ++- .../src/navigation/view-modal.js | 127 +++++++--- packages/block-library/src/navigation/view.js | 105 +++++--- packages/block-library/src/search/index.php | 2 + packages/block-library/src/search/view.js | 238 +++++++++++++----- 8 files changed, 410 insertions(+), 162 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index e98f711b5c85a..f160d2a7080d3 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -172,6 +172,40 @@ function gutenberg_reregister_core_block_types() { add_action( 'init', 'gutenberg_reregister_core_block_types' ); +/** + * Adds the defer loading strategy to all registered blocks. + * + * This function would not be part of core merge. Instead, the register_block_script_handle() function would be patched + * as follows. + * + * ``` + * --- a/wp-includes/blocks.php + * +++ b/wp-includes/blocks.php + * @ @ -153,7 +153,8 @ @ function register_block_script_handle( $metadata, $field_name, $index = 0 ) { + * $script_handle, + * $script_uri, + * $script_dependencies, + * - isset( $script_asset['version'] ) ? $script_asset['version'] : false + * + isset( $script_asset['version'] ) ? $script_asset['version'] : false, + * + array( 'strategy' => 'defer' ) + * ); + * if ( ! $result ) { + * return false; + * ``` + * + * @see register_block_script_handle() + */ +function gutenberg_defer_block_view_scripts() { + $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); + foreach ( $block_types as $block_type ) { + foreach ( $block_type->view_script_handles as $view_script_handle ) { + wp_script_add_data( $view_script_handle, 'strategy', 'defer' ); + } + } +} + +add_action( 'init', 'gutenberg_defer_block_view_scripts', 100 ); + /** * Deregisters the existing core block type and its assets. * diff --git a/lib/experimental/interactivity-api/scripts.php b/lib/experimental/interactivity-api/scripts.php index e95bf518c75f7..ed1fca8550070 100644 --- a/lib/experimental/interactivity-api/scripts.php +++ b/lib/experimental/interactivity-api/scripts.php @@ -7,20 +7,32 @@ */ /** - * Move interactive scripts to the footer. This is a temporary measure to make - * it work with `wp_store` and it should be replaced with deferred scripts or - * modules. + * Makes sure that interactivity scripts execute after all `wp_store` directives have been printed to the page. + * + * In WordPress 6.3+ this is achieved by printing in the head but marking the scripts with defer. This has the benefit + * of early discovery so the script is loaded by the browser, while at the same time not blocking rendering. In older + * versions of WordPress, this is achieved by loading the scripts in the footer. + * + * @link https://make.wordpress.org/core/2023/07/14/registering-scripts-with-async-and-defer-attributes-in-wordpress-6-3/ */ function gutenberg_interactivity_move_interactive_scripts_to_the_footer() { - // Move the @wordpress/interactivity package to the footer. - wp_script_add_data( 'wp-interactivity', 'group', 1 ); + $supports_defer = version_compare( strtok( get_bloginfo( 'version' ), '-' ), '6.3', '>=' ); + if ( $supports_defer ) { + // Defer execution of @wordpress/interactivity package but continue loading in head. + wp_script_add_data( 'wp-interactivity', 'strategy', 'defer' ); + wp_script_add_data( 'wp-interactivity', 'group', 0 ); + } else { + // Move the @wordpress/interactivity package to the footer. + wp_script_add_data( 'wp-interactivity', 'group', 1 ); + } // Move all the view scripts of the interactive blocks to the footer. $registered_blocks = \WP_Block_Type_Registry::get_instance()->get_all_registered(); foreach ( array_values( $registered_blocks ) as $block ) { if ( isset( $block->supports['interactivity'] ) && $block->supports['interactivity'] ) { foreach ( $block->view_script_handles as $handle ) { - wp_script_add_data( $handle, 'group', 1 ); + // Note that all block view scripts are already made defer by default. + wp_script_add_data( $handle, 'group', $supports_defer ? 0 : 1 ); } } } diff --git a/packages/block-library/src/file/index.php b/packages/block-library/src/file/index.php index 621f07b5b88ee..7dd77b20f466c 100644 --- a/packages/block-library/src/file/index.php +++ b/packages/block-library/src/file/index.php @@ -24,7 +24,7 @@ function gutenberg_block_core_file_update_interactive_view_script( $metadata ) { } /** - * When the `core/file` block is rendering, check if we need to enqueue the `'wp-block-file-view` script. + * When the `core/file` block is rendering, check if we need to enqueue the `wp-block-file-view` script. * * @param array $attributes The block attributes. * @param string $content The block content. diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index 79988c3ee350b..52376aba0d44e 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -671,19 +671,33 @@ function render_block_core_navigation( $attributes, $content, $block ) { $inner_blocks_html .= ''; } - // If the script already exists, there is no point in removing it from viewScript. - $should_load_view_script = ( $is_responsive_menu || ( $has_submenus && ( $attributes['openSubmenusOnClick'] || $attributes['showSubmenuIcon'] ) ) ); - $view_js_file = 'wp-block-navigation-view'; - if ( ! wp_script_is( $view_js_file ) ) { - $script_handles = $block->block_type->view_script_handles; - - // If the script is not needed, and it is still in the `view_script_handles`, remove it. - if ( ! $should_load_view_script && in_array( $view_js_file, $script_handles, true ) ) { - $block->block_type->view_script_handles = array_diff( $script_handles, array( $view_js_file, 'wp-block-navigation-view-2' ) ); - } - // If the script is needed, but it was previously removed, add it again. - if ( $should_load_view_script && ! in_array( $view_js_file, $script_handles, true ) ) { - $block->block_type->view_script_handles = array_merge( $script_handles, array( $view_js_file, 'wp-block-navigation-view-2' ) ); + $needed_script_map = array( + 'wp-block-navigation-view' => ( $has_submenus && ( $attributes['openSubmenusOnClick'] || $attributes['showSubmenuIcon'] ) ), + 'wp-block-navigation-view-2' => $is_responsive_menu, + ); + + $should_load_view_script = false; + if ( gutenberg_should_block_use_interactivity_api( 'core/navigation' ) ) { + // TODO: The script is still loaded even when it isn't needed when the Interactivity API is used. + $should_load_view_script = count( array_filter( $needed_script_map ) ) > 0; + } else { + foreach ( $needed_script_map as $view_script_handle => $is_view_script_needed ) { + + // If the script already exists, there is no point in removing it from viewScript. + if ( wp_script_is( $view_script_handle ) ) { + continue; + } + + $script_handles = $block->block_type->view_script_handles; + + // If the script is not needed, and it is still in the `view_script_handles`, remove it. + if ( ! $is_view_script_needed && in_array( $view_script_handle, $script_handles, true ) ) { + $block->block_type->view_script_handles = array_diff( $script_handles, array( $view_script_handle ) ); + } + // If the script is needed, but it was previously removed, add it again. + if ( $is_view_script_needed && ! in_array( $view_script_handle, $script_handles, true ) ) { + $block->block_type->view_script_handles = array_merge( $script_handles, array( $view_script_handle ) ); + } } } diff --git a/packages/block-library/src/navigation/view-modal.js b/packages/block-library/src/navigation/view-modal.js index 9477d262816d9..62de6e8808bf0 100644 --- a/packages/block-library/src/navigation/view-modal.js +++ b/packages/block-library/src/navigation/view-modal.js @@ -1,16 +1,22 @@ +/*eslint-env browser*/ /** * External dependencies */ import MicroModal from 'micromodal'; // Responsive navigation toggle. -function navigationToggleModal( modal ) { + +/** + * Toggles responsive navigation. + * + * @param {HTMLDivElement} modal + * @param {boolean} isHidden + */ +function navigationToggleModal( modal, isHidden ) { const dialogContainer = modal.querySelector( `.wp-block-navigation__responsive-dialog` ); - const isHidden = 'true' === modal.getAttribute( 'aria-hidden' ); - modal.classList.toggle( 'has-modal-open', ! isHidden ); dialogContainer.toggleAttribute( 'aria-modal', ! isHidden ); @@ -23,10 +29,15 @@ function navigationToggleModal( modal ) { } // Add a class to indicate the modal is open. - const htmlElement = document.documentElement; - htmlElement.classList.toggle( 'has-modal-open' ); + document.documentElement.classList.toggle( 'has-modal-open' ); } +/** + * Checks whether the provided link is an anchor on the current page. + * + * @param {HTMLAnchorElement} node + * @return {boolean} Is anchor. + */ function isLinkToAnchorOnCurrentPage( node ) { return ( node.hash && @@ -37,42 +48,80 @@ function isLinkToAnchorOnCurrentPage( node ) { ); } -window.addEventListener( 'load', () => { - MicroModal.init( { - onShow: navigationToggleModal, - onClose: navigationToggleModal, - openClass: 'is-menu-open', +/** + * Handles effects after opening the modal. + * + * @param {HTMLDivElement} modal + */ +function onShow( modal ) { + navigationToggleModal( modal, false ); + modal.addEventListener( 'click', handleAnchorLinkClicksInsideModal, { + passive: true, } ); +} - // Close modal automatically on clicking anchor links inside modal. - const navigationLinks = document.querySelectorAll( - '.wp-block-navigation-item__content' - ); +/** + * Handles effects after closing the modal. + * + * @param {HTMLDivElement} modal + */ +function onClose( modal ) { + navigationToggleModal( modal, true ); + modal.removeEventListener( 'click', handleAnchorLinkClicksInsideModal, { + passive: true, + } ); +} - navigationLinks.forEach( function ( link ) { - // Ignore non-anchor links and anchor links which open on a new tab. - if ( - ! isLinkToAnchorOnCurrentPage( link ) || - link.attributes?.target === '_blank' - ) { - return; - } +/** + * Handle clicks to anchor links in modal using event delegation by closing modal automatically + * + * @param {UIEvent} event + */ +function handleAnchorLinkClicksInsideModal( event ) { + const link = event.target.closest( '.wp-block-navigation-item__content' ); + if ( ! ( link instanceof HTMLAnchorElement ) ) { + return; + } - // Find the specific parent modal for this link - // since .close() won't work without an ID if there are - // multiple navigation menus in a post/page. - const modal = link.closest( - '.wp-block-navigation__responsive-container' - ); - const modalId = modal?.getAttribute( 'id' ); + // Ignore non-anchor links and anchor links which open on a new tab. + if ( + ! isLinkToAnchorOnCurrentPage( link ) || + link.attributes?.target === '_blank' + ) { + return; + } - link.addEventListener( 'click', () => { - // check if modal exists and is open before trying to close it - // otherwise Micromodal will toggle the `has-modal-open` class - // on the html tag which prevents scrolling - if ( modalId && modal.classList.contains( 'has-modal-open' ) ) { - MicroModal.close( modalId ); - } - } ); - } ); -} ); + // Find the specific parent modal for this link + // since .close() won't work without an ID if there are + // multiple navigation menus in a post/page. + const modal = link.closest( '.wp-block-navigation__responsive-container' ); + const modalId = modal?.getAttribute( 'id' ); + if ( ! modalId ) { + return; + } + + // check if modal exists and is open before trying to close it + // otherwise Micromodal will toggle the `has-modal-open` class + // on the html tag which prevents scrolling + if ( modalId && modal.classList.contains( 'has-modal-open' ) ) { + MicroModal.close( modalId ); + } +} + +// MicroModal.init() does not support event delegation for the open trigger, so here MicroModal.show() is called manually. +document.addEventListener( + 'click', + ( event ) => { + /** @type {HTMLElement} */ + const target = event.target; + + if ( target.dataset.micromodalTrigger ) { + MicroModal.show( target.dataset.micromodalTrigger, { + onShow, + onClose, + openClass: 'is-menu-open', + } ); + } + }, + { passive: true } +); diff --git a/packages/block-library/src/navigation/view.js b/packages/block-library/src/navigation/view.js index 19805a44ae4ae..d808d1707d5bf 100644 --- a/packages/block-library/src/navigation/view.js +++ b/packages/block-library/src/navigation/view.js @@ -1,62 +1,94 @@ +/*eslint-env browser*/ // Open on click functionality. -function closeSubmenus( element ) { - element + +/** + * Keep track of whether a submenu is open to short-circuit delegated event listeners. + * + * @type {boolean} + */ +let hasOpenSubmenu = false; + +/** + * Close submenu items for a navigation item. + * + * @param {HTMLElement} navigationItem - Either a NAV or LI element. + */ +function closeSubmenus( navigationItem ) { + navigationItem .querySelectorAll( '[aria-expanded="true"]' ) .forEach( function ( toggle ) { toggle.setAttribute( 'aria-expanded', 'false' ); } ); + hasOpenSubmenu = false; } -function toggleSubmenuOnClick( event ) { - const buttonToggle = event.target.closest( '[aria-expanded]' ); - const isSubmenuOpen = buttonToggle.getAttribute( 'aria-expanded' ); +/** + * Toggle submenu on click. + * + * @param {HTMLButtonElement} buttonToggle + */ +function toggleSubmenuOnClick( buttonToggle ) { + const isSubmenuOpen = + buttonToggle.getAttribute( 'aria-expanded' ) === 'true'; + const navigationItem = buttonToggle.closest( '.wp-block-navigation-item' ); - if ( isSubmenuOpen === 'true' ) { - closeSubmenus( buttonToggle.closest( '.wp-block-navigation-item' ) ); + if ( isSubmenuOpen ) { + closeSubmenus( navigationItem ); } else { // Close all sibling submenus. - const parentElement = buttonToggle.closest( - '.wp-block-navigation-item' - ); const navigationParent = buttonToggle.closest( '.wp-block-navigation__submenu-container, .wp-block-navigation__container, .wp-block-page-list' ); navigationParent .querySelectorAll( '.wp-block-navigation-item' ) - .forEach( function ( child ) { - if ( child !== parentElement ) { + .forEach( ( child ) => { + if ( child !== navigationItem ) { closeSubmenus( child ); } } ); + // Open submenu. buttonToggle.setAttribute( 'aria-expanded', 'true' ); + hasOpenSubmenu = true; } } -// Necessary for some themes such as TT1 Blocks, where -// scripts could be loaded before the body. -window.addEventListener( 'load', () => { - const submenuButtons = document.querySelectorAll( - '.wp-block-navigation-submenu__toggle' - ); +// Open on button click or close on click outside. +document.addEventListener( + 'click', + function ( event ) { + const target = event.target; + const button = target.closest( '.wp-block-navigation-submenu__toggle' ); - submenuButtons.forEach( function ( button ) { - button.addEventListener( 'click', toggleSubmenuOnClick ); - } ); + // Close any other open submenus. + if ( hasOpenSubmenu ) { + const navigationBlocks = document.querySelectorAll( + '.wp-block-navigation' + ); + navigationBlocks.forEach( function ( block ) { + if ( ! block.contains( target ) ) { + closeSubmenus( block ); + } + } ); + } + + // Now open the submenu if one was clicked. + if ( button instanceof HTMLButtonElement ) { + toggleSubmenuOnClick( button ); + } + }, + { passive: true } +); + +// Close on focus outside or escape key. +document.addEventListener( + 'keyup', + function ( event ) { + // Abort if there aren't any submenus open anyway. + if ( ! hasOpenSubmenu ) { + return; + } - // Close on click outside. - document.addEventListener( 'click', function ( event ) { - const navigationBlocks = document.querySelectorAll( - '.wp-block-navigation' - ); - navigationBlocks.forEach( function ( block ) { - if ( ! block.contains( event.target ) ) { - closeSubmenus( block ); - } - } ); - } ); - // Close on focus outside or escape key. - document.addEventListener( 'keyup', function ( event ) { const submenuBlocks = document.querySelectorAll( '.wp-block-navigation-item.has-child' ); @@ -70,5 +102,6 @@ window.addEventListener( 'load', () => { toggle?.focus(); } } ); - } ); -} ); + }, + { passive: true } +); diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php index b0fed54d2f385..670ceb0eb66c5 100644 --- a/packages/block-library/src/search/index.php +++ b/packages/block-library/src/search/index.php @@ -140,8 +140,10 @@ function render_block_core_search( $attributes, $content, $block ) { $button->add_class( implode( ' ', $button_classes ) ); if ( 'expand-searchfield' === $attributes['buttonBehavior'] && 'button-only' === $attributes['buttonPosition'] ) { $button->set_attribute( 'aria-label', __( 'Expand search field' ) ); + $button->set_attribute( 'data-toggled-aria-label', __( 'Submit Search' ) ); $button->set_attribute( 'aria-controls', 'wp-block-search__input-' . $input_id ); $button->set_attribute( 'aria-expanded', 'false' ); + $button->set_attribute( 'type', 'button' ); // Will be set to submit after clicking. } else { $button->set_attribute( 'aria-label', wp_strip_all_tags( $attributes['buttonText'] ) ); } diff --git a/packages/block-library/src/search/view.js b/packages/block-library/src/search/view.js index 0909121b25bf0..5aaf1dd1ef3ad 100644 --- a/packages/block-library/src/search/view.js +++ b/packages/block-library/src/search/view.js @@ -1,68 +1,172 @@ -window.addEventListener( 'DOMContentLoaded', () => { - const hiddenClass = 'wp-block-search__searchfield-hidden'; - - Array.from( - document.getElementsByClassName( - 'wp-block-search__button-behavior-expand' - ) - ).forEach( ( block ) => { - const searchField = block.querySelector( '.wp-block-search__input' ); - const searchButton = block.querySelector( '.wp-block-search__button' ); - const searchLabel = block.querySelector( '.wp-block-search__label' ); - const ariaLabel = searchButton.getAttribute( 'aria-label' ); - const id = searchField.getAttribute( 'id' ); - - const toggleSearchField = ( showSearchField ) => { - if ( showSearchField ) { - searchField.removeAttribute( 'aria-hidden' ); - searchField.removeAttribute( 'tabindex' ); - searchButton.removeAttribute( 'aria-expanded' ); - searchButton.removeAttribute( 'aria-controls' ); - searchButton.setAttribute( 'type', 'submit' ); - searchButton.setAttribute( 'aria-label', 'Submit Search' ); - - return block.classList.remove( hiddenClass ); - } - - searchButton.removeAttribute( 'type' ); - searchField.setAttribute( 'aria-hidden', 'true' ); - searchField.setAttribute( 'tabindex', '-1' ); - searchButton.setAttribute( 'aria-expanded', 'false' ); - searchButton.setAttribute( 'aria-controls', id ); - searchButton.setAttribute( 'aria-label', ariaLabel ); - return block.classList.add( hiddenClass ); - }; - - const hideSearchField = ( e ) => { - if ( ! e.target.closest( '.wp-block-search' ) ) { - return toggleSearchField( false ); - } - - if ( e.key === 'Escape' ) { - searchButton.focus(); - return toggleSearchField( false ); - } - }; - - const handleButtonClick = ( e ) => { - if ( block.classList.contains( hiddenClass ) ) { - e.preventDefault(); - searchField.focus(); - toggleSearchField( true ); - } - }; - - searchButton.removeAttribute( 'type' ); - searchField.addEventListener( 'keydown', ( e ) => { - hideSearchField( e ); - } ); - searchButton.addEventListener( 'click', handleButtonClick ); - searchButton.addEventListener( 'keydown', ( e ) => { - hideSearchField( e ); - } ); - if ( searchLabel ) { - searchLabel.addEventListener( 'click', handleButtonClick ); - } - document.body.addEventListener( 'click', hideSearchField ); +/*eslint-env browser*/ + +/** @type {?HTMLFormElement} */ +let expandedSearchBlock = null; + +const hiddenClass = 'wp-block-search__searchfield-hidden'; + +/** + * Toggles aria-label with data-toggled-aria-label. + * + * @param {HTMLElement} element + */ +function toggleAriaLabel( element ) { + if ( ! ( 'toggledAriaLabel' in element.dataset ) ) { + throw new Error( 'Element lacks toggledAriaLabel in dataset.' ); + } + + const ariaLabel = element.dataset.toggledAriaLabel; + element.dataset.toggledAriaLabel = element.ariaLabel; + element.ariaLabel = ariaLabel; +} + +/** + * Gets search input. + * + * @param {HTMLFormElement} block Search block. + * @return {HTMLInputElement} Search input. + */ +function getSearchInput( block ) { + return block.querySelector( '.wp-block-search__input' ); +} + +/** + * Gets search button. + * + * @param {HTMLFormElement} block Search block. + * @return {HTMLButtonElement} Search button. + */ +function getSearchButton( block ) { + return block.querySelector( '.wp-block-search__button' ); +} + +/** + * Handles keydown event to collapse an expanded Search block (when pressing Escape key). + * + * @param {KeyboardEvent} event + */ +function handleKeydownEvent( event ) { + if ( ! expandedSearchBlock ) { + // In case the event listener wasn't removed in time. + return; + } + + if ( event.key === 'Escape' ) { + const block = expandedSearchBlock; // This is nullified by collapseExpandedSearchBlock(). + collapseExpandedSearchBlock(); + getSearchButton( block ).focus(); + } +} + +/** + * Handles keyup event to collapse an expanded Search block (e.g. when tabbing out of expanded Search block). + * + * @param {KeyboardEvent} event + */ +function handleKeyupEvent( event ) { + if ( ! expandedSearchBlock ) { + // In case the event listener wasn't removed in time. + return; + } + + if ( event.target.closest( '.wp-block-search' ) !== expandedSearchBlock ) { + collapseExpandedSearchBlock(); + } +} + +/** + * Expands search block. + * + * Inverse of what is done in collapseExpandedSearchBlock(). + * + * @param {HTMLFormElement} block Search block. + */ +function expandSearchBlock( block ) { + // Make sure only one is open at a time. + if ( expandedSearchBlock ) { + collapseExpandedSearchBlock(); + } + + const searchField = getSearchInput( block ); + const searchButton = getSearchButton( block ); + + searchButton.type = 'submit'; + searchField.ariaHidden = 'false'; + searchField.tabIndex = 0; + searchButton.ariaExpanded = 'true'; + searchButton.removeAttribute( 'aria-controls' ); // Note: Seemingly not reflected with searchButton.ariaControls. + toggleAriaLabel( searchButton ); + block.classList.remove( hiddenClass ); + + searchField.focus(); // Note that Chrome seems to do this automatically. + + // The following two must be inverse of what is done in collapseExpandedSearchBlock(). + document.addEventListener( 'keydown', handleKeydownEvent, { + passive: true, + } ); + document.addEventListener( 'keyup', handleKeyupEvent, { + passive: true, + } ); + + expandedSearchBlock = block; +} + +/** + * Collapses the expanded search block. + * + * Inverse of what is done in expandSearchBlock(). + */ +function collapseExpandedSearchBlock() { + if ( ! expandedSearchBlock ) { + throw new Error( 'Expected expandedSearchBlock to be defined.' ); + } + const block = expandedSearchBlock; + const searchField = getSearchInput( block ); + const searchButton = getSearchButton( block ); + + searchButton.type = 'button'; + searchField.ariaHidden = 'true'; + searchField.tabIndex = -1; + searchButton.ariaExpanded = 'false'; + searchButton.setAttribute( 'aria-controls', searchField.id ); // Note: Seemingly not reflected with searchButton.ariaControls. + toggleAriaLabel( searchButton ); + block.classList.add( hiddenClass ); + + // The following two must be inverse of what is done in expandSearchBlock(). + document.removeEventListener( 'keydown', handleKeydownEvent, { + passive: true, } ); -} ); + document.removeEventListener( 'keyup', handleKeyupEvent, { + passive: true, + } ); + + expandedSearchBlock = null; +} + +// Listen for click events anywhere on the document so this script can be loaded asynchronously in the head. +document.addEventListener( + 'click', + ( event ) => { + // Get the ancestor expandable Search block of the clicked element. + const block = event.target.closest( + '.wp-block-search__button-behavior-expand' + ); + + /* + * If there is already an expanded search block and either the current click was not for a Search block or it was + * for another block, then collapse the currently-expanded block. + */ + if ( expandedSearchBlock && block !== expandedSearchBlock ) { + collapseExpandedSearchBlock(); + } + + // If the click was on or inside a collapsed Search block, expand it. + if ( + block instanceof HTMLFormElement && + block.classList.contains( hiddenClass ) + ) { + expandSearchBlock( block ); + } + }, + { passive: true } +); From 6ba753e433e40e3ecc55d73249fd482ea6f6225a Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 26 Jul 2023 10:19:18 +1000 Subject: [PATCH 39/61] Patterns: Reset current page when search filters change (#52933) Co-authored-by: Glen Davies --- .../components/page-patterns/patterns-list.js | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 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 2744359bf0628..01525fc5dccab 100644 --- a/packages/edit-site/src/components/page-patterns/patterns-list.js +++ b/packages/edit-site/src/components/page-patterns/patterns-list.js @@ -78,6 +78,16 @@ export default function PatternsList( { categoryId, type } ) { } ); + const updateSearchFilter = ( value ) => { + setCurrentPage( 1 ); + setFilterValue( value ); + }; + + const updateSyncFilter = ( value ) => { + setCurrentPage( 1 ); + setSyncFilter( value ); + }; + const id = useId(); const titleId = `${ id }-title`; const descriptionId = `${ id }-description`; @@ -90,14 +100,12 @@ export default function PatternsList( { categoryId, type } ) { const pageIndex = currentPage - 1; const numPages = Math.ceil( patterns.length / PAGE_SIZE ); - const list = useMemo( - () => - patterns.slice( - pageIndex * PAGE_SIZE, - pageIndex * PAGE_SIZE + PAGE_SIZE - ), - [ pageIndex, patterns ] - ); + const list = useMemo( () => { + return patterns.slice( + pageIndex * PAGE_SIZE, + pageIndex * PAGE_SIZE + PAGE_SIZE + ); + }, [ pageIndex, patterns ] ); const asyncList = useAsyncList( list, { step: 10 } ); @@ -138,7 +146,9 @@ export default function PatternsList( { categoryId, type } ) { setFilterValue( value ) } + onChange={ ( value ) => + updateSearchFilter( value ) + } placeholder={ __( 'Search patterns' ) } label={ __( 'Search patterns' ) } value={ filterValue } @@ -152,7 +162,7 @@ export default function PatternsList( { categoryId, type } ) { label={ __( 'Filter by sync status' ) } value={ syncFilter } isBlock - onChange={ ( value ) => setSyncFilter( value ) } + onChange={ ( value ) => updateSyncFilter( value ) } __nextHasNoMarginBottom > { Object.entries( SYNC_FILTERS ).map( From 56e9efcb080eda3b4b8bf0ddd5623f68c21547e1 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Wed, 26 Jul 2023 12:21:37 +1200 Subject: [PATCH 40/61] Fix patterns search crash: check for existence of defaultView before attempting to get styles (#52956) --- packages/block-editor/src/components/editor-styles/index.js | 4 ++-- .../rich-text/src/component/use-selection-change-compat.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/editor-styles/index.js b/packages/block-editor/src/components/editor-styles/index.js index 66f2a08f115a4..3c66c5beb08d3 100644 --- a/packages/block-editor/src/components/editor-styles/index.js +++ b/packages/block-editor/src/components/editor-styles/index.js @@ -43,13 +43,13 @@ function useDarkThemeBodyClassName( styles ) { body.appendChild( tempCanvas ); backgroundColor = defaultView - .getComputedStyle( tempCanvas, null ) + ?.getComputedStyle( tempCanvas, null ) .getPropertyValue( 'background-color' ); body.removeChild( tempCanvas ); } else { backgroundColor = defaultView - .getComputedStyle( canvas, null ) + ?.getComputedStyle( canvas, null ) .getPropertyValue( 'background-color' ); } const colordBackgroundColor = colord( backgroundColor ); diff --git a/packages/rich-text/src/component/use-selection-change-compat.js b/packages/rich-text/src/component/use-selection-change-compat.js index 7a684f584263e..d067d5ec70ff7 100644 --- a/packages/rich-text/src/component/use-selection-change-compat.js +++ b/packages/rich-text/src/component/use-selection-change-compat.js @@ -21,7 +21,7 @@ export function useSelectionChangeCompat() { return useRefEffect( ( element ) => { const { ownerDocument } = element; const { defaultView } = ownerDocument; - const selection = defaultView.getSelection(); + const selection = defaultView?.getSelection(); let range; From 06c704e727944949ad797c63a70c52c9fa4a8d63 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 26 Jul 2023 09:21:57 +0400 Subject: [PATCH 41/61] Site Editor: Remove unnecessary selector from 'PageTemplates' (#52903) --- .../src/components/page-templates/index.js | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/packages/edit-site/src/components/page-templates/index.js b/packages/edit-site/src/components/page-templates/index.js index 3246d0fe884b7..5b0a306fa7fef 100644 --- a/packages/edit-site/src/components/page-templates/index.js +++ b/packages/edit-site/src/components/page-templates/index.js @@ -8,8 +8,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'; /** @@ -21,7 +20,6 @@ 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'; export default function PageTemplates() { const { records: templates } = useEntityRecords( @@ -32,15 +30,6 @@ export default function PageTemplates() { } ); - const { canCreate } = useSelect( ( select ) => { - const { supportsTemplatePartsMode } = - select( editSiteStore ).getSettings(); - return { - postType: select( coreStore ).getPostType( 'wp_template' ), - canCreate: ! supportsTemplatePartsMode, - }; - } ); - const columns = [ { header: __( 'Template' ), @@ -89,13 +78,11 @@ export default function PageTemplates() { - ) + } > { templates && } From bdde553b1796c556b93d97da8ffb6a89e5d955ea Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:27:02 +1000 Subject: [PATCH 42/61] List View: Ensure onDrop does not fire if there is no target (#52959) --- .../src/components/list-view/use-list-view-drop-zone.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js b/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js index aa5bfe0299292..a1a369d3f9408 100644 --- a/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js +++ b/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js @@ -489,7 +489,11 @@ export default function useListViewDropZone( { dropZoneElement } ) { const ref = useDropZone( { dropZoneElement, - onDrop: onBlockDrop, + onDrop( event ) { + if ( target ) { + onBlockDrop( event ); + } + }, onDragLeave() { throttled.cancel(); setTarget( null ); From 6b1e9377937b7c3ad1704e4979179700d5ba456a Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Wed, 26 Jul 2023 09:05:52 +0100 Subject: [PATCH 43/61] List: allow ENTER on multi selection (#52947) --- packages/block-editor/src/store/actions.js | 50 ++++++++----------- .../various/multi-block-selection.spec.js | 6 +-- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index 1d7ec460fe8d9..17573d76d016f 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -948,36 +948,30 @@ export const __unstableSplitSelection = valueA = remove( valueA, selectionA.offset, valueA.text.length ); valueB = remove( valueB, 0, selectionB.offset ); - dispatch.replaceBlocks( - select.getSelectedBlockClientIds(), - [ - { - // Preserve the original client ID. - ...blockA, - attributes: { - ...blockA.attributes, - [ selectionA.attributeKey ]: toHTMLString( { - value: valueA, - ...mapRichTextSettings( attributeDefinitionA ), - } ), - }, + dispatch.replaceBlocks( select.getSelectedBlockClientIds(), [ + { + // Preserve the original client ID. + ...blockA, + attributes: { + ...blockA.attributes, + [ selectionA.attributeKey ]: toHTMLString( { + value: valueA, + ...mapRichTextSettings( attributeDefinitionA ), + } ), }, - createBlock( getDefaultBlockName() ), - { - // Preserve the original client ID. - ...blockB, - attributes: { - ...blockB.attributes, - [ selectionB.attributeKey ]: toHTMLString( { - value: valueB, - ...mapRichTextSettings( attributeDefinitionB ), - } ), - }, + }, + { + // Preserve the original client ID. + ...blockB, + attributes: { + ...blockB.attributes, + [ selectionB.attributeKey ]: toHTMLString( { + value: valueB, + ...mapRichTextSettings( attributeDefinitionB ), + } ), }, - ], - 1, // If we don't pass the `indexToSelect` it will default to the last block. - select.getSelectedBlocksInitialCaretPosition() - ); + }, + ] ); }; /** diff --git a/test/e2e/specs/editor/various/multi-block-selection.spec.js b/test/e2e/specs/editor/various/multi-block-selection.spec.js index 26dee1291ba2f..4ca43d3f292d2 100644 --- a/test/e2e/specs/editor/various/multi-block-selection.spec.js +++ b/test/e2e/specs/editor/various/multi-block-selection.spec.js @@ -1113,13 +1113,9 @@ test.describe( 'Multi-block selection', () => { name: 'core/paragraph', attributes: { content: '1' }, }, - { - name: 'core/paragraph', - attributes: { content: '|' }, - }, { name: 'core/heading', - attributes: { level: 2, content: '2' }, + attributes: { level: 2, content: '|2' }, }, ] ); } ); From a78a63299678f142878f57737769550ca946bfee Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 26 Jul 2023 11:31:38 +0300 Subject: [PATCH 44/61] Experimental: Fix TinyMCE removal for heartbeat requests (#52935) * Experimental: Fix TinyMCE removal for heartbeat requests * intval => absint Post IDs are supposed to be non-negative integers. Co-authored-by: George Mamadashvili * get_post() cannot return a WP_Error --------- Co-authored-by: George Mamadashvili --- lib/experimental/disable-tinymce.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/experimental/disable-tinymce.php b/lib/experimental/disable-tinymce.php index 824f1ab9a73ae..cfe716d834ff7 100644 --- a/lib/experimental/disable-tinymce.php +++ b/lib/experimental/disable-tinymce.php @@ -61,14 +61,15 @@ function gutenberg_post_being_edited_requires_classic_block() { return false; } - // Handle the post editor. - if ( ! empty( $_GET['post'] ) && ! empty( $_GET['action'] ) && 'edit' === $_GET['action'] ) { - $current_post = get_post( intval( $_GET['post'] ) ); - if ( ! $current_post || is_wp_error( $current_post ) ) { - return false; - } + // Continue only if we're in the post editor. + if ( empty( $_GET['post'] ) || empty( $_GET['action'] ) || 'edit' !== $_GET['action'] ) { + return false; + } - $content = $current_post->post_content; + // Bail if for some reason the post isn't found. + $current_post = get_post( absint( $_GET['post'] ) ); + if ( ! $current_post ) { + return false; } // Check if block editor is disabled by "Classic Editor" or another plugin. @@ -79,6 +80,7 @@ function_exists( 'use_block_editor_for_post_type' ) && return true; } + $content = $current_post->post_content; if ( empty( $content ) ) { return false; } From 2b8f1ce45391b7e70ffdcf9fc7cf2425ece11bcd Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Wed, 26 Jul 2023 11:38:03 +0300 Subject: [PATCH 45/61] Add `Open styles revisions` command conditionally (#52945) * Add `Open styles revisions` command conditionally * feedback * remove test * use boolean --- .../src/hooks/commands/use-common-commands.js | 86 ++++++++++++++----- .../user-global-styles-revisions.spec.js | 16 ---- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/packages/edit-site/src/hooks/commands/use-common-commands.js b/packages/edit-site/src/hooks/commands/use-common-commands.js index e10a04526bee1..0933776e7bb14 100644 --- a/packages/edit-site/src/hooks/commands/use-common-commands.js +++ b/packages/edit-site/src/hooks/commands/use-common-commands.js @@ -114,13 +114,68 @@ function useGlobalStylesOpenCssCommands() { }; } -export function useCommonCommands() { +function useGlobalStylesOpenRevisionsCommands() { const { openGeneralSidebar, setEditorCanvasContainerView, setCanvasMode } = unlock( useDispatch( editSiteStore ) ); + const { getCanvasMode } = unlock( useSelect( editSiteStore ) ); const { params } = useLocation(); const isMobileViewport = useViewportMatch( 'medium', '<' ); - const isListPage = getIsListPage( params, isMobileViewport ); - const isEditorPage = ! isListPage; + const isEditorPage = ! getIsListPage( params, isMobileViewport ); + const history = useHistory(); + const hasRevisions = useSelect( + ( select ) => + select( coreStore ).getCurrentThemeGlobalStylesRevisions()?.length, + [] + ); + const commands = useMemo( () => { + if ( ! hasRevisions ) { + return []; + } + + return [ + { + name: 'core/edit-site/open-global-styles-revisions', + label: __( 'Open styles revisions' ), + icon: backup, + callback: ( { close } ) => { + close(); + if ( ! isEditorPage ) { + history.push( { + path: '/wp_global_styles', + canvas: 'edit', + } ); + } + if ( isEditorPage && getCanvasMode() !== 'edit' ) { + setCanvasMode( 'edit' ); + } + openGeneralSidebar( 'edit-site/global-styles' ); + setEditorCanvasContainerView( 'global-styles-revisions' ); + }, + }, + ]; + }, [ + hasRevisions, + history, + openGeneralSidebar, + setEditorCanvasContainerView, + isEditorPage, + getCanvasMode, + setCanvasMode, + ] ); + + return { + isLoading: false, + commands, + }; +} + +export function useCommonCommands() { + const { openGeneralSidebar, setCanvasMode } = unlock( + useDispatch( editSiteStore ) + ); + const { params } = useLocation(); + const isMobileViewport = useViewportMatch( 'medium', '<' ); + const isEditorPage = ! getIsListPage( params, isMobileViewport ); const { getCanvasMode } = unlock( useSelect( editSiteStore ) ); const { set } = useDispatch( preferencesStore ); const { createInfoNotice } = useDispatch( noticesStore ); @@ -139,26 +194,6 @@ export function useCommonCommands() { }; }, [] ); - useCommand( { - name: 'core/edit-site/open-global-styles-revisions', - label: __( 'Open styles revisions' ), - icon: backup, - callback: ( { close } ) => { - close(); - if ( ! isEditorPage ) { - history.push( { - path: '/wp_global_styles', - canvas: 'edit', - } ); - } - if ( isEditorPage && getCanvasMode() !== 'edit' ) { - setCanvasMode( 'edit' ); - } - openGeneralSidebar( 'edit-site/global-styles' ); - setEditorCanvasContainerView( 'global-styles-revisions' ); - }, - } ); - useCommand( { name: 'core/edit-site/open-styles', label: __( 'Open styles' ), @@ -228,4 +263,9 @@ export function useCommonCommands() { name: 'core/edit-site/open-styles-css', hook: useGlobalStylesOpenCssCommands, } ); + + useCommandLoader( { + name: 'core/edit-site/open-styles-revisions', + hook: useGlobalStylesOpenRevisionsCommands, + } ); } diff --git a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js index cb90ebe5edf25..ada0dc3152ac2 100644 --- a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js +++ b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js @@ -26,22 +26,6 @@ test.describe( 'Global styles revisions', () => { await admin.visitSiteEditor(); } ); - test( 'should display no revisions message if landing via command center', async ( { - page, - } ) => { - await page - .getByRole( 'button', { name: 'Open command palette' } ) - .focus(); - await page.keyboard.press( 'Meta+k' ); - await page.keyboard.type( 'styles revisions' ); - await page - .getByRole( 'option', { name: 'Open styles revisions' } ) - .click(); - await expect( - page.getByTestId( 'global-styles-no-revisions' ) - ).toHaveText( 'No results found.' ); - } ); - test( 'should display revisions UI when there is more than 1 revision', async ( { page, editor, From b0e3f20d4e4bdd2253c6b6ad1b491f8a5a093900 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 26 Jul 2023 11:41:04 +0300 Subject: [PATCH 46/61] Block Library: Set freeform handler only if Classic block exists (#52936) * Block Library: Set freeform handler only if Classic block exists * Better way to check for classic block existence --- packages/block-library/src/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index 911bd0c37451e..736b552bf4259 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -282,7 +282,11 @@ export const registerCoreBlocks = ( blocks.forEach( ( { init } ) => init() ); setDefaultBlockName( paragraph.name ); - if ( window.wp && window.wp.oldEditor ) { + if ( + window.wp && + window.wp.oldEditor && + blocks.some( ( { name } ) => name === classic.name ) + ) { setFreeformContentHandlerName( classic.name ); } setUnregisteredTypeHandlerName( missing.name ); From b0fb1c2211005f8ba4c32923554f0cac875e774c Mon Sep 17 00:00:00 2001 From: Bernie Reiter <96308+ockham@users.noreply.github.com> Date: Wed, 26 Jul 2023 11:40:44 +0200 Subject: [PATCH 47/61] Experiment: Auto-inserting blocks on the frontend and in the editor (via REST API) (#51449) --- lib/compat/wordpress-6.3/rest-api.php | 19 +- lib/experimental/auto-inserting-blocks.php | 246 ++++++++++++++++++ ...tenberg-rest-block-patterns-controller.php | 40 +++ lib/experimental/rest-api.php | 11 + lib/experiments-page.php | 12 + lib/load.php | 6 + packages/block-library/src/pattern/index.php | 12 +- 7 files changed, 337 insertions(+), 9 deletions(-) create mode 100644 lib/experimental/auto-inserting-blocks.php create mode 100644 lib/experimental/class-gutenberg-rest-block-patterns-controller.php diff --git a/lib/compat/wordpress-6.3/rest-api.php b/lib/compat/wordpress-6.3/rest-api.php index ecb8f52392fef..d7fa31cd33fe6 100644 --- a/lib/compat/wordpress-6.3/rest-api.php +++ b/lib/compat/wordpress-6.3/rest-api.php @@ -85,15 +85,18 @@ function add_modified_wp_template_schema() { } add_filter( 'rest_api_init', 'add_modified_wp_template_schema' ); -/** - * Registers the block patterns REST API routes. - */ -function gutenberg_register_rest_block_patterns() { - $block_patterns = new Gutenberg_REST_Block_Patterns_Controller_6_3(); - $block_patterns->register_routes(); +// If the Auto-inserting Blocks experiment is enabled, we load the block patterns +// controller in lib/experimental/rest-api.php instead. +if ( ! gutenberg_is_experiment_enabled( 'gutenberg-auto-inserting-blocks' ) ) { + /** + * Registers the block patterns REST API routes. + */ + function gutenberg_register_rest_block_patterns() { + $block_patterns = new Gutenberg_REST_Block_Patterns_Controller_6_3(); + $block_patterns->register_routes(); + } + add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); } -add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); - /** * Registers the Navigation Fallbacks REST API routes. diff --git a/lib/experimental/auto-inserting-blocks.php b/lib/experimental/auto-inserting-blocks.php new file mode 100644 index 0000000000000..9df94fcbfcd8f --- /dev/null +++ b/lib/experimental/auto-inserting-blocks.php @@ -0,0 +1,246 @@ + 0 ) { + if ( ! is_string( $block['innerContent'][ $chunk_index ] ) ) { + $anchor_block_index--; + } + $chunk_index++; + } + // Since WP_Block::render() iterates over `inner_content` (rather than `inner_blocks`) + // when rendering blocks, we also need to insert a value (`null`, to mark a block + // location) into that array. + array_splice( $block['innerContent'], $chunk_index, 0, array( null ) ); + } + return $block; + }; +} + +/** + * Register blocks for auto-insertion, based on their block.json metadata. + * + * @param array $settings Array of determined settings for registering a block type. + * @param array $metadata Metadata provided for registering a block type. + * @return array Updated settings array. + */ +function gutenberg_register_auto_inserted_blocks( $settings, $metadata ) { + if ( ! isset( $metadata['__experimentalAutoInsert'] ) ) { + return $settings; + } + $auto_insert = $metadata['__experimentalAutoInsert']; + + /** + * Map the camelCased position string from block.json to the snake_cased block type position + * used in the auto-inserting block registration function. + * + * @var array + */ + $property_mappings = array( + 'before' => 'before', + 'after' => 'after', + 'firstChild' => 'first_child', + 'lastChild' => 'last_child', + ); + + $inserted_block_name = $metadata['name']; + foreach ( $auto_insert as $anchor_block_name => $position ) { + // Avoid infinite recursion (auto-inserting next to or into self). + if ( $inserted_block_name === $anchor_block_name ) { + _doing_it_wrong( + __METHOD__, + __( 'Cannot auto-insert block next to itself.', 'gutenberg' ), + '6.4.0' + ); + continue; + } + + if ( ! isset( $property_mappings[ $position ] ) ) { + continue; + } + + $mapped_position = $property_mappings[ $position ]; + + gutenberg_register_auto_inserted_block( $inserted_block_name, $mapped_position, $anchor_block_name ); + + $settings['auto_insert'][ $anchor_block_name ] = $mapped_position; + } + + return $settings; +} +add_filter( 'block_type_metadata_settings', 'gutenberg_register_auto_inserted_blocks', 10, 2 ); + +/** + * Register block for auto-insertion into the frontend and REST API. + * + * Register a block for auto-insertion into the frontend and into the markup + * returned by the templates and patterns REST API endpoints. + * + * This is currently done by filtering parsed blocks as obtained from a block template + * template part, or pattern and injecting the auto-inserted block where applicable. + * + * @todo In the long run, we'd likely want some sort of registry for auto-inserted blocks. + * + * @param string $inserted_block The name of the block to insert. + * @param string $position The desired position of the auto-inserted block, relative to its anchor block. + * Can be 'before', 'after', 'first_child', or 'last_child'. + * @param string $anchor_block The name of the block to insert the auto-inserted block next to. + * @return void + */ +function gutenberg_register_auto_inserted_block( $inserted_block, $position, $anchor_block ) { + $inserted_block = array( + 'blockName' => $inserted_block, + 'attrs' => array(), + 'innerHTML' => '', + 'innerContent' => array(), + 'innerBlocks' => array(), + ); + + $inserter = gutenberg_auto_insert_block( $inserted_block, $position, $anchor_block ); + add_filter( 'gutenberg_serialize_block', $inserter, 10, 1 ); +} + +/** + * Parse and reserialize block templates to allow running filters. + * + * By parsing a block template's content and then reserializing it + * via `gutenberg_serialize_blocks()`, we are able to run filters + * on the parsed blocks. + * + * @param WP_Block_Template[] $query_result Array of found block templates. + * @return WP_Block_Template[] Updated array of found block templates. + */ +function gutenberg_parse_and_serialize_block_templates( $query_result ) { + foreach ( $query_result as $block_template ) { + if ( 'custom' === $block_template->source ) { + continue; + } + $blocks = parse_blocks( $block_template->content ); + $block_template->content = gutenberg_serialize_blocks( $blocks ); + } + + return $query_result; +} +add_filter( 'get_block_templates', 'gutenberg_parse_and_serialize_block_templates', 10, 1 ); + +/** + * Filters the block template object after it has been (potentially) fetched from the theme file. + * + * By parsing a block template's content and then reserializing it + * via `gutenberg_serialize_blocks()`, we are able to run filters + * on the parsed blocks. + * + * @param WP_Block_Template|null $block_template The found block template, or null if there is none. + */ +function gutenberg_parse_and_serialize_blocks( $block_template ) { + + $blocks = parse_blocks( $block_template->content ); + $block_template->content = gutenberg_serialize_blocks( $blocks ); + + return $block_template; +} +add_filter( 'get_block_file_template', 'gutenberg_parse_and_serialize_blocks', 10, 1 ); + +// Helper functions. +// ----------------- +// The sole purpose of the following two functions (`gutenberg_serialize_block` +// and `gutenberg_serialize_blocks`), which are otherwise copies of their unprefixed +// counterparts (`serialize_block` and `serialize_blocks`) is to apply a filter +// (also called `gutenberg_serialize_block`) as an entry point for modifications +// to the parsed blocks. + +/** + * Filterable version of `serialize_block()`. + * + * This function is identical to `serialize_block()`, except that it applies + * the `gutenberg_serialize_block` filter to each block before it is serialized. + * + * @param array $block The block to be serialized. + * @return string The serialized block. + * + * @see serialize_block() + */ +function gutenberg_serialize_block( $block ) { + $block_content = ''; + + /** + * Filters a parsed block before it is serialized. + * + * @param array $block The block to be serialized. + */ + $block = apply_filters( 'gutenberg_serialize_block', $block ); + + $index = 0; + foreach ( $block['innerContent'] as $chunk ) { + if ( is_string( $chunk ) ) { + $block_content .= $chunk; + } else { // Compare to WP_Block::render(). + $inner_block = $block['innerBlocks'][ $index++ ]; + $block_content .= gutenberg_serialize_block( $inner_block ); + } + } + + if ( ! is_array( $block['attrs'] ) ) { + $block['attrs'] = array(); + } + + return get_comment_delimited_block_content( + $block['blockName'], + $block['attrs'], + $block_content + ); +} + +/** + * Filterable version of `serialize_blocks()`. + * + * This function is identical to `serialize_blocks()`, except that it applies + * the `gutenberg_serialize_block` filter to each block before it is serialized. + * + * @param array $blocks The blocks to be serialized. + * @return string[] The serialized blocks. + * + * @see serialize_blocks() + */ +function gutenberg_serialize_blocks( $blocks ) { + return implode( '', array_map( 'gutenberg_serialize_block', $blocks ) ); +} diff --git a/lib/experimental/class-gutenberg-rest-block-patterns-controller.php b/lib/experimental/class-gutenberg-rest-block-patterns-controller.php new file mode 100644 index 0000000000000..1ac567959b146 --- /dev/null +++ b/lib/experimental/class-gutenberg-rest-block-patterns-controller.php @@ -0,0 +1,40 @@ +get_data(); + + $blocks = parse_blocks( $data['content'] ); + $data['content'] = gutenberg_serialize_blocks( $blocks ); // Serialize or render? + + return rest_ensure_response( $data ); + } +} diff --git a/lib/experimental/rest-api.php b/lib/experimental/rest-api.php index 7c6a9bf74d739..8e548600b3875 100644 --- a/lib/experimental/rest-api.php +++ b/lib/experimental/rest-api.php @@ -10,6 +10,17 @@ die( 'Silence is golden.' ); } +if ( gutenberg_is_experiment_enabled( 'gutenberg-auto-inserting-blocks' ) ) { + /** + * Registers the block patterns REST API routes. + */ + function gutenberg_register_rest_block_patterns() { + $block_patterns = new Gutenberg_REST_Block_Patterns_Controller(); + $block_patterns->register_routes(); + } + add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); +} + /** * Registers the customizer nonces REST API routes. */ diff --git a/lib/experiments-page.php b/lib/experiments-page.php index ce30242d20f81..3f468d0cbd12d 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -91,6 +91,18 @@ function gutenberg_initialize_experiments_settings() { ) ); + add_settings_field( + 'gutenberg-auto-inserting-blocks', + __( 'Auto-inserting blocks', 'gutenberg' ), + 'gutenberg_display_experiment_field', + 'gutenberg-experiments', + 'gutenberg_experiments_section', + array( + 'label' => __( 'Test Auto-inserting blocks', 'gutenberg' ), + 'id' => 'gutenberg-auto-inserting-blocks', + ) + ); + register_setting( 'gutenberg-experiments', 'gutenberg-experiments' diff --git a/lib/load.php b/lib/load.php index 71be1068b7061..85e9f9575e6e6 100644 --- a/lib/load.php +++ b/lib/load.php @@ -69,6 +69,9 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/experimental/class-wp-rest-customizer-nonces.php'; } require_once __DIR__ . '/experimental/class-gutenberg-rest-template-revision-count.php'; + if ( gutenberg_is_experiment_enabled( 'gutenberg-auto-inserting-blocks' ) ) { + require_once __DIR__ . '/experimental/class-gutenberg-rest-block-patterns-controller.php'; + } require_once __DIR__ . '/experimental/rest-api.php'; } @@ -117,6 +120,9 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/experimental/disable-tinymce.php'; } +if ( gutenberg_is_experiment_enabled( 'gutenberg-auto-inserting-blocks' ) ) { + require __DIR__ . '/experimental/auto-inserting-blocks.php'; +} require __DIR__ . '/experimental/interactivity-api/class-wp-interactivity-store.php'; require __DIR__ . '/experimental/interactivity-api/store.php'; require __DIR__ . '/experimental/interactivity-api/scripts.php'; diff --git a/packages/block-library/src/pattern/index.php b/packages/block-library/src/pattern/index.php index 6368bdb7b7487..bc42e891d9f1f 100644 --- a/packages/block-library/src/pattern/index.php +++ b/packages/block-library/src/pattern/index.php @@ -41,7 +41,17 @@ function render_block_core_pattern( $attributes ) { } $pattern = $registry->get_registered( $slug ); - return do_blocks( $pattern['content'] ); + $content = $pattern['content']; + + $gutenberg_experiments = get_option( 'gutenberg-experiments' ); + if ( $gutenberg_experiments && ! empty( $gutenberg_experiments['gutenberg-auto-inserting-blocks'] ) ) { + // TODO: In the long run, we'd likely want to have a filter in the `WP_Block_Patterns_Registry` class + // instead to allow us plugging in code like this. + $blocks = parse_blocks( $content ); + $content = gutenberg_serialize_blocks( $blocks ); + } + + return do_blocks( $content ); } add_action( 'init', 'register_block_core_pattern' ); From d17cd9cdf53120248fc2ef6eddbd7bae266c1e3d Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 26 Jul 2023 13:01:51 +0300 Subject: [PATCH 48/61] Experimental: Backend handle freeform blocks with TinyMCE removal (#52938) --- lib/experimental/disable-tinymce.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/experimental/disable-tinymce.php b/lib/experimental/disable-tinymce.php index cfe716d834ff7..ff9185cff567d 100644 --- a/lib/experimental/disable-tinymce.php +++ b/lib/experimental/disable-tinymce.php @@ -87,7 +87,8 @@ function_exists( 'use_block_editor_for_post_type' ) && $parsed_blocks = parse_blocks( $content ); foreach ( $parsed_blocks as $block ) { - if ( empty( $block['blockName'] ) && strlen( trim( $block['innerHTML'] ) ) > 0 ) { + $is_freeform_block = empty( $block['blockName'] ) || 'core/freeform' === $block['blockName']; + if ( $is_freeform_block && strlen( trim( $block['innerHTML'] ) ) > 0 ) { return true; } } From 3d2bffd7c0ddbbf4cd2ab1e927b416728bc0c7e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mendon=C3=A7a?= Date: Wed, 26 Jul 2023 13:03:54 +0100 Subject: [PATCH 49/61] I18N: Add missing Gettext wrapper on strings in Edit Post overview sidebar (#52971) * I18N: Add missing gettext wrapper * Add context to disambiguate 'Outline' that is commonly used on borders. --- .../src/components/secondary-sidebar/list-view-sidebar.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js index a4e6e639fbd76..77a56617cb1c6 100644 --- a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js +++ b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js @@ -11,7 +11,7 @@ import { import { useDispatch } from '@wordpress/data'; import { focus } from '@wordpress/dom'; import { useRef, useState } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; +import { __, _x } from '@wordpress/i18n'; import { closeSmall } from '@wordpress/icons'; import { useShortcut } from '@wordpress/keyboard-shortcuts'; import { ESCAPE } from '@wordpress/keycodes'; @@ -140,12 +140,12 @@ export default function ListViewSidebar() { tabs={ [ { name: 'list-view', - title: 'List View', + title: _x( 'List View', 'Post overview' ), className: 'edit-post-sidebar__panel-tab', }, { name: 'outline', - title: 'Outline', + title: _x( 'Outline', 'Post overview' ), className: 'edit-post-sidebar__panel-tab', }, ] } From 0185efd8a5b87287b77823cd01771ce96ef9e8b6 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 26 Jul 2023 12:12:54 +0000 Subject: [PATCH 50/61] Bump plugin version to 16.3.0 --- gutenberg.php | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index cb3300bfae0b4..980b2304ac796 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality. * Requires at least: 6.1 * Requires PHP: 5.6 - * Version: 16.3.0-rc.1 + * Version: 16.3.0 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index 12ebe3296be94..0687de2ca68ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.3.0-rc.1", + "version": "16.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e91a45d78d55a..e34ee35b9a5f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "16.3.0-rc.1", + "version": "16.3.0", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From fbd0735edff563baddd10efe09f6fe1ff21de351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor?= <27339341+priethor@users.noreply.github.com> Date: Wed, 26 Jul 2023 14:26:13 +0200 Subject: [PATCH 51/61] Add GH action to enforce PR labels (#52760) * Add GH action to enforce PR labels * Update enforce-pr-labels.yml * Update enforce-pr-labels.yml * Update enforce-pr-labels.yml --- .github/workflows/enforce-pr-labels.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/enforce-pr-labels.yml diff --git a/.github/workflows/enforce-pr-labels.yml b/.github/workflows/enforce-pr-labels.yml new file mode 100644 index 0000000000000..0ea94ba5f2292 --- /dev/null +++ b/.github/workflows/enforce-pr-labels.yml @@ -0,0 +1,18 @@ +name: Enforce labels on Pull Request +on: + pull_request: + types: [opened, labeled, unlabeled, synchronize] +jobs: + type-related-labels: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: mheap/github-action-required-labels@v5 + with: + mode: exactly + count: 1 + labels: "[Type] Accessibility (a11y), [Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Performance, [Type] Project Management, [Type] Security, [Type] WP Core Ticket" + add_comment: true + message: "## ⚠️ Type of PR label error\n To merge this PR, it requires {{ errorString }} {{ count }} label indicating the type of PR. Other labels are optional and not being checked here. \n- **Type-related labels to choose from**: {{ provided }}.\n- **Labels found**: {{ applied }}." From a56252d7a631d47107cf8878a8a09ce397a69831 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 26 Jul 2023 12:41:44 +0000 Subject: [PATCH 52/61] Update Changelog for 16.3.0 --- changelog.txt | 99 ++++++++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 56 deletions(-) diff --git a/changelog.txt b/changelog.txt index ede5cff91e4ed..ff4942d8e06a1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,15 +1,13 @@ == Changelog == -= 16.3.0-rc.1 = - - += 16.3.0 = ## Changelog ### Enhancements #### Site Editor -- Edit Site: Add delay and fade-in animation to loading spinner. ([51902](https://github.com/WordPress/gutenberg/pull/51902)) +- Add delay and fade-in animation to loading spinner. ([51902](https://github.com/WordPress/gutenberg/pull/51902)) - Make "My patterns" category permanently visible. ([52531](https://github.com/WordPress/gutenberg/pull/52531)) - Remove "Theme patterns" heading in Pattern library. ([52570](https://github.com/WordPress/gutenberg/pull/52570)) - Remove sidebar group descriptions. ([52453](https://github.com/WordPress/gutenberg/pull/52453)) @@ -17,27 +15,27 @@ - Swap pattern creation options. ([52726](https://github.com/WordPress/gutenberg/pull/52726)) - Update Dashboard button tooltips in the site editor. ([52465](https://github.com/WordPress/gutenberg/pull/52465)) - Update Site Editor frame z-index. ([52180](https://github.com/WordPress/gutenberg/pull/52180)) -- Update descriptions in Pattern library. ([52468](https://github.com/WordPress/gutenberg/pull/52468)) - Update locked pattern tooltips. ([52497](https://github.com/WordPress/gutenberg/pull/52497)) - Update navigation menu title size & weight in detail panels. ([52477](https://github.com/WordPress/gutenberg/pull/52477)) - Update pattern library copy. ([52340](https://github.com/WordPress/gutenberg/pull/52340)) -- Update: Show more intuitive archive titles on Query Title block. ([52521](https://github.com/WordPress/gutenberg/pull/52521)) +- Show more intuitive archive titles on Query Title block. ([52521](https://github.com/WordPress/gutenberg/pull/52521)) +- Adapt template part hint copy. ([52527](https://github.com/WordPress/gutenberg/pull/52527)) #### Patterns - Add hint to show template part move. ([52395](https://github.com/WordPress/gutenberg/pull/52395)) - Add renaming, duplication, and deletion options. ([52270](https://github.com/WordPress/gutenberg/pull/52270)) - Add sync tooltip. ([52458](https://github.com/WordPress/gutenberg/pull/52458)) - Display all custom template part areas in sidebar nav. ([52355](https://github.com/WordPress/gutenberg/pull/52355)) -- Don't override the rootClientID in create menu - only set if undefined. ([52713](https://github.com/WordPress/gutenberg/pull/52713)) +- Don't override the rootClientID in create menu - only set if undefined. ([52713](https://github.com/WordPress/gutenberg/pull/52713)) - Enable focus mode editing. ([52427](https://github.com/WordPress/gutenberg/pull/52427)) - Remove `reusable` text from menu once rename hint has been dismissed. ([52664](https://github.com/WordPress/gutenberg/pull/52664)) - Stop endless snackbars appearing. ([52012](https://github.com/WordPress/gutenberg/pull/52012)) -- Try: Sticky header and pagination on Patterns page. ([52663](https://github.com/WordPress/gutenberg/pull/52663)) +- Sticky header and pagination on Patterns page. ([52663](https://github.com/WordPress/gutenberg/pull/52663)) - Update manage pattern links to go to site editor if available. ([52403](https://github.com/WordPress/gutenberg/pull/52403)) -- [Patterns] Separate sync status into a filter control. ([52303](https://github.com/WordPress/gutenberg/pull/52303)) +- Separate sync status into a filter control. ([52303](https://github.com/WordPress/gutenberg/pull/52303)) #### Components -- Adapt template part hint copy. ([52527](https://github.com/WordPress/gutenberg/pull/52527)) + - Adding support for defined IDs in `TextControl` component. ([52028](https://github.com/WordPress/gutenberg/pull/52028)) - Updated "position" default value. ([52148](https://github.com/WordPress/gutenberg/pull/52148)) @@ -48,30 +46,17 @@ #### Block Library - Add back old Navigation and File blocks JavaScript implementation when Gutenberg is not installed. ([52553](https://github.com/WordPress/gutenberg/pull/52553)) - Home link block: Add 'current-menu-item'. ([51478](https://github.com/WordPress/gutenberg/pull/51478)) +- Use next40pxDefaultSize on RangeControl components. ([52257](https://github.com/WordPress/gutenberg/pull/52257)) #### Block Editor - Add maxLength to LinkControl search item URLs. ([52523](https://github.com/WordPress/gutenberg/pull/52523)) -- i18n: Make the tab labels of `ColorGradientSettingsDropdown` component translatable. ([52669](https://github.com/WordPress/gutenberg/pull/52669)) +- Make the tab labels of `ColorGradientSettingsDropdown` component translatable. ([52669](https://github.com/WordPress/gutenberg/pull/52669)) +- Add support for arrays to `setImmutably` util. ([52280](https://github.com/WordPress/gutenberg/pull/52280)) +- Stabilize `defaultBlock`, `directInsert` API's and `getDirectInsertBlock` selector. ([52083](https://github.com/WordPress/gutenberg/pull/52083)) #### NUX - Restore `@wordpress/nux` to trunk. ([52455](https://github.com/WordPress/gutenberg/pull/52455)) -#### Block API -- Block Editor: Add support for arrays to `setImmutably` util. ([52280](https://github.com/WordPress/gutenberg/pull/52280)) - -#### Inspector Controls -- Use next40pxDefaultSize on RangeControl components. ([52257](https://github.com/WordPress/gutenberg/pull/52257)) - -#### Fonts API -- Font Face: To generate and print font-face styles for theme.json fonts. ([51770](https://github.com/WordPress/gutenberg/pull/51770)) - - -### New APIs - -#### Nested / Inner Blocks -- Stabilize `defaultBlock`, `directInsert` API's and `getDirectInsertBlock` selector. ([52083](https://github.com/WordPress/gutenberg/pull/52083)) - - ### Bug Fixes #### Patterns @@ -84,27 +69,29 @@ - Ensure that the unsaved title is not persisted when reopening the modal. ([52473](https://github.com/WordPress/gutenberg/pull/52473)) - Fix bug with Create Patterns menu not showing in site editor page editing. ([52671](https://github.com/WordPress/gutenberg/pull/52671)) - Fix renaming in Site View sidebar rename saves all edits for Template Parts and Navigation Menus. ([52373](https://github.com/WordPress/gutenberg/pull/52373)) -- Fix: Patterns & template parts: Remove "apply globally" option from block settings. ([52160](https://github.com/WordPress/gutenberg/pull/52160)) +- Patterns & template parts: Remove "apply globally" option from block settings. ([52160](https://github.com/WordPress/gutenberg/pull/52160)) - Rename edit label to Edit Block Pattern to resolve edge case in Chrome. ([52496](https://github.com/WordPress/gutenberg/pull/52496)) - Show uncategorized patterns on the Editor > Patterns page. ([52633](https://github.com/WordPress/gutenberg/pull/52633)) - Site Editor Patterns: Filter out patterns that are not available in the inserter. ([52675](https://github.com/WordPress/gutenberg/pull/52675)) - Update the title of Pattern block in the block inspector card. ([52010](https://github.com/WordPress/gutenberg/pull/52010)) #### Site Editor -- Edit Site: Fix the pattern with the post types becomes the placeholder pattern when editing template part. ([52503](https://github.com/WordPress/gutenberg/pull/52503)) +- Fix the pattern with the post types becomes the placeholder pattern when editing template part. ([52503](https://github.com/WordPress/gutenberg/pull/52503)) - Fix "Manage all patterns" link appearance. ([52532](https://github.com/WordPress/gutenberg/pull/52532)) - Fix document title icon appearance. ([52424](https://github.com/WordPress/gutenberg/pull/52424)) - Fix entering edit mode in site editor. ([52406](https://github.com/WordPress/gutenberg/pull/52406)) -- Fix missing Add Template Part button in Template Parts page. ([52542](https://github.com/WordPress/gutenberg/pull/52542)) +- Fix missing "Add Template Part" button in Template Parts page. ([52542](https://github.com/WordPress/gutenberg/pull/52542)) - Fix undo/redo in site editor code editor's mode. ([52695](https://github.com/WordPress/gutenberg/pull/52695)) - Remove status icon. ([52457](https://github.com/WordPress/gutenberg/pull/52457)) - Reset device preview type when exiting the editing mode. ([52566](https://github.com/WordPress/gutenberg/pull/52566)) - ResizableFrame: Fix styling in Firefox. ([52700](https://github.com/WordPress/gutenberg/pull/52700)) - Site Editor Pages: Load the appropriate template if posts page set. ([52266](https://github.com/WordPress/gutenberg/pull/52266)) - Site Editor Patterns: Ensure sidebar does not shrink when long pattern titles are used. ([52547](https://github.com/WordPress/gutenberg/pull/52547)) -- Use lowercase p in in "Manage Patterns". ([52617](https://github.com/WordPress/gutenberg/pull/52617)) +- Use lowercase p in "Manage Patterns". ([52617](https://github.com/WordPress/gutenberg/pull/52617)) - Do not navigate to the styles pages unless you're in a random listing page. ([52728](https://github.com/WordPress/gutenberg/pull/52728)) - Fix multiple navigation blocks in pattern template. ([52707](https://github.com/WordPress/gutenberg/pull/52707)) +- Don't allow creating template part on the Patterns page for non-block themes. ([52656](https://github.com/WordPress/gutenberg/pull/52656)) +- Exit template focus when opening the W menu. ([52235](https://github.com/WordPress/gutenberg/pull/52235)) #### Block Library - Fix console warning by improving error handling in Nav block classic menu conversion. ([52591](https://github.com/WordPress/gutenberg/pull/52591)) @@ -117,13 +104,16 @@ - Rich Text/Footnotes: Fix getRichTextValues for useInnerBlocksProps.save. ([52682](https://github.com/WordPress/gutenberg/pull/52682)) - Search block: Enqueue view script through block.json. ([52552](https://github.com/WordPress/gutenberg/pull/52552)) - Use `_get_block_template_file` function and set $area variable. ([52708](https://github.com/WordPress/gutenberg/pull/52708)) +- Cover Block: Fix block deprecation when fixed background is enabled. ([51612](https://github.com/WordPress/gutenberg/pull/51612)) +- Fix image block v6 deprecation. ([52822](https://github.com/WordPress/gutenberg/pull/52822)) +- Image: Use the correct method for caption class in recent deprecation. ([52853](https://github.com/WordPress/gutenberg/pull/52853)) #### Accessibility -- Change Delete page menu item to Move to trash. ([52641](https://github.com/WordPress/gutenberg/pull/52641)) +- Change "Delete page" menu item to "Move to trash". ([52641](https://github.com/WordPress/gutenberg/pull/52641)) - Change password input to type text so contents are visible. ([52622](https://github.com/WordPress/gutenberg/pull/52622)) - Do not autofocus page title field in the 'Draft a new page' modal dialog. ([52603](https://github.com/WordPress/gutenberg/pull/52603)) - Fix Shift+Tab to Block Toolbar. ([52613](https://github.com/WordPress/gutenberg/pull/52613)) -- Item: Unify focus style and add default font styles. ([52495](https://github.com/WordPress/gutenberg/pull/52495)) +- Unify focus style and add default font styles. ([52495](https://github.com/WordPress/gutenberg/pull/52495)) - Navigation block: Add notice on reduced accessibility. ([52251](https://github.com/WordPress/gutenberg/pull/52251)) - Password protected field: Remove autofocus and improve placeholder text consistency. ([52634](https://github.com/WordPress/gutenberg/pull/52634)) - ResizableFrame: Make keyboard accessible. ([52443](https://github.com/WordPress/gutenberg/pull/52443)) @@ -133,12 +123,12 @@ - Add 'reusable' keyword to Pattern blocks. ([52543](https://github.com/WordPress/gutenberg/pull/52543)) - Avoid errors in Dimension visualizers when switching between iframed and non-iframed editors. ([52588](https://github.com/WordPress/gutenberg/pull/52588)) - Ensure synced patterns are accounted for in 'getAllowedBlocks'. ([52546](https://github.com/WordPress/gutenberg/pull/52546)) -- Fix: Remove link action of Link UI for draft pages created from Nav block does not correctly remove link. ([52415](https://github.com/WordPress/gutenberg/pull/52415)) +- Remove link action of Link UI for draft pages created from Nav block does not correctly remove link. ([52415](https://github.com/WordPress/gutenberg/pull/52415)) - LinkControl: Add width to ensure ellipsis truncating works. ([52575](https://github.com/WordPress/gutenberg/pull/52575)) - LinkControl: Fix mark highlight to bold. ([52517](https://github.com/WordPress/gutenberg/pull/52517)) - Post Content link color should not be applied to placeholder component links. ([52367](https://github.com/WordPress/gutenberg/pull/52367)) - Fix highlight change when using transform menu. ([52752](https://github.com/WordPress/gutenberg/pull/52752)) -- Fix: Apply text color selection to link color. ([52379](https://github.com/WordPress/gutenberg/pull/52379)) +- Apply text color selection to link color. ([52379](https://github.com/WordPress/gutenberg/pull/52379)) #### Components - Block Editor: Display variation icon in the 'BlockDraggable' component. ([52502](https://github.com/WordPress/gutenberg/pull/52502)) @@ -149,7 +139,7 @@ - Top Toolbar: Move the preferences selection into the main useSelect. ([52332](https://github.com/WordPress/gutenberg/pull/52332)) #### Post Editor -- Editor: Remove a block select button from the multi-entity saving flow. ([52753](https://github.com/WordPress/gutenberg/pull/52753)) +- Remove a block select button from the multi-entity saving flow. ([52753](https://github.com/WordPress/gutenberg/pull/52753)) - Fix Site editor page when JS support is disabled. ([52376](https://github.com/WordPress/gutenberg/pull/52376)) - Fix initial block parsing. ([52417](https://github.com/WordPress/gutenberg/pull/52417)) - Simplify the code editor of edit-post. ([52751](https://github.com/WordPress/gutenberg/pull/52751)) @@ -161,7 +151,6 @@ #### Themes - Fix admin_url() for preview link of block themes. ([52399](https://github.com/WordPress/gutenberg/pull/52399)) -- Site Editor: Don't allow creating template part on the Patterns page for non-block themes. ([52656](https://github.com/WordPress/gutenberg/pull/52656)) #### Fonts API - Deprecate and make Fonts API non-functional. ([52485](https://github.com/WordPress/gutenberg/pull/52485)) @@ -170,13 +159,6 @@ #### Extensibility - Page Content Focus: Ignore page content within a Query Loop block. ([52351](https://github.com/WordPress/gutenberg/pull/52351)) -#### Page Content Focus -- Exit template focus when opening the W menu. ([52235](https://github.com/WordPress/gutenberg/pull/52235)) - -#### Block Validation/Deprecation -- Cover Block: Fix block deprecation when fixed background is enabled. ([51612](https://github.com/WordPress/gutenberg/pull/51612)) - - ### Performance #### Post Editor @@ -194,16 +176,17 @@ ### Experiments -#### Project Management -- Github workflow: Add a PHP backport changes action. ([52096](https://github.com/WordPress/gutenberg/pull/52096)) - #### Interactivity API - Prevent scripts from loading if behaviors are not used. ([52140](https://github.com/WordPress/gutenberg/pull/52140)) +#### Fonts API +- Font Face: To generate and print font-face styles for theme.json fonts. ([51770](https://github.com/WordPress/gutenberg/pull/51770)) + + ### Documentation -- (readme.md) Document the new process for releasing point releases for old release branches. ([49968](https://github.com/WordPress/gutenberg/pull/49968)) +- Document the new process for releasing point releases for old release branches. ([49968](https://github.com/WordPress/gutenberg/pull/49968)) - Add layout API documentation. ([52673](https://github.com/WordPress/gutenberg/pull/52673)) - Added README for the "caption" component. ([52033](https://github.com/WordPress/gutenberg/pull/52033)) - Added documentation text-transform component #52072. ([52243](https://github.com/WordPress/gutenberg/pull/52243)) @@ -216,9 +199,8 @@ ### Code Quality +#### Block Editor - Add missing `@emotion/react` dep to block-editor. ([52475](https://github.com/WordPress/gutenberg/pull/52475)) -- Code Data: Fix ESLint warning for 'useEntityProp' hook. ([52757](https://github.com/WordPress/gutenberg/pull/52757)) -- Lodash: Deprecate `_.set()`. ([52407](https://github.com/WordPress/gutenberg/pull/52407)) - Lodash: Remove remaining `_.get()` from block editor and deprecate. ([52561](https://github.com/WordPress/gutenberg/pull/52561)) - Make use of accessing private APIs from thunks directly. ([52214](https://github.com/WordPress/gutenberg/pull/52214)) @@ -238,7 +220,7 @@ #### Post Editor - EntityRecordItem: Fix ESLint warnings and remove unnecessary memoization. ([52630](https://github.com/WordPress/gutenberg/pull/52630)) - PostPreviewButton: Rewrite to functional, avoid state transitions in lifecycles. ([44971](https://github.com/WordPress/gutenberg/pull/44971)) -- correct a typo: Sapce -> space. ([52578](https://github.com/WordPress/gutenberg/pull/52578)) +- Correct a typo: Sapce -> space. ([52578](https://github.com/WordPress/gutenberg/pull/52578)) #### Site Editor - Fix incorrect 'useSelect' usage. ([52683](https://github.com/WordPress/gutenberg/pull/52683)) @@ -247,6 +229,10 @@ #### Reusable Blocks - Update package to use relative path. ([52712](https://github.com/WordPress/gutenberg/pull/52712)) +#### Core Data +- Core Data: Fix ESLint warning for 'useEntityProp' hook. ([52757](https://github.com/WordPress/gutenberg/pull/52757)) + + ### Tools @@ -261,21 +247,20 @@ - Try fixing block context end-to-end test failure. ([52513](https://github.com/WordPress/gutenberg/pull/52513)) - Use posts instead of template parts for navigation color tests. ([52654](https://github.com/WordPress/gutenberg/pull/52654)) - end-to-end Test Utils: Improve test reliability in plugins/themes and login procedures. ([52144](https://github.com/WordPress/gutenberg/pull/52144)) -- test: Enable jest-watch-typeahead for native tests. ([51869](https://github.com/WordPress/gutenberg/pull/51869)) -- test: Expand mobile editor tests. ([52446](https://github.com/WordPress/gutenberg/pull/52446)) +- Enable jest-watch-typeahead for native tests. ([51869](https://github.com/WordPress/gutenberg/pull/51869)) +- Expand mobile editor tests. ([52446](https://github.com/WordPress/gutenberg/pull/52446)) #### Build Tooling - Backport tools: Sort PRs to be cherry picked by merged/closed date. ([52667](https://github.com/WordPress/gutenberg/pull/52667)) - Create block interactive template. ([52612](https://github.com/WordPress/gutenberg/pull/52612)) - Fix Webpack to watch the `interactivity` package files. ([52642](https://github.com/WordPress/gutenberg/pull/52642)) - Update caniuse-lite, browserslist and core-js. ([52420](https://github.com/WordPress/gutenberg/pull/52420)) +- Lodash: Deprecate `_.set()`. ([52407](https://github.com/WordPress/gutenberg/pull/52407)) #### Project Management - Update issue gardening automation with new label. ([52173](https://github.com/WordPress/gutenberg/pull/52173)) - -### Various - - Revert "Update Changelog for 16.1.2". ([52433](https://github.com/WordPress/gutenberg/pull/52433)) +- Github workflow: Add a PHP backport changes action. ([52096](https://github.com/WordPress/gutenberg/pull/52096)) ## First time contributors @@ -294,6 +279,8 @@ The following contributors merged PRs in this release: @aaronrobertshaw @afercia @andrewhayward @andrewserong @anomiex @arthur791004 @BenjaminZekavica @bfintal @carolinan @Clorith @dcalhoun @derekblank @diegohaz @draganescu @ellatrix @fluiddot @fullofcaffeine @geriux @getdave @ghorivipul97 @glendaviesnz @hellofromtonya @jameskoster @jeryj @jorgefilipecosta @jsnajdr @juanmaguitar @kevin940726 @luisherranz @MaggieCabrera @Mamaduka @michalczaplinski @mirka @noisysocks @ntsekouras @peterwilsoncc @pooja-muchandikar @Presskopp @priethor @ramonjd @richtabor @SantosGuillamot @SavPhill @SaxonF @scruffian @sethrubenstein @spacedmonkey @swissspidy @t-hamano @tellthemachines @tyxla @walbo @westonruter @youknowriad + + = 16.2.1 = ## Changelog From c54dec515ba8dcfcbeab80bd68bb534540a6a335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor?= <27339341+priethor@users.noreply.github.com> Date: Wed, 26 Jul 2023 15:23:48 +0200 Subject: [PATCH 53/61] Make PR label checks non-blocking to merge (#52975) --- .github/workflows/enforce-pr-labels.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/enforce-pr-labels.yml b/.github/workflows/enforce-pr-labels.yml index 0ea94ba5f2292..d5f43a2ac63fa 100644 --- a/.github/workflows/enforce-pr-labels.yml +++ b/.github/workflows/enforce-pr-labels.yml @@ -6,7 +6,6 @@ jobs: type-related-labels: runs-on: ubuntu-latest permissions: - issues: write pull-requests: write steps: - uses: mheap/github-action-required-labels@v5 @@ -16,3 +15,5 @@ jobs: labels: "[Type] Accessibility (a11y), [Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Performance, [Type] Project Management, [Type] Security, [Type] WP Core Ticket" add_comment: true message: "## ⚠️ Type of PR label error\n To merge this PR, it requires {{ errorString }} {{ count }} label indicating the type of PR. Other labels are optional and not being checked here. \n- **Type-related labels to choose from**: {{ provided }}.\n- **Labels found**: {{ applied }}." + token: ${{ secrets.GITHUB_TOKEN }} + exit_type: success From 01adad73560ddd437c5d7dc62e3cc8255e2d7729 Mon Sep 17 00:00:00 2001 From: James Koster Date: Wed, 26 Jul 2023 14:36:18 +0100 Subject: [PATCH 54/61] Add xhuge breakpoint (#52942) --- packages/base-styles/_breakpoints.scss | 1 + packages/base-styles/_mixins.scss | 6 ++++++ packages/edit-site/src/components/page-patterns/style.scss | 3 +++ 3 files changed, 10 insertions(+) diff --git a/packages/base-styles/_breakpoints.scss b/packages/base-styles/_breakpoints.scss index 3dd164c99753a..e180757b8cac9 100644 --- a/packages/base-styles/_breakpoints.scss +++ b/packages/base-styles/_breakpoints.scss @@ -3,6 +3,7 @@ */ // Most used breakpoints +$break-xhuge: 1920px; $break-huge: 1440px; $break-wide: 1280px; $break-xlarge: 1080px; diff --git a/packages/base-styles/_mixins.scss b/packages/base-styles/_mixins.scss index 7dbba40c95aa9..da49b110ddbe7 100644 --- a/packages/base-styles/_mixins.scss +++ b/packages/base-styles/_mixins.scss @@ -5,6 +5,12 @@ * Breakpoint mixins */ +@mixin break-xhuge() { + @media (min-width: #{ ($break-xhuge) }) { + @content; + } +} + @mixin break-huge() { @media (min-width: #{ ($break-huge) }) { @content; diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss index 99c4c4c04757e..400cc36aeaa43 100644 --- a/packages/edit-site/src/components/page-patterns/style.scss +++ b/packages/edit-site/src/components/page-patterns/style.scss @@ -134,6 +134,9 @@ @include break-huge { grid-template-columns: 1fr 1fr 1fr; } + @include break-xhuge { + grid-template-columns: 1fr 1fr 1fr 1fr; + } .edit-site-patterns__pattern { break-inside: avoid-column; display: flex; From fdfffbc41b2219b1d3df940fe2d6240d8c244131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor?= <27339341+priethor@users.noreply.github.com> Date: Wed, 26 Jul 2023 15:46:33 +0200 Subject: [PATCH 55/61] Enforce PR labels workflow: Try using a different token (#52979) --- .github/workflows/enforce-pr-labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/enforce-pr-labels.yml b/.github/workflows/enforce-pr-labels.yml index d5f43a2ac63fa..14c0c60139676 100644 --- a/.github/workflows/enforce-pr-labels.yml +++ b/.github/workflows/enforce-pr-labels.yml @@ -15,5 +15,5 @@ jobs: labels: "[Type] Accessibility (a11y), [Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Performance, [Type] Project Management, [Type] Security, [Type] WP Core Ticket" add_comment: true message: "## ⚠️ Type of PR label error\n To merge this PR, it requires {{ errorString }} {{ count }} label indicating the type of PR. Other labels are optional and not being checked here. \n- **Type-related labels to choose from**: {{ provided }}.\n- **Labels found**: {{ applied }}." - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.GUTENBERG_TOKEN }} exit_type: success From 15035302bbbf259651f1b29993ac2ce5d9a13317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor?= <27339341+priethor@users.noreply.github.com> Date: Wed, 26 Jul 2023 15:56:48 +0200 Subject: [PATCH 56/61] Enforce PR labels: change permissions to `read` (#52980) --- .github/workflows/enforce-pr-labels.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/enforce-pr-labels.yml b/.github/workflows/enforce-pr-labels.yml index 14c0c60139676..6d718068b8b7d 100644 --- a/.github/workflows/enforce-pr-labels.yml +++ b/.github/workflows/enforce-pr-labels.yml @@ -6,7 +6,8 @@ jobs: type-related-labels: runs-on: ubuntu-latest permissions: - pull-requests: write + issues: read + pull-requests: read steps: - uses: mheap/github-action-required-labels@v5 with: @@ -15,5 +16,5 @@ jobs: labels: "[Type] Accessibility (a11y), [Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Performance, [Type] Project Management, [Type] Security, [Type] WP Core Ticket" add_comment: true message: "## ⚠️ Type of PR label error\n To merge this PR, it requires {{ errorString }} {{ count }} label indicating the type of PR. Other labels are optional and not being checked here. \n- **Type-related labels to choose from**: {{ provided }}.\n- **Labels found**: {{ applied }}." - token: ${{ secrets.GUTENBERG_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} exit_type: success From ab8ec235e44f2cd37938c506488eeb41d083e5be Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:43:57 +0100 Subject: [PATCH 57/61] List: fix merging nested lists (#52949) --- .../src/list-item/hooks/use-merge.js | 17 ++-- test/e2e/specs/editor/blocks/list.spec.js | 81 +++++++++++++++++++ 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/packages/block-library/src/list-item/hooks/use-merge.js b/packages/block-library/src/list-item/hooks/use-merge.js index 8c186b27f6dc9..da81fe0a7bfde 100644 --- a/packages/block-library/src/list-item/hooks/use-merge.js +++ b/packages/block-library/src/list-item/hooks/use-merge.js @@ -107,11 +107,18 @@ export default function useMerge( clientId, onMerge ) { } else if ( previousBlockClientId ) { const trailingId = getTrailingId( previousBlockClientId ); registry.batch( () => { - moveBlocksToPosition( - getBlockOrder( clientId ), - clientId, - previousBlockClientId - ); + // When merging a list item with a previous trailing list + // item, we also need to move any nested list items. First, + // check if there's a listed list. If there's a nested list, + // append its nested list items to the trailing list. + const [ nestedListClientId ] = getBlockOrder( clientId ); + if ( nestedListClientId ) { + moveBlocksToPosition( + getBlockOrder( nestedListClientId ), + nestedListClientId, + getBlockRootClientId( trailingId ) + ); + } mergeBlocks( trailingId, clientId ); } ); } else { diff --git a/test/e2e/specs/editor/blocks/list.spec.js b/test/e2e/specs/editor/blocks/list.spec.js index 41c846a975077..f51b559e61384 100644 --- a/test/e2e/specs/editor/blocks/list.spec.js +++ b/test/e2e/specs/editor/blocks/list.spec.js @@ -1367,4 +1367,85 @@ test.describe( 'List (@firefox)', () => { ` ); } ); + + test( 'should merge two list items with nested lists', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { + name: 'core/list', + innerBlocks: [ + { + name: 'core/list-item', + attributes: { content: '1' }, + innerBlocks: [ + { + name: 'core/list', + innerBlocks: [ + { + name: 'core/list-item', + attributes: { content: 'a' }, + }, + ], + }, + ], + }, + { + name: 'core/list-item', + attributes: { content: '2' }, + innerBlocks: [ + { + name: 'core/list', + innerBlocks: [ + { + name: 'core/list-item', + attributes: { content: 'b' }, + }, + ], + }, + ], + }, + ], + } ); + + // Navigate to the third item. + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'ArrowDown' ); + await page.keyboard.press( 'ArrowDown' ); + + await page.keyboard.press( 'Backspace' ); + + // Test caret position. + await page.keyboard.type( '‸' ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/list', + innerBlocks: [ + { + name: 'core/list-item', + attributes: { content: '1' }, + innerBlocks: [ + { + name: 'core/list', + innerBlocks: [ + { + name: 'core/list-item', + attributes: { content: 'a‸2' }, + }, + { + name: 'core/list-item', + attributes: { content: 'b' }, + }, + ], + }, + ], + }, + ], + }, + ] ); + } ); } ); From 7eddee3be86dbeefc06990d817768e15de42d9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor?= <27339341+priethor@users.noreply.github.com> Date: Wed, 26 Jul 2023 18:10:17 +0200 Subject: [PATCH 58/61] Enforce PR labels: use `pull_request_target` event to work with PRs coming from forks (#52981) --- .github/workflows/enforce-pr-labels.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/enforce-pr-labels.yml b/.github/workflows/enforce-pr-labels.yml index 6d718068b8b7d..6d6ea87eea2f4 100644 --- a/.github/workflows/enforce-pr-labels.yml +++ b/.github/workflows/enforce-pr-labels.yml @@ -1,13 +1,12 @@ name: Enforce labels on Pull Request on: - pull_request: + pull_request_target: types: [opened, labeled, unlabeled, synchronize] jobs: type-related-labels: runs-on: ubuntu-latest permissions: - issues: read - pull-requests: read + pull-requests: write steps: - uses: mheap/github-action-required-labels@v5 with: @@ -16,5 +15,4 @@ jobs: labels: "[Type] Accessibility (a11y), [Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Performance, [Type] Project Management, [Type] Security, [Type] WP Core Ticket" add_comment: true message: "## ⚠️ Type of PR label error\n To merge this PR, it requires {{ errorString }} {{ count }} label indicating the type of PR. Other labels are optional and not being checked here. \n- **Type-related labels to choose from**: {{ provided }}.\n- **Labels found**: {{ applied }}." - token: ${{ secrets.GITHUB_TOKEN }} exit_type: success From 168bce1b1560b41e7b2cade55e737b87b13dfe1d Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Wed, 26 Jul 2023 22:45:37 +0100 Subject: [PATCH 59/61] Footnotes: disable based on post type (#52934) * Footnotes: disable based on post type * Address feedback * Fix typo * Format: disable if block is not registered * Lock usesContext api * Use Symbol instead of Math.random --- .../src/components/rich-text/format-edit.js | 100 +++++++++++------- packages/block-editor/src/private-apis.js | 2 + packages/block-library/src/footnotes/edit.js | 12 +++ .../block-library/src/footnotes/format.js | 25 ++++- packages/block-library/src/footnotes/index.js | 1 - 5 files changed, 99 insertions(+), 41 deletions(-) diff --git a/packages/block-editor/src/components/rich-text/format-edit.js b/packages/block-editor/src/components/rich-text/format-edit.js index 75b077ab321d4..a70b9f8f77881 100644 --- a/packages/block-editor/src/components/rich-text/format-edit.js +++ b/packages/block-editor/src/components/rich-text/format-edit.js @@ -2,43 +2,67 @@ * WordPress dependencies */ import { getActiveFormat, getActiveObject } from '@wordpress/rich-text'; +import { useContext, useMemo } from '@wordpress/element'; -export default function FormatEdit( { - formatTypes, - onChange, - onFocus, - value, - forwardedRef, -} ) { - return formatTypes.map( ( settings ) => { - const { name, edit: Edit } = settings; - - if ( ! Edit ) { - return null; - } - - const activeFormat = getActiveFormat( value, name ); - const isActive = activeFormat !== undefined; - const activeObject = getActiveObject( value ); - const isObjectActive = - activeObject !== undefined && activeObject.type === name; - - return ( - - ); - } ); +/** + * Internal dependencies + */ +import BlockContext from '../block-context'; + +const DEFAULT_BLOCK_CONTEXT = {}; + +export const usesContextKey = Symbol( 'usesContext' ); + +function Edit( { onChange, onFocus, value, forwardedRef, settings } ) { + const { + name, + edit: EditFunction, + [ usesContextKey ]: usesContext, + } = settings; + + const blockContext = useContext( BlockContext ); + + // Assign context values using the block type's declared context needs. + const context = useMemo( () => { + return usesContext + ? Object.fromEntries( + Object.entries( blockContext ).filter( ( [ key ] ) => + usesContext.includes( key ) + ) + ) + : DEFAULT_BLOCK_CONTEXT; + }, [ usesContext, blockContext ] ); + + if ( ! EditFunction ) { + return null; + } + + const activeFormat = getActiveFormat( value, name ); + const isActive = activeFormat !== undefined; + const activeObject = getActiveObject( value ); + const isObjectActive = + activeObject !== undefined && activeObject.type === name; + + return ( + + ); +} + +export default function FormatEdit( { formatTypes, ...props } ) { + return formatTypes.map( ( settings ) => ( + + ) ); } diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index 20aeaa2a79040..e4bdb5d37b16b 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -23,6 +23,7 @@ import { default as ReusableBlocksRenameHint, useReusableBlocksRenameHint, } from './components/inserter/reusable-block-rename-hint'; +import { usesContextKey } from './components/rich-text/format-edit'; /** * Private @wordpress/block-editor APIs. @@ -49,4 +50,5 @@ lock( privateApis, { ResolutionTool, ReusableBlocksRenameHint, useReusableBlocksRenameHint, + usesContextKey, } ); diff --git a/packages/block-library/src/footnotes/edit.js b/packages/block-library/src/footnotes/edit.js index fdfe7a94039af..b8b92170fe217 100644 --- a/packages/block-library/src/footnotes/edit.js +++ b/packages/block-library/src/footnotes/edit.js @@ -17,6 +17,18 @@ export default function FootnotesEdit( { context: { postType, postId } } ) { const footnotes = meta?.footnotes ? JSON.parse( meta.footnotes ) : []; const blockProps = useBlockProps(); + if ( postType !== 'post' && postType !== 'page' ) { + return ( +
+ } + label={ __( 'Footnotes' ) } + // To do: add instructions. We can't add new string in RC. + /> +
+ ); + } + if ( ! footnotes.length ) { return (
diff --git a/packages/block-library/src/footnotes/format.js b/packages/block-library/src/footnotes/format.js index eb700787d02ee..2086005a50993 100644 --- a/packages/block-library/src/footnotes/format.js +++ b/packages/block-library/src/footnotes/format.js @@ -12,14 +12,18 @@ import { insertObject } from '@wordpress/rich-text'; import { RichTextToolbarButton, store as blockEditorStore, + privateApis, } from '@wordpress/block-editor'; import { useSelect, useDispatch, useRegistry } from '@wordpress/data'; -import { createBlock } from '@wordpress/blocks'; +import { createBlock, store as blocksStore } from '@wordpress/blocks'; /** * Internal dependencies */ import { name } from './block.json'; +import { unlock } from '../lock-unlock'; + +const { usesContextKey } = unlock( privateApis ); export const formatName = 'core/footnote'; export const format = { @@ -30,7 +34,13 @@ export const format = { 'data-fn': 'data-fn', }, contentEditable: false, - edit: function Edit( { value, onChange, isObjectActive } ) { + [ usesContextKey ]: [ 'postType' ], + edit: function Edit( { + value, + onChange, + isObjectActive, + context: { postType }, + } ) { const registry = useRegistry(); const { getSelectedBlockClientId, @@ -38,9 +48,20 @@ export const format = { getBlockName, getBlocks, } = useSelect( blockEditorStore ); + const footnotesBlockType = useSelect( ( select ) => + select( blocksStore ).getBlockType( name ) + ); const { selectionChange, insertBlock } = useDispatch( blockEditorStore ); + if ( ! footnotesBlockType ) { + return null; + } + + if ( postType !== 'post' && postType !== 'page' ) { + return null; + } + function onClick() { registry.batch( () => { let id; diff --git a/packages/block-library/src/footnotes/index.js b/packages/block-library/src/footnotes/index.js index c0f3d60ada543..c5e851af7e033 100644 --- a/packages/block-library/src/footnotes/index.js +++ b/packages/block-library/src/footnotes/index.js @@ -21,7 +21,6 @@ export const settings = { edit, }; -// Would be good to remove the format and HoR if the block is unregistered. registerFormatType( formatName, format ); export const init = () => { From 1ca79227da74ed66ff09691639669d9a2555914b Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Thu, 27 Jul 2023 11:18:05 +1200 Subject: [PATCH 60/61] Patterns: correctly color code unsynced patterns titles in site editor (#52958) Co-authored-by: Andrew Serong <14988353+andrewserong@users.noreply.github.com> --- .../header-edit-mode/document-actions/index.js | 5 ++++- .../header-edit-mode/document-actions/style.scss | 11 +++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js index 3a048e753631f..eea8616be1536 100644 --- a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js +++ b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js @@ -128,7 +128,10 @@ function TemplateDocumentActions( { className, onBack } ) { return ( 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 d26bbdaf28ff6..95ebfb28ff000 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 @@ -24,6 +24,15 @@ @include break-large() { width: min(100%, 450px); } + + &.is-synced-entity { + .edit-site-document-actions__title { + color: var(--wp-block-synced-color); + h1 { + color: var(--wp-block-synced-color); + } + } + } } .edit-site-document-actions__command { @@ -36,7 +45,6 @@ .edit-site-document-actions__title { flex-grow: 1; - color: var(--wp-block-synced-color); overflow: hidden; grid-column: 2 / 3; @@ -48,7 +56,6 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - color: var(--wp-block-synced-color); } .edit-site-document-actions.is-page & { From 4404be955e3daf2ac87b1cdd3d211f9a16868ddf Mon Sep 17 00:00:00 2001 From: Derek Blank Date: Thu, 27 Jul 2023 09:18:48 +1000 Subject: [PATCH 61/61] [RNMobile] Do not include MediaReplaceFlow component in native Gallery toolbar (#52966) * Do not display MediaReplaceFlow component in native Gallery block * Add integration test to hide MediaReplaceFlow component in Gallery block * Update CHANGELOG * Update test selector syntax Co-authored-by: David Calhoun --------- Co-authored-by: David Calhoun --- .../media-replace-flow/index.native.js | 15 ++++++-- packages/block-library/src/gallery/edit.js | 38 ++++++++++--------- .../src/gallery/test/index.native.js | 14 +++++++ packages/react-native-editor/CHANGELOG.md | 1 + 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/packages/block-editor/src/components/media-replace-flow/index.native.js b/packages/block-editor/src/components/media-replace-flow/index.native.js index 49e98a3b3b9ca..ca2ce4ee78c63 100644 --- a/packages/block-editor/src/components/media-replace-flow/index.native.js +++ b/packages/block-editor/src/components/media-replace-flow/index.native.js @@ -1,3 +1,12 @@ -// MediaReplaceFlow component is not yet implemented in the native version, -// so we return an empty component instead. -export default () => null; +/** + * External dependencies + */ +import { View } from 'react-native'; + +// MediaReplaceFlow component is not yet implemented in the native version. +// For testing purposes, we are using an empty View component with a testID prop. +const MediaReplaceFlow = () => { + return ; +}; + +export default MediaReplaceFlow; diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 5ae8cd2d6820d..198892e286aba 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -635,25 +635,27 @@ function GalleryEdit( props ) { /> ) } - - image.id ) - .map( ( image ) => image.id ) } - addToGallery={ hasImageIds } - /> - { Platform.isWeb && ( - + <> + + image.id ) + .map( ( image ) => image.id ) } + addToGallery={ hasImageIds } + /> + + + ) } { expect( getEditorHtml() ).toMatchSnapshot(); } ); + it( 'does not display MediaReplaceFlow component within the block toolbar', async () => { + const screen = await initializeWithGalleryBlock( { + numberOfItems: 3, + media, + } ); + const { queryByTestId } = screen; + + fireEvent.press( getBlock( screen, 'Gallery' ) ); + + // Expect the native MediaReplaceFlow component to not be present in the block toolbar + const mediaReplaceFlow = queryByTestId( 'media-replace-flow' ); + expect( mediaReplaceFlow ).toBeNull(); + } ); + // Test cases related to TC013 - Settings - Columns // Reference: https://github.com/wordpress-mobile/test-cases/blob/trunk/test-cases/gutenberg/gallery.md#tc013 describe( 'Columns setting', () => { diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 4ddfda16c8c78..b13b21a698a8e 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -10,6 +10,7 @@ For each user feature we should also add a importance categorization label to i --> ## Unreleased +- [*] Remove visual gap in mobile toolbar when a Gallery block is selected [#52966] ## 1.100.1 - [**] Add WP hook for registering non-core blocks [#52791]