From 5e37a1316d0a39bc880bf6efff9b9f26db381690 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Fri, 20 Sep 2024 11:43:45 +0200 Subject: [PATCH 01/13] useToolsPanel: calculate menuItems in layout effect to avoid painting intermediate state (#65494) * useToolsPanel: calculate menuItems in layout effect to avoid painting intermediate state * useToolsPanel: remove setState deps, calculate derived values in layout effects * ToolsPanelItem: also use layout effect to prevent loops * Changelog entry Co-authored-by: jsnajdr Co-authored-by: ciampo --- packages/components/CHANGELOG.md | 4 + .../src/tools-panel/tools-panel-item/hook.ts | 13 +- .../src/tools-panel/tools-panel/hook.ts | 131 ++++++++---------- 3 files changed, 68 insertions(+), 80 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 6607700b2d2c25..03d6eb30e336b0 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Bug Fixes + +- `ToolsPanel`: avoid paining intermediate unfinished states ([#65494](https://github.com/WordPress/gutenberg/pull/65494)). + ## 28.8.0 (2024-09-19) ### Bug Fixes diff --git a/packages/components/src/tools-panel/tools-panel-item/hook.ts b/packages/components/src/tools-panel/tools-panel-item/hook.ts index 1e33e7c6740ded..27a0ceb27e7ce7 100644 --- a/packages/components/src/tools-panel/tools-panel-item/hook.ts +++ b/packages/components/src/tools-panel/tools-panel-item/hook.ts @@ -2,12 +2,7 @@ * WordPress dependencies */ import { usePrevious } from '@wordpress/compose'; -import { - useCallback, - useEffect, - useLayoutEffect, - useMemo, -} from '@wordpress/element'; +import { useCallback, useLayoutEffect, useMemo } from '@wordpress/element'; /** * Internal dependencies @@ -101,7 +96,7 @@ export function useToolsPanelItem( deregisterPanelItem, ] ); - useEffect( () => { + useLayoutEffect( () => { if ( hasMatchingPanel ) { registerResetAllFilter( resetAllFilterCallback ); } @@ -127,7 +122,7 @@ export function useToolsPanelItem( const isValueSet = hasValue(); // Notify the panel when an item's value has changed except for optional // items without value because the item should not cause itself to hide. - useEffect( () => { + useLayoutEffect( () => { if ( ! isShownByDefault && ! isValueSet ) { return; } @@ -143,7 +138,7 @@ export function useToolsPanelItem( // Determine if the panel item's corresponding menu is being toggled and // trigger appropriate callback if it is. - useEffect( () => { + useLayoutEffect( () => { // We check whether this item is currently registered as items rendered // via fills can persist through the parent panel being remounted. // See: https://github.com/WordPress/gutenberg/pull/45673 diff --git a/packages/components/src/tools-panel/tools-panel/hook.ts b/packages/components/src/tools-panel/tools-panel/hook.ts index 931bf2494e6e34..d67d732d4df671 100644 --- a/packages/components/src/tools-panel/tools-panel/hook.ts +++ b/packages/components/src/tools-panel/tools-panel/hook.ts @@ -3,7 +3,7 @@ */ import { useCallback, - useEffect, + useLayoutEffect, useMemo, useRef, useState, @@ -101,7 +101,7 @@ export function useToolsPanel( // the resetAll task. Without this, the flag is cleared after the first // control updates and forces a rerender with subsequent controls then // believing they need to reset, unfortunately using stale data. - useEffect( () => { + useLayoutEffect( () => { if ( wasResetting ) { isResettingRef.current = false; } @@ -114,76 +114,66 @@ export function useToolsPanel( ResetAllFilter[] >( [] ); - const registerPanelItem = useCallback( - ( item: ToolsPanelItem ) => { - // Add item to panel items. - setPanelItems( ( items ) => { - const newItems = [ ...items ]; - // If an item with this label has already been registered, remove it - // first. This can happen when an item is moved between the default - // and optional groups. - const existingIndex = newItems.findIndex( - ( oldItem ) => oldItem.label === item.label - ); - if ( existingIndex !== -1 ) { - newItems.splice( existingIndex, 1 ); - } - return [ ...newItems, item ]; - } ); + const registerPanelItem = useCallback( ( item: ToolsPanelItem ) => { + // Add item to panel items. + setPanelItems( ( items ) => { + const newItems = [ ...items ]; + // If an item with this label has already been registered, remove it + // first. This can happen when an item is moved between the default + // and optional groups. + const existingIndex = newItems.findIndex( + ( oldItem ) => oldItem.label === item.label + ); + if ( existingIndex !== -1 ) { + newItems.splice( existingIndex, 1 ); + } + return [ ...newItems, item ]; + } ); - // Track the initial order of item registration. This is used for - // maintaining menu item order later. - setMenuItemOrder( ( items ) => { - if ( items.includes( item.label ) ) { - return items; - } + // Track the initial order of item registration. This is used for + // maintaining menu item order later. + setMenuItemOrder( ( items ) => { + if ( items.includes( item.label ) ) { + return items; + } - return [ ...items, item.label ]; - } ); - }, - [ setPanelItems, setMenuItemOrder ] - ); + return [ ...items, item.label ]; + } ); + }, [] ); // Panels need to deregister on unmount to avoid orphans in menu state. // This is an issue when panel items are being injected via SlotFills. - const deregisterPanelItem = useCallback( - ( label: string ) => { - // When switching selections between components injecting matching - // controls, e.g. both panels have a "padding" control, the - // deregistration of the first panel doesn't occur until after the - // registration of the next. - setPanelItems( ( items ) => { - const newItems = [ ...items ]; - const index = newItems.findIndex( - ( item ) => item.label === label - ); - if ( index !== -1 ) { - newItems.splice( index, 1 ); - } - return newItems; - } ); - }, - [ setPanelItems ] - ); + const deregisterPanelItem = useCallback( ( label: string ) => { + // When switching selections between components injecting matching + // controls, e.g. both panels have a "padding" control, the + // deregistration of the first panel doesn't occur until after the + // registration of the next. + setPanelItems( ( items ) => { + const newItems = [ ...items ]; + const index = newItems.findIndex( + ( item ) => item.label === label + ); + if ( index !== -1 ) { + newItems.splice( index, 1 ); + } + return newItems; + } ); + }, [] ); const registerResetAllFilter = useCallback( ( newFilter: ResetAllFilter ) => { - setResetAllFilters( ( filters ) => { - return [ ...filters, newFilter ]; - } ); + setResetAllFilters( ( filters ) => [ ...filters, newFilter ] ); }, - [ setResetAllFilters ] + [] ); const deregisterResetAllFilter = useCallback( ( filterToRemove: ResetAllFilter ) => { - setResetAllFilters( ( filters ) => { - return filters.filter( - ( filter ) => filter !== filterToRemove - ); - } ); + setResetAllFilters( ( filters ) => + filters.filter( ( filter ) => filter !== filterToRemove ) + ); }, - [ setResetAllFilters ] + [] ); // Manage and share display state of menu items representing child controls. @@ -193,17 +183,16 @@ export function useToolsPanel( } ); // Setup menuItems state as panel items register themselves. - useEffect( () => { - setMenuItems( ( prevState ) => { - const items = generateMenuItems( { + useLayoutEffect( () => { + setMenuItems( ( currentMenuItems ) => + generateMenuItems( { panelItems, shouldReset: false, - currentMenuItems: prevState, + currentMenuItems, menuItemOrder, - } ); - return items; - } ); - }, [ panelItems, setMenuItems, menuItemOrder ] ); + } ) + ); + }, [ panelItems, menuItemOrder ] ); // Updates the status of the panel’s menu items. For default items the // value represents whether it differs from the default and for optional @@ -225,7 +214,7 @@ export function useToolsPanel( return newState; } ); }, - [ setMenuItems ] + [] ); // Whether all optional menu items are hidden or not must be tracked @@ -235,7 +224,7 @@ export function useToolsPanel( const [ areAllOptionalControlsHidden, setAreAllOptionalControlsHidden ] = useState( false ); - useEffect( () => { + useLayoutEffect( () => { if ( isMenuItemTypeEmpty( menuItems?.default ) && ! isMenuItemTypeEmpty( menuItems?.optional ) @@ -245,7 +234,7 @@ export function useToolsPanel( ).some( ( [ , isSelected ] ) => isSelected ); setAreAllOptionalControlsHidden( allControlsHidden ); } - }, [ menuItems, setAreAllOptionalControlsHidden ] ); + }, [ menuItems ] ); const cx = useCx(); const classes = useMemo( () => { @@ -297,7 +286,7 @@ export function useToolsPanel( setMenuItems( newMenuItems ); }, - [ menuItems, panelItems, setMenuItems ] + [ menuItems, panelItems ] ); // Resets display of children and executes resetAll callback if available. @@ -314,7 +303,7 @@ export function useToolsPanel( shouldReset: true, } ); setMenuItems( resetMenuItems ); - }, [ panelItems, resetAllFilters, resetAll, setMenuItems, menuItemOrder ] ); + }, [ panelItems, resetAllFilters, resetAll, menuItemOrder ] ); // Assist ItemGroup styling when there are potentially hidden placeholder // items by identifying first & last items that are toggled on for display. From 27fe9522dda47bc1f2d0aec74e6eafa8c1eeb84a Mon Sep 17 00:00:00 2001 From: dhruvang21 <105810308+dhruvang21@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:40:10 +0530 Subject: [PATCH 02/13] Added keywords to query loop block (#65515) * Add keywords to query loop block * Update keyword post types to custom post types Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> --------- Co-authored-by: dhruvang21 Co-authored-by: mikachan --- packages/block-library/src/query/block.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-library/src/query/block.json b/packages/block-library/src/query/block.json index 22bfa7b713801c..b2225192c6b218 100644 --- a/packages/block-library/src/query/block.json +++ b/packages/block-library/src/query/block.json @@ -5,6 +5,7 @@ "title": "Query Loop", "category": "theme", "description": "An advanced block that allows displaying post types based on different query parameters and visual configurations.", + "keywords": [ "posts", "list", "blog", "blogs", "custom post types" ], "textdomain": "default", "attributes": { "queryId": { From e9c38589390678f2fa97a16223394492cae7b389 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 20 Sep 2024 12:14:12 +0100 Subject: [PATCH 03/13] Select Mode: Blocks outside the main sections root should be disabled (#65518) Co-authored-by: youknowriad Co-authored-by: getdave Co-authored-by: draganescu --- packages/block-editor/src/store/selectors.js | 73 ++++++++---- .../block-editor/src/store/test/selectors.js | 105 ++++++++++++++++-- 2 files changed, 150 insertions(+), 28 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 806e2b5f5993eb..20d6627398886c 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2901,6 +2901,14 @@ export function __unstableIsWithinBlockOverlay( state, clientId ) { return false; } +function isWithinBlock( state, clientId, parentClientId ) { + let parent = state.blocks.parents.get( clientId ); + while ( !! parent && parent !== parentClientId ) { + parent = state.blocks.parents.get( parent ); + } + return parent === parentClientId; +} + /** * @typedef {import('../components/block-editing-mode').BlockEditingMode} BlockEditingMode */ @@ -2968,36 +2976,58 @@ export const getBlockEditingMode = createRegistrySelector( return 'disabled'; } + if ( editorMode === 'navigation' ) { + const sectionRootClientId = getSectionRootClientId( state ); + + // The root section is "default mode" + if ( clientId === sectionRootClientId ) { + return 'default'; + } + + // Sections should always be contentOnly in navigation mode. + const sectionsClientIds = getBlockOrder( + state, + sectionRootClientId + ); + if ( sectionsClientIds.includes( clientId ) ) { + return 'contentOnly'; + } + + // Blocks outside sections should be disabled. + const isWithinSectionRoot = isWithinBlock( + state, + clientId, + sectionRootClientId + ); + if ( ! isWithinSectionRoot ) { + return 'disabled'; + } + + // The rest of the blocks depend on whether they are content blocks or not. + // This "flattens" the sections tree. + const name = getBlockName( state, clientId ); + const isContent = + select( blocksStore ).__experimentalHasContentRoleAttribute( + name + ); + return isContent ? 'contentOnly' : 'disabled'; + } + + // In normal mode, consider that an explicitely set editing mode takes over. const blockEditingMode = state.blockEditingModes.get( clientId ); if ( blockEditingMode ) { return blockEditingMode; } + + // In normal mode, top level is default mode. if ( ! clientId ) { return 'default'; } - const sectionRootClientId = getSectionRootClientId( state ); - if ( - editorMode === 'navigation' && - clientId === sectionRootClientId - ) { - return 'default'; - } - const sectionsClientIds = getBlockOrder( - state, - sectionRootClientId - ); + const rootClientId = getBlockRootClientId( state, clientId ); const templateLock = getTemplateLock( state, rootClientId ); - if ( - templateLock === 'contentOnly' || - editorMode === 'navigation' - ) { - // Sections should always be contentOnly in navigation mode. - // This will also cause them to display in List View providing - // a structure. - if ( sectionsClientIds.includes( clientId ) ) { - return 'contentOnly'; - } + // If the parent of the block is contentOnly locked, check whether it's a content block. + if ( templateLock === 'contentOnly' ) { const name = getBlockName( state, clientId ); const isContent = select( blocksStore ).__experimentalHasContentRoleAttribute( @@ -3005,6 +3035,7 @@ export const getBlockEditingMode = createRegistrySelector( ); return isContent ? 'contentOnly' : 'disabled'; } + // Otherwise, check if there's an ancestor that is contentOnly const parentMode = getBlockEditingMode( state, rootClientId ); return parentMode === 'contentOnly' ? 'default' : parentMode; } diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js index 04899a9ee243fd..2895f6573dc54f 100644 --- a/packages/block-editor/src/store/test/selectors.js +++ b/packages/block-editor/src/store/test/selectors.js @@ -15,6 +15,7 @@ import { select, dispatch } from '@wordpress/data'; */ import * as selectors from '../selectors'; import { store } from '../'; +import { sectionRootClientIdKey } from '../private-keys'; const { getBlockName, @@ -4382,12 +4383,28 @@ describe( 'getBlockEditingMode', () => { settings: {}, blocks: { byClientId: new Map( [ - [ '6cf70164-9097-4460-bcbf-200560546988', {} ], // Header - [ 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', {} ], // Group - [ 'b26fc763-417d-4f01-b81c-2ec61e14a972', {} ], // | Post Title - [ '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', {} ], // | Post Content - [ 'b3247f75-fd94-4fef-97f9-5bfd162cc416', {} ], // | | Paragraph - [ 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', {} ], // | | Paragraph + [ + '6cf70164-9097-4460-bcbf-200560546988', + { name: 'core/template-part' }, + ], // Header + [ + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', + { name: 'core/group' }, + ], // Group + [ + 'b26fc763-417d-4f01-b81c-2ec61e14a972', + { name: 'core/post-title' }, + ], // | Post Title + [ + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + { name: 'core/group' }, + ], // | Group + [ 'b3247f75-fd94-4fef-97f9-5bfd162cc416', { name: 'core/p' } ], // | | Paragraph + [ 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', { name: 'core/p' } ], // | | Paragraph + [ + '9b9c5c3f-2e46-4f02-9e14-9fed515b958s', + { name: 'core/group' }, + ], // | | Group ] ), order: new Map( [ [ @@ -4411,10 +4428,12 @@ describe( 'getBlockEditingMode', () => { [ 'b3247f75-fd94-4fef-97f9-5bfd162cc416', 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', + '9b9c5c3f-2e46-4f02-9e14-9fed515b958s', ], ], [ 'b3247f75-fd94-4fef-97f9-5bfd162cc416', [] ], [ 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', [] ], + [ '9b9c5c3f-2e46-4f02-9e14-9fed515b958s', [] ], ] ), parents: new Map( [ [ '6cf70164-9097-4460-bcbf-200560546988', '' ], @@ -4435,6 +4454,10 @@ describe( 'getBlockEditingMode', () => { 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c', '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', ], + [ + '9b9c5c3f-2e46-4f02-9e14-9fed515b958s', + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f', + ], ] ), }, blockListSettings: { @@ -4444,7 +4467,18 @@ describe( 'getBlockEditingMode', () => { blockEditingModes: new Map( [] ), }; - const __experimentalHasContentRoleAttribute = jest.fn( () => false ); + const navigationModeStateWithRootSection = { + ...baseState, + editorMode: 'navigation', + settings: { + [ sectionRootClientIdKey ]: 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337', // The group is the "main" container + }, + }; + + const __experimentalHasContentRoleAttribute = jest.fn( ( name ) => { + // consider paragraphs as content blocks. + return name === 'core/p'; + } ); getBlockEditingMode.registry = { select: jest.fn( () => ( { __experimentalHasContentRoleAttribute, @@ -4573,4 +4607,61 @@ describe( 'getBlockEditingMode', () => { getBlockEditingMode( state, 'b3247f75-fd94-4fef-97f9-5bfd162cc416' ) ).toBe( 'contentOnly' ); } ); + + it( 'in navigation mode, the root section container is default', () => { + expect( + getBlockEditingMode( + navigationModeStateWithRootSection, + 'ef45d5fd-5234-4fd5-ac4f-c3736c7f9337' + ) + ).toBe( 'default' ); + } ); + + it( 'in navigation mode, anything outside the section container is disabled', () => { + expect( + getBlockEditingMode( + navigationModeStateWithRootSection, + '6cf70164-9097-4460-bcbf-200560546988' + ) + ).toBe( 'disabled' ); + } ); + + it( 'in navigation mode, sections are contentOnly', () => { + expect( + getBlockEditingMode( + navigationModeStateWithRootSection, + 'b26fc763-417d-4f01-b81c-2ec61e14a972' + ) + ).toBe( 'contentOnly' ); + expect( + getBlockEditingMode( + navigationModeStateWithRootSection, + '9b9c5c3f-2e46-4f02-9e14-9fe9515b958f' + ) + ).toBe( 'contentOnly' ); + } ); + + it( 'in navigation mode, blocks with content attributes within sections are contentOnly', () => { + expect( + getBlockEditingMode( + navigationModeStateWithRootSection, + 'b3247f75-fd94-4fef-97f9-5bfd162cc416' + ) + ).toBe( 'contentOnly' ); + expect( + getBlockEditingMode( + navigationModeStateWithRootSection, + 'e178812d-ce5e-48c7-a945-8ae4ffcbbb7c' + ) + ).toBe( 'contentOnly' ); + } ); + + it( 'in navigation mode, blocks without content attributes within sections are disabled', () => { + expect( + getBlockEditingMode( + navigationModeStateWithRootSection, + '9b9c5c3f-2e46-4f02-9e14-9fed515b958s' + ) + ).toBe( 'disabled' ); + } ); } ); From ff112d76363fb3a89c8ab4e3c6fcfa52c37bd3ff Mon Sep 17 00:00:00 2001 From: Mario Santos <34552881+SantosGuillamot@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:46:47 +0200 Subject: [PATCH 04/13] Block Bindings: Remove key fallback in bindings get values and rely on source label (#65517) * Always fall back to source label * Adapt e2e tests Co-authored-by: SantosGuillamot Co-authored-by: gziolo --- .../src/hooks/use-bindings-attributes.js | 5 ++-- .../editor/various/block-bindings.spec.js | 26 +------------------ 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/packages/block-editor/src/hooks/use-bindings-attributes.js b/packages/block-editor/src/hooks/use-bindings-attributes.js index e1ebf5fda6b8ee..ac045004cc654b 100644 --- a/packages/block-editor/src/hooks/use-bindings-attributes.js +++ b/packages/block-editor/src/hooks/use-bindings-attributes.js @@ -162,9 +162,8 @@ export const withBlockBindingSupport = createHigherOrderComponent( let values = {}; if ( ! source.getValues ) { Object.keys( bindings ).forEach( ( attr ) => { - // Default to the `key` or the source label when `getValues` doesn't exist - values[ attr ] = - bindings[ attr ].args?.key || source.label; + // Default to the the source label when `getValues` doesn't exist. + values[ attr ] = source.label; } ); } else { values = source.getValues( { diff --git a/test/e2e/specs/editor/various/block-bindings.spec.js b/test/e2e/specs/editor/various/block-bindings.spec.js index c556c469698ebd..90a5d2b1da9f10 100644 --- a/test/e2e/specs/editor/various/block-bindings.spec.js +++ b/test/e2e/specs/editor/various/block-bindings.spec.js @@ -66,7 +66,7 @@ test.describe( 'Block bindings', () => { ); } ); - test( 'should show the key of the custom field in server sources with key', async ( { + test( 'should always show the source label in server-only sources', async ( { editor, } ) => { await editor.insertBlock( { @@ -86,30 +86,6 @@ test.describe( 'Block bindings', () => { const paragraphBlock = editor.canvas.getByRole( 'document', { name: 'Block: Paragraph', } ); - await expect( paragraphBlock ).toHaveText( - 'text_custom_field' - ); - } ); - - test( 'should show the source label in server sources without key', async ( { - editor, - } ) => { - await editor.insertBlock( { - name: 'core/paragraph', - attributes: { - content: 'paragraph default content', - metadata: { - bindings: { - content: { - source: 'core/server-source', - }, - }, - }, - }, - } ); - const paragraphBlock = editor.canvas.getByRole( 'document', { - name: 'Block: Paragraph', - } ); await expect( paragraphBlock ).toHaveText( 'Server Source' ); } ); From 2632234b2bdd6ef8e8b89e7521d370cfa0041764 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 20 Sep 2024 14:39:28 +0200 Subject: [PATCH 05/13] Script Modules: Centralize registration (#65460) Rework how Script Modules are registered in Gutenberg. Script Module registration is handled in one central place. A combined assets file is used for Script Modules and registration. This means that dependencies and versions will be used correctly and kept up-to-date while avoiding repeated file reads. Block library Script Module assets that are enqueued on demand _are registered in a centralized location_. The assets are enqueued on demand. **This requires a Core change** since the block library PHP files are synced to Core and also require centralized Script Module registration (https://github.com/WordPress/wordpress-develop/pull/7360). This solves a problem where Gutenberg-specific code was being shipped in Core through block-library. The block library Script Module asset Module IDs are renamed to indicate they are view files and align with the naming from #65064: @wordpress/block-library/query is @wordpress/block-library/query/view (indicating it is a view file). --- This is sufficient to change Script Modules to use Gutenberg in a backwards compatible way: - `@wordpress/ineractivity` and `@wordpress/interactivity-router` were registered on `wp_enqueue_scripts`. That action fires after the `wp_default_scripts` used here. Registering an already registered Script Module is a no-op. This change registers first. - The only other Script Modules currently available in Core are from the block library. Those have been registered conditionally on use. The ID is changed here, so there's little risk of the wrong version being used. There is a Core companion PR that will be necessary to land: https://github.com/WordPress/wordpress-develop/pull/7360 --- Co-authored-by: sirreal Co-authored-by: gziolo --- .github/workflows/unit-test.yml | 5 +- backport-changelog/6.7/7360.md | 3 ++ lib/client-assets.php | 50 +++++++++++++++++++ lib/experimental/script-modules.php | 25 +--------- lib/interactivity-api.php | 31 ------------ packages/block-library/src/file/index.php | 13 +---- packages/block-library/src/image/index.php | 14 +----- .../block-library/src/navigation/index.php | 13 +---- packages/block-library/src/query/index.php | 22 +------- packages/block-library/src/search/index.php | 13 +---- tools/webpack/script-modules.js | 12 +++-- 11 files changed, 72 insertions(+), 129 deletions(-) create mode 100644 backport-changelog/6.7/7360.md diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index c0f70070908c1c..b80804b824e3cd 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -135,7 +135,9 @@ jobs: uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: build-assets - path: ./build/ + path: | + ./build/ + ./build-module/ test-php: name: PHP ${{ matrix.php }}${{ matrix.multisite && ' multisite' || '' }}${{ matrix.wordpress != '' && format( ' (WP {0}) ', matrix.wordpress ) || '' }} on ubuntu-latest @@ -212,7 +214,6 @@ jobs: uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: build-assets - path: ./build - name: Docker debug information run: | diff --git a/backport-changelog/6.7/7360.md b/backport-changelog/6.7/7360.md new file mode 100644 index 00000000000000..b2fb8efd624b93 --- /dev/null +++ b/backport-changelog/6.7/7360.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/7360 + +* https://github.com/WordPress/gutenberg/pull/65460 diff --git a/lib/client-assets.php b/lib/client-assets.php index 62e874d6b06c82..2343530e5595a7 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -601,6 +601,56 @@ function gutenberg_register_vendor_scripts( $scripts ) { } add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' ); +/** + * Registers or re-registers Gutenberg Script Modules. + * + * Script modules that are registered by Core will be re-registered by Gutenberg. + * + * @since 19.3.0 + */ +function gutenberg_default_script_modules() { + /* + * Expects multidimensional array like: + * + * 'interactivity/index.min.js' => array('dependencies' => array(…), 'version' => '…'), + * 'interactivity/debug.min.js' => array('dependencies' => array(…), 'version' => '…'), + * 'interactivity-router/index.min.js' => … + */ + $assets = include gutenberg_dir_path() . '/build-module/assets.php'; + + foreach ( $assets as $file_name => $script_module_data ) { + /* + * Build the WordPress Script Module ID from the file name. + * Prepend `@wordpress/` and remove extensions and `/index` if present: + * - interactivity/index.min.js => @wordpress/interactivity + * - interactivity/debug.min.js => @wordpress/interactivity/debug + * - block-library/query/view.js => @wordpress/block-library/query/view + */ + $script_module_id = '@wordpress/' . preg_replace( '~(?:/index)?\.min\.js$~D', '', $file_name, 1 ); + switch ( $script_module_id ) { + /* + * Interactivity exposes two entrypoints, "/index" and "/debug". + * "/debug" should replalce "/index" in devlopment. + */ + case '@wordpress/interactivity/debug': + if ( ! SCRIPT_DEBUG ) { + continue 2; + } + $script_module_id = '@wordpress/interactivity'; + break; + case '@wordpress/interactivity': + if ( SCRIPT_DEBUG ) { + continue 2; + } + break; + } + + $path = gutenberg_url( "build-module/{$file_name}" ); + wp_register_script_module( $script_module_id, $path, $script_module_data['dependencies'], $script_module_data['version'] ); + } +} +remove_action( 'wp_default_scripts', 'wp_default_script_modules' ); +add_action( 'wp_default_scripts', 'gutenberg_default_script_modules' ); /* * Always remove the Core action hook while gutenberg_enqueue_stored_styles() exists to avoid styles being printed twice. diff --git a/lib/experimental/script-modules.php b/lib/experimental/script-modules.php index f65bc1704dd890..fe23786fc03621 100644 --- a/lib/experimental/script-modules.php +++ b/lib/experimental/script-modules.php @@ -239,26 +239,5 @@ function gutenberg_a11y_script_module_html() { . '
' . ''; } - -/** - * Registers Gutenberg Script Modules. - * - * @since 19.3 - */ -function gutenberg_register_script_modules() { - // When in production, use the plugin's version as the default asset version; - // else (for development or test) default to use the current time. - $default_version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time(); - - wp_deregister_script_module( '@wordpress/a11y' ); - wp_register_script_module( - '@wordpress/a11y', - gutenberg_url( 'build-module/a11y/index.min.js' ), - array(), - $default_version - ); - - add_action( 'wp_footer', 'gutenberg_a11y_script_module_html' ); - add_action( 'admin_footer', 'gutenberg_a11y_script_module_html' ); -} -add_action( 'init', 'gutenberg_register_script_modules' ); +add_action( 'wp_footer', 'gutenberg_a11y_script_module_html' ); +add_action( 'admin_footer', 'gutenberg_a11y_script_module_html' ); diff --git a/lib/interactivity-api.php b/lib/interactivity-api.php index c00d68bc70e8e2..ff68936f054a7e 100644 --- a/lib/interactivity-api.php +++ b/lib/interactivity-api.php @@ -5,37 +5,6 @@ * @package gutenberg */ -/** - * Deregisters the Core Interactivity API Modules and replace them - * with the ones from the Gutenberg plugin. - */ -function gutenberg_reregister_interactivity_script_modules() { - $default_version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time(); - wp_deregister_script_module( '@wordpress/interactivity' ); - wp_deregister_script_module( '@wordpress/interactivity-router' ); - - wp_register_script_module( - '@wordpress/interactivity', - gutenberg_url( '/build-module/' . ( SCRIPT_DEBUG ? 'interactivity/debug.min.js' : 'interactivity/index.min.js' ) ), - array(), - $default_version - ); - - wp_register_script_module( - '@wordpress/interactivity-router', - gutenberg_url( '/build-module/interactivity-router/index.min.js' ), - array( - array( - 'id' => '@wordpress/a11y', - 'import' => 'dynamic', - ), - '@wordpress/interactivity', - ), - $default_version - ); -} -add_action( 'init', 'gutenberg_reregister_interactivity_script_modules' ); - /** * Adds script data to the interactivity-router script module. * diff --git a/packages/block-library/src/file/index.php b/packages/block-library/src/file/index.php index 85cc840201da59..8ea668d56d8545 100644 --- a/packages/block-library/src/file/index.php +++ b/packages/block-library/src/file/index.php @@ -19,18 +19,7 @@ function render_block_core_file( $attributes, $content ) { // If it's interactive, enqueue the script module and add the directives. if ( ! empty( $attributes['displayPreview'] ) ) { - $suffix = wp_scripts_get_suffix(); - if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { - $module_url = gutenberg_url( '/build-module/block-library/file/view.min.js' ); - } - - wp_register_script_module( - '@wordpress/block-library/file', - isset( $module_url ) ? $module_url : includes_url( "blocks/file/view{$suffix}.js" ), - array( '@wordpress/interactivity' ), - defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : get_bloginfo( 'version' ) - ); - wp_enqueue_script_module( '@wordpress/block-library/file' ); + wp_enqueue_script_module( '@wordpress/block-library/file/view' ); $processor = new WP_HTML_Tag_Processor( $content ); $processor->next_tag(); diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index abbb03c0952452..5d7815a1f2f3fb 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -70,19 +70,7 @@ function render_block_core_image( $attributes, $content, $block ) { isset( $lightbox_settings['enabled'] ) && true === $lightbox_settings['enabled'] ) { - $suffix = wp_scripts_get_suffix(); - if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { - $module_url = gutenberg_url( '/build-module/block-library/image/view.min.js' ); - } - - wp_register_script_module( - '@wordpress/block-library/image', - isset( $module_url ) ? $module_url : includes_url( "blocks/image/view{$suffix}.js" ), - array( '@wordpress/interactivity' ), - defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : get_bloginfo( 'version' ) - ); - - wp_enqueue_script_module( '@wordpress/block-library/image' ); + wp_enqueue_script_module( '@wordpress/block-library/image/view' ); /* * This render needs to happen in a filter with priority 15 to ensure that diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index ec72b03b6906f0..10fec84ed59d9c 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -622,18 +622,7 @@ private static function get_nav_element_directives( $is_interactive ) { */ private static function handle_view_script_module_loading( $attributes, $block, $inner_blocks ) { if ( static::is_interactive( $attributes, $inner_blocks ) ) { - $suffix = wp_scripts_get_suffix(); - if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { - $module_url = gutenberg_url( '/build-module/block-library/navigation/view.min.js' ); - } - - wp_register_script_module( - '@wordpress/block-library/navigation', - isset( $module_url ) ? $module_url : includes_url( "blocks/navigation/view{$suffix}.js" ), - array( '@wordpress/interactivity' ), - defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : get_bloginfo( 'version' ) - ); - wp_enqueue_script_module( '@wordpress/block-library/navigation' ); + wp_enqueue_script_module( '@wordpress/block-library/navigation/view' ); } } diff --git a/packages/block-library/src/query/index.php b/packages/block-library/src/query/index.php index d10db26529854e..043f351e11d7f1 100644 --- a/packages/block-library/src/query/index.php +++ b/packages/block-library/src/query/index.php @@ -24,27 +24,7 @@ function render_block_core_query( $attributes, $content, $block ) { // Enqueue the script module and add the necessary directives if the block is // interactive. if ( $is_interactive ) { - $suffix = wp_scripts_get_suffix(); - if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { - $module_url = gutenberg_url( '/build-module/block-library/query/view.min.js' ); - } - - wp_register_script_module( - '@wordpress/block-library/query', - isset( $module_url ) ? $module_url : includes_url( "blocks/query/view{$suffix}.js" ), - array( - array( - 'id' => '@wordpress/interactivity', - 'import' => 'static', - ), - array( - 'id' => '@wordpress/interactivity-router', - 'import' => 'dynamic', - ), - ), - defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : get_bloginfo( 'version' ) - ); - wp_enqueue_script_module( '@wordpress/block-library/query' ); + wp_enqueue_script_module( '@wordpress/block-library/query/view' ); $p = new WP_HTML_Tag_Processor( $content ); if ( $p->next_tag() ) { diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php index fb09cdd36406e8..e4259bb0ce2c7f 100644 --- a/packages/block-library/src/search/index.php +++ b/packages/block-library/src/search/index.php @@ -80,18 +80,7 @@ function render_block_core_search( $attributes ) { // If it's interactive, enqueue the script module and add the directives. $is_expandable_searchfield = 'button-only' === $button_position; if ( $is_expandable_searchfield ) { - $suffix = wp_scripts_get_suffix(); - if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { - $module_url = gutenberg_url( '/build-module/block-library/search/view.min.js' ); - } - - wp_register_script_module( - '@wordpress/block-library/search', - isset( $module_url ) ? $module_url : includes_url( "blocks/search/view{$suffix}.js" ), - array( '@wordpress/interactivity' ), - defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : get_bloginfo( 'version' ) - ); - wp_enqueue_script_module( '@wordpress/block-library/search' ); + wp_enqueue_script_module( '@wordpress/block-library/search/view' ); $input->set_attribute( 'data-wp-bind--aria-hidden', '!context.isSearchInputVisible' ); $input->set_attribute( 'data-wp-bind--tabindex', 'state.tabindex' ); diff --git a/tools/webpack/script-modules.js b/tools/webpack/script-modules.js index 18287c96d83c8a..021f11f5f5ed95 100644 --- a/tools/webpack/script-modules.js +++ b/tools/webpack/script-modules.js @@ -89,11 +89,11 @@ module.exports = { }, output: { devtoolNamespace: 'wp', - filename: './build-module/[name].min.js', + filename: '[name].min.js', library: { type: 'module', }, - path: join( __dirname, '..', '..' ), + path: join( __dirname, '..', '..', 'build-module' ), environment: { module: true }, module: true, chunkFormat: 'module', @@ -102,7 +102,13 @@ module.exports = { resolve: { extensions: [ '.js', '.ts', '.tsx' ], }, - plugins: [ ...plugins, new DependencyExtractionWebpackPlugin() ], + plugins: [ + ...plugins, + new DependencyExtractionWebpackPlugin( { + combineAssets: true, + combinedOutputFile: `./assets.php`, + } ), + ], watchOptions: { ignored: [ '**/node_modules' ], aggregateTimeout: 500, From 0d2ebd0c5b5198ef8946aae62886960efd87df9e Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Fri, 20 Sep 2024 14:32:45 +0100 Subject: [PATCH 06/13] Decouple "zoom/scaling the canvas" from zoom out mode (without mode rename) (#65482) * Use separate state for Zoom Level Co-authored-by: getdave Co-authored-by: draganescu Co-authored-by: MaggieCabrera # Conflicts: # packages/block-editor/src/components/block-tools/zoom-out-toolbar.js # packages/block-editor/src/components/tool-selector/index.js # packages/block-editor/src/store/reducer.js # packages/edit-site/src/components/global-styles/screen-style-variations.js # packages/editor/src/components/visual-editor/index.js * Refactor useZoomOut hook to use new state # Conflicts: # packages/block-editor/src/hooks/use-zoom-out.js * Use Zoom and Editor Mode in Toggle * Only hide footer when zoom level is set # Conflicts: # packages/editor/src/components/editor-interface/index.js * Adjust double click to exit zoom out mode * Standardise nomenclature * More standardisation of nomenclature * Reduce complexity of state and actions * Fix accidental breakage of Styles panel zoom * Fix hook * Reduce hook to single effect --- packages/block-editor/README.md | 4 +- .../use-block-props/use-zoom-out-mode-exit.js | 10 ++-- .../block-tools/zoom-out-toolbar.js | 7 ++- .../src/components/tool-selector/index.js | 5 +- .../block-editor/src/hooks/use-zoom-out.js | 48 ++++++++----------- .../block-editor/src/store/private-actions.js | 23 +++++++++ .../src/store/private-selectors.js | 20 ++++++++ packages/block-editor/src/store/reducer.js | 20 ++++++++ .../src/components/editor-interface/index.js | 10 ++-- .../src/components/visual-editor/index.js | 14 +++--- .../src/components/zoom-out-toggle/index.js | 23 ++++++--- 11 files changed, 133 insertions(+), 51 deletions(-) diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index a474d1df7e56d0..75d7e8879572d4 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -1027,11 +1027,11 @@ _Parameters_ ### useZoomOut -A hook used to set the editor mode to zoomed out mode, invoking the hook sets the mode. +A hook used to set the zoomed out view, invoking the hook sets the mode. _Parameters_ -- _zoomOut_ `boolean`: If we should enter into zoomOut mode or not +- _zoomOut_ `boolean`: If we should zoom out or not. ### Warning diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-zoom-out-mode-exit.js b/packages/block-editor/src/components/block-list/use-block-props/use-zoom-out-mode-exit.js index d0001bd3b33c68..92c54bac9b806d 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-zoom-out-mode-exit.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-zoom-out-mode-exit.js @@ -16,14 +16,17 @@ import { unlock } from '../../../lock-unlock'; * @param {string} clientId Block client ID. */ export function useZoomOutModeExit( { editorMode } ) { - const { getSettings } = useSelect( blockEditorStore ); - const { __unstableSetEditorMode } = unlock( + const { getSettings, isZoomOut } = unlock( useSelect( blockEditorStore ) ); + const { __unstableSetEditorMode, resetZoomLevel } = unlock( useDispatch( blockEditorStore ) ); return useRefEffect( ( node ) => { - if ( editorMode !== 'zoom-out' ) { + // In "compose" mode. + const composeMode = editorMode === 'zoom-out' && isZoomOut(); + + if ( ! composeMode ) { return; } @@ -39,6 +42,7 @@ export function useZoomOutModeExit( { editorMode } ) { __experimentalSetIsInserterOpened( false ); } __unstableSetEditorMode( 'edit' ); + resetZoomLevel(); } } diff --git a/packages/block-editor/src/components/block-tools/zoom-out-toolbar.js b/packages/block-editor/src/components/block-tools/zoom-out-toolbar.js index 560bfc6ebb58d6..b8736de11481a0 100644 --- a/packages/block-editor/src/components/block-tools/zoom-out-toolbar.js +++ b/packages/block-editor/src/components/block-tools/zoom-out-toolbar.js @@ -15,6 +15,7 @@ import BlockDraggable from '../block-draggable'; import BlockMover from '../block-mover'; import Shuffle from '../block-toolbar/shuffle'; import NavigableToolbar from '../navigable-toolbar'; +import { unlock } from '../../lock-unlock'; export default function ZoomOutToolbar( { clientId, __unstableContentRef } ) { const selected = useSelect( @@ -76,8 +77,9 @@ export default function ZoomOutToolbar( { clientId, __unstableContentRef } ) { setIsInserterOpened, } = selected; - const { removeBlock, __unstableSetEditorMode } = - useDispatch( blockEditorStore ); + const { removeBlock, __unstableSetEditorMode, resetZoomLevel } = unlock( + useDispatch( blockEditorStore ) + ); const showBlockDraggable = canMove && ! isBlockTemplatePart; @@ -132,6 +134,7 @@ export default function ZoomOutToolbar( { clientId, __unstableContentRef } ) { setIsInserterOpened( false ); } __unstableSetEditorMode( 'edit' ); + resetZoomLevel(); __unstableContentRef.current?.focus(); } } /> diff --git a/packages/block-editor/src/components/tool-selector/index.js b/packages/block-editor/src/components/tool-selector/index.js index bf55c78caf489a..53fab037ba3684 100644 --- a/packages/block-editor/src/components/tool-selector/index.js +++ b/packages/block-editor/src/components/tool-selector/index.js @@ -18,6 +18,7 @@ import { Icon, edit as editIcon } from '@wordpress/icons'; * Internal dependencies */ import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; const selectIcon = ( select( blockEditorStore ).__unstableGetEditorMode(), [] ); - const { __unstableSetEditorMode } = useDispatch( blockEditorStore ); + const { __unstableSetEditorMode } = unlock( + useDispatch( blockEditorStore ) + ); return ( { // Only set this on mount so we know what to return to when we unmount. - if ( ! originalEditingModeRef.current ) { - originalEditingModeRef.current = mode; + if ( ! originalIsZoomOutRef.current ) { + originalIsZoomOutRef.current = isZoomOut(); } - return () => { - // We need to use __unstableGetEditorMode() here and not `mode`, as mode may not update on unmount - if ( - __unstableGetEditorMode() === 'zoom-out' && - __unstableGetEditorMode() !== originalEditingModeRef.current - ) { - __unstableSetEditorMode( originalEditingModeRef.current ); - } - }; - }, [] ); - - // The effect opens the zoom-out view if we want it open and it's not currently in zoom-out mode. - useEffect( () => { - if ( zoomOut && mode !== 'zoom-out' ) { - __unstableSetEditorMode( 'zoom-out' ); + // The effect opens the zoom-out view if we want it open and the canvas is not currently zoomed-out. + if ( zoomOut && isZoomOut() === false ) { + setZoomLevel( 50 ); } else if ( ! zoomOut && - __unstableGetEditorMode() === 'zoom-out' && - originalEditingModeRef.current !== mode + isZoomOut() && + originalIsZoomOutRef.current !== isZoomOut() ) { - __unstableSetEditorMode( originalEditingModeRef.current ); + setZoomLevel( originalIsZoomOutRef.current ? 50 : 100 ); } - }, [ __unstableGetEditorMode, __unstableSetEditorMode, zoomOut ] ); // Mode is deliberately excluded from the dependencies so that the effect does not run when mode changes. + + return () => { + if ( isZoomOut() && isZoomOut() !== originalIsZoomOutRef.current ) { + setZoomLevel( originalIsZoomOutRef.current ? 50 : 100 ); + } + }; + }, [ isZoomOut, setZoomLevel, zoomOut ] ); } diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index dc57d61fd6b76c..441a07202c42ac 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -383,3 +383,26 @@ export const modifyContentLockBlock = focusModeToRevert ); }; + +/** + * Sets the zoom level. + * + * @param {number} zoom the new zoom level + * @return {Object} Action object. + */ +export function setZoomLevel( zoom = 100 ) { + return { + type: 'SET_ZOOM_LEVEL', + zoom, + }; +} + +/** + * Resets the Zoom state. + * @return {Object} Action object. + */ +export function resetZoomLevel() { + return { + type: 'RESET_ZOOM_LEVEL', + }; +} diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 0e77e8e2ed433d..d8955bd6342c4c 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -616,3 +616,23 @@ export function isZoomOutMode( state ) { export function getSectionRootClientId( state ) { return state.settings?.[ sectionRootClientIdKey ]; } + +/** + * Returns the zoom out state. + * + * @param {Object} state Global application state. + * @return {boolean} The zoom out state. + */ +export function getZoomLevel( state ) { + return state.zoomLevel; +} + +/** + * Returns whether the editor is considered zoomed out. + * + * @param {Object} state Global application state. + * @return {boolean} Whether the editor is zoomed. + */ +export function isZoomOut( state ) { + return getZoomLevel( state ) < 100; +} diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 3a99d18ef17ee3..827a2141f44c1c 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -2060,6 +2060,25 @@ export function hoveredBlockClientId( state = false, action ) { return state; } +/** + * Reducer setting zoom out state. + * + * @param {boolean} state Current state. + * @param {Object} action Dispatched action. + * + * @return {boolean} Updated state. + */ +export function zoomLevel( state = 100, action ) { + switch ( action.type ) { + case 'SET_ZOOM_LEVEL': + return action.zoom; + case 'RESET_ZOOM_LEVEL': + return 100; + } + + return state; +} + const combinedReducers = combineReducers( { blocks, isDragging, @@ -2092,6 +2111,7 @@ const combinedReducers = combineReducers( { openedBlockSettingsMenu, registeredInserterMediaCategories, hoveredBlockClientId, + zoomLevel, } ); function withAutomaticChangeReset( reducer ) { diff --git a/packages/editor/src/components/editor-interface/index.js b/packages/editor/src/components/editor-interface/index.js index 645e5fb6f53a26..0709b91175712d 100644 --- a/packages/editor/src/components/editor-interface/index.js +++ b/packages/editor/src/components/editor-interface/index.js @@ -32,6 +32,8 @@ import TextEditor from '../text-editor'; import VisualEditor from '../visual-editor'; import EditorContentSlotFill from './content-slot-fill'; +import { unlock } from '../../lock-unlock'; + const interfaceLabels = { /* translators: accessibility text for the editor top bar landmark region. */ header: __( 'Editor top bar' ), @@ -71,12 +73,13 @@ export default function EditorInterface( { nextShortcut, showBlockBreadcrumbs, documentLabel, - blockEditorMode, + isZoomOut, } = useSelect( ( select ) => { const { get } = select( preferencesStore ); const { getEditorSettings, getPostTypeLabel } = select( editorStore ); const editorSettings = getEditorSettings(); const postTypeLabel = getPostTypeLabel(); + const { isZoomOut: _isZoomOut } = unlock( select( blockEditorStore ) ); return { mode: select( editorStore ).getEditorMode(), @@ -94,8 +97,7 @@ export default function EditorInterface( { showBlockBreadcrumbs: get( 'core', 'showBlockBreadcrumbs' ), // translators: Default label for the Document in the Block Breadcrumb. documentLabel: postTypeLabel || _x( 'Document', 'noun' ), - blockEditorMode: - select( blockEditorStore ).__unstableGetEditorMode(), + isZoomOut: _isZoomOut(), }; }, [] ); const isLargeViewport = useViewportMatch( 'medium' ); @@ -206,7 +208,7 @@ export default function EditorInterface( { isLargeViewport && showBlockBreadcrumbs && isRichEditingEnabled && - blockEditorMode !== 'zoom-out' && + ! isZoomOut && mode === 'visual' && ( ) diff --git a/packages/editor/src/components/visual-editor/index.js b/packages/editor/src/components/visual-editor/index.js index 2ff115272d614b..88d2dac8ffd77c 100644 --- a/packages/editor/src/components/visual-editor/index.js +++ b/packages/editor/src/components/visual-editor/index.js @@ -174,17 +174,19 @@ function VisualEditor( { hasRootPaddingAwareAlignments, themeHasDisabledLayoutStyles, themeSupportsLayout, - isZoomOutMode, + isZoomedOut, } = useSelect( ( select ) => { - const { getSettings, __unstableGetEditorMode } = - select( blockEditorStore ); + const { getSettings, isZoomOut: _isZoomOut } = unlock( + select( blockEditorStore ) + ); + const _settings = getSettings(); return { themeHasDisabledLayoutStyles: _settings.disableLayoutStyles, themeSupportsLayout: _settings.supportsLayout, hasRootPaddingAwareAlignments: _settings.__experimentalFeatures?.useRootPaddingAwareAlignments, - isZoomOutMode: __unstableGetEditorMode() === 'zoom-out', + isZoomedOut: _isZoomOut(), }; }, [] ); @@ -336,7 +338,7 @@ function VisualEditor( { ] ); const zoomOutProps = - isZoomOutMode && ! isTabletViewport + isZoomedOut && ! isTabletViewport ? { scale: 'default', frameSize: '48px', @@ -355,7 +357,7 @@ function VisualEditor( { // Disable resizing in mobile viewport. ! isMobileViewport && // Dsiable resizing in zoomed-out mode. - ! isZoomOutMode; + ! isZoomedOut; const shouldIframe = ! disableIframe || [ 'Tablet', 'Mobile' ].includes( deviceType ); diff --git a/packages/editor/src/components/zoom-out-toggle/index.js b/packages/editor/src/components/zoom-out-toggle/index.js index e8c7b1e50510ab..214b1c51fd6255 100644 --- a/packages/editor/src/components/zoom-out-toggle/index.js +++ b/packages/editor/src/components/zoom-out-toggle/index.js @@ -8,16 +8,27 @@ import { useDispatch, useSelect } from '@wordpress/data'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { square as zoomOutIcon } from '@wordpress/icons'; +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; + const ZoomOutToggle = () => { - const { isZoomOutMode } = useSelect( ( select ) => ( { - isZoomOutMode: - select( blockEditorStore ).__unstableGetEditorMode() === 'zoom-out', + const { isZoomOut } = useSelect( ( select ) => ( { + isZoomOut: unlock( select( blockEditorStore ) ).isZoomOut(), } ) ); - const { __unstableSetEditorMode } = useDispatch( blockEditorStore ); + const { resetZoomLevel, setZoomLevel, __unstableSetEditorMode } = unlock( + useDispatch( blockEditorStore ) + ); const handleZoomOut = () => { - __unstableSetEditorMode( isZoomOutMode ? 'edit' : 'zoom-out' ); + if ( isZoomOut ) { + resetZoomLevel(); + } else { + setZoomLevel( 50 ); + } + __unstableSetEditorMode( isZoomOut ? 'edit' : 'zoom-out' ); }; return ( @@ -25,7 +36,7 @@ const ZoomOutToggle = () => { onClick={ handleZoomOut } icon={ zoomOutIcon } label={ __( 'Toggle Zoom Out' ) } - isPressed={ isZoomOutMode } + isPressed={ isZoomOut } size="compact" /> ); From eaf2258cdeed35a4d3c690d66892981a85c4d9ed Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 20 Sep 2024 15:19:34 +0100 Subject: [PATCH 07/13] =?UTF-8?q?Revert=20"useToolsPanel:=20calculate=20me?= =?UTF-8?q?nuItems=20in=20layout=20effect=20to=20avoid=20painting=E2=80=A6?= =?UTF-8?q?"=20(#65533)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5e37a1316d0a39bc880bf6efff9b9f26db381690. --- packages/components/CHANGELOG.md | 4 - .../src/tools-panel/tools-panel-item/hook.ts | 13 +- .../src/tools-panel/tools-panel/hook.ts | 131 ++++++++++-------- 3 files changed, 80 insertions(+), 68 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 03d6eb30e336b0..6607700b2d2c25 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,10 +2,6 @@ ## Unreleased -### Bug Fixes - -- `ToolsPanel`: avoid paining intermediate unfinished states ([#65494](https://github.com/WordPress/gutenberg/pull/65494)). - ## 28.8.0 (2024-09-19) ### Bug Fixes diff --git a/packages/components/src/tools-panel/tools-panel-item/hook.ts b/packages/components/src/tools-panel/tools-panel-item/hook.ts index 27a0ceb27e7ce7..1e33e7c6740ded 100644 --- a/packages/components/src/tools-panel/tools-panel-item/hook.ts +++ b/packages/components/src/tools-panel/tools-panel-item/hook.ts @@ -2,7 +2,12 @@ * WordPress dependencies */ import { usePrevious } from '@wordpress/compose'; -import { useCallback, useLayoutEffect, useMemo } from '@wordpress/element'; +import { + useCallback, + useEffect, + useLayoutEffect, + useMemo, +} from '@wordpress/element'; /** * Internal dependencies @@ -96,7 +101,7 @@ export function useToolsPanelItem( deregisterPanelItem, ] ); - useLayoutEffect( () => { + useEffect( () => { if ( hasMatchingPanel ) { registerResetAllFilter( resetAllFilterCallback ); } @@ -122,7 +127,7 @@ export function useToolsPanelItem( const isValueSet = hasValue(); // Notify the panel when an item's value has changed except for optional // items without value because the item should not cause itself to hide. - useLayoutEffect( () => { + useEffect( () => { if ( ! isShownByDefault && ! isValueSet ) { return; } @@ -138,7 +143,7 @@ export function useToolsPanelItem( // Determine if the panel item's corresponding menu is being toggled and // trigger appropriate callback if it is. - useLayoutEffect( () => { + useEffect( () => { // We check whether this item is currently registered as items rendered // via fills can persist through the parent panel being remounted. // See: https://github.com/WordPress/gutenberg/pull/45673 diff --git a/packages/components/src/tools-panel/tools-panel/hook.ts b/packages/components/src/tools-panel/tools-panel/hook.ts index d67d732d4df671..931bf2494e6e34 100644 --- a/packages/components/src/tools-panel/tools-panel/hook.ts +++ b/packages/components/src/tools-panel/tools-panel/hook.ts @@ -3,7 +3,7 @@ */ import { useCallback, - useLayoutEffect, + useEffect, useMemo, useRef, useState, @@ -101,7 +101,7 @@ export function useToolsPanel( // the resetAll task. Without this, the flag is cleared after the first // control updates and forces a rerender with subsequent controls then // believing they need to reset, unfortunately using stale data. - useLayoutEffect( () => { + useEffect( () => { if ( wasResetting ) { isResettingRef.current = false; } @@ -114,66 +114,76 @@ export function useToolsPanel( ResetAllFilter[] >( [] ); - const registerPanelItem = useCallback( ( item: ToolsPanelItem ) => { - // Add item to panel items. - setPanelItems( ( items ) => { - const newItems = [ ...items ]; - // If an item with this label has already been registered, remove it - // first. This can happen when an item is moved between the default - // and optional groups. - const existingIndex = newItems.findIndex( - ( oldItem ) => oldItem.label === item.label - ); - if ( existingIndex !== -1 ) { - newItems.splice( existingIndex, 1 ); - } - return [ ...newItems, item ]; - } ); + const registerPanelItem = useCallback( + ( item: ToolsPanelItem ) => { + // Add item to panel items. + setPanelItems( ( items ) => { + const newItems = [ ...items ]; + // If an item with this label has already been registered, remove it + // first. This can happen when an item is moved between the default + // and optional groups. + const existingIndex = newItems.findIndex( + ( oldItem ) => oldItem.label === item.label + ); + if ( existingIndex !== -1 ) { + newItems.splice( existingIndex, 1 ); + } + return [ ...newItems, item ]; + } ); - // Track the initial order of item registration. This is used for - // maintaining menu item order later. - setMenuItemOrder( ( items ) => { - if ( items.includes( item.label ) ) { - return items; - } + // Track the initial order of item registration. This is used for + // maintaining menu item order later. + setMenuItemOrder( ( items ) => { + if ( items.includes( item.label ) ) { + return items; + } - return [ ...items, item.label ]; - } ); - }, [] ); + return [ ...items, item.label ]; + } ); + }, + [ setPanelItems, setMenuItemOrder ] + ); // Panels need to deregister on unmount to avoid orphans in menu state. // This is an issue when panel items are being injected via SlotFills. - const deregisterPanelItem = useCallback( ( label: string ) => { - // When switching selections between components injecting matching - // controls, e.g. both panels have a "padding" control, the - // deregistration of the first panel doesn't occur until after the - // registration of the next. - setPanelItems( ( items ) => { - const newItems = [ ...items ]; - const index = newItems.findIndex( - ( item ) => item.label === label - ); - if ( index !== -1 ) { - newItems.splice( index, 1 ); - } - return newItems; - } ); - }, [] ); + const deregisterPanelItem = useCallback( + ( label: string ) => { + // When switching selections between components injecting matching + // controls, e.g. both panels have a "padding" control, the + // deregistration of the first panel doesn't occur until after the + // registration of the next. + setPanelItems( ( items ) => { + const newItems = [ ...items ]; + const index = newItems.findIndex( + ( item ) => item.label === label + ); + if ( index !== -1 ) { + newItems.splice( index, 1 ); + } + return newItems; + } ); + }, + [ setPanelItems ] + ); const registerResetAllFilter = useCallback( ( newFilter: ResetAllFilter ) => { - setResetAllFilters( ( filters ) => [ ...filters, newFilter ] ); + setResetAllFilters( ( filters ) => { + return [ ...filters, newFilter ]; + } ); }, - [] + [ setResetAllFilters ] ); const deregisterResetAllFilter = useCallback( ( filterToRemove: ResetAllFilter ) => { - setResetAllFilters( ( filters ) => - filters.filter( ( filter ) => filter !== filterToRemove ) - ); + setResetAllFilters( ( filters ) => { + return filters.filter( + ( filter ) => filter !== filterToRemove + ); + } ); }, - [] + [ setResetAllFilters ] ); // Manage and share display state of menu items representing child controls. @@ -183,16 +193,17 @@ export function useToolsPanel( } ); // Setup menuItems state as panel items register themselves. - useLayoutEffect( () => { - setMenuItems( ( currentMenuItems ) => - generateMenuItems( { + useEffect( () => { + setMenuItems( ( prevState ) => { + const items = generateMenuItems( { panelItems, shouldReset: false, - currentMenuItems, + currentMenuItems: prevState, menuItemOrder, - } ) - ); - }, [ panelItems, menuItemOrder ] ); + } ); + return items; + } ); + }, [ panelItems, setMenuItems, menuItemOrder ] ); // Updates the status of the panel’s menu items. For default items the // value represents whether it differs from the default and for optional @@ -214,7 +225,7 @@ export function useToolsPanel( return newState; } ); }, - [] + [ setMenuItems ] ); // Whether all optional menu items are hidden or not must be tracked @@ -224,7 +235,7 @@ export function useToolsPanel( const [ areAllOptionalControlsHidden, setAreAllOptionalControlsHidden ] = useState( false ); - useLayoutEffect( () => { + useEffect( () => { if ( isMenuItemTypeEmpty( menuItems?.default ) && ! isMenuItemTypeEmpty( menuItems?.optional ) @@ -234,7 +245,7 @@ export function useToolsPanel( ).some( ( [ , isSelected ] ) => isSelected ); setAreAllOptionalControlsHidden( allControlsHidden ); } - }, [ menuItems ] ); + }, [ menuItems, setAreAllOptionalControlsHidden ] ); const cx = useCx(); const classes = useMemo( () => { @@ -286,7 +297,7 @@ export function useToolsPanel( setMenuItems( newMenuItems ); }, - [ menuItems, panelItems ] + [ menuItems, panelItems, setMenuItems ] ); // Resets display of children and executes resetAll callback if available. @@ -303,7 +314,7 @@ export function useToolsPanel( shouldReset: true, } ); setMenuItems( resetMenuItems ); - }, [ panelItems, resetAllFilters, resetAll, menuItemOrder ] ); + }, [ panelItems, resetAllFilters, resetAll, setMenuItems, menuItemOrder ] ); // Assist ItemGroup styling when there are potentially hidden placeholder // items by identifying first & last items that are toggled on for display. From 8e795580bdd03724f0a894a34418ea58e208b3e3 Mon Sep 17 00:00:00 2001 From: Vipul Gupta <55375170+vipul0425@users.noreply.github.com> Date: Fri, 20 Sep 2024 21:10:10 +0530 Subject: [PATCH 08/13] fix: Makes the edit mode persistent in top toolbar mode. (#65511) Co-authored-by: vipul0425 Co-authored-by: youknowriad Co-authored-by: jameskoster Co-authored-by: andrewserong --- packages/editor/src/components/document-tools/index.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/editor/src/components/document-tools/index.js b/packages/editor/src/components/document-tools/index.js index 54121652bbf131..6a8c20c8d70551 100644 --- a/packages/editor/src/components/document-tools/index.js +++ b/packages/editor/src/components/document-tools/index.js @@ -38,10 +38,8 @@ function DocumentTools( { className, disableBlockTools = false } ) { listViewShortcut, inserterSidebarToggleRef, listViewToggleRef, - hasFixedToolbar, showIconLabels, } = useSelect( ( select ) => { - const { getSettings } = select( blockEditorStore ); const { get } = select( preferencesStore ); const { isListViewOpened, @@ -60,7 +58,6 @@ function DocumentTools( { className, disableBlockTools = false } ) { ), inserterSidebarToggleRef: getInserterSidebarToggleRef(), listViewToggleRef: getListViewToggleRef(), - hasFixedToolbar: getSettings().hasFixedToolbar, showIconLabels: get( 'core', 'showIconLabels' ), isDistractionFree: get( 'core', 'distractionFree' ), isVisualMode: getEditorMode() === 'visual', @@ -137,7 +134,7 @@ function DocumentTools( { className, disableBlockTools = false } ) { ) } { ( isWideViewport || ! showIconLabels ) && ( <> - { isLargeViewport && ! hasFixedToolbar && ( + { isLargeViewport && ( Date: Fri, 20 Sep 2024 17:33:13 +0100 Subject: [PATCH 09/13] Select Mode: Prevent the inbetween inserter from triggering within sections (#65529) Co-authored-by: youknowriad Co-authored-by: mikachan Co-authored-by: jameskoster --- .../src/components/block-list/use-in-between-inserter.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-list/use-in-between-inserter.js b/packages/block-editor/src/components/block-list/use-in-between-inserter.js index bb307816fd1501..2b76804785a576 100644 --- a/packages/block-editor/src/components/block-list/use-in-between-inserter.js +++ b/packages/block-editor/src/components/block-list/use-in-between-inserter.js @@ -11,6 +11,7 @@ import { isRTL } from '@wordpress/i18n'; */ import { store as blockEditorStore } from '../../store'; import { InsertionPointOpenRef } from '../block-tools/insertion-point'; +import { unlock } from '../../lock-unlock'; export function useInBetweenInserter() { const openRef = useContext( InsertionPointOpenRef ); @@ -31,7 +32,8 @@ export function useInBetweenInserter() { getBlockEditingMode, getBlockName, getBlockAttributes, - } = useSelect( blockEditorStore ); + getParentSectionBlock, + } = unlock( useSelect( blockEditorStore ) ); const { showInsertionPoint, hideInsertionPoint } = useDispatch( blockEditorStore ); @@ -133,7 +135,8 @@ export function useInBetweenInserter() { const clientId = element.id.slice( 'block-'.length ); if ( ! clientId || - __unstableIsWithinBlockOverlay( clientId ) + __unstableIsWithinBlockOverlay( clientId ) || + !! getParentSectionBlock( clientId ) ) { return; } From b36cbf4a1c7ad8d3a1a7a38871e153bec9497a4b Mon Sep 17 00:00:00 2001 From: James Koster Date: Fri, 20 Sep 2024 17:49:35 +0100 Subject: [PATCH 10/13] Fix grid layout padding on small screens (#64878) Co-authored-by: jameskoster Co-authored-by: jasmussen --- packages/dataviews/src/components/dataviews/style.scss | 1 - packages/dataviews/src/dataviews-layouts/grid/style.scss | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/dataviews/src/components/dataviews/style.scss b/packages/dataviews/src/components/dataviews/style.scss index 8909c7cf1c7cfd..aa8fbcfb009c05 100644 --- a/packages/dataviews/src/components/dataviews/style.scss +++ b/packages/dataviews/src/components/dataviews/style.scss @@ -80,7 +80,6 @@ padding: $grid-unit-15 $grid-unit-30; } - .dataviews-view-grid, .dataviews-no-results, .dataviews-loading { padding-left: $grid-unit-30; diff --git a/packages/dataviews/src/dataviews-layouts/grid/style.scss b/packages/dataviews/src/dataviews-layouts/grid/style.scss index 5fab362b0b47b6..6286ed94685a04 100644 --- a/packages/dataviews/src/dataviews-layouts/grid/style.scss +++ b/packages/dataviews/src/dataviews-layouts/grid/style.scss @@ -163,3 +163,11 @@ .dataviews-view-grid__card.is-selected .dataviews-selection-checkbox { top: $grid-unit-10; } + +/* stylelint-disable-next-line scss/at-rule-no-unknown -- '@container' not globally permitted */ +@container (max-width: 430px) { + .dataviews-view-grid { + padding-left: $grid-unit-30; + padding-right: $grid-unit-30; + } +} From b1d925530d22a8531a0f9c32ffe8ec4430005ab7 Mon Sep 17 00:00:00 2001 From: Sarah Norris <1645628+mikachan@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:50:41 +0100 Subject: [PATCH 11/13] Query Loop: Remove is_singular() check and fix test (#65483) * Replace query_posts() with set() * Fix test_rendering_post_template_with_main_query_loop_already_started test * Remove is_singular check Co-authored-by: mikachan Co-authored-by: jffng Co-authored-by: David Herrera <697432+dlh01@users.noreply.github.com> --- packages/block-library/src/post-template/index.php | 5 ----- phpunit/blocks/render-post-template-test.php | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/block-library/src/post-template/index.php b/packages/block-library/src/post-template/index.php index 64cdd156a54310..9126355c096a57 100644 --- a/packages/block-library/src/post-template/index.php +++ b/packages/block-library/src/post-template/index.php @@ -64,11 +64,6 @@ function render_block_core_post_template( $attributes, $content, $block ) { if ( in_the_loop() ) { $query = clone $wp_query; $query->rewind_posts(); - - // If in a single post of any post type, default to the 'post' post type. - if ( is_singular() ) { - query_posts( array( 'post_type' => 'post' ) ); - } } else { $query = $wp_query; } diff --git a/phpunit/blocks/render-post-template-test.php b/phpunit/blocks/render-post-template-test.php index 6241f6f0605164..e929e459654fe7 100644 --- a/phpunit/blocks/render-post-template-test.php +++ b/phpunit/blocks/render-post-template-test.php @@ -122,7 +122,7 @@ public function test_rendering_post_template_with_main_query_loop_already_starte global $wp_query, $wp_the_query; // Query block with post template block. - $content = ''; + $content = ''; $content .= ''; $content .= ''; $content .= ''; From 219065c3be5881a68a8667fda9c04c7b7f546763 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Sat, 21 Sep 2024 05:04:37 +0900 Subject: [PATCH 12/13] Command Palette: Fix "Add new page" command for hybrid theme (#65534) Co-authored-by: t-hamano Co-authored-by: ramonjd --- .../src/admin-navigation-commands.js | 79 +++++++++++-------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/packages/core-commands/src/admin-navigation-commands.js b/packages/core-commands/src/admin-navigation-commands.js index be5e19386b65b4..c0d8bb084b46ad 100644 --- a/packages/core-commands/src/admin-navigation-commands.js +++ b/packages/core-commands/src/admin-navigation-commands.js @@ -6,8 +6,8 @@ import { __ } from '@wordpress/i18n'; import { plus } from '@wordpress/icons'; import { getPath } from '@wordpress/url'; import { store as coreStore } from '@wordpress/core-data'; -import { useDispatch } from '@wordpress/data'; -import { useMemo } from '@wordpress/element'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { useCallback, useMemo } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; import { privateApis as routerPrivateApis } from '@wordpress/router'; @@ -23,46 +23,55 @@ function useAddNewPageCommand() { 'site-editor.php' ); const history = useHistory(); + const isBlockBasedTheme = useSelect( ( select ) => { + return select( coreStore ).getCurrentTheme()?.is_block_theme; + }, [] ); const { saveEntityRecord } = useDispatch( coreStore ); const { createErrorNotice } = useDispatch( noticesStore ); - const createPageEntity = async ( { close } ) => { - try { - const page = await saveEntityRecord( - 'postType', - 'page', - { - status: 'draft', - }, - { - throwOnError: true, + const createPageEntity = useCallback( + async ( { close } ) => { + try { + const page = await saveEntityRecord( + 'postType', + 'page', + { + status: 'draft', + }, + { + throwOnError: true, + } + ); + if ( page?.id ) { + history.push( { + postId: page.id, + postType: 'page', + canvas: 'edit', + } ); } - ); - if ( page?.id ) { - history.push( { - postId: page.id, - postType: 'page', - canvas: 'edit', + } catch ( error ) { + const errorMessage = + error.message && error.code !== 'unknown_error' + ? error.message + : __( 'An error occurred while creating the item.' ); + + createErrorNotice( errorMessage, { + type: 'snackbar', } ); + } finally { + close(); } - } catch ( error ) { - const errorMessage = - error.message && error.code !== 'unknown_error' - ? error.message - : __( 'An error occurred while creating the item.' ); - - createErrorNotice( errorMessage, { - type: 'snackbar', - } ); - } finally { - close(); - } - }; + }, + [ createErrorNotice, history, saveEntityRecord ] + ); const commands = useMemo( () => { - const addNewPage = isSiteEditor - ? createPageEntity - : () => ( document.location.href = 'post-new.php?post_type=page' ); + const addNewPage = + isSiteEditor && isBlockBasedTheme + ? createPageEntity + : () => + ( document.location.href = + 'post-new.php?post_type=page' ); return [ { name: 'core/add-new-page', @@ -71,7 +80,7 @@ function useAddNewPageCommand() { callback: addNewPage, }, ]; - }, [ createPageEntity, isSiteEditor ] ); + }, [ createPageEntity, isSiteEditor, isBlockBasedTheme ] ); return { isLoading: false, From e691b04fff49023687919e31184f20f8862fa13c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:27:11 -0700 Subject: [PATCH 13/13] DataViews documentation: add high-level graph explaining DataViews and data sources interaction (#65457) Co-authored-by: oandregal Co-authored-by: youknowriad Co-authored-by: juanmaguitar --- packages/dataviews/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/dataviews/README.md b/packages/dataviews/README.md index 3ce895680a15c5..95b8fc898555c3 100644 --- a/packages/dataviews/README.md +++ b/packages/dataviews/README.md @@ -2,6 +2,10 @@ DataViews is a component that provides an API to render datasets using different types of layouts (table, grid, list, etc.). +DataViews is data agnostic, it can work with data coming from a static (JSON file) or dynamic source (HTTP Request) — it just requires the data to be an array of objects that have an unique identifier. Consumers are responsible to query the data source appropiately based on the DataViews props: + +![DataViews flow](https://developer.wordpress.org/files/2024/09/368600071-20aa078f-7c3d-406d-8dd0-8b764addd22a.png "DataViews flow") + ## Installation Install the module