From 0ea6c91963d989daa103630649df9d746a7efbce Mon Sep 17 00:00:00 2001 From: Joen A <1204802+jasmussen@users.noreply.github.com> Date: Fri, 28 Jul 2023 11:47:34 +0200 Subject: [PATCH 01/14] Top toolbar: Fix issues with save button overlap, and plugin buttons (#53101) * Shorten the width of the top toolbar overlay and make doc title smaller. * Add a scrim and a margin to handle plugin buttons better. --- .../src/components/block-tools/style.scss | 15 ++++++++++++--- .../components/header/header-toolbar/style.scss | 4 ++++ .../header-edit-mode/document-actions/style.scss | 5 +++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-tools/style.scss b/packages/block-editor/src/components/block-tools/style.scss index 6ff3bbc721a10..1f08727bbfb9b 100644 --- a/packages/block-editor/src/components/block-tools/style.scss +++ b/packages/block-editor/src/components/block-tools/style.scss @@ -125,9 +125,18 @@ display: none; } + // Add a scrim to the right of the collapsed button. + &.is-collapsed::after { + content: ""; + position: absolute; + left: 100%; + width: $grid-unit-60; + height: 100%; + background: linear-gradient(to right, $white, transparent); + } + // on desktop and tablet viewports the toolbar is fixed // on top of interface header - @include break-medium() { &.is-fixed { @@ -308,7 +317,7 @@ } } - // on tablet vewports the toolbar is fixed + // on tablet viewports the toolbar is fixed // on top of interface header and covers the whole header // except for the inserter on the left @include break-medium() { @@ -328,7 +337,7 @@ // in full screen mode we need to account for // the combined with of the tools at the right of the header and the margin left // of the toolbar which includes four buttons - width: calc(100% - 240px - #{4 * $grid-unit-80}); + width: calc(100% - 280px - #{4 * $grid-unit-80}); } } } diff --git a/packages/edit-post/src/components/header/header-toolbar/style.scss b/packages/edit-post/src/components/header/header-toolbar/style.scss index 694dcb5a2d678..4a53be477e025 100644 --- a/packages/edit-post/src/components/header/header-toolbar/style.scss +++ b/packages/edit-post/src/components/header/header-toolbar/style.scss @@ -82,6 +82,10 @@ align-items: center; padding-left: $grid-unit-10; + // Some plugins add buttons here despite best practices. + // Push them a bit rightwards to fit the top toolbar. + margin-right: $grid-unit-10; + @include break-small() { padding-left: $grid-unit-30; } 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 dd442264e6398..d3116b4576490 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 @@ -13,6 +13,11 @@ border-radius: 4px; width: min(100%, 450px); + // Make the document title shorter in top-toolbar mode, as it has to be covered. + .has-fixed-toolbar & { + width: min(100%, 380px); + } + .components-button { &:hover { color: var(--wp-block-synced-color); From b93e9c12c9acad8dd2eb4783faf198a6b1d07cfb Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Fri, 28 Jul 2023 16:55:37 +0100 Subject: [PATCH 02/14] Remove block tools back compat component schedule for deprecated in 6.3 (#53115) * Removes usage of BlockToolsBackCompat * Remove unwanted BlockTools from Nav sidebar --- packages/block-editor/src/components/block-list/index.js | 9 +++------ .../navigation-menu-content.js | 5 +---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js index 6df09478931a2..04b767d9568b7 100644 --- a/packages/block-editor/src/components/block-list/index.js +++ b/packages/block-editor/src/components/block-list/index.js @@ -33,7 +33,6 @@ import { useInBetweenInserter } from './use-in-between-inserter'; import { store as blockEditorStore } from '../../store'; import { usePreParsePatterns } from '../../utils/pre-parse-patterns'; import { LayoutProvider, defaultLayout } from './layout'; -import BlockToolsBackCompat from '../block-tools/back-compat'; import { useBlockSelectionClearer } from '../block-selection-clearer'; import { useInnerBlocksProps } from '../inner-blocks'; import { @@ -127,11 +126,9 @@ function Root( { className, ...settings } ) { export default function BlockList( settings ) { usePreParsePatterns(); return ( - - - - - + + + ); } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js index 35a06b53786ec..6cc3138c9c2f7 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js @@ -5,7 +5,6 @@ import { privateApis as blockEditorPrivateApis, store as blockEditorStore, BlockList, - BlockTools, } from '@wordpress/block-editor'; import { useDispatch, useSelect } from '@wordpress/data'; import { createBlock } from '@wordpress/blocks'; @@ -106,9 +105,7 @@ export default function NavigationMenuContent( { rootClientId } ) { /> ) }
- - - +
); From 9a04e31fe8e34991d42ff9e032cd612ac4a1e421 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Mon, 31 Jul 2023 11:12:18 +0200 Subject: [PATCH 03/14] Footnotes/RichText: fix getRichTextValues for deeply nested blocks (#53034) --- .../rich-text/get-rich-text-values.js | 50 ++++++++----------- packages/blocks/src/api/serializer.js | 11 ++-- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/packages/block-editor/src/components/rich-text/get-rich-text-values.js b/packages/block-editor/src/components/rich-text/get-rich-text-values.js index 9af2eb054cf6c..bd1c62ea5e6f6 100644 --- a/packages/block-editor/src/components/rich-text/get-rich-text-values.js +++ b/packages/block-editor/src/components/rich-text/get-rich-text-values.js @@ -18,13 +18,13 @@ import { Content } from './content'; * except that it does not render the elements to a string, but instead collects * the values of all rich text `Content` elements. */ -function addValuesForElement( element, ...args ) { +function addValuesForElement( element, values, innerBlocks ) { if ( null === element || undefined === element || false === element ) { return; } if ( Array.isArray( element ) ) { - return addValuesForElements( element, ...args ); + return addValuesForElements( element, values, innerBlocks ); } switch ( typeof element ) { @@ -38,13 +38,12 @@ function addValuesForElement( element, ...args ) { switch ( type ) { case StrictMode: case Fragment: - return addValuesForElements( props.children, ...args ); + return addValuesForElements( props.children, values, innerBlocks ); case RawHTML: return; case InnerBlocks.Content: - return addValuesForBlocks( ...args ); + return addValuesForBlocks( values, innerBlocks ); case Content: - const [ values ] = args; values.push( props.value ); return; } @@ -52,21 +51,19 @@ function addValuesForElement( element, ...args ) { switch ( typeof type ) { case 'string': if ( typeof props.children !== 'undefined' ) { - return addValuesForElements( props.children, ...args ); + return addValuesForElements( + props.children, + values, + innerBlocks + ); } return; case 'function': - if ( - type.prototype && - typeof type.prototype.render === 'function' - ) { - return addValuesForElement( - new type( props ).render(), - ...args - ); - } - - return addValuesForElement( type( props ), ...args ); + const el = + type.prototype && typeof type.prototype.render === 'function' + ? new type( props ).render() + : type( props ); + return addValuesForElement( el, values, innerBlocks ); } } @@ -78,20 +75,17 @@ function addValuesForElements( children, ...args ) { } } -function _getSaveElement( name, attributes, innerBlocks ) { - return getSaveElement( - name, - attributes, - innerBlocks.map( ( block ) => - _getSaveElement( block.name, block.attributes, block.innerBlocks ) - ) - ); -} - function addValuesForBlocks( values, blocks ) { for ( let i = 0; i < blocks.length; i++ ) { const { name, attributes, innerBlocks } = blocks[ i ]; - const saveElement = _getSaveElement( name, attributes, innerBlocks ); + const saveElement = getSaveElement( + name, + attributes, + // Instead of letting save elements use `useInnerBlocksProps.save`, + // force them to use InnerBlocks.Content instead so we can intercept + // a single component. + + ); addValuesForElement( saveElement, values, innerBlocks ); } } diff --git a/packages/blocks/src/api/serializer.js b/packages/blocks/src/api/serializer.js index 92559b1a0e717..6c45f08ee6aab 100644 --- a/packages/blocks/src/api/serializer.js +++ b/packages/blocks/src/api/serializer.js @@ -98,12 +98,11 @@ export function getBlockProps( props = {} ) { */ export function getInnerBlocksProps( props = {} ) { const { innerBlocks } = innerBlocksPropsProvider; - const [ firstBlock ] = innerBlocks ?? []; - if ( ! firstBlock ) return props; - // If the innerBlocks passed to `getSaveElement` are not blocks but already - // components, return the props as is. This is the case for - // `getRichTextValues`. - if ( ! firstBlock.clientId ) return { ...props, children: innerBlocks }; + // Allow a different component to be passed to getSaveElement to handle + // inner blocks, bypassing the default serialisation. + if ( ! Array.isArray( innerBlocks ) ) { + return { ...props, children: innerBlocks }; + } // Value is an array of blocks, so defer to block serializer. const html = serialize( innerBlocks, { isInnerBlocks: true } ); // Use special-cased raw HTML tag to avoid default escaping. From 86b95ec412425565dab37b9149f74770747ab5d2 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Mon, 31 Jul 2023 06:13:48 -0700 Subject: [PATCH 04/14] Defer to preceding handlers in command palette keyboard shortcut (#53001) --- packages/commands/src/components/command-menu.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/commands/src/components/command-menu.js b/packages/commands/src/components/command-menu.js index aa77925763007..085b67997e482 100644 --- a/packages/commands/src/components/command-menu.js +++ b/packages/commands/src/components/command-menu.js @@ -165,7 +165,11 @@ export function CommandMenu() { useShortcut( 'core/commands', + /** @type {import('react').KeyboardEventHandler} */ ( event ) => { + // Bails to avoid obscuring the effect of the preceding handler(s). + if ( event.defaultPrevented ) return; + event.preventDefault(); if ( isOpen ) { close(); From 6903bb1576186396fb288561dea59a045a6c556d Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Tue, 1 Aug 2023 04:47:10 +0900 Subject: [PATCH 05/14] Image block: fix image size at wide and full width (#53184) --- packages/block-library/src/image/image.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index fd410fe3cbe5f..5fe5c93f4199b 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -566,9 +566,9 @@ export default function Image( { className={ borderProps.className } style={ { width: - ( width && height ) || aspectRatio ? '100%' : 'inherit', + ( width && height ) || aspectRatio ? '100%' : undefined, height: - ( width && height ) || aspectRatio ? '100%' : 'inherit', + ( width && height ) || aspectRatio ? '100%' : undefined, objectFit: scale, ...borderProps.style, } } From 1d8bb10394ec184344a4ee8167673f8d6e8f2179 Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Tue, 1 Aug 2023 03:18:29 +0200 Subject: [PATCH 06/14] Fix regression with Edit site Navigate regions (#52940) * Make the navigabel region wrap the inert sidebar. * Adjust animation. --- .../edit-site/src/components/layout/index.js | 47 +++++++++++-------- .../src/components/layout/style.scss | 4 +- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/packages/edit-site/src/components/layout/index.js b/packages/edit-site/src/components/layout/index.js index 1a1c2f3a3830a..75a42360d599d 100644 --- a/packages/edit-site/src/components/layout/index.js +++ b/packages/edit-site/src/components/layout/index.js @@ -260,28 +260,35 @@ export default function Layout() {
- - + - - + + diff --git a/packages/edit-site/src/components/layout/style.scss b/packages/edit-site/src/components/layout/style.scss index 2fc8bbc6a5091..11c7bdeeaf2a1 100644 --- a/packages/edit-site/src/components/layout/style.scss +++ b/packages/edit-site/src/components/layout/style.scss @@ -58,7 +58,7 @@ display: flex; } -.edit-site-layout__sidebar { +.edit-site-layout__sidebar-region { z-index: z-index(".edit-site-layout__sidebar"); width: 100vw; flex-shrink: 0; @@ -75,7 +75,7 @@ top: 0; } - > div { + .edit-site-layout__sidebar { display: flex; flex-direction: column; height: 100%; From 0bcda10cb0846b759ef2ee7790c0a4f883383569 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Tue, 1 Aug 2023 10:12:14 +0800 Subject: [PATCH 07/14] Fix not expanding pattern in page editor (#53169) --------- Co-authored-by: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> --- packages/block-library/src/pattern/edit.js | 20 ++++++++++++++++++- .../disable-non-page-content-blocks.js | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/pattern/edit.js b/packages/block-library/src/pattern/edit.js index b4900536ec274..824960f90b379 100644 --- a/packages/block-library/src/pattern/edit.js +++ b/packages/block-library/src/pattern/edit.js @@ -9,7 +9,12 @@ import { useBlockProps, } from '@wordpress/block-editor'; -const PatternEdit = ( { attributes, clientId } ) => { +/** + * Internal dependencies + */ +import { unlock } from '../lock-unlock'; + +const PatternEdit = ( { attributes, clientId, rootClientId } ) => { const selectedPattern = useSelect( ( select ) => select( blockEditorStore ).__experimentalGetParsedPattern( @@ -20,6 +25,8 @@ const PatternEdit = ( { attributes, clientId } ) => { const { replaceBlocks, __unstableMarkNextChangeAsNotPersistent } = useDispatch( blockEditorStore ); + const { setBlockEditingMode } = unlock( useDispatch( blockEditorStore ) ); + const { getBlockEditingMode } = unlock( useSelect( blockEditorStore ) ); // Run this effect when the component loads. // This adds the Pattern's contents to the post. @@ -38,15 +45,26 @@ const PatternEdit = ( { attributes, clientId } ) => { const clonedBlocks = selectedPattern.blocks.map( ( block ) => cloneBlock( block ) ); + const rootEditingMode = getBlockEditingMode( rootClientId ); + // Temporarily set the root block to default mode to allow replacing the pattern. + // This could happen when the page is disabling edits of non-content blocks. + __unstableMarkNextChangeAsNotPersistent(); + setBlockEditingMode( rootClientId, 'default' ); __unstableMarkNextChangeAsNotPersistent(); replaceBlocks( clientId, clonedBlocks ); + // Restore the root block's original mode. + __unstableMarkNextChangeAsNotPersistent(); + setBlockEditingMode( rootClientId, rootEditingMode ); } ); } }, [ + rootClientId, clientId, selectedPattern?.blocks, __unstableMarkNextChangeAsNotPersistent, replaceBlocks, + getBlockEditingMode, + setBlockEditingMode, ] ); const props = useBlockProps(); diff --git a/packages/edit-site/src/components/page-content-focus-manager/disable-non-page-content-blocks.js b/packages/edit-site/src/components/page-content-focus-manager/disable-non-page-content-blocks.js index 7b184ff253c7e..eb0c622840a75 100644 --- a/packages/edit-site/src/components/page-content-focus-manager/disable-non-page-content-blocks.js +++ b/packages/edit-site/src/components/page-content-focus-manager/disable-non-page-content-blocks.js @@ -49,7 +49,7 @@ export function useDisableNonPageContentBlocks() { const withDisableNonPageContentBlocks = createHigherOrderComponent( ( BlockEdit ) => ( props ) => { - const isDescendentOfQueryLoop = !! props.context.queryId; + const isDescendentOfQueryLoop = props.context.queryId !== undefined; const isPageContent = PAGE_CONTENT_BLOCK_TYPES.includes( props.name ) && ! isDescendentOfQueryLoop; From bb9bc101785e14fc03c842c0bbac1aa31497a091 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Tue, 1 Aug 2023 04:27:06 +0200 Subject: [PATCH 08/14] Footnotes: fix published preview (#53072) * Footnotes: fix published preview * remove var dump * Fix php lint * PHP lint * Address feedback * Add e2e test --- .../block-library/src/footnotes/index.php | 73 +++++++++++++++++++ packages/core-data/src/actions.js | 9 ++- packages/editor/src/store/selectors.js | 4 +- .../specs/editor/various/footnotes.spec.js | 40 ++++++++++ 4 files changed, 121 insertions(+), 5 deletions(-) diff --git a/packages/block-library/src/footnotes/index.php b/packages/block-library/src/footnotes/index.php index 9815033820406..712cd571c8b0e 100644 --- a/packages/block-library/src/footnotes/index.php +++ b/packages/block-library/src/footnotes/index.php @@ -212,3 +212,76 @@ 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 ); + +/** + * The REST API autosave endpoint doesn't save meta, so we can use the + * `wp_creating_autosave` when it updates an exiting autosave, and + * `_wp_put_post_revision` when it creates a new autosave. + * + * @since 6.3.0 + * + * @param int|array $autosave The autosave ID or array. + */ +function _wp_rest_api_autosave_meta( $autosave ) { + // Ensure it's a REST API request. + if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) { + return; + } + + $body = rest_get_server()->get_raw_data(); + $body = json_decode( $body, true ); + + if ( ! isset( $body['meta']['footnotes'] ) ) { + return; + } + + // `wp_creating_autosave` passes the array, + // `_wp_put_post_revision` passes the ID. + $id = is_int( $autosave ) ? $autosave : $autosave['ID']; + + if ( ! $id ) { + return; + } + + update_post_meta( $id, 'footnotes', $body['meta']['footnotes'] ); +} +// See https://github.com/WordPress/wordpress-develop/blob/2103cb9966e57d452c94218bbc3171579b536a40/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php#L391C1-L391C1. +add_action( 'wp_creating_autosave', '_wp_rest_api_autosave_meta' ); +// See https://github.com/WordPress/wordpress-develop/blob/2103cb9966e57d452c94218bbc3171579b536a40/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php#L398. +// Then https://github.com/WordPress/wordpress-develop/blob/2103cb9966e57d452c94218bbc3171579b536a40/src/wp-includes/revision.php#L367. +add_action( '_wp_put_post_revision', '_wp_rest_api_autosave_meta' ); + +/** + * This is a workaround for the autosave endpoint returning early if the + * revision field are equal. The problem is that "footnotes" is not real + * revision post field, so there's nothing to compare against. + * + * This trick sets the "footnotes" field (value doesn't matter), which will + * cause the autosave endpoint to always update the latest revision. That should + * be fine, it should be ok to update the revision even if nothing changed. Of + * course, this is temporary fix. + * + * @since 6.3.0 + * + * @param WP_Post $prepared_post The prepared post object. + * @param WP_REST_Request $request The request object. + * + * See https://github.com/WordPress/wordpress-develop/blob/2103cb9966e57d452c94218bbc3171579b536a40/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php#L365-L384. + * See https://github.com/WordPress/wordpress-develop/blob/2103cb9966e57d452c94218bbc3171579b536a40/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php#L219. + */ +function _wp_rest_api_force_autosave_difference( $prepared_post, $request ) { + // We only want to be altering POST requests. + if ( $request->get_method() !== 'POST' ) { + return $prepared_post; + } + + // Only alter requests for the '/autosaves' route. + if ( substr( $request->get_route(), -strlen( '/autosaves' ) ) !== '/autosaves' ) { + return $prepared_post; + } + + $prepared_post->footnotes = '[]'; + return $prepared_post; +} + +add_filter( 'rest_pre_insert_post', '_wp_rest_api_force_autosave_difference', 10, 2 ); diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index 2170e3ffcb4ae..56b03dc274bf3 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -554,9 +554,12 @@ export const saveEntityRecord = data = Object.keys( data ).reduce( ( acc, key ) => { if ( - [ 'title', 'excerpt', 'content' ].includes( - key - ) + [ + 'title', + 'excerpt', + 'content', + 'meta', + ].includes( key ) ) { acc[ key ] = data[ key ]; } diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index 51b307dce9e7c..ecc1a0f8240a9 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -604,8 +604,8 @@ export const isEditedPostAutosaveable = createRegistrySelector( return true; } - // If the title or excerpt has changed, the post is autosaveable. - return [ 'title', 'excerpt' ].some( + // If title, excerpt, or meta have changed, the post is autosaveable. + return [ 'title', 'excerpt', 'meta' ].some( ( field ) => getPostRawValue( autosave[ field ] ) !== getEditedPostAttribute( state, field ) diff --git a/test/e2e/specs/editor/various/footnotes.spec.js b/test/e2e/specs/editor/various/footnotes.spec.js index ad3ab149095d2..b96d4530cb499 100644 --- a/test/e2e/specs/editor/various/footnotes.spec.js +++ b/test/e2e/specs/editor/various/footnotes.spec.js @@ -360,4 +360,44 @@ test.describe( 'Footnotes', () => { }, ] ); } ); + + test( 'can be previewed when published', async ( { editor, page } ) => { + await editor.canvas.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'a' ); + + await editor.showBlockToolbar(); + await editor.clickBlockToolbarButton( 'More' ); + await page.locator( 'button:text("Footnote")' ).click(); + + await page.keyboard.type( '1' ); + + // Publish post. + await editor.publishPost(); + + await editor.canvas.click( 'ol.wp-block-footnotes li span' ); + await page.keyboard.press( 'End' ); + await page.keyboard.type( '2' ); + + const editorPage = page; + const previewPage = await editor.openPreviewPage(); + + await expect( + previewPage.locator( 'ol.wp-block-footnotes li' ) + ).toHaveText( '12 ↩︎' ); + + await previewPage.close(); + await editorPage.bringToFront(); + + // Test again, this time with an existing revision (different code + // path). + await editor.canvas.click( 'ol.wp-block-footnotes li span' ); + await page.keyboard.press( 'End' ); + await page.keyboard.type( '3' ); + + const previewPage2 = await editor.openPreviewPage(); + + await expect( + previewPage2.locator( 'ol.wp-block-footnotes li' ) + ).toHaveText( '123 ↩︎' ); + } ); } ); From ef4f1f2cbc683daf136934dc80cb1116493dea76 Mon Sep 17 00:00:00 2001 From: Ramon Date: Tue, 1 Aug 2023 12:29:34 +1000 Subject: [PATCH 09/14] Footnotes: disable for synced patterns and prevent duplication for pages in site editor (#53003) * Initial commit: - Prevent footnote creation withing core/block - Only insert a footnote if one isn't found in the entity block list * Try grabbing controlled blocks from parent post content block * Cache `selectedClientId` Get hasParentCoreBlocks using separate useSelect call. * Rename hasParentCoreBlocks to isBlockWithinPattern Add comments * Removing while loop since we're already fetching the post content parent in the `getBlockParentsByBlockName` call above * Reinstating while loop because it can deal with nested blocks --------- Co-authored-by: Andrew Serong <14988353+andrewserong@users.noreply.github.com> --- .../block-library/src/footnotes/format.js | 52 +++++++++++++++++-- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/packages/block-library/src/footnotes/format.js b/packages/block-library/src/footnotes/format.js index 2086005a50993..c7730c53316b7 100644 --- a/packages/block-library/src/footnotes/format.js +++ b/packages/block-library/src/footnotes/format.js @@ -26,6 +26,10 @@ import { unlock } from '../lock-unlock'; const { usesContextKey } = unlock( privateApis ); export const formatName = 'core/footnote'; + +const POST_CONTENT_BLOCK_NAME = 'core/post-content'; +const SYNCED_PATTERN_BLOCK_NAME = 'core/block'; + export const format = { title: __( 'Footnote' ), tagName: 'sup', @@ -44,13 +48,30 @@ export const format = { const registry = useRegistry(); const { getSelectedBlockClientId, + getBlocks, getBlockRootClientId, getBlockName, - getBlocks, + getBlockParentsByBlockName, } = useSelect( blockEditorStore ); const footnotesBlockType = useSelect( ( select ) => select( blocksStore ).getBlockType( name ) ); + /* + * This useSelect exists because we need to use its return value + * outside the event callback. + */ + const isBlockWithinPattern = useSelect( ( select ) => { + const { + getBlockParentsByBlockName: _getBlockParentsByBlockName, + getSelectedBlockClientId: _getSelectedBlockClientId, + } = select( blockEditorStore ); + const parentCoreBlocks = _getBlockParentsByBlockName( + _getSelectedBlockClientId(), + SYNCED_PATTERN_BLOCK_NAME + ); + return parentCoreBlocks && parentCoreBlocks.length > 0; + }, [] ); + const { selectionChange, insertBlock } = useDispatch( blockEditorStore ); @@ -62,6 +83,11 @@ export const format = { return null; } + // Checks if the selected block lives within a pattern. + if ( isBlockWithinPattern ) { + return null; + } + function onClick() { registry.batch( () => { let id; @@ -86,10 +112,27 @@ export const format = { onChange( newValue ); } + const selectedClientId = getSelectedBlockClientId(); + + /* + * Attempts to find a common parent post content block. + * This allows for locating blocks within a page edited in the site editor. + */ + const parentPostContent = getBlockParentsByBlockName( + selectedClientId, + POST_CONTENT_BLOCK_NAME + ); + + // When called with a post content block, getBlocks will return + // the block with controlled inner blocks included. + const blocks = parentPostContent.length + ? getBlocks( parentPostContent[ 0 ] ) + : getBlocks(); + // BFS search to find the first footnote block. let fnBlock = null; { - const queue = [ ...getBlocks() ]; + const queue = [ ...blocks ]; while ( queue.length ) { const block = queue.shift(); if ( block.name === name ) { @@ -104,12 +147,11 @@ export const format = { // When there is no footnotes block in the post, create one and // insert it at the bottom. if ( ! fnBlock ) { - const clientId = getSelectedBlockClientId(); - let rootClientId = getBlockRootClientId( clientId ); + let rootClientId = getBlockRootClientId( selectedClientId ); while ( rootClientId && - getBlockName( rootClientId ) !== 'core/post-content' + getBlockName( rootClientId ) !== POST_CONTENT_BLOCK_NAME ) { rootClientId = getBlockRootClientId( rootClientId ); } From 2d8d1da238f45a189e39bf87fe7ffa0309a6458e Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Tue, 1 Aug 2023 05:17:06 +0200 Subject: [PATCH 10/14] Footnotes: add missing _ in revision field filter (#53135) * Footnotes: add missing _ in revision field filter * Use correct hook name * Revert prefixing callback names --- packages/block-library/src/footnotes/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/footnotes/index.php b/packages/block-library/src/footnotes/index.php index 712cd571c8b0e..bd7734d7d02d1 100644 --- a/packages/block-library/src/footnotes/index.php +++ b/packages/block-library/src/footnotes/index.php @@ -211,7 +211,7 @@ function wp_add_footnotes_to_revision( $fields ) { 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 ); +add_filter( '_wp_post_revision_field_footnotes', 'wp_get_footnotes_from_revision', 10, 3 ); /** * The REST API autosave endpoint doesn't save meta, so we can use the From 2baf81ceb17df1616a9f331c73ac02340d0738c5 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Tue, 1 Aug 2023 07:07:53 +0300 Subject: [PATCH 11/14] don't display BlockContextualToolbar at all in contentonly locking (#53110) --- .../block-tools/block-contextual-toolbar.js | 78 ++++++++++--------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js b/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js index f0fc28c7fbbd2..7dcd88d1cfd10 100644 --- a/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js +++ b/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js @@ -32,48 +32,56 @@ function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) { const toolbarButtonRef = useRef(); const isLargeViewport = useViewportMatch( 'medium' ); - const { blockType, hasParents, showParentSelector, selectedBlockClientId } = - useSelect( ( select ) => { - const { - getBlockName, - getBlockParents, - getSelectedBlockClientIds, - getBlockEditingMode, - } = unlock( select( blockEditorStore ) ); - const { getBlockType } = select( blocksStore ); - const selectedBlockClientIds = getSelectedBlockClientIds(); - const _selectedBlockClientId = selectedBlockClientIds[ 0 ]; - const parents = getBlockParents( _selectedBlockClientId ); - const firstParentClientId = parents[ parents.length - 1 ]; - const parentBlockName = getBlockName( firstParentClientId ); - const parentBlockType = getBlockType( parentBlockName ); + const { + blockType, + hasParents, + showParentSelector, + selectedBlockClientId, + isContentOnly, + } = useSelect( ( select ) => { + const { + getBlockName, + getBlockParents, + getSelectedBlockClientIds, + getBlockEditingMode, + } = unlock( select( blockEditorStore ) ); + const { getBlockType } = select( blocksStore ); + const selectedBlockClientIds = getSelectedBlockClientIds(); + const _selectedBlockClientId = selectedBlockClientIds[ 0 ]; + const parents = getBlockParents( _selectedBlockClientId ); + const firstParentClientId = parents[ parents.length - 1 ]; + const parentBlockName = getBlockName( firstParentClientId ); + const parentBlockType = getBlockType( parentBlockName ); - return { - selectedBlockClientId: _selectedBlockClientId, - blockType: - _selectedBlockClientId && - getBlockType( getBlockName( _selectedBlockClientId ) ), - hasParents: parents.length, - showParentSelector: - parentBlockType && - getBlockEditingMode( firstParentClientId ) === 'default' && - hasBlockSupport( - parentBlockType, - '__experimentalParentSelector', - true - ) && - selectedBlockClientIds.length <= 1 && - getBlockEditingMode( _selectedBlockClientId ) === 'default', - }; - }, [] ); + return { + selectedBlockClientId: _selectedBlockClientId, + blockType: + _selectedBlockClientId && + getBlockType( getBlockName( _selectedBlockClientId ) ), + hasParents: parents.length, + isContentOnly: + getBlockEditingMode( _selectedBlockClientId ) === 'contentOnly', + showParentSelector: + parentBlockType && + getBlockEditingMode( firstParentClientId ) === 'default' && + hasBlockSupport( + parentBlockType, + '__experimentalParentSelector', + true + ) && + selectedBlockClientIds.length <= 1 && + getBlockEditingMode( _selectedBlockClientId ) === 'default', + }; + }, [] ); useEffect( () => { setIsCollapsed( false ); }, [ selectedBlockClientId ] ); if ( - blockType && - ! hasBlockSupport( blockType, '__experimentalToolbar', true ) + isContentOnly || + ( blockType && + ! hasBlockSupport( blockType, '__experimentalToolbar', true ) ) ) { return null; } From abe73df2f2bcb161f03b4a817942e4495c4a4b83 Mon Sep 17 00:00:00 2001 From: Ramon Date: Tue, 1 Aug 2023 10:57:23 +1000 Subject: [PATCH 12/14] Render the footer conditionally in the global styles sidebar component so that any side effects from the footer wrapper are not rendered, e.g., styles and what not (#53204) Ensure that the precise bottom margin persists if the footer isn't rendered --- .../index.js | 80 ++++++++++--------- .../sidebar-navigation-screen/index.js | 12 ++- .../sidebar-navigation-screen/style.scss | 4 + 3 files changed, 57 insertions(+), 39 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..818e1abb82443 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 @@ -108,30 +108,10 @@ function SidebarNavigationScreenGlobalStylesContent() { ); } -function SidebarNavigationScreenGlobalStylesFooter( { onClickRevisions } ) { - const { revisions, isLoading } = useGlobalStylesRevisions(); - const { revisionsCount } = useSelect( ( select ) => { - const { getEntityRecord, __experimentalGetCurrentGlobalStylesId } = - select( coreStore ); - - const globalStylesId = __experimentalGetCurrentGlobalStylesId(); - const globalStyles = globalStylesId - ? getEntityRecord( 'root', 'globalStyles', globalStylesId ) - : undefined; - - return { - revisionsCount: - globalStyles?._links?.[ 'version-history' ]?.[ 0 ]?.count ?? 0, - }; - }, [] ); - - const hasRevisions = revisionsCount >= 2; - const modified = revisions?.[ 0 ]?.modified; - - if ( ! hasRevisions || isLoading || ! modified ) { - return null; - } - +function SidebarNavigationScreenGlobalStylesFooter( { + modifiedDateTime, + onClickRevisions, +} ) { return ( - @@ -162,6 +142,8 @@ function SidebarNavigationScreenGlobalStylesFooter( { onClickRevisions } ) { } export default function SidebarNavigationScreenGlobalStyles() { + const { revisions, isLoading: isLoadingRevisions } = + useGlobalStylesRevisions(); const { openGeneralSidebar, setIsListViewOpened } = useDispatch( editSiteStore ); const isMobileViewport = useViewportMatch( 'medium', '<' ); @@ -171,15 +153,28 @@ export default function SidebarNavigationScreenGlobalStyles() { const { createNotice } = useDispatch( noticesStore ); const { set: setPreference } = useDispatch( preferencesStore ); const { get: getPrefference } = useSelect( preferencesStore ); - const { isViewMode, isStyleBookOpened } = useSelect( ( select ) => { - const { getCanvasMode, getEditorCanvasContainerView } = unlock( - select( editSiteStore ) - ); - return { - isViewMode: 'view' === getCanvasMode(), - isStyleBookOpened: 'style-book' === getEditorCanvasContainerView(), - }; - }, [] ); + const { isViewMode, isStyleBookOpened, revisionsCount } = useSelect( + ( select ) => { + const { getCanvasMode, getEditorCanvasContainerView } = unlock( + select( editSiteStore ) + ); + const { getEntityRecord, __experimentalGetCurrentGlobalStylesId } = + select( coreStore ); + const globalStylesId = __experimentalGetCurrentGlobalStylesId(); + const globalStyles = globalStylesId + ? getEntityRecord( 'root', 'globalStyles', globalStylesId ) + : undefined; + return { + isViewMode: 'view' === getCanvasMode(), + isStyleBookOpened: + 'style-book' === getEditorCanvasContainerView(), + revisionsCount: + globalStyles?._links?.[ 'version-history' ]?.[ 0 ]?.count ?? + 0, + }; + }, + [] + ); const turnOffDistractionFreeMode = useCallback( () => { const isDistractionFree = getPrefference( @@ -226,6 +221,12 @@ export default function SidebarNavigationScreenGlobalStyles() { setEditorCanvasContainerView( 'global-styles-revisions' ); }, [ openGlobalStyles, setEditorCanvasContainerView ] ); + // If there are no revisions, do not render a footer. + const hasRevisions = revisionsCount >= 2; + const modifiedDateTime = revisions?.[ 0 ]?.modified; + const shouldShowGlobalStylesFooter = + hasRevisions && ! isLoadingRevisions && modifiedDateTime; + return ( <> } footer={ - + shouldShowGlobalStylesFooter && ( + + ) } actions={ <> 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 d26cc4c7d62a0..f8824332f5152 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen/index.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ @@ -51,7 +56,12 @@ export default function SidebarNavigationScreen( { return ( <> diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss index 26a30da286fc8..65fe323157b9a 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss @@ -9,6 +9,10 @@ // Ensure the sidebar is always at least as tall as the viewport. // This allows the footer section to be sticky at the bottom of the viewport. flex-grow: 1; + margin-bottom: $grid-unit-20; + &.has-footer { + margin-bottom: 0; + } } .edit-site-sidebar-navigation-screen__content { From 2bc0dad6de6d10aa77a80826bf369514ce0f0cb9 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Tue, 1 Aug 2023 15:18:32 +1000 Subject: [PATCH 13/14] Pattern: Add getBlockRootClientId call (#53206) --- packages/block-library/src/pattern/edit.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/pattern/edit.js b/packages/block-library/src/pattern/edit.js index 824960f90b379..bf5b84eeb0296 100644 --- a/packages/block-library/src/pattern/edit.js +++ b/packages/block-library/src/pattern/edit.js @@ -14,7 +14,7 @@ import { */ import { unlock } from '../lock-unlock'; -const PatternEdit = ( { attributes, clientId, rootClientId } ) => { +const PatternEdit = ( { attributes, clientId } ) => { const selectedPattern = useSelect( ( select ) => select( blockEditorStore ).__experimentalGetParsedPattern( @@ -26,7 +26,9 @@ const PatternEdit = ( { attributes, clientId, rootClientId } ) => { const { replaceBlocks, __unstableMarkNextChangeAsNotPersistent } = useDispatch( blockEditorStore ); const { setBlockEditingMode } = unlock( useDispatch( blockEditorStore ) ); - const { getBlockEditingMode } = unlock( useSelect( blockEditorStore ) ); + const { getBlockRootClientId, getBlockEditingMode } = unlock( + useSelect( blockEditorStore ) + ); // Run this effect when the component loads. // This adds the Pattern's contents to the post. @@ -40,6 +42,7 @@ const PatternEdit = ( { attributes, clientId, rootClientId } ) => { // because nested pattern blocks cannot be inserted if the parent block supports // inner blocks but doesn't have blockSettings in the state. window.queueMicrotask( () => { + const rootClientId = getBlockRootClientId( clientId ); // Clone blocks from the pattern before insertion to ensure they receive // distinct client ids. See https://github.com/WordPress/gutenberg/issues/50628. const clonedBlocks = selectedPattern.blocks.map( ( block ) => @@ -58,13 +61,13 @@ const PatternEdit = ( { attributes, clientId, rootClientId } ) => { } ); } }, [ - rootClientId, clientId, selectedPattern?.blocks, __unstableMarkNextChangeAsNotPersistent, replaceBlocks, getBlockEditingMode, setBlockEditingMode, + getBlockRootClientId, ] ); const props = useBlockProps(); From 1662b7ba265add1082288ebcb71f5aa957fc09a3 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Tue, 1 Aug 2023 17:38:01 +1200 Subject: [PATCH 14/14] Fix color and behavior of unsynced patterns in block inserter (#53205) --- .../components/inserter-list-item/index.js | 4 +++- .../inserter/hooks/use-block-types-state.js | 21 +++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/inserter-list-item/index.js b/packages/block-editor/src/components/inserter-list-item/index.js index 43510cdd52d96..dc755e8767623 100644 --- a/packages/block-editor/src/components/inserter-list-item/index.js +++ b/packages/block-editor/src/components/inserter-list-item/index.js @@ -49,7 +49,9 @@ function InserterListItem( { ]; }, [ item.name, item.initialAttributes, item.initialAttributes ] ); - const isSynced = isReusableBlock( item ) || isTemplatePart( item ); + const isSynced = + ( isReusableBlock( item ) && item.syncStatus !== 'unsynced' ) || + isTemplatePart( item ); return ( { ); const onSelectItem = useCallback( - ( { name, initialAttributes, innerBlocks }, shouldFocusBlock ) => { - const insertedBlock = createBlock( - name, - initialAttributes, - createBlocksFromInnerBlocksTemplate( innerBlocks ) - ); + ( + { name, initialAttributes, innerBlocks, syncStatus, content }, + shouldFocusBlock + ) => { + const insertedBlock = + syncStatus === 'unsynced' + ? parse( content, { + __unstableSkipMigrationLogs: true, + } ) + : createBlock( + name, + initialAttributes, + createBlocksFromInnerBlocksTemplate( innerBlocks ) + ); onInsert( insertedBlock, undefined, shouldFocusBlock ); },