From 4aa1d984375cb9467e9cc5248c9804a4bd97b2f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Tue, 12 Dec 2023 19:23:43 +0100 Subject: [PATCH 01/13] DataViews: update sorting semantics (#56717) --- packages/dataviews/src/view-actions.js | 23 +++++++---------------- packages/dataviews/src/view-table.js | 25 +++++++------------------ 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/packages/dataviews/src/view-actions.js b/packages/dataviews/src/view-actions.js index 4d012d4e5a38f..a5330c08f299c 100644 --- a/packages/dataviews/src/view-actions.js +++ b/packages/dataviews/src/view-actions.js @@ -232,22 +232,13 @@ function SortMenu( { fields, view, onChangeView } ) { } onSelect={ ( event ) => { event.preventDefault(); - if ( - sortedDirection === direction - ) { - onChangeView( { - ...view, - sort: undefined, - } ); - } else { - onChangeView( { - ...view, - sort: { - field: field.id, - direction, - }, - } ); - } + onChangeView( { + ...view, + sort: { + field: field.id, + direction, + }, + } ); } } > { info.label } diff --git a/packages/dataviews/src/view-table.js b/packages/dataviews/src/view-table.js index e34d99008657b..3f5891f076791 100644 --- a/packages/dataviews/src/view-table.js +++ b/packages/dataviews/src/view-table.js @@ -118,24 +118,13 @@ function HeaderMenu( { field, view, onChangeView } ) { } onSelect={ ( event ) => { event.preventDefault(); - if ( - isSorted && - view.sort.direction === - direction - ) { - onChangeView( { - ...view, - sort: undefined, - } ); - } else { - onChangeView( { - ...view, - sort: { - field: field.id, - direction, - }, - } ); - } + onChangeView( { + ...view, + sort: { + field: field.id, + direction, + }, + } ); } } > { info.label } From 5b688d4656dfb9534aa478b1076eabeac2fd7969 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Tue, 12 Dec 2023 19:56:35 +0100 Subject: [PATCH 02/13] Create-block-interactive-template: Add all files to the generated plugin zip (#56943) * Add a files field to the package.json to add all files to the plugin zip * Update changelog --- packages/create-block-interactive-template/CHANGELOG.md | 1 + packages/create-block-interactive-template/index.js | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/create-block-interactive-template/CHANGELOG.md b/packages/create-block-interactive-template/CHANGELOG.md index 735790d07b803..388c9de959e43 100644 --- a/packages/create-block-interactive-template/CHANGELOG.md +++ b/packages/create-block-interactive-template/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Add all files to the generated plugin zip. [#56943](https://github.com/WordPress/gutenberg/pull/56943) - Prevent crash when Gutenberg plugin is not installed. [#56941](https://github.com/WordPress/gutenberg/pull/56941) ## 1.10.1 (2023-12-07) diff --git a/packages/create-block-interactive-template/index.js b/packages/create-block-interactive-template/index.js index b2682600f7af6..6e5ffcb9cc9ae 100644 --- a/packages/create-block-interactive-template/index.js +++ b/packages/create-block-interactive-template/index.js @@ -10,6 +10,7 @@ module.exports = { description: 'An interactive block with the Interactivity API', dashicon: 'media-interactive', npmDependencies: [ '@wordpress/interactivity' ], + customPackageJSON: { files: [ '[^.]*' ] }, supports: { interactivity: true, }, From 482ac0c420ce25b4794fcb87c46667cb62169f18 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Tue, 12 Dec 2023 19:13:10 +0000 Subject: [PATCH 03/13] Fix: Fatal php error if a template was created by an author that was deleted. (#56990) --- lib/compat/wordpress-6.5/rest-api.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/compat/wordpress-6.5/rest-api.php b/lib/compat/wordpress-6.5/rest-api.php index 3b82815c41e42..12d789fb58b86 100644 --- a/lib/compat/wordpress-6.5/rest-api.php +++ b/lib/compat/wordpress-6.5/rest-api.php @@ -91,7 +91,11 @@ function _gutenberg_get_wp_templates_author_text_field( $template_object ) { case 'site': return get_bloginfo( 'name' ); case 'user': - return get_user_by( 'id', $template_object['author'] )->get( 'display_name' ); + $author = get_user_by( 'id', $template_object['author'] ); + if ( ! $author ) { + return __( 'Unknown author', 'gutenberg' ); + } + return $author->get( 'display_name' ); } } From c90bb031ee5ee72ec382c10a123cd713e9c78f65 Mon Sep 17 00:00:00 2001 From: Chad Chadbourne <13856531+chad1008@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:23:15 -0500 Subject: [PATCH 04/13] Implement `Tabs` in editor settings (#55360) * implement Tabs in editor settings sidebar * remove duplicated styles from editor sidebar * incorporate initial feedback * pass props to Tabs directly * add `closeGeneralSidebar` to `onSelect` callback * set TabPanels to `focuasable={false}` * detect when sidebar is closed. pass `selectedTabId` of `null` * improve `Tabs` `onSelect` callback * remove `aria-label` and `data-label` props * add note explaining null selected tab when sidebar is closed * update e2e test * style updates * update internal component structure to avoid rerenders * remove fragment * prevent infinite loop when opening third party sidebar * update e2e tests for `Tabs` compatibility * fix double top margin on tabpanels * update to use new tabId prop * remove import that is no longer needed after rebase * fix keyboard navigable blocks test * fix visibility and tab order tests * fix footnotes tests * fix change detection test --- .../editor/various/change-detection.test.js | 6 +- .../specs/editor/various/editor-modes.test.js | 15 +- .../specs/editor/various/preferences.test.js | 10 +- .../specs/editor/various/sidebar.test.js | 29 +-- .../sidebar/settings-header/index.js | 84 ++------- .../sidebar/settings-header/style.scss | 74 -------- .../sidebar/settings-sidebar/index.js | 172 ++++++++++++------ .../src/components/sidebar/style.scss | 16 +- packages/edit-post/src/style.scss | 1 - .../editor/plugins/custom-post-types.spec.js | 2 +- .../block-hierarchy-navigation.spec.js | 2 +- .../specs/editor/various/footnotes.spec.js | 4 +- .../various/keyboard-navigable-blocks.spec.js | 8 +- 13 files changed, 180 insertions(+), 243 deletions(-) delete mode 100644 packages/edit-post/src/components/sidebar/settings-header/style.scss diff --git a/packages/e2e-tests/specs/editor/various/change-detection.test.js b/packages/e2e-tests/specs/editor/various/change-detection.test.js index 62057c4cbb2bc..0eb673671222f 100644 --- a/packages/e2e-tests/specs/editor/various/change-detection.test.js +++ b/packages/e2e-tests/specs/editor/various/change-detection.test.js @@ -370,7 +370,11 @@ describe( 'Change detection', () => { it( 'consecutive edits to the same attribute should mark the post as dirty after a save', async () => { // Open the sidebar block settings. await openDocumentSettingsSidebar(); - await page.click( '.edit-post-sidebar__panel-tab[data-label="Block"]' ); + + const blockInspectorTab = await page.waitForXPath( + '//button[@role="tab"][contains(text(), "Block")]' + ); + await blockInspectorTab.click(); // Insert a paragraph. await clickBlockAppender(); diff --git a/packages/e2e-tests/specs/editor/various/editor-modes.test.js b/packages/e2e-tests/specs/editor/various/editor-modes.test.js index 81878ebf7208e..aea6536f605bb 100644 --- a/packages/e2e-tests/specs/editor/various/editor-modes.test.js +++ b/packages/e2e-tests/specs/editor/various/editor-modes.test.js @@ -102,21 +102,24 @@ describe( 'Editing modes (visual/HTML)', () => { expect( title ).toBe( 'Paragraph' ); // The Block inspector should be active. - let blockInspectorTab = await page.$( - '.edit-post-sidebar__panel-tab.is-active[data-label="Block"]' + let [ blockInspectorTab ] = await page.$x( + '//button[@role="tab"][@aria-selected="true"][contains(text(), "Block")]' ); expect( blockInspectorTab ).not.toBeNull(); await switchEditorModeTo( 'Code' ); // The Block inspector should not be active anymore. - blockInspectorTab = await page.$( - '.edit-post-sidebar__panel-tab.is-active[data-label="Block"]' + [ blockInspectorTab ] = await page.$x( + '//button[@role="tab"][@aria-selected="true"][contains(text(), "Block")]' ); - expect( blockInspectorTab ).toBeNull(); + expect( blockInspectorTab ).toBeUndefined(); // No block is selected. - await page.click( '.edit-post-sidebar__panel-tab[data-label="Block"]' ); + const inactiveBlockInspectorTab = await page.waitForXPath( + '//button[@role="tab"][contains(text(), "Block")]' + ); + inactiveBlockInspectorTab.click(); const noBlocksElement = await page.$( '.block-editor-block-inspector__no-blocks' ); diff --git a/packages/e2e-tests/specs/editor/various/preferences.test.js b/packages/e2e-tests/specs/editor/various/preferences.test.js index 98249637c7e96..54990a4004422 100644 --- a/packages/e2e-tests/specs/editor/various/preferences.test.js +++ b/packages/e2e-tests/specs/editor/various/preferences.test.js @@ -17,7 +17,7 @@ describe( 'preferences', () => { async function getActiveSidebarTabText() { try { return await page.$eval( - '.edit-post-sidebar__panel-tab.is-active', + 'div[aria-label="Editor settings"] [role="tab"][aria-selected="true"]', ( node ) => node.textContent ); } catch ( error ) { @@ -29,11 +29,15 @@ describe( 'preferences', () => { } it( 'remembers sidebar dismissal between sessions', async () => { + const blockTab = await page.waitForXPath( + `//button[@role="tab"][contains(text(), 'Block')]` + ); + // Open by default. expect( await getActiveSidebarTabText() ).toBe( 'Post' ); // Change to "Block" tab. - await page.click( '.edit-post-sidebar__panel-tab[aria-label="Block"]' ); + await blockTab.click(); expect( await getActiveSidebarTabText() ).toBe( 'Block' ); // Regression test: Reload resets to document tab. @@ -46,7 +50,7 @@ describe( 'preferences', () => { // Dismiss. await page.click( - '.edit-post-sidebar__panel-tabs [aria-label="Close Settings"]' + 'div[aria-label="Editor settings"] div[role="tablist"] + button[aria-label="Close Settings"]' ); expect( await getActiveSidebarTabText() ).toBe( null ); diff --git a/packages/e2e-tests/specs/editor/various/sidebar.test.js b/packages/e2e-tests/specs/editor/various/sidebar.test.js index 2e5d46eec2f7a..0cd39093aabb8 100644 --- a/packages/e2e-tests/specs/editor/various/sidebar.test.js +++ b/packages/e2e-tests/specs/editor/various/sidebar.test.js @@ -13,7 +13,8 @@ import { } from '@wordpress/e2e-test-utils'; const SIDEBAR_SELECTOR = '.edit-post-sidebar'; -const ACTIVE_SIDEBAR_TAB_SELECTOR = '.edit-post-sidebar__panel-tab.is-active'; +const ACTIVE_SIDEBAR_TAB_SELECTOR = + 'div[aria-label="Editor settings"] [role="tab"][aria-selected="true"]'; const ACTIVE_SIDEBAR_BUTTON_TEXT = 'Post'; describe( 'Sidebar', () => { @@ -99,22 +100,24 @@ describe( 'Sidebar', () => { // Tab lands at first (presumed selected) option "Post". await page.keyboard.press( 'Tab' ); - const isActiveDocumentTab = await page.evaluate( - () => - document.activeElement.textContent === 'Post' && - document.activeElement.classList.contains( 'is-active' ) + + // The Post tab should be focused and selected. + const [ documentInspectorTab ] = await page.$x( + '//button[@role="tab"][@aria-selected="true"][contains(text(), "Post")]' ); - expect( isActiveDocumentTab ).toBe( true ); + expect( documentInspectorTab ).toBeDefined(); + expect( documentInspectorTab ).toHaveFocus(); - // Tab into and activate "Block". - await page.keyboard.press( 'Tab' ); + // Arrow key into and activate "Block". + await page.keyboard.press( 'ArrowRight' ); await page.keyboard.press( 'Space' ); - const isActiveBlockTab = await page.evaluate( - () => - document.activeElement.textContent === 'Block' && - document.activeElement.classList.contains( 'is-active' ) + + // The Block tab should be focused and selected. + const [ blockInspectorTab ] = await page.$x( + '//button[@role="tab"][@aria-selected="true"][contains(text(), "Block")]' ); - expect( isActiveBlockTab ).toBe( true ); + expect( blockInspectorTab ).toBeDefined(); + expect( blockInspectorTab ).toHaveFocus(); } ); it( 'should be possible to programmatically remove Document Settings panels', async () => { diff --git a/packages/edit-post/src/components/sidebar/settings-header/index.js b/packages/edit-post/src/components/sidebar/settings-header/index.js index ef32450e7209f..368bd3e9e50db 100644 --- a/packages/edit-post/src/components/sidebar/settings-header/index.js +++ b/packages/edit-post/src/components/sidebar/settings-header/index.js @@ -1,22 +1,20 @@ /** * WordPress dependencies */ -import { Button } from '@wordpress/components'; -import { __, _x, sprintf } from '@wordpress/i18n'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { privateApis as componentsPrivateApis } from '@wordpress/components'; +import { __, _x } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; import { store as editorStore } from '@wordpress/editor'; /** * Internal dependencies */ -import { store as editPostStore } from '../../../store'; +import { unlock } from '../../../lock-unlock'; +import { sidebars } from '../settings-sidebar'; -const SettingsHeader = ( { sidebarName } ) => { - const { openGeneralSidebar } = useDispatch( editPostStore ); - const openDocumentSettings = () => - openGeneralSidebar( 'edit-post/document' ); - const openBlockSettings = () => openGeneralSidebar( 'edit-post/block' ); +const { Tabs } = unlock( componentsPrivateApis ); +const SettingsHeader = () => { const { documentLabel, isTemplateMode } = useSelect( ( select ) => { const { getPostTypeLabel, getRenderingMode } = select( editorStore ); @@ -27,66 +25,16 @@ const SettingsHeader = ( { sidebarName } ) => { }; }, [] ); - const [ documentAriaLabel, documentActiveClass ] = - sidebarName === 'edit-post/document' - ? // translators: ARIA label for the Document sidebar tab, selected. %s: Document label. - [ sprintf( __( '%s (selected)' ), documentLabel ), 'is-active' ] - : [ documentLabel, '' ]; - - const [ blockAriaLabel, blockActiveClass ] = - sidebarName === 'edit-post/block' - ? // translators: ARIA label for the Block Settings Sidebar tab, selected. - [ __( 'Block (selected)' ), 'is-active' ] - : // translators: ARIA label for the Block Settings Sidebar tab, not selected. - [ __( 'Block' ), '' ]; - - const [ templateAriaLabel, templateActiveClass ] = - sidebarName === 'edit-post/document' - ? [ __( 'Template (selected)' ), 'is-active' ] - : [ __( 'Template' ), '' ]; - - /* Use a list so screen readers will announce how many tabs there are. */ return ( - + + + { isTemplateMode ? __( 'Template' ) : documentLabel } + + + { /* translators: Text label for the Block Settings Sidebar tab. */ } + { __( 'Block' ) } + + ); }; diff --git a/packages/edit-post/src/components/sidebar/settings-header/style.scss b/packages/edit-post/src/components/sidebar/settings-header/style.scss deleted file mode 100644 index aaf7698cb6ddb..0000000000000 --- a/packages/edit-post/src/components/sidebar/settings-header/style.scss +++ /dev/null @@ -1,74 +0,0 @@ -// This tab style CSS is duplicated verbatim in -// /packages/components/src/tab-panel/style.scss -.components-button.edit-post-sidebar__panel-tab { - position: relative; - border-radius: 0; - height: $grid-unit-60; - background: transparent; - border: none; - box-shadow: none; - cursor: pointer; - padding: 3px $grid-unit-20; // Use padding to offset the is-active border, this benefits Windows High Contrast mode - margin-left: 0; - font-weight: 500; - - &:focus:not(:disabled) { - position: relative; - box-shadow: none; - outline: none; - } - - // Tab indicator - &::after { - content: ""; - position: absolute; - right: 0; - bottom: 0; - left: 0; - pointer-events: none; - - // Draw the indicator. - background: var(--wp-admin-theme-color); - height: calc(0 * var(--wp-admin-border-width-focus)); - border-radius: 0; - - // Animation - transition: all 0.1s linear; - @include reduce-motion("transition"); - } - - // Active. - &.is-active::after { - height: calc(1 * var(--wp-admin-border-width-focus)); - - // Windows high contrast mode. - outline: 2px solid transparent; - outline-offset: -1px; - } - - // Focus. - &::before { - content: ""; - position: absolute; - top: $grid-unit-15; - right: $grid-unit-15; - bottom: $grid-unit-15; - left: $grid-unit-15; - pointer-events: none; - - // Draw the indicator. - box-shadow: 0 0 0 0 transparent; - border-radius: $radius-block-ui; - - // Animation - transition: all 0.1s linear; - @include reduce-motion("transition"); - } - - &:focus-visible::before { - box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); - - // Windows high contrast mode. - outline: 2px solid transparent; - } -} diff --git a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js index e566ea400c12b..9fa27c6ac2ade 100644 --- a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js +++ b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js @@ -5,8 +5,8 @@ import { BlockInspector, store as blockEditorStore, } from '@wordpress/block-editor'; -import { useSelect } from '@wordpress/data'; -import { Platform } from '@wordpress/element'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { Platform, useCallback, useContext } from '@wordpress/element'; import { isRTL, __ } from '@wordpress/i18n'; import { drawerLeft, drawerRight } from '@wordpress/icons'; import { store as interfaceStore } from '@wordpress/interface'; @@ -29,54 +29,43 @@ import PluginDocumentSettingPanel from '../plugin-document-setting-panel'; import PluginSidebarEditPost from '../plugin-sidebar'; import TemplateSummary from '../template-summary'; import { store as editPostStore } from '../../../store'; +import { privateApis as componentsPrivateApis } from '@wordpress/components'; +import { unlock } from '../../../lock-unlock'; + +const { Tabs } = unlock( componentsPrivateApis ); const SIDEBAR_ACTIVE_BY_DEFAULT = Platform.select( { web: true, native: false, } ); +export const sidebars = { + document: 'edit-post/document', + block: 'edit-post/block', +}; -const SettingsSidebar = () => { - const { sidebarName, keyboardShortcut, isTemplateMode } = useSelect( - ( select ) => { - // The settings sidebar is used by the edit-post/document and edit-post/block sidebars. - // sidebarName represents the sidebar that is active or that should be active when the SettingsSidebar toggle button is pressed. - // If one of the two sidebars is active the component will contain the content of that sidebar. - // When neither of the two sidebars is active we can not simply return null, because the PluginSidebarEditPost - // component, besides being used to render the sidebar, also renders the toggle button. In that case sidebarName - // should contain the sidebar that will be active when the toggle button is pressed. If a block - // is selected, that should be edit-post/block otherwise it's edit-post/document. - let sidebar = select( interfaceStore ).getActiveComplementaryArea( - editPostStore.name - ); - if ( - ! [ 'edit-post/document', 'edit-post/block' ].includes( - sidebar - ) - ) { - if ( select( blockEditorStore ).getBlockSelectionStart() ) { - sidebar = 'edit-post/block'; - } - sidebar = 'edit-post/document'; - } - const shortcut = select( - keyboardShortcutsStore - ).getShortcutRepresentation( 'core/edit-post/toggle-sidebar' ); - return { - sidebarName: sidebar, - keyboardShortcut: shortcut, - isTemplateMode: - select( editorStore ).getRenderingMode() === - 'template-only', - }; - }, - [] - ); +const SidebarContent = ( { + sidebarName, + keyboardShortcut, + isTemplateMode, +} ) => { + // Because `PluginSidebarEditPost` renders a `ComplementaryArea`, we + // need to forward the `Tabs` context so it can be passed through the + // underlying slot/fill. + const tabsContextValue = useContext( Tabs.Context ); return ( } + header={ + + + + } closeLabel={ __( 'Close Settings' ) } + // This classname is added so we can apply a corrective negative + // margin to the panel. + // see https://github.com/WordPress/gutenberg/pull/55360#pullrequestreview-1737671049 + className="edit-post-sidebar__panel" headerClassName="edit-post-sidebar__panel-tabs" /* translators: button label text should, if possible, be under 16 characters. */ title={ __( 'Settings' ) } @@ -84,25 +73,96 @@ const SettingsSidebar = () => { icon={ isRTL() ? drawerLeft : drawerRight } isActiveByDefault={ SIDEBAR_ACTIVE_BY_DEFAULT } > - { ! isTemplateMode && sidebarName === 'edit-post/document' && ( - <> - - - - - - - - - - - ) } - { isTemplateMode && sidebarName === 'edit-post/document' && ( - - ) } - { sidebarName === 'edit-post/block' && } + + + { ! isTemplateMode && ( + <> + + + + + + + + + + + ) } + { isTemplateMode && } + + + + + ); }; +const SettingsSidebar = () => { + const { + sidebarName, + isSettingsSidebarActive, + keyboardShortcut, + isTemplateMode, + } = useSelect( ( select ) => { + // The settings sidebar is used by the edit-post/document and edit-post/block sidebars. + // sidebarName represents the sidebar that is active or that should be active when the SettingsSidebar toggle button is pressed. + // If one of the two sidebars is active the component will contain the content of that sidebar. + // When neither of the two sidebars is active we can not simply return null, because the PluginSidebarEditPost + // component, besides being used to render the sidebar, also renders the toggle button. In that case sidebarName + // should contain the sidebar that will be active when the toggle button is pressed. If a block + // is selected, that should be edit-post/block otherwise it's edit-post/document. + let sidebar = select( interfaceStore ).getActiveComplementaryArea( + editPostStore.name + ); + let isSettingsSidebar = true; + if ( ! [ sidebars.document, sidebars.block ].includes( sidebar ) ) { + isSettingsSidebar = false; + if ( select( blockEditorStore ).getBlockSelectionStart() ) { + sidebar = sidebars.block; + } + sidebar = sidebars.document; + } + const shortcut = select( + keyboardShortcutsStore + ).getShortcutRepresentation( 'core/edit-post/toggle-sidebar' ); + return { + sidebarName: sidebar, + isSettingsSidebarActive: isSettingsSidebar, + keyboardShortcut: shortcut, + isTemplateMode: + select( editorStore ).getRenderingMode() === 'template-only', + }; + }, [] ); + + const { openGeneralSidebar } = useDispatch( editPostStore ); + + const onTabSelect = useCallback( + ( newSelectedTabId ) => { + if ( !! newSelectedTabId ) { + openGeneralSidebar( newSelectedTabId ); + } + }, + [ openGeneralSidebar ] + ); + + return ( + + + + ); +}; + export default SettingsSidebar; diff --git a/packages/edit-post/src/components/sidebar/style.scss b/packages/edit-post/src/components/sidebar/style.scss index 7b10eaec0d224..1921c5cfd7b31 100644 --- a/packages/edit-post/src/components/sidebar/style.scss +++ b/packages/edit-post/src/components/sidebar/style.scss @@ -1,20 +1,8 @@ .components-panel__header.edit-post-sidebar__panel-tabs { - justify-content: flex-start; padding-left: 0; padding-right: $grid-unit-20; - border-top: 0; - margin-top: 0; - - ul { - display: flex; - } - li { - margin: 0; - } .components-button.has-icon { - display: none; - margin: 0 0 0 auto; padding: 0; min-width: $icon-size; height: $icon-size; @@ -24,3 +12,7 @@ } } } + +.edit-post-sidebar__panel { + margin-top: -1px; +} diff --git a/packages/edit-post/src/style.scss b/packages/edit-post/src/style.scss index 53219bc6a3736..88916bf70f76d 100644 --- a/packages/edit-post/src/style.scss +++ b/packages/edit-post/src/style.scss @@ -12,7 +12,6 @@ @import "./components/sidebar/post-format/style.scss"; @import "./components/sidebar/post-slug/style.scss"; @import "./components/sidebar/post-visibility/style.scss"; -@import "./components/sidebar/settings-header/style.scss"; @import "./components/sidebar/template-summary/style.scss"; @import "./components/text-editor/style.scss"; @import "./components/visual-editor/style.scss"; diff --git a/test/e2e/specs/editor/plugins/custom-post-types.spec.js b/test/e2e/specs/editor/plugins/custom-post-types.spec.js index 17a497f26cee0..01dde03650ef7 100644 --- a/test/e2e/specs/editor/plugins/custom-post-types.spec.js +++ b/test/e2e/specs/editor/plugins/custom-post-types.spec.js @@ -31,7 +31,7 @@ test.describe( 'Test Custom Post Types', () => { await editor.openDocumentSettingsSidebar(); await page .getByRole( 'region', { name: 'Editor settings' } ) - .getByRole( 'button', { + .getByRole( 'tab', { name: 'Hierarchical No Title', } ) .click(); diff --git a/test/e2e/specs/editor/various/block-hierarchy-navigation.spec.js b/test/e2e/specs/editor/various/block-hierarchy-navigation.spec.js index f0bfe5bff203f..a695b0a9ead67 100644 --- a/test/e2e/specs/editor/various/block-hierarchy-navigation.spec.js +++ b/test/e2e/specs/editor/various/block-hierarchy-navigation.spec.js @@ -127,7 +127,7 @@ test.describe( 'Navigating the block hierarchy', () => { await pageUtils.pressKeys( 'ctrl+`' ); // Navigate to the block settings sidebar and tweak the column count. - await pageUtils.pressKeys( 'Tab', { times: 5 } ); + await pageUtils.pressKeys( 'Tab', { times: 4 } ); await expect( page.getByRole( 'slider', { name: 'Columns' } ) ).toBeFocused(); diff --git a/test/e2e/specs/editor/various/footnotes.spec.js b/test/e2e/specs/editor/various/footnotes.spec.js index 14a2fc653e387..6102f48749543 100644 --- a/test/e2e/specs/editor/various/footnotes.spec.js +++ b/test/e2e/specs/editor/various/footnotes.spec.js @@ -362,7 +362,7 @@ test.describe( 'Footnotes', () => { await editor.openDocumentSettingsSidebar(); await page .getByRole( 'region', { name: 'Editor settings' } ) - .getByRole( 'button', { name: 'Post' } ) + .getByRole( 'tab', { name: 'Post' } ) .click(); await page.locator( 'a:text("2 Revisions")' ).click(); await page.locator( '.revisions-controls .ui-slider-handle' ).focus(); @@ -440,7 +440,7 @@ test.describe( 'Footnotes', () => { await editor.openDocumentSettingsSidebar(); await page .getByRole( 'region', { name: 'Editor settings' } ) - .getByRole( 'button', { name: 'Post' } ) + .getByRole( 'tab', { name: 'Post' } ) .click(); // Visit the published post. diff --git a/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js b/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js index 080abe011206a..84536c88227ce 100644 --- a/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js +++ b/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js @@ -75,9 +75,7 @@ test.describe( 'Order of block keyboard navigation', () => { ); await page.keyboard.press( 'Tab' ); - await KeyboardNavigableBlocks.expectLabelToHaveFocus( - 'Post (selected)' - ); + await KeyboardNavigableBlocks.expectLabelToHaveFocus( 'Post' ); } ); test( 'allows tabbing in navigation mode if no block is selected (reverse)', async ( { @@ -151,7 +149,7 @@ test.describe( 'Order of block keyboard navigation', () => { ); await page.keyboard.press( 'Tab' ); - await KeyboardNavigableBlocks.expectLabelToHaveFocus( 'Post' ); + await KeyboardNavigableBlocks.expectLabelToHaveFocus( 'Block' ); await pageUtils.pressKeys( 'shift+Tab' ); await KeyboardNavigableBlocks.expectLabelToHaveFocus( @@ -233,7 +231,7 @@ class KeyboardNavigableBlocks { await expect( activeElement ).toHaveText( paragraphText ); await this.page.keyboard.press( 'Tab' ); - await this.expectLabelToHaveFocus( 'Post' ); + await this.expectLabelToHaveFocus( 'Block' ); // Need to shift+tab here to end back in the block. If not, we'll be in the next region and it will only require 4 region jumps instead of 5. await this.pageUtils.pressKeys( 'shift+Tab' ); From b149b9647f29fe57ab4feb04545c7e99cf61faa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Tue, 12 Dec 2023 20:57:14 +0100 Subject: [PATCH 05/13] Fix e2e test (#56992) --- test/e2e/specs/site-editor/new-templates-list.spec.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/e2e/specs/site-editor/new-templates-list.spec.js b/test/e2e/specs/site-editor/new-templates-list.spec.js index 8bb98b6ad5355..34daeb6d40f09 100644 --- a/test/e2e/specs/site-editor/new-templates-list.spec.js +++ b/test/e2e/specs/site-editor/new-templates-list.spec.js @@ -33,10 +33,7 @@ test.describe( 'Templates', () => { name: 'Template', includeHidden: true, } ) - .getByRole( 'heading', { - level: 3, - includeHidden: true, - } ) + .getByRole( 'link', { includeHidden: true } ) .first(); await expect( firstTitle ).toHaveText( 'Tag Archives' ); // Ascending by title. @@ -57,7 +54,7 @@ test.describe( 'Templates', () => { await page.keyboard.type( 'tag' ); const titles = page .getByRole( 'region', { name: 'Template' } ) - .getByRole( 'heading', { level: 3 } ); + .getByRole( 'link' ); await expect( titles ).toHaveCount( 1 ); await expect( titles.first() ).toHaveText( 'Tag Archives' ); await page.getByRole( 'button', { name: 'Reset filters' } ).click(); From 4d61a94e0622eed5e45529a7fcb9a95cd09a61a7 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 13 Dec 2023 08:54:27 +1100 Subject: [PATCH 06/13] Allow dragging between adjacent container blocks based on a threshold (#56466) * Try: Allow drag between blocks based on a threshold * Update insertion point to reflect threshold * Fix threshold for Group block by passing in dropZoneElement * Simplify by reusing operation option * Re-use logic that factors in the dragged blocks if we are ultimately dropping at the same level * Simplify a little further * Factor in orientation * Add minimum height for threshold so that short blocks are still usable * Allow threshold when parent block is horizontal, i.e. a Row block --- .../components/use-block-drop-zone/index.js | 128 ++++++++++++++++-- .../src/components/use-on-block-drop/index.js | 3 +- packages/block-library/src/group/edit.js | 5 +- 3 files changed, 120 insertions(+), 16 deletions(-) diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index 25dc6ee408982..cb3c3ae6a28a3 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -20,6 +20,10 @@ import { } from '../../utils/math'; import { store as blockEditorStore } from '../../store'; +const THRESHOLD_DISTANCE = 30; +const MINIMUM_HEIGHT_FOR_THRESHOLD = 120; +const MINIMUM_WIDTH_FOR_THRESHOLD = 120; + /** @typedef {import('../../utils/math').WPPoint} WPPoint */ /** @typedef {import('../use-on-block-drop/types').WPDropOperation} WPDropOperation */ @@ -48,24 +52,86 @@ import { store as blockEditorStore } from '../../store'; * @param {WPBlockData[]} blocksData The block data list. * @param {WPPoint} position The position of the item being dragged. * @param {WPBlockListOrientation} orientation The orientation of the block list. + * @param {Object} options Additional options. * @return {[number, WPDropOperation]} The drop target position. */ export function getDropTargetPosition( blocksData, position, - orientation = 'vertical' + orientation = 'vertical', + options = {} ) { const allowedEdges = orientation === 'horizontal' ? [ 'left', 'right' ] : [ 'top', 'bottom' ]; - const isRightToLeft = isRTL(); - let nearestIndex = 0; let insertPosition = 'before'; let minDistance = Infinity; + const { + dropZoneElement, + parentBlockOrientation, + rootBlockIndex = 0, + } = options; + + // Allow before/after when dragging over the top/bottom edges of the drop zone. + if ( dropZoneElement && parentBlockOrientation !== 'horizontal' ) { + const rect = dropZoneElement.getBoundingClientRect(); + const [ distance, edge ] = getDistanceToNearestEdge( position, rect, [ + 'top', + 'bottom', + ] ); + + // If dragging over the top or bottom of the drop zone, insert the block + // before or after the parent block. This only applies to blocks that use + // a drop zone element, typically container blocks such as Group or Cover. + if ( + rect.height > MINIMUM_HEIGHT_FOR_THRESHOLD && + distance < THRESHOLD_DISTANCE + ) { + if ( edge === 'top' ) { + return [ rootBlockIndex, 'before' ]; + } + if ( edge === 'bottom' ) { + return [ rootBlockIndex + 1, 'after' ]; + } + } + } + + const isRightToLeft = isRTL(); + + // Allow before/after when dragging over the left/right edges of the drop zone. + if ( dropZoneElement && parentBlockOrientation === 'horizontal' ) { + const rect = dropZoneElement.getBoundingClientRect(); + const [ distance, edge ] = getDistanceToNearestEdge( position, rect, [ + 'left', + 'right', + ] ); + + // If dragging over the left or right of the drop zone, insert the block + // before or after the parent block. This only applies to blocks that use + // a drop zone element, typically container blocks such as Group. + if ( + rect.width > MINIMUM_WIDTH_FOR_THRESHOLD && + distance < THRESHOLD_DISTANCE + ) { + if ( + ( isRightToLeft && edge === 'right' ) || + ( ! isRightToLeft && edge === 'left' ) + ) { + return [ rootBlockIndex, 'before' ]; + } + if ( + ( isRightToLeft && edge === 'left' ) || + ( ! isRightToLeft && edge === 'right' ) + ) { + return [ rootBlockIndex + 1, 'after' ]; + } + } + } + blocksData.forEach( ( { isUnmodifiedDefaultBlock, getBoundingClientRect, blockIndex } ) => { const rect = getBoundingClientRect(); @@ -150,19 +216,27 @@ export default function useBlockDropZone( { operation: 'insert', } ); - const isDisabled = useSelect( + const { isDisabled, parentBlockClientId, rootBlockIndex } = useSelect( ( select ) => { const { __unstableIsWithinBlockOverlay, __unstableHasActiveBlockOverlayActive, + getBlockIndex, + getBlockParents, getBlockEditingMode, } = select( blockEditorStore ); const blockEditingMode = getBlockEditingMode( targetRootClientId ); - return ( - blockEditingMode !== 'default' || - __unstableHasActiveBlockOverlayActive( targetRootClientId ) || - __unstableIsWithinBlockOverlay( targetRootClientId ) - ); + return { + parentBlockClientId: + getBlockParents( targetRootClientId, true )[ 0 ] || '', + rootBlockIndex: getBlockIndex( targetRootClientId ), + isDisabled: + blockEditingMode !== 'default' || + __unstableHasActiveBlockOverlayActive( + targetRootClientId + ) || + __unstableIsWithinBlockOverlay( targetRootClientId ), + }; }, [ targetRootClientId ] ); @@ -172,9 +246,15 @@ export default function useBlockDropZone( { const { showInsertionPoint, hideInsertionPoint } = useDispatch( blockEditorStore ); - const onBlockDrop = useOnBlockDrop( targetRootClientId, dropTarget.index, { - operation: dropTarget.operation, - } ); + const onBlockDrop = useOnBlockDrop( + dropTarget.operation === 'before' || dropTarget.operation === 'after' + ? parentBlockClientId + : targetRootClientId, + dropTarget.index, + { + operation: dropTarget.operation, + } + ); const throttled = useThrottle( useCallback( ( event, ownerDocument ) => { @@ -211,7 +291,16 @@ export default function useBlockDropZone( { const [ targetIndex, operation ] = getDropTargetPosition( blocksData, { x: event.clientX, y: event.clientY }, - getBlockListSettings( targetRootClientId )?.orientation + getBlockListSettings( targetRootClientId )?.orientation, + { + dropZoneElement, + parentBlockClientId, + parentBlockOrientation: parentBlockClientId + ? getBlockListSettings( parentBlockClientId ) + ?.orientation + : undefined, + rootBlockIndex, + } ); registry.batch( () => { @@ -219,18 +308,29 @@ export default function useBlockDropZone( { index: targetIndex, operation, } ); - showInsertionPoint( targetRootClientId, targetIndex, { + + const insertionPointClientId = [ + 'before', + 'after', + ].includes( operation ) + ? parentBlockClientId + : targetRootClientId; + + showInsertionPoint( insertionPointClientId, targetIndex, { operation, } ); } ); }, [ + dropZoneElement, getBlocks, targetRootClientId, getBlockListSettings, registry, showInsertionPoint, getBlockIndex, + parentBlockClientId, + rootBlockIndex, ] ), 200 diff --git a/packages/block-editor/src/components/use-on-block-drop/index.js b/packages/block-editor/src/components/use-on-block-drop/index.js index 72ea6a698c343..ab0da8ad99e2a 100644 --- a/packages/block-editor/src/components/use-on-block-drop/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/index.js @@ -292,9 +292,10 @@ export default function useOnBlockDrop( operation, getBlockOrder, getBlocksByClientId, - insertBlocks, moveBlocksToPosition, + registry, removeBlocks, + replaceBlocks, targetBlockIndex, targetRootClientId, ] diff --git a/packages/block-library/src/group/edit.js b/packages/block-library/src/group/edit.js index 9c8690c4e0e8e..a763bc95e60d7 100644 --- a/packages/block-library/src/group/edit.js +++ b/packages/block-library/src/group/edit.js @@ -10,6 +10,7 @@ import { store as blockEditorStore, } from '@wordpress/block-editor'; import { SelectControl } from '@wordpress/components'; +import { useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { View } from '@wordpress/primitives'; @@ -97,7 +98,8 @@ function GroupEdit( { attributes, name, setAttributes, clientId } ) { themeSupportsLayout || type === 'flex' || type === 'grid'; // Hooks. - const blockProps = useBlockProps(); + const ref = useRef(); + const blockProps = useBlockProps( { ref } ); const [ showPlaceholder, setShowPlaceholder ] = useShouldShowPlaceHolder( { attributes, @@ -124,6 +126,7 @@ function GroupEdit( { attributes, name, setAttributes, clientId } ) { ? blockProps : { className: 'wp-block-group__inner-container' }, { + dropZoneElement: ref.current, templateLock, allowedBlocks, renderAppender, From 9a46ad1773b736eeffb54755e7cddb7e25c8462c Mon Sep 17 00:00:00 2001 From: Ramon Date: Wed, 13 Dec 2023 09:15:36 +1100 Subject: [PATCH 07/13] Global style revisions: show change summary on selected item (#56577) * Moving date format setting call into the comoponent rejigging getLabel function adding css var * Testing a message to indicate that the revisions state is the same as the editor state. * Working on change list, adding translations. WIP * Adding more translations. * Revert button-in-item for another day Reduce depth of changeset and remove unused translations Display aria-label on button instead of tooltip * Removing shuffle function and fixing up block spacing translation * Using the revision has the Map key. This allows us to cache the revision sets themselves and account for changes to Unsaved revisions. * Used WeakMap in favour of Map for garbage collection, if it helps at all * Remove hasMore var - unneeded because it's only used once * Tidying up - remove .map loop * getGlobalStylesChanges was doing nothing! Removed. * Using revision + previousRevision combo for cache key to ensure that the results are cached for the same two objects Returning from cache where maxResults value is smaller than cached results Added first tests * Moving maxResults decisions to consuming component. getRevisionChanges returns an unadulterated array. * Move get blockNames to main component * Have to use map because WeakMap wants the same reference as the object key. * Remove the trailing comma on truncated results * Test commit: listing changes, showing `and n more` * Test commit: grouping changes using tuples * Reverting back to comma-separate list of changes Added e2e assertion * Swapping order of author name and changes block Moving everything into the button so it's clickable. * Don't live in the past, man --- .../screen-revisions/get-revision-changes.js | 171 ++++++++++++++++ .../global-styles/screen-revisions/index.js | 15 +- .../screen-revisions/revisions-buttons.js | 103 ++++++++-- .../global-styles/screen-revisions/style.scss | 12 +- .../test/get-revision-changes.js | 191 ++++++++++++++++++ .../user-global-styles-revisions.spec.js | 5 + 6 files changed, 467 insertions(+), 30 deletions(-) create mode 100644 packages/edit-site/src/components/global-styles/screen-revisions/get-revision-changes.js create mode 100644 packages/edit-site/src/components/global-styles/screen-revisions/test/get-revision-changes.js diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/get-revision-changes.js b/packages/edit-site/src/components/global-styles/screen-revisions/get-revision-changes.js new file mode 100644 index 0000000000000..fed075eb923ff --- /dev/null +++ b/packages/edit-site/src/components/global-styles/screen-revisions/get-revision-changes.js @@ -0,0 +1,171 @@ +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; + +const globalStylesChangesCache = new Map(); +const EMPTY_ARRAY = []; + +const translationMap = { + caption: __( 'Caption' ), + link: __( 'Link' ), + button: __( 'Button' ), + heading: __( 'Heading' ), + 'settings.color': __( 'Color settings' ), + 'settings.typography': __( 'Typography settings' ), + 'styles.color': __( 'Colors' ), + 'styles.spacing': __( 'Spacing' ), + 'styles.typography': __( 'Typography' ), +}; + +const isObject = ( obj ) => obj !== null && typeof obj === 'object'; + +/** + * Get the translation for a given global styles key. + * @param {string} key A key representing a path to a global style property or setting. + * @param {Record} blockNames A key/value pair object of block names and their rendered titles. + * @return {string|undefined} A translated key or undefined if no translation exists. + */ +function getTranslation( key, blockNames ) { + if ( translationMap[ key ] ) { + return translationMap[ key ]; + } + + const keyArray = key.split( '.' ); + + if ( keyArray?.[ 0 ] === 'blocks' ) { + const blockName = blockNames[ keyArray[ 1 ] ]; + return blockName + ? sprintf( + // translators: %s: block name. + __( '%s block' ), + blockName + ) + : keyArray[ 1 ]; + } + + if ( keyArray?.[ 0 ] === 'elements' ) { + return sprintf( + // translators: %s: element name, e.g., heading button, link, caption. + __( '%s element' ), + translationMap[ keyArray[ 1 ] ] + ); + } + + return undefined; +} + +/** + * A deep comparison of two objects, optimized for comparing global styles. + * @param {Object} changedObject The changed object to compare. + * @param {Object} originalObject The original object to compare against. + * @param {string} parentPath A key/value pair object of block names and their rendered titles. + * @return {string[]} An array of paths whose values have changed. + */ +function deepCompare( changedObject, originalObject, parentPath = '' ) { + // We have two non-object values to compare. + if ( ! isObject( changedObject ) && ! isObject( originalObject ) ) { + /* + * Only return a path if the value has changed. + * And then only the path name up to 2 levels deep. + */ + return changedObject !== originalObject + ? parentPath.split( '.' ).slice( 0, 2 ).join( '.' ) + : undefined; + } + + // Enable comparison when an object doesn't have a corresponding property to compare. + changedObject = isObject( changedObject ) ? changedObject : {}; + originalObject = isObject( originalObject ) ? originalObject : {}; + + const allKeys = new Set( [ + ...Object.keys( changedObject ), + ...Object.keys( originalObject ), + ] ); + + let diffs = []; + for ( const key of allKeys ) { + const path = parentPath ? parentPath + '.' + key : key; + const changedPath = deepCompare( + changedObject[ key ], + originalObject[ key ], + path + ); + if ( changedPath ) { + diffs = diffs.concat( changedPath ); + } + } + return diffs; +} + +/** + * Get an array of translated summarized global styles changes. + * Results are cached using a Map() key of `JSON.stringify( { revision, previousRevision } )`. + * + * @param {Object} revision The changed object to compare. + * @param {Object} previousRevision The original object to compare against. + * @param {Record} blockNames A key/value pair object of block names and their rendered titles. + * @return {string[]} An array of translated changes. + */ +export default function getRevisionChanges( + revision, + previousRevision, + blockNames +) { + const cacheKey = JSON.stringify( { revision, previousRevision } ); + + if ( globalStylesChangesCache.has( cacheKey ) ) { + return globalStylesChangesCache.get( cacheKey ); + } + + /* + * Compare the two revisions with normalized keys. + * The order of these keys determines the order in which + * they'll appear in the results. + */ + const changedValueTree = deepCompare( + { + styles: { + color: revision?.styles?.color, + typography: revision?.styles?.typography, + spacing: revision?.styles?.spacing, + }, + blocks: revision?.styles?.blocks, + elements: revision?.styles?.elements, + settings: revision?.settings, + }, + { + styles: { + color: previousRevision?.styles?.color, + typography: previousRevision?.styles?.typography, + spacing: previousRevision?.styles?.spacing, + }, + blocks: previousRevision?.styles?.blocks, + elements: previousRevision?.styles?.elements, + settings: previousRevision?.settings, + } + ); + + if ( ! changedValueTree.length ) { + globalStylesChangesCache.set( cacheKey, EMPTY_ARRAY ); + return EMPTY_ARRAY; + } + + // Remove duplicate results. + const result = [ ...new Set( changedValueTree ) ] + /* + * Translate the keys. + * Remove duplicate or empty translations. + */ + .reduce( ( acc, curr ) => { + const translation = getTranslation( curr, blockNames ); + if ( translation && ! acc.includes( translation ) ) { + acc.push( translation ); + } + return acc; + }, [] ); + + globalStylesChangesCache.set( cacheKey, result ); + + return result; +} 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 90bf68e579cb7..aa380c5a9fbd0 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,7 +7,6 @@ import { __experimentalUseNavigator as useNavigator, __experimentalConfirmDialog as ConfirmDialog, Spinner, - __experimentalSpacer as Spacer, } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; @@ -135,7 +134,8 @@ function ScreenRevisions() { } }, [ shouldSelectFirstItem, firstRevision ] ); - // Only display load button if there is a revision to load and it is different from the current editor styles. + // Only display load button if there is a revision to load, + // and it is different from the current editor styles. const isLoadButtonEnabled = !! currentlySelectedRevisionId && ! selectedRevisionMatchesEditorStyles; const shouldShowRevisions = ! isLoading && revisions.length; @@ -156,7 +156,7 @@ function ScreenRevisions() { { isLoading && ( ) } - { shouldShowRevisions ? ( + { shouldShowRevisions && ( <> { isLoadButtonEnabled && ( @@ -215,14 +216,6 @@ function ScreenRevisions() { ) } - ) : ( - - { - // 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.' ) - } - ) } ); diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js b/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js index 2786bf6d79121..0893006942572 100644 --- a/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js +++ b/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js @@ -6,28 +6,69 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { __, sprintf } from '@wordpress/i18n'; +import { __, _n, sprintf } from '@wordpress/i18n'; import { Button } from '@wordpress/components'; import { dateI18n, getDate, humanTimeDiff, getSettings } from '@wordpress/date'; import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; +import { useMemo } from '@wordpress/element'; +import { getBlockTypes } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import getRevisionChanges from './get-revision-changes'; const DAY_IN_MILLISECONDS = 60 * 60 * 1000 * 24; +const MAX_CHANGES = 7; + +function ChangesSummary( { revision, previousRevision, blockNames } ) { + const changes = getRevisionChanges( + revision, + previousRevision, + blockNames + ); + const changesLength = changes.length; + + if ( ! changesLength ) { + return null; + } + + // Truncate to `n` results if necessary. + if ( changesLength > MAX_CHANGES ) { + const deleteCount = changesLength - MAX_CHANGES; + const andMoreText = sprintf( + // translators: %d: number of global styles changes that are not displayed in the UI. + _n( '…and %d more change.', '…and %d more changes.', deleteCount ), + deleteCount + ); + changes.splice( MAX_CHANGES, deleteCount, andMoreText ); + } + + return ( + + { changes.join( ', ' ) } + + ); +} /** * Returns a button label for the revision. * * @param {string|number} id A revision object. - * @param {boolean} isLatest Whether the revision is the most current. * @param {string} authorDisplayName Author name. * @param {string} formattedModifiedDate Revision modified date formatted. + * @param {boolean} areStylesEqual Whether the revision matches the current editor styles. * @return {string} Translated label. */ function getRevisionLabel( id, - isLatest, authorDisplayName, - formattedModifiedDate + formattedModifiedDate, + areStylesEqual ) { if ( 'parent' === id ) { return __( 'Reset the styles to the theme defaults' ); @@ -35,21 +76,23 @@ function getRevisionLabel( if ( 'unsaved' === id ) { return sprintf( - /* translators: %s author display name */ + /* translators: %s: author display name */ __( 'Unsaved changes by %s' ), authorDisplayName ); } - return isLatest + return areStylesEqual ? sprintf( - /* translators: %1$s author display name, %2$s: revision creation date */ - __( 'Changes saved by %1$s on %2$s (current)' ), + // translators: %1$s: author display name, %2$s: revision creation date. + __( + 'Changes saved by %1$s on %2$s. This revision matches current editor styles.' + ), authorDisplayName, formattedModifiedDate ) : sprintf( - /* translators: %1$s author display name, %2$s: revision creation date */ + // translators: %1$s: author display name, %2$s: revision creation date. __( 'Changes saved by %1$s on %2$s' ), authorDisplayName, formattedModifiedDate @@ -67,7 +110,12 @@ function getRevisionLabel( * @param {props} Component props. * @return {JSX.Element} The modal component. */ -function RevisionsButtons( { userRevisions, selectedRevisionId, onChange } ) { +function RevisionsButtons( { + userRevisions, + selectedRevisionId, + onChange, + canApplyRevision, +} ) { const { currentThemeName, currentUser } = useSelect( ( select ) => { const { getCurrentTheme, getCurrentUser } = select( coreStore ); const currentTheme = getCurrentTheme(); @@ -77,8 +125,15 @@ function RevisionsButtons( { userRevisions, selectedRevisionId, onChange } ) { currentUser: getCurrentUser(), }; }, [] ); + const blockNames = useMemo( () => { + const blockTypes = getBlockTypes(); + return blockTypes.reduce( ( accumulator, { name, title } ) => { + accumulator[ name ] = title; + return accumulator; + }, {} ); + }, [] ); const dateNowInMs = getDate().getTime(); - const { date: dateFormat, datetimeAbbreviated } = getSettings().formats; + const { datetimeAbbreviated } = getSettings().formats; return (
    { userRevisions.map( ( revision, index ) => { - const { id, isLatest, author, modified } = revision; + const { id, author, modified } = revision; const isUnsaved = 'unsaved' === id; // Unsaved changes are created by the current user. const revisionAuthor = isUnsaved ? currentUser : author; const authorDisplayName = revisionAuthor?.name || __( 'User' ); const authorAvatar = revisionAuthor?.avatar_urls?.[ '48' ]; + const isFirstItem = index === 0; const isSelected = selectedRevisionId ? selectedRevisionId === id - : index === 0; + : isFirstItem; + const areStylesEqual = ! canApplyRevision && isSelected; const isReset = 'parent' === id; const modifiedDate = getDate( modified ); const displayDate = modified && dateNowInMs - modifiedDate.getTime() > DAY_IN_MILLISECONDS - ? dateI18n( dateFormat, modifiedDate ) + ? dateI18n( datetimeAbbreviated, modifiedDate ) : humanTimeDiff( modified ); const revisionLabel = getRevisionLabel( id, - isLatest, authorDisplayName, - dateI18n( datetimeAbbreviated, modifiedDate ) + dateI18n( datetimeAbbreviated, modifiedDate ), + areStylesEqual ); return ( @@ -116,6 +173,7 @@ function RevisionsButtons( { userRevisions, selectedRevisionId, onChange } ) { 'edit-site-global-styles-screen-revisions__revision-item', { 'is-selected': isSelected, + 'is-active': areStylesEqual, 'is-reset': isReset, } ) } @@ -127,7 +185,7 @@ function RevisionsButtons( { userRevisions, selectedRevisionId, onChange } ) { onClick={ () => { onChange( revision ); } } - label={ revisionLabel } + aria-label={ revisionLabel } > { isReset ? ( @@ -150,6 +208,17 @@ function RevisionsButtons( { userRevisions, selectedRevisionId, onChange } ) { { displayDate } ) } + { isSelected && ( + + ) } { { + const revision = { + id: 10, + styles: { + typography: { + fontSize: 'var(--wp--preset--font-size--potato)', + fontStyle: 'normal', + fontWeight: '600', + lineHeight: '1.85', + fontFamily: 'var(--wp--preset--font-family--asparagus)', + }, + spacing: { + padding: { + top: '36px', + right: '89px', + bottom: '133px', + left: 'var(--wp--preset--spacing--20)', + }, + blockGap: '114px', + }, + elements: { + heading: { + typography: { + letterSpacing: '37px', + }, + }, + caption: { + color: { + text: 'var(--wp--preset--color--pineapple)', + }, + }, + }, + color: { + text: 'var(--wp--preset--color--tomato)', + }, + blocks: { + 'core/paragraph': { + color: { + text: '#000000', + }, + }, + }, + }, + settings: { + color: { + palette: { + theme: [ + { + slug: 'one', + color: 'pink', + }, + ], + }, + }, + }, + }; + const previousRevision = { + id: 9, + styles: { + typography: { + fontSize: 'var(--wp--preset--font-size--fungus)', + fontStyle: 'normal', + fontWeight: '600', + lineHeight: '1.85', + fontFamily: 'var(--wp--preset--font-family--grapes)', + }, + spacing: { + padding: { + top: '36px', + right: '89px', + bottom: '133px', + left: 'var(--wp--preset--spacing--20)', + }, + blockGap: '114px', + }, + elements: { + heading: { + typography: { + letterSpacing: '37px', + }, + }, + caption: { + typography: { + fontSize: '1.11rem', + fontStyle: 'normal', + fontWeight: '600', + }, + }, + link: { + typography: { + lineHeight: 2, + textDecoration: 'line-through', + }, + color: { + text: 'var(--wp--preset--color--egg)', + }, + }, + }, + color: { + text: 'var(--wp--preset--color--tomato)', + background: 'var(--wp--preset--color--pumpkin)', + }, + blocks: { + 'core/paragraph': { + color: { + text: '#fff', + }, + }, + }, + }, + settings: { + color: { + palette: { + theme: [ + { + slug: 'one', + color: 'blue', + }, + ], + }, + }, + }, + }; + const blockNames = { + 'core/paragraph': 'Paragraph', + }; + it( 'returns a list of changes and caches them', () => { + const resultA = getRevisionChanges( + revision, + previousRevision, + blockNames + ); + expect( resultA ).toEqual( [ + 'Colors', + 'Typography', + 'Paragraph block', + 'Caption element', + 'Link element', + 'Color settings', + ] ); + + const resultB = getRevisionChanges( + revision, + previousRevision, + blockNames + ); + + expect( resultA ).toBe( resultB ); + } ); + + it( 'skips unknown and unchanged keys', () => { + const result = getRevisionChanges( + { + styles: { + frogs: { + legs: 'green', + }, + typography: { + fontSize: '1rem', + }, + settings: { + '': { + '': 'foo', + }, + }, + }, + }, + { + styles: { + frogs: { + legs: 'yellow', + }, + typography: { + fontSize: '1rem', + }, + settings: { + '': { + '': 'bar', + }, + }, + }, + } + ); + expect( result ).toEqual( [] ); + } ); +} ); 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 2d51b5ac5014b..a27bb28adbb91 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 @@ -55,6 +55,11 @@ test.describe( 'Global styles revisions', () => { name: /^Changes saved by /, } ); + // Shows changes made in the revision. + await expect( + page.getByTestId( 'global-styles-revision-changes' ) + ).toHaveText( 'Colors' ); + // There should be 2 revisions not including the reset to theme defaults button. await expect( revisionButtons ).toHaveCount( currentRevisions.length + 1 From 1dd952f3267200ee5fad288df26033d5fd33854c Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Tue, 12 Dec 2023 22:24:17 +0000 Subject: [PATCH 08/13] Bump plugin version to 17.2.1 --- gutenberg.php | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index 7970dd5461fc4..f39a0cb519c8a 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.3 * Requires PHP: 7.0 - * Version: 17.2.0 + * Version: 17.2.1 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index ded852521693f..6d9382ec9fb2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gutenberg", - "version": "17.2.0", + "version": "17.2.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "gutenberg", - "version": "17.2.0", + "version": "17.2.1", "hasInstallScript": true, "license": "GPL-2.0-or-later", "dependencies": { diff --git a/package.json b/package.json index 122a1368eaf1c..580c100354a96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "17.2.0", + "version": "17.2.1", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From 2936660d526a8112479d9c87a92cd6ae7a455a94 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Tue, 12 Dec 2023 22:38:23 +0000 Subject: [PATCH 09/13] Update Changelog for 17.2.1 --- changelog.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/changelog.txt b/changelog.txt index 2f7d2f02ff629..65745b416e4bd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,20 @@ == Changelog == += 17.2.1 = + +## Changelog + +### Bug Fixes + +- Fix: Fatal php error if a template was created by an author that was deleted ([56990](https://github.com/WordPress/gutenberg/pull/56990)) + +## Contributors + +The following contributors merged PRs in this release: + +@jorgefilipecosta + + = 17.2.0 = From e07802b3d2395c4563c6e8a8013bb2224e0927d7 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Wed, 13 Dec 2023 11:49:41 +0900 Subject: [PATCH 10/13] Editor Canvas: Fix animation when device type changes (#56970) * Editor Canvas: Fix animation when device type changes * Include margin in deviceStyles --- .../block-editor/src/components/use-resize-canvas/README.md | 2 +- .../block-editor/src/components/use-resize-canvas/index.js | 5 ++++- packages/editor/src/components/editor-canvas/index.js | 5 ++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/use-resize-canvas/README.md b/packages/block-editor/src/components/use-resize-canvas/README.md index 51e583f8def47..ce8f06adea5d8 100644 --- a/packages/block-editor/src/components/use-resize-canvas/README.md +++ b/packages/block-editor/src/components/use-resize-canvas/README.md @@ -1,6 +1,6 @@ # useResizeCanvas -This React hook generates inline CSS suitable for resizing a container to fit a device's dimensions. It adjusts the CSS according to the current device dimensions. It has no effect on desktop. +This React hook generates inline CSS suitable for resizing a container to fit a device's dimensions. It adjusts the CSS according to the current device dimensions. On-page CSS media queries are also updated to match the width of the device. diff --git a/packages/block-editor/src/components/use-resize-canvas/index.js b/packages/block-editor/src/components/use-resize-canvas/index.js index fab0b7a15e2af..a843f16005636 100644 --- a/packages/block-editor/src/components/use-resize-canvas/index.js +++ b/packages/block-editor/src/components/use-resize-canvas/index.js @@ -67,7 +67,10 @@ export default function useResizeCanvas( deviceType ) { overflowY: 'auto', }; default: - return null; + return { + marginLeft: marginHorizontal, + marginRight: marginHorizontal, + }; } }; diff --git a/packages/editor/src/components/editor-canvas/index.js b/packages/editor/src/components/editor-canvas/index.js index 921f3ce23c0ee..1f74dfd262ff5 100644 --- a/packages/editor/src/components/editor-canvas/index.js +++ b/packages/editor/src/components/editor-canvas/index.js @@ -304,7 +304,10 @@ function EditorCanvas( height="100%" iframeProps={ { ...iframeProps, - style: { ...iframeProps?.style, ...deviceStyles }, + style: { + ...iframeProps?.style, + ...deviceStyles, + }, } } > { themeSupportsLayout && From 9855f1629143b107abb3a07a8ca689cc11b28fd2 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 13 Dec 2023 14:11:36 +1100 Subject: [PATCH 11/13] Background image support: Remove double output of styling rules (#56997) --- lib/block-supports/background.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/block-supports/background.php b/lib/block-supports/background.php index b4779b1a150e4..ab2fa84361fc2 100644 --- a/lib/block-supports/background.php +++ b/lib/block-supports/background.php @@ -103,4 +103,7 @@ function gutenberg_render_background_support( $block_content, $block ) { ) ); +if ( function_exists( 'wp_render_background_support' ) ) { + remove_filter( 'render_block', 'wp_render_background_support' ); +} add_filter( 'render_block', 'gutenberg_render_background_support', 10, 2 ); From 4fb8952c4ed1e8a918241bf4b1dabf623d9eb166 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Wed, 13 Dec 2023 08:54:29 +0200 Subject: [PATCH 12/13] Editor: Fix display of edit template blocks notification (#56978) --- .../editor-canvas/edit-template-blocks-notification.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/components/editor-canvas/edit-template-blocks-notification.js b/packages/editor/src/components/editor-canvas/edit-template-blocks-notification.js index 047ca6688ff02..566311e20cadc 100644 --- a/packages/editor/src/components/editor-canvas/edit-template-blocks-notification.js +++ b/packages/editor/src/components/editor-canvas/edit-template-blocks-notification.js @@ -42,7 +42,7 @@ export default function EditTemplateBlocksNotification( { contentRef } ) { useEffect( () => { const handleClick = async ( event ) => { - if ( renderingMode === 'template-only' ) { + if ( renderingMode !== 'template-locked' ) { return; } if ( ! event.target.classList.contains( 'is-root-container' ) ) { @@ -71,7 +71,7 @@ export default function EditTemplateBlocksNotification( { contentRef } ) { }; const handleDblClick = ( event ) => { - if ( renderingMode === 'template-only' ) { + if ( renderingMode !== 'template-locked' ) { return; } if ( ! event.target.classList.contains( 'is-root-container' ) ) { From 77a8b55446c7a92ebdd430f2f09eaa804c32f66b Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 13 Dec 2023 10:15:14 +0100 Subject: [PATCH 13/13] Framework: Bundle the BlockTools component within BlockCanvas (#56996) --- packages/block-editor/README.md | 5 +- .../src/components/block-canvas/index.js | 48 ++++++++++++------- .../components/sidebar-block-editor/index.js | 17 +++---- .../src/components/visual-editor/index.js | 11 ++--- .../components/block-editor/editor-canvas.js | 9 +--- .../block-editor/site-editor-canvas.js | 19 ++------ .../src/components/editor-canvas/index.js | 28 +++++------ platform-docs/docs/basic-concepts/ui.md | 2 +- storybook/stories/playground/box/index.js | 2 - .../stories/playground/fullpage/index.js | 5 +- .../playground/with-undo-redo/index.js | 2 - .../helpers/integration-test-editor.js | 5 +- 12 files changed, 62 insertions(+), 91 deletions(-) diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 56ab5f1bd94d9..6c39b5dcc44b4 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -19,7 +19,6 @@ import { useState } from 'react'; import { BlockEditorProvider, BlockList, - BlockTools, WritingFlow, } from '@wordpress/block-editor'; @@ -32,9 +31,7 @@ function MyEditorComponent() { onInput={ ( blocks ) => updateBlocks( blocks ) } onChange={ ( blocks ) => updateBlocks( blocks ) } > - - - + ); } diff --git a/packages/block-editor/src/components/block-canvas/index.js b/packages/block-editor/src/components/block-canvas/index.js index 97aec461df7d8..7d64897690721 100644 --- a/packages/block-editor/src/components/block-canvas/index.js +++ b/packages/block-editor/src/components/block-canvas/index.js @@ -2,11 +2,13 @@ * WordPress dependencies */ import { useMergeRefs } from '@wordpress/compose'; +import { useRef } from '@wordpress/element'; /** * Internal dependencies */ import BlockList from '../block-list'; +import BlockTools from '../block-tools'; import EditorStyles from '../editor-styles'; import Iframe from '../iframe'; import WritingFlow from '../writing-flow'; @@ -23,11 +25,15 @@ export function ExperimentalBlockCanvas( { } ) { const resetTypingRef = useMouseMoveTypingReset(); const clearerRef = useBlockSelectionClearer(); - const contentRef = useMergeRefs( [ contentRefProp, clearerRef ] ); + const localRef = useRef(); + const contentRef = useMergeRefs( [ contentRefProp, clearerRef, localRef ] ); if ( ! shouldIframe ) { return ( - <> + { children } - + ); } return ( - + + ); } diff --git a/packages/customize-widgets/src/components/sidebar-block-editor/index.js b/packages/customize-widgets/src/components/sidebar-block-editor/index.js index c2e10bca16ec0..80deb12dfcf74 100644 --- a/packages/customize-widgets/src/components/sidebar-block-editor/index.js +++ b/packages/customize-widgets/src/components/sidebar-block-editor/index.js @@ -8,7 +8,6 @@ import { useMemo, createPortal } from '@wordpress/element'; import { BlockList, BlockToolbar, - BlockTools, BlockInspector, privateApis as blockEditorPrivateApis, __unstableBlockSettingsMenuFirstItem, @@ -120,15 +119,13 @@ export default function SidebarBlockEditor( { { ( isFixedToolbarActive || ! isMediumViewport ) && ( ) } - - - - - + + + { createPortal( // This is a temporary hack to prevent button component inside diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index b929e03bc453a..fd9b4a6ff8bb6 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -10,8 +10,7 @@ import { store as editorStore, privateApis as editorPrivateApis, } from '@wordpress/editor'; -import { BlockTools } from '@wordpress/block-editor'; -import { useRef, useMemo } from '@wordpress/element'; +import { useMemo } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; import { store as blocksStore } from '@wordpress/blocks'; @@ -59,8 +58,6 @@ export default function VisualEditor( { styles } ) { paddingBottom = '40vh'; } - const ref = useRef(); - styles = useMemo( () => [ ...styles, @@ -80,21 +77,19 @@ export default function VisualEditor( { styles } ) { renderingMode === 'template-only'; return ( - - + ); } diff --git a/packages/edit-site/src/components/block-editor/editor-canvas.js b/packages/edit-site/src/components/block-editor/editor-canvas.js index d7dbf6fb07a7a..01bc4cdfa2ddf 100644 --- a/packages/edit-site/src/components/block-editor/editor-canvas.js +++ b/packages/edit-site/src/components/block-editor/editor-canvas.js @@ -25,13 +25,7 @@ import { const { EditorCanvas: EditorCanvasRoot } = unlock( editorPrivateApis ); -function EditorCanvas( { - enableResizing, - settings, - children, - contentRef, - ...props -} ) { +function EditorCanvas( { enableResizing, settings, children, ...props } ) { const { hasBlocks, isFocusMode, templateType, canvasMode, isZoomOutMode } = useSelect( ( select ) => { const { getBlockCount, __unstableGetEditorMode } = @@ -107,7 +101,6 @@ function EditorCanvas( { return ( { const { getEditedPostType, getCanvasMode } = unlock( select( editSiteStore ) @@ -53,7 +49,6 @@ export default function SiteEditorCanvas() { // Disable resizing in mobile viewport. ! isMobileViewport; - const contentRef = useRef(); const isTemplateTypeNavigation = templateType === NAVIGATION_POST_TYPE; const isNavigationFocusMode = isTemplateTypeNavigation && isFocusMode; const forceFullHeight = isNavigationFocusMode; @@ -66,18 +61,11 @@ export default function SiteEditorCanvas() { { editorCanvasView } ) : ( - { - // Clear selected block when clicking on the gray background. - if ( event.target === event.currentTarget ) { - clearSelectedBlock(); - } - } } > { resizeObserver } - + ) } diff --git a/packages/editor/src/components/editor-canvas/index.js b/packages/editor/src/components/editor-canvas/index.js index 1f74dfd262ff5..cd87db0d4bf5e 100644 --- a/packages/editor/src/components/editor-canvas/index.js +++ b/packages/editor/src/components/editor-canvas/index.js @@ -16,7 +16,7 @@ import { privateApis as blockEditorPrivateApis, __experimentalUseResizeCanvas as useResizeCanvas, } from '@wordpress/block-editor'; -import { useEffect, useRef, useMemo, forwardRef } from '@wordpress/element'; +import { useEffect, useRef, useMemo } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; import { parse } from '@wordpress/blocks'; import { store as coreStore } from '@wordpress/core-data'; @@ -72,19 +72,16 @@ function checkForPostContentAtRootLevel( blocks ) { return false; } -function EditorCanvas( - { - // Ideally as we unify post and site editors, we won't need these props. - autoFocus, - className, - renderAppender, - styles, - disableIframe = false, - iframeProps, - children, - }, - ref -) { +function EditorCanvas( { + // Ideally as we unify post and site editors, we won't need these props. + autoFocus, + className, + renderAppender, + styles, + disableIframe = false, + iframeProps, + children, +} ) { const { renderingMode, postContentAttributes, @@ -288,7 +285,6 @@ function EditorCanvas( const typewriterRef = useTypewriter(); const contentRef = useMergeRefs( [ - ref, localRef, renderingMode === 'post-only' ? typewriterRef : undefined, ].filter( ( r ) => !! r ) @@ -382,4 +378,4 @@ function EditorCanvas( ); } -export default forwardRef( EditorCanvas ); +export default EditorCanvas; diff --git a/platform-docs/docs/basic-concepts/ui.md b/platform-docs/docs/basic-concepts/ui.md index 8b6e706683d08..0dccef3c239b0 100644 --- a/platform-docs/docs/basic-concepts/ui.md +++ b/platform-docs/docs/basic-concepts/ui.md @@ -17,7 +17,7 @@ The Gutenberg platform allows you to render these pieces separately and lay them ## The Block Toolbar -Wrapping your `BlockCanvas` component within the `BlockTools` wrapper allows the editor to render a block toolbar adjacent to the selected block. +The block toolbar is rendered automatically next to the selected block by default. But if you set the flag `hasFixedToolbar` to true in your `BlockEditorProvider` settings, you will be able to use the `BlockToolbar` component to render the block toolbar in your place of choice. ## The Block Inspector diff --git a/storybook/stories/playground/box/index.js b/storybook/stories/playground/box/index.js index 4cb7047b73ec2..3fb3c3b5862c4 100644 --- a/storybook/stories/playground/box/index.js +++ b/storybook/stories/playground/box/index.js @@ -7,7 +7,6 @@ import { BlockEditorProvider, BlockCanvas, BlockToolbar, - BlockTools, } from '@wordpress/block-editor'; /** @@ -38,7 +37,6 @@ export default function EditorBox() { } } > - diff --git a/storybook/stories/playground/fullpage/index.js b/storybook/stories/playground/fullpage/index.js index 961c15f71f31d..8b8c037ceb72a 100644 --- a/storybook/stories/playground/fullpage/index.js +++ b/storybook/stories/playground/fullpage/index.js @@ -5,7 +5,6 @@ import { useEffect, useState } from '@wordpress/element'; import { BlockCanvas, BlockEditorProvider, - BlockTools, BlockInspector, } from '@wordpress/block-editor'; import { registerCoreBlocks } from '@wordpress/block-library'; @@ -46,9 +45,9 @@ export default function EditorFullPage() {
    - +
    - +
    ); diff --git a/storybook/stories/playground/with-undo-redo/index.js b/storybook/stories/playground/with-undo-redo/index.js index 537ea16aade99..8bef2d184f8c5 100644 --- a/storybook/stories/playground/with-undo-redo/index.js +++ b/storybook/stories/playground/with-undo-redo/index.js @@ -8,7 +8,6 @@ import { BlockEditorProvider, BlockCanvas, BlockToolbar, - BlockTools, } from '@wordpress/block-editor'; import { Button } from '@wordpress/components'; import { undo as undoIcon, redo as redoIcon } from '@wordpress/icons'; @@ -60,7 +59,6 @@ export default function EditorWithUndoRedo() { label="Redo" /> - diff --git a/test/integration/helpers/integration-test-editor.js b/test/integration/helpers/integration-test-editor.js index dc83c1bfbe6bd..1317dec7b9226 100644 --- a/test/integration/helpers/integration-test-editor.js +++ b/test/integration/helpers/integration-test-editor.js @@ -10,7 +10,6 @@ import userEvent from '@testing-library/user-event'; import { useState, useEffect } from '@wordpress/element'; import { BlockEditorProvider, - BlockTools, BlockInspector, privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; @@ -76,9 +75,7 @@ export function Editor( { testBlocks, settings = {} } ) { settings={ settings } > - - - + ); }