From 926290659088d5b5515de01577da927b00444418 Mon Sep 17 00:00:00 2001 From: Mario Santos <34552881+SantosGuillamot@users.noreply.github.com> Date: Tue, 15 Oct 2024 10:26:59 +0200 Subject: [PATCH 01/23] Add initial tests for image rendering (#66010) - Add initial tests for image rendering Co-authored-by: SantosGuillamot Co-authored-by: cbravobernal --- phpunit/blocks/render-block-image-test.php | 62 ++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 phpunit/blocks/render-block-image-test.php diff --git a/phpunit/blocks/render-block-image-test.php b/phpunit/blocks/render-block-image-test.php new file mode 100644 index 0000000000000..fe39aee8c50b9 --- /dev/null +++ b/phpunit/blocks/render-block-image-test.php @@ -0,0 +1,62 @@ +'; + $parsed_blocks = parse_blocks( + '' + ); + $parsed_block = $parsed_blocks[0]; + $block = new WP_Block( $parsed_block ); + + $rendered_block = gutenberg_render_block_core_image( $attributes, $content, $block ); + $this->assertStringContainsString( 'aria-label="test render"', $rendered_block ); + } + + /** + * @covers ::render_block_core_image + */ + public function test_should_not_render_block_core_image_when_src_is_not_defined() { + $attributes = array(); + $content = '
'; + $parsed_blocks = parse_blocks( + '' + ); + $parsed_block = $parsed_blocks[0]; + $block = new WP_Block( $parsed_block ); + + $rendered_block = gutenberg_render_block_core_image( $attributes, $content, $block ); + $this->assertEquals( '', $rendered_block ); + } + + /** + * @covers ::render_block_core_image + */ + public function test_should_not_render_block_core_image_when_src_is_empty_string() { + $attributes = array(); + $content = '
'; + $parsed_blocks = parse_blocks( + '' + ); + $parsed_block = $parsed_blocks[0]; + $block = new WP_Block( $parsed_block ); + + $rendered_block = gutenberg_render_block_core_image( $attributes, $content, $block ); + $this->assertEquals( '', $rendered_block ); + } +} From 1e8f5e01d7d2f43cd16f00aef03f3d16f543ae2f Mon Sep 17 00:00:00 2001 From: Mario Santos <34552881+SantosGuillamot@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:12:05 +0200 Subject: [PATCH 02/23] Block Bindings: Bootstrap server sources earlier (#66058) * Remove bindings bootstrap functions * Remove bootstrap calls * Remove old filter * Adapt unit tests * Add compat filter in Gutenberg * Only run script if not already registered * Remove extra label in test * Remove unit test * Add backport changelog * Return warning if any client-only prop is defined Co-authored-by: SantosGuillamot Co-authored-by: cbravobernal Co-authored-by: gziolo Co-authored-by: ryanwelcher --- backport-changelog/6.7/7552.md | 3 + lib/compat/wordpress-6.7/block-bindings.php | 42 +++++-------- packages/blocks/src/api/registration.js | 15 +++-- packages/blocks/src/api/test/registration.js | 60 +++---------------- packages/blocks/src/store/private-actions.js | 14 ----- packages/blocks/src/store/reducer.js | 16 ----- packages/customize-widgets/src/index.js | 8 +-- .../e2e-tests/plugins/block-bindings/index.js | 1 - packages/edit-post/src/index.js | 2 - packages/edit-site/src/index.js | 6 +- packages/edit-widgets/src/index.js | 6 +- packages/editor/src/bindings/api.js | 33 +--------- packages/editor/src/private-apis.js | 6 +- 13 files changed, 41 insertions(+), 171 deletions(-) create mode 100644 backport-changelog/6.7/7552.md diff --git a/backport-changelog/6.7/7552.md b/backport-changelog/6.7/7552.md new file mode 100644 index 0000000000000..d304162b3cae6 --- /dev/null +++ b/backport-changelog/6.7/7552.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/7552 + +* https://github.com/WordPress/gutenberg/pull/66058 diff --git a/lib/compat/wordpress-6.7/block-bindings.php b/lib/compat/wordpress-6.7/block-bindings.php index a8f68c0f0f04e..70ba523ac966e 100644 --- a/lib/compat/wordpress-6.7/block-bindings.php +++ b/lib/compat/wordpress-6.7/block-bindings.php @@ -6,38 +6,28 @@ */ /** - * Adds the block bindings sources registered in the server to the editor settings. - * - * This allows them to be bootstrapped in the editor. - * - * @param array $settings The block editor settings from the `block_editor_settings_all` filter. - * @return array The editor settings including the block bindings sources. + * Bootstrap the block bindings sources registered in the server. */ -function gutenberg_add_server_block_bindings_sources_to_editor_settings( $editor_settings ) { - // Check if the sources are already exposed in the editor settings. - if ( isset( $editor_settings['blockBindingsSources'] ) ) { - return $editor_settings; - } - - $registered_block_bindings_sources = get_all_registered_block_bindings_sources(); - if ( ! empty( $registered_block_bindings_sources ) ) { - // Initialize array. - $editor_settings['blockBindingsSources'] = array(); - foreach ( $registered_block_bindings_sources as $source_name => $source_properties ) { - // Add source with the label to editor settings. - $editor_settings['blockBindingsSources'][ $source_name ] = array( - 'label' => $source_properties->label, +function gutenberg_bootstrap_server_block_bindings_sources() { + $registered_sources = get_all_registered_block_bindings_sources(); + if ( ! empty( $registered_sources ) ) { + $filtered_sources = array(); + foreach ( $registered_sources as $source ) { + $filtered_sources[] = array( + 'name' => $source->name, + 'label' => $source->label, + 'usesContext' => $source->uses_context, ); - // Add `usesContext` property if exists. - if ( ! empty( $source_properties->uses_context ) ) { - $editor_settings['blockBindingsSources'][ $source_name ]['usesContext'] = $source_properties->uses_context; - } } + $script = sprintf( 'for ( const source of %s ) { ! wp.blocks.getBlockBindingsSource( source.name ) && wp.blocks.registerBlockBindingsSource( source ); }', wp_json_encode( $filtered_sources ) ); + wp_add_inline_script( + 'wp-blocks', + $script + ); } - return $editor_settings; } -add_filter( 'block_editor_settings_all', 'gutenberg_add_server_block_bindings_sources_to_editor_settings', 10 ); +add_action( 'enqueue_block_editor_assets', 'gutenberg_bootstrap_server_block_bindings_sources', 5 ); /** * Initialize `canUpdateBlockBindings` editor setting if it doesn't exist. By default, it is `true` only for admin users. diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js index 31be38b861c28..ae12b0cfbcf49 100644 --- a/packages/blocks/src/api/registration.js +++ b/packages/blocks/src/api/registration.js @@ -809,13 +809,16 @@ export const registerBlockBindingsSource = ( source ) => { /* * Check if the source has been already registered on the client. - * If the `getValues` property is defined, it could be assumed the source is already registered. + * If any property expected to be "client-only" is defined, return a warning. */ - if ( existingSource?.getValues ) { - warning( - 'Block bindings source "' + name + '" is already registered.' - ); - return; + const serverProps = [ 'label', 'usesContext' ]; + for ( const prop in existingSource ) { + if ( ! serverProps.includes( prop ) && existingSource[ prop ] ) { + warning( + 'Block bindings source "' + name + '" is already registered.' + ); + return; + } } // Check the `name` property is correct. diff --git a/packages/blocks/src/api/test/registration.js b/packages/blocks/src/api/test/registration.js index 483949af4fe5f..99ca6390d2d3b 100644 --- a/packages/blocks/src/api/test/registration.js +++ b/packages/blocks/src/api/test/registration.js @@ -1513,10 +1513,8 @@ describe( 'blocks', () => { } ); it( 'should not override label from the server', () => { - // Bootstrap source from the server. - unlock( - dispatch( blocksStore ) - ).addBootstrappedBlockBindingsSource( { + // Simulate bootstrap source from the server. + registerBlockBindingsSource( { name: 'core/server', label: 'Server label', } ); @@ -1543,10 +1541,8 @@ describe( 'blocks', () => { } ); it( 'should add usesContext when only defined in the server', () => { - // Bootstrap source from the server. - unlock( - dispatch( blocksStore ) - ).addBootstrappedBlockBindingsSource( { + // Simulate bootstrap source from the server. + registerBlockBindingsSource( { name: 'core/testing', label: 'testing', usesContext: [ 'postId', 'postType' ], @@ -1562,10 +1558,8 @@ describe( 'blocks', () => { } ); it( 'should add usesContext when only defined in the client', () => { - // Bootstrap source from the server. - unlock( - dispatch( blocksStore ) - ).addBootstrappedBlockBindingsSource( { + // Simulate bootstrap source from the server. + registerBlockBindingsSource( { name: 'core/testing', label: 'testing', } ); @@ -1581,10 +1575,8 @@ describe( 'blocks', () => { } ); it( 'should merge usesContext from server and client without duplicates', () => { - // Bootstrap source from the server. - unlock( - dispatch( blocksStore ) - ).addBootstrappedBlockBindingsSource( { + // Simulate bootstrap source from the server. + registerBlockBindingsSource( { name: 'core/testing', label: 'testing', usesContext: [ 'postId', 'postType' ], @@ -1705,42 +1697,6 @@ describe( 'blocks', () => { 'Block bindings source "core/test-source" is already registered.' ); } ); - - it( 'should correctly merge properties when bootstrap happens after registration', () => { - // Register source in the client. - const clientOnlyProperties = { - getValues: () => 'values', - setValues: () => 'new values', - canUserEditValue: () => true, - }; - registerBlockBindingsSource( { - name: 'core/custom-source', - label: 'Client Label', - usesContext: [ 'postId', 'postType' ], - ...clientOnlyProperties, - } ); - - // Bootstrap source from the server. - unlock( - dispatch( blocksStore ) - ).addBootstrappedBlockBindingsSource( { - name: 'core/custom-source', - label: 'Server Label', - usesContext: [ 'postId', 'serverContext' ], - } ); - - // Check that the bootstrap values prevail and the client properties are still there. - expect( getBlockBindingsSource( 'core/custom-source' ) ).toEqual( { - // Should use the server label. - label: 'Server Label', - // Should merge usesContext from server and client. - usesContext: [ 'postId', 'postType', 'serverContext' ], - // Should keep client properties. - ...clientOnlyProperties, - } ); - - unregisterBlockBindingsSource( 'core/custom-source' ); - } ); } ); describe( 'unregisterBlockBindingsSource', () => { diff --git a/packages/blocks/src/store/private-actions.js b/packages/blocks/src/store/private-actions.js index 02f8506b3c3bf..bfefe56773d77 100644 --- a/packages/blocks/src/store/private-actions.js +++ b/packages/blocks/src/store/private-actions.js @@ -70,17 +70,3 @@ export function removeBlockBindingsSource( name ) { name, }; } - -/** - * Add bootstrapped block bindings sources, usually initialized from the server. - * - * @param {string} source Name of the source to bootstrap. - */ -export function addBootstrappedBlockBindingsSource( source ) { - return { - type: 'ADD_BOOTSTRAPPED_BLOCK_BINDINGS_SOURCE', - name: source.name, - label: source.label, - usesContext: source.usesContext, - }; -} diff --git a/packages/blocks/src/store/reducer.js b/packages/blocks/src/store/reducer.js index 7c7fb4763a1cb..16594a79271e6 100644 --- a/packages/blocks/src/store/reducer.js +++ b/packages/blocks/src/store/reducer.js @@ -417,22 +417,6 @@ export function blockBindingsSources( state = {}, action ) { getFieldsList, }, }; - case 'ADD_BOOTSTRAPPED_BLOCK_BINDINGS_SOURCE': - return { - ...state, - [ action.name ]: { - /* - * Keep the exisitng properties in case the source has been registered - * in the client before bootstrapping. - */ - ...state[ action.name ], - label: action.label, - usesContext: getMergedUsesContext( - state[ action.name ]?.usesContext, - action.usesContext - ), - }, - }; case 'REMOVE_BLOCK_BINDINGS_SOURCE': return omit( state, action.name ); } diff --git a/packages/customize-widgets/src/index.js b/packages/customize-widgets/src/index.js index 35be3c2ee4b14..df96d645ee700 100644 --- a/packages/customize-widgets/src/index.js +++ b/packages/customize-widgets/src/index.js @@ -39,10 +39,7 @@ const DISABLED_BLOCKS = [ ]; const ENABLE_EXPERIMENTAL_FSE_BLOCKS = false; -const { - registerCoreBlockBindingsSources, - bootstrapBlockBindingsSourcesFromServer, -} = unlock( editorPrivateApis ); +const { registerCoreBlockBindingsSources } = unlock( editorPrivateApis ); /** * Initializes the widgets block editor in the customizer. @@ -67,9 +64,6 @@ export function initialize( editorName, blockEditorSettings ) { ); } ); registerCoreBlocks( coreBlocks ); - bootstrapBlockBindingsSourcesFromServer( - blockEditorSettings?.blockBindingsSources - ); registerCoreBlockBindingsSources(); registerLegacyWidgetBlock(); if ( globalThis.IS_GUTENBERG_PLUGIN ) { diff --git a/packages/e2e-tests/plugins/block-bindings/index.js b/packages/e2e-tests/plugins/block-bindings/index.js index c31502631307d..5c364257caed1 100644 --- a/packages/e2e-tests/plugins/block-bindings/index.js +++ b/packages/e2e-tests/plugins/block-bindings/index.js @@ -19,7 +19,6 @@ const setValues = ( { dispatch, bindings } ) => { registerBlockBindingsSource( { name: 'testing/complete-source', - label: 'Complete Source', getValues, setValues, canUserEditValue: () => true, diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index daf789cb0a2ec..685ffc56f63a8 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -28,7 +28,6 @@ import { unlock } from './lock-unlock'; const { BackButton: __experimentalMainDashboardButton, registerCoreBlockBindingsSources, - bootstrapBlockBindingsSourcesFromServer, } = unlock( editorPrivateApis ); /** @@ -95,7 +94,6 @@ export function initializeEditor( } registerCoreBlocks(); - bootstrapBlockBindingsSourcesFromServer( settings?.blockBindingsSources ); registerCoreBlockBindingsSources(); registerLegacyWidgetBlock( { inserter: false } ); registerWidgetGroupBlock( { inserter: false } ); diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index 1aceecc4d8b1f..83d25bcd0c03a 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -28,10 +28,7 @@ import { store as editSiteStore } from './store'; import { unlock } from './lock-unlock'; import App from './components/app'; -const { - registerCoreBlockBindingsSources, - bootstrapBlockBindingsSourcesFromServer, -} = unlock( editorPrivateApis ); +const { registerCoreBlockBindingsSources } = unlock( editorPrivateApis ); /** * Initializes the site editor screen. @@ -48,7 +45,6 @@ export function initializeEditor( id, settings ) { ( { name } ) => name !== 'core/freeform' ); registerCoreBlocks( coreBlocks ); - bootstrapBlockBindingsSourcesFromServer( settings?.blockBindingsSources ); registerCoreBlockBindingsSources(); dispatch( blocksStore ).setFreeformFallbackBlockName( 'core/html' ); registerLegacyWidgetBlock( { inserter: false } ); diff --git a/packages/edit-widgets/src/index.js b/packages/edit-widgets/src/index.js index fadd488a702cf..8788ee2b99ea1 100644 --- a/packages/edit-widgets/src/index.js +++ b/packages/edit-widgets/src/index.js @@ -44,10 +44,7 @@ const disabledBlocks = [ ...( ALLOW_REUSABLE_BLOCKS ? [] : [ 'core/block' ] ), ]; -const { - registerCoreBlockBindingsSources, - bootstrapBlockBindingsSourcesFromServer, -} = unlock( editorPrivateApis ); +const { registerCoreBlockBindingsSources } = unlock( editorPrivateApis ); /** * Initializes the block editor in the widgets screen. @@ -78,7 +75,6 @@ export function initializeEditor( id, settings ) { dispatch( blocksStore ).reapplyBlockTypeFilters(); registerCoreBlocks( coreBlocks ); - bootstrapBlockBindingsSourcesFromServer( settings?.blockBindingsSources ); registerCoreBlockBindingsSources(); registerLegacyWidgetBlock(); if ( globalThis.IS_GUTENBERG_PLUGIN ) { diff --git a/packages/editor/src/bindings/api.js b/packages/editor/src/bindings/api.js index 84003fab7eaf7..2d32d76abbc3b 100644 --- a/packages/editor/src/bindings/api.js +++ b/packages/editor/src/bindings/api.js @@ -1,18 +1,13 @@ /** * WordPress dependencies */ -import { - store as blocksStore, - registerBlockBindingsSource, -} from '@wordpress/blocks'; -import { dispatch } from '@wordpress/data'; +import { registerBlockBindingsSource } from '@wordpress/blocks'; /** * Internal dependencies */ import patternOverrides from './pattern-overrides'; import postMeta from './post-meta'; -import { unlock } from '../lock-unlock'; /** * Function to register core block bindings sources provided by the editor. @@ -28,29 +23,3 @@ export function registerCoreBlockBindingsSources() { registerBlockBindingsSource( patternOverrides ); registerBlockBindingsSource( postMeta ); } - -/** - * Function to bootstrap core block bindings sources defined in the server. - * - * @param {Object} sources Object containing the sources to bootstrap. - * - * @example - * ```js - * import { bootstrapBlockBindingsSourcesFromServer } from '@wordpress/editor'; - * - * bootstrapBlockBindingsSourcesFromServer( sources ); - * ``` - */ -export function bootstrapBlockBindingsSourcesFromServer( sources ) { - if ( sources ) { - const { addBootstrappedBlockBindingsSource } = unlock( - dispatch( blocksStore ) - ); - for ( const [ name, args ] of Object.entries( sources ) ) { - addBootstrappedBlockBindingsSource( { - name, - ...args, - } ); - } - } -} diff --git a/packages/editor/src/private-apis.js b/packages/editor/src/private-apis.js index d4a5c3eebbb4c..f9a6d4d17904e 100644 --- a/packages/editor/src/private-apis.js +++ b/packages/editor/src/private-apis.js @@ -23,10 +23,7 @@ import { mergeBaseAndUserConfigs, GlobalStylesProvider, } from './components/global-styles-provider'; -import { - registerCoreBlockBindingsSources, - bootstrapBlockBindingsSourcesFromServer, -} from './bindings/api'; +import { registerCoreBlockBindingsSources } from './bindings/api'; const { store: interfaceStore, ...remainingInterfaceApis } = interfaceApis; @@ -47,7 +44,6 @@ lock( privateApis, { ViewMoreMenuGroup, ResizableEditor, registerCoreBlockBindingsSources, - bootstrapBlockBindingsSourcesFromServer, // This is a temporary private API while we're updating the site editor to use EditorProvider. interfaceStore, From 9671329c386d2b743f14ef314823fbf915366ebd Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Tue, 15 Oct 2024 13:31:32 +0300 Subject: [PATCH 03/23] Make zoom out vertical toolbar consistent (#65627) * enable vertical toolbar for non full width elements, anchor based on parent * Update packages/block-editor/src/components/block-popover/index.js Co-authored-by: Dave Smith * Update packages/block-editor/src/components/block-popover/index.js Co-authored-by: Dave Smith * make zoom out check a dependency of the memoization, improve code readability * comment typos * subscribe to state instead of calculating zoom out view state when calculating the anchor * get the section wrapper for anchoring instead of the parent * use a selector instead of computing on the fly the parent section * check if the block element exists yet before computing the anchor * check if the block element exists yet before computing the anchor * differentiate between section toolbar and block toolbar for correct positioning when both are visible * address some nits * make the select in anchor setting rerun when block selection changes * fix bug with anchor rect when zoom out not engaged * fix typo * Use root container element in post editor as popover anchor * improve comment * improve comment to max improvement possible Co-authored-by: Dave Smith * mega nit commit Co-authored-by: Dave Smith * Fix bug with Posts with no full width blocks * give up on section root, always seek canvas element to position vertical toolbar, also fix typo * introduce the concept of canvas via a 1st variable * Use `__unstableContentRef` for zoomed out toolbar positioning instead of dom classname --------- Co-authored-by: draganescu Co-authored-by: getdave Co-authored-by: talldan Co-authored-by: ciampo Co-authored-by: jsnajdr Co-authored-by: MaggieCabrera Co-authored-by: richtabor Co-authored-by: stokesman Co-authored-by: andrewserong --- .../src/components/block-popover/index.js | 66 ++++++++++++++++++- .../block-tools/use-show-block-tools.js | 13 +++- .../block-tools/zoom-out-popover.js | 3 +- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/block-popover/index.js b/packages/block-editor/src/components/block-popover/index.js index 47022e336e486..637ab1cb8a53e 100644 --- a/packages/block-editor/src/components/block-popover/index.js +++ b/packages/block-editor/src/components/block-popover/index.js @@ -8,6 +8,7 @@ import clsx from 'clsx'; */ import { useMergeRefs } from '@wordpress/compose'; import { Popover } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; import { forwardRef, useMemo, @@ -21,6 +22,8 @@ import { import { useBlockElement } from '../block-list/use-block-props/use-block-refs'; import usePopoverScroll from './use-popover-scroll'; import { rectUnion, getVisibleElementBounds } from '../../utils/dom'; +import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; const MAX_POPOVER_RECOMPUTE_COUNTER = Number.MAX_SAFE_INTEGER; @@ -74,12 +77,38 @@ function BlockPopover( }; }, [ selectedElement ] ); + const { isZoomOut, parentSectionBlock, isSectionSelected } = useSelect( + ( select ) => { + const { + isZoomOut: isZoomOutSelector, + getSectionRootClientId, + getParentSectionBlock, + getBlockOrder, + } = unlock( select( blockEditorStore ) ); + + return { + isZoomOut: isZoomOutSelector(), + parentSectionBlock: + getParentSectionBlock( clientId ) ?? clientId, + isSectionSelected: getBlockOrder( + getSectionRootClientId() + ).includes( clientId ), + }; + }, + [ clientId ] + ); + + // This element is used to position the zoom out view vertical toolbar + // correctly, relative to the selected section. + const parentSectionElement = useBlockElement( parentSectionBlock ); + const popoverAnchor = useMemo( () => { if ( // popoverDimensionsRecomputeCounter is by definition always equal or greater // than 0. This check is only there to satisfy the correctness of the // exhaustive-deps rule for the `useMemo` hook. popoverDimensionsRecomputeCounter < 0 || + ( isZoomOut && ! parentSectionElement ) || ! selectedElement || ( bottomClientId && ! lastSelectedElement ) ) { @@ -88,6 +117,35 @@ function BlockPopover( return { getBoundingClientRect() { + // The zoom out view has a vertical block toolbar that should always + // be on the edge of the canvas, aligned to the top of the currently + // selected section. This condition changes the anchor of the toolbar + // to the section instead of the block to handle blocks that are + // not full width and nested blocks to keep section height. + if ( isZoomOut && isSectionSelected ) { + // Compute the height based on the parent section of the + // selected block, because the selected block may be + // shorter than the section. + const canvasElementRect = getVisibleElementBounds( + __unstableContentRef.current + ); + const parentSectionElementRect = + getVisibleElementBounds( parentSectionElement ); + const anchorHeight = + parentSectionElementRect.bottom - + parentSectionElementRect.top; + + // Always use the width of the section root element to make sure + // the toolbar is always on the edge of the canvas. + const anchorWidth = canvasElementRect.width; + return new window.DOMRectReadOnly( + canvasElementRect.left, + parentSectionElementRect.top, + anchorWidth, + anchorHeight + ); + } + return lastSelectedElement ? rectUnion( getVisibleElementBounds( selectedElement ), @@ -98,10 +156,14 @@ function BlockPopover( contextElement: selectedElement, }; }, [ + popoverDimensionsRecomputeCounter, + isZoomOut, + parentSectionElement, + selectedElement, bottomClientId, lastSelectedElement, - selectedElement, - popoverDimensionsRecomputeCounter, + isSectionSelected, + __unstableContentRef, ] ); if ( ! selectedElement || ( bottomClientId && ! lastSelectedElement ) ) { diff --git a/packages/block-editor/src/components/block-tools/use-show-block-tools.js b/packages/block-editor/src/components/block-tools/use-show-block-tools.js index 02a8f0583bcdd..97737b4ec2f5b 100644 --- a/packages/block-editor/src/components/block-tools/use-show-block-tools.js +++ b/packages/block-editor/src/components/block-tools/use-show-block-tools.js @@ -8,6 +8,7 @@ import { isUnmodifiedDefaultBlock } from '@wordpress/blocks'; * Internal dependencies */ import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; /** * Source of truth for which block tools are showing in the block editor. @@ -24,7 +25,9 @@ export function useShowBlockTools() { getSettings, __unstableGetEditorMode, isTyping, - } = select( blockEditorStore ); + getBlockOrder, + getSectionRootClientId, + } = unlock( select( blockEditorStore ) ); const clientId = getSelectedBlockClientId() || getFirstMultiSelectedBlockClientId(); @@ -42,10 +45,14 @@ export function useShowBlockTools() { editorMode === 'edit' && isEmptyDefaultBlock; const isZoomOut = editorMode === 'zoom-out'; + const isSectionSelected = getBlockOrder( + getSectionRootClientId() + ).includes( clientId ); const _showZoomOutToolbar = + clientId && isZoomOut && - block?.attributes?.align === 'full' && - ! _showEmptyBlockSideInserter; + ! _showEmptyBlockSideInserter && + isSectionSelected; const _showBlockToolbarPopover = ! _showZoomOutToolbar && ! getSettings().hasFixedToolbar && diff --git a/packages/block-editor/src/components/block-tools/zoom-out-popover.js b/packages/block-editor/src/components/block-tools/zoom-out-popover.js index a1f2990a5cc1e..7a5c2243cf054 100644 --- a/packages/block-editor/src/components/block-tools/zoom-out-popover.js +++ b/packages/block-editor/src/components/block-tools/zoom-out-popover.js @@ -5,7 +5,7 @@ import clsx from 'clsx'; /** * Internal dependencies */ -import BlockPopover from '../block-popover'; +import { PrivateBlockPopover as BlockPopover } from '../block-popover'; import useBlockToolbarPopoverProps from './use-block-toolbar-popover-props'; import useSelectedBlockToolProps from './use-selected-block-tool-props'; import ZoomOutToolbar from './zoom-out-toolbar'; @@ -29,6 +29,7 @@ export default function ZoomOutPopover( { clientId, __unstableContentRef } ) { return ( Date: Tue, 15 Oct 2024 16:35:54 +0530 Subject: [PATCH 04/23] Refactor setting panel of cover block control (#65432) Co-authored-by: akasunil Co-authored-by: aaronrobertshaw --- .../src/cover/edit/inspector-controls.js | 147 ++++++++++++------ packages/block-library/src/cover/test/edit.js | 4 +- 2 files changed, 105 insertions(+), 46 deletions(-) diff --git a/packages/block-library/src/cover/edit/inspector-controls.js b/packages/block-library/src/cover/edit/inspector-controls.js index fd83ba716ddd4..2ed9b055d49f3 100644 --- a/packages/block-library/src/cover/edit/inspector-controls.js +++ b/packages/block-library/src/cover/edit/inspector-controls.js @@ -5,12 +5,12 @@ import { useMemo } from '@wordpress/element'; import { ExternalLink, FocalPointPicker, - PanelBody, RangeControl, TextareaControl, ToggleControl, SelectControl, __experimentalUseCustomUnits as useCustomUnits, + __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalUnitControl as UnitControl, __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue, @@ -31,6 +31,7 @@ import { __ } from '@wordpress/i18n'; */ import { COVER_MIN_HEIGHT, mediaPosition } from '../shared'; import { unlock } from '../../lock-unlock'; +import { useToolsPanelDropdownMenuProps } from '../../utils/hooks'; const { cleanEmptyObject } = unlock( blockEditorPrivateApis ); @@ -160,72 +161,130 @@ export default function CoverInspectorControls( { ), }; + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); + return ( <> { !! url && ( - + { + setAttributes( { + hasParallax: false, + focalPoint: undefined, + isRepeated: false, + alt: '', + } ); + } } + dropdownMenuProps={ dropdownMenuProps } + > { isImageBackground && ( <> - + isShownByDefault + hasValue={ () => hasParallax } + onDeselect={ () => + setAttributes( { + hasParallax: false, + focalPoint: undefined, + } ) + } + > + + - + isShownByDefault + hasValue={ () => isRepeated } + onDeselect={ () => + setAttributes( { + isRepeated: false, + } ) + } + > + + ) } { showFocalPointPicker && ( - + isShownByDefault + hasValue={ () => !! focalPoint } + onDeselect={ () => setAttributes( { - focalPoint: newFocalPoint, + focalPoint: undefined, } ) } - /> + > + + setAttributes( { + focalPoint: newFocalPoint, + } ) + } + /> + ) } { ! useFeaturedImage && url && ! isVideoBackground && ( - - setAttributes( { alt: newAlt } ) + isShownByDefault + hasValue={ () => !! alt } + onDeselect={ () => + setAttributes( { alt: '' } ) } - help={ - <> - + > + + setAttributes( { alt: newAlt } ) + } + help={ + <> + + { __( + 'Describe the purpose of the image.' + ) } + +
{ __( - 'Describe the purpose of the image.' + 'Leave empty if decorative.' ) } -
-
- { __( 'Leave empty if decorative.' ) } - - } - /> + + } + /> + ) } -
+ ) }
{ colorGradientSettings.hasColorsOrGradients && ( diff --git a/packages/block-library/src/cover/test/edit.js b/packages/block-library/src/cover/test/edit.js index 97c52a47ec4d7..348cd88edfd96 100644 --- a/packages/block-library/src/cover/test/edit.js +++ b/packages/block-library/src/cover/test/edit.js @@ -190,7 +190,7 @@ describe( 'Cover block', () => { test( 'does not display media settings panel if url is not set', async () => { await setup(); expect( - screen.queryByRole( 'button', { + screen.queryByRole( 'heading', { name: 'Settings', } ) ).not.toBeInTheDocument(); @@ -202,7 +202,7 @@ describe( 'Cover block', () => { await selectBlock( 'Block: Cover' ); expect( - screen.getByRole( 'button', { + screen.getByRole( 'heading', { name: 'Settings', } ) ).toBeInTheDocument(); From 43417f3752dd6f3e5fe723ef54af7dd58123215b Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Tue, 15 Oct 2024 12:09:45 +0100 Subject: [PATCH 05/23] Correct capability for the Experiments page (#66118) Co-authored-by: t-hamano Co-authored-by: rmccue Co-authored-by: swissspidy --- lib/init.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/init.php b/lib/init.php index 88dcba4525f6e..13ca26d4b9e83 100644 --- a/lib/init.php +++ b/lib/init.php @@ -51,7 +51,7 @@ function gutenberg_menu() { 'gutenberg', __( 'Experiments Settings', 'gutenberg' ), __( 'Experiments', 'gutenberg' ), - 'edit_posts', + 'manage_options', 'gutenberg-experiments', 'the_gutenberg_experiments' ); From 98f3f6a5b3cccfcc8d94cefa9a4bee9750478132 Mon Sep 17 00:00:00 2001 From: Nick Diego Date: Tue, 15 Oct 2024 06:43:50 -0500 Subject: [PATCH 06/23] Revert the manage fonts button. (#66107) Co-authored-by: ndiego Co-authored-by: ramonjd Co-authored-by: kevin940726 Co-authored-by: afercia Co-authored-by: carolinan Co-authored-by: colorful-tones Co-authored-by: richtabor --- .../components/global-styles/font-families.js | 51 +++++++++++-------- .../specs/site-editor/font-library.spec.js | 18 +++---- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/font-families.js b/packages/edit-site/src/components/global-styles/font-families.js index cd1697b7b79bd..f3e81efbe597b 100644 --- a/packages/edit-site/src/components/global-styles/font-families.js +++ b/packages/edit-site/src/components/global-styles/font-families.js @@ -6,9 +6,11 @@ import { __experimentalText as Text, __experimentalItemGroup as ItemGroup, __experimentalVStack as VStack, + __experimentalHStack as HStack, Button, } from '@wordpress/components'; import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { settings } from '@wordpress/icons'; import { useContext } from '@wordpress/element'; /** @@ -67,10 +69,18 @@ function FontFamilies() { /> ) } - + + + { __( 'Fonts' ) } + + ) } - ); diff --git a/test/e2e/specs/site-editor/font-library.spec.js b/test/e2e/specs/site-editor/font-library.spec.js index 6d699f4b02a63..7271768206d1b 100644 --- a/test/e2e/specs/site-editor/font-library.spec.js +++ b/test/e2e/specs/site-editor/font-library.spec.js @@ -38,7 +38,7 @@ test.describe( 'Font Library', () => { ).toBeVisible(); } ); - test( 'should display the "Add fonts" button', async ( { page } ) => { + test( 'should display the "Manage fonts" icon', async ( { page } ) => { await page .getByRole( 'region', { name: 'Editor top bar' } ) .getByRole( 'button', { name: 'Styles' } ) @@ -46,10 +46,10 @@ test.describe( 'Font Library', () => { await page .getByRole( 'button', { name: 'Typography Styles' } ) .click(); - const addFontsButton = page.getByRole( 'button', { - name: 'Add fonts', + const manageFontsIcon = page.getByRole( 'button', { + name: 'Manage fonts', } ); - await expect( addFontsButton ).toBeVisible(); + await expect( manageFontsIcon ).toBeVisible(); } ); } ); @@ -66,9 +66,7 @@ test.describe( 'Font Library', () => { } ); } ); - test( 'should display the "Manage fonts" button', async ( { - page, - } ) => { + test( 'should display the "Manage fonts" icon', async ( { page } ) => { await page .getByRole( 'region', { name: 'Editor top bar' } ) .getByRole( 'button', { name: 'Styles' } ) @@ -76,13 +74,13 @@ test.describe( 'Font Library', () => { await page .getByRole( 'button', { name: 'Typography Styles' } ) .click(); - const manageFontsButton = page.getByRole( 'button', { + const manageFontsIcon = page.getByRole( 'button', { name: 'Manage fonts', } ); - await expect( manageFontsButton ).toBeVisible(); + await expect( manageFontsIcon ).toBeVisible(); } ); - test( 'should open the "Manage fonts" modal when clicking the "Manage fonts" button', async ( { + test( 'should open the "Manage fonts" modal when clicking the "Manage fonts" icon', async ( { page, } ) => { await page From d2b654a100cf069b54aac02909d7863f6578636f Mon Sep 17 00:00:00 2001 From: Jerry Jones Date: Tue, 15 Oct 2024 09:15:07 -0500 Subject: [PATCH 07/23] Position scaled html within available container space (#66034) * Positions the iframed HTML canvas via translateX rather than margin-left on the iframe in order to have the scrollbar for the iframe available instead of hidden off canvas. This also fixes issues with the vertical toolbar and inserters not rerendering to their new positions. * Renames prevContainerWidthRef to initialContainerWidth so it's clearer that this is the point when zoom out was initialized, then renames the CSS vars prev-container-width to outer-container-width, using the larger value of containerWidth or initialContainerWidth so it can be more consistently named. * Force largest available window size to scale from If you started with a sidebar open, then entered zoom out, then closed the sidebar, your scaled canvas would be too large. It should match the same size as if you start with no sidebars open, then enter zoom out. This also fixes an issue where scaling could be larger than 1. * Only animate scaling on entry and exit of zoom out mode to improve animations when opening and closing sidebars. * Known divergence: When starting from a smaller canvas (sidebars open), entering zoom out, then closing sidebars, there will be reflow on the canvas. --------- Co-authored-by: jeryj Co-authored-by: ajlende Co-authored-by: stokesman Co-authored-by: ciampo Co-authored-by: MaggieCabrera Co-authored-by: draganescu Co-authored-by: getdave Co-authored-by: dhruvang21 Co-authored-by: AhmarZaidi Co-authored-by: kevin940726 --- packages/base-styles/_animations.scss | 5 -- .../src/components/block-canvas/style.scss | 1 - .../src/components/iframe/content.scss | 25 +++++-- .../src/components/iframe/index.js | 67 +++++++++++++++---- .../src/components/iframe/style.scss | 11 +-- test/e2e/specs/site-editor/zoom-out.spec.js | 4 +- 6 files changed, 83 insertions(+), 30 deletions(-) diff --git a/packages/base-styles/_animations.scss b/packages/base-styles/_animations.scss index 87e5f035f46a6..e5bbf86375735 100644 --- a/packages/base-styles/_animations.scss +++ b/packages/base-styles/_animations.scss @@ -36,11 +36,6 @@ @include reduce-motion("animation"); } -@mixin editor-canvas-resize-animation() { - transition: all 0.4s cubic-bezier(0.46, 0.03, 0.52, 0.96); - @include reduce-motion("transition"); -} - // Deprecated @mixin edit-post__fade-in-animation($speed: 0.08s, $delay: 0s) { @warn "The `edit-post__fade-in-animation` mixin is deprecated. Use `animation__fade-in` instead."; diff --git a/packages/block-editor/src/components/block-canvas/style.scss b/packages/block-editor/src/components/block-canvas/style.scss index 1395b5c0a437d..9e924cb79bace 100644 --- a/packages/block-editor/src/components/block-canvas/style.scss +++ b/packages/block-editor/src/components/block-canvas/style.scss @@ -4,5 +4,4 @@ iframe[name="editor-canvas"] { height: 100%; display: block; background-color: transparent; - @include editor-canvas-resize-animation; } diff --git a/packages/block-editor/src/components/iframe/content.scss b/packages/block-editor/src/components/iframe/content.scss index 069274e66bdfd..4a0e2d519f914 100644 --- a/packages/block-editor/src/components/iframe/content.scss +++ b/packages/block-editor/src/components/iframe/content.scss @@ -5,7 +5,21 @@ .block-editor-iframe__html { transform-origin: top center; - @include editor-canvas-resize-animation; + // 400ms should match the animation speed used in iframe/index.js + $zoomOutAnimation: all 400ms cubic-bezier(0.46, 0.03, 0.52, 0.96); + + // We don't want to animate the transform of the translateX because it is used + // to "center" the canvas. Leaving it on causes the canvas to slide around in + // odd ways. + transition: $zoomOutAnimation, transform 0s scale 0s; + @include reduce-motion("transition"); + + &.zoom-out-animation { + // we only want to animate the scaling when entering zoom out. When sidebars + // are toggled, the resizing of the iframe handles scaling the canvas as well, + // and the doubled animations cause very odd animations. + transition: $zoomOutAnimation, transform 0s; + } } .block-editor-iframe__html.is-zoomed-out { @@ -13,10 +27,11 @@ $frame-size: var(--wp-block-editor-iframe-zoom-out-frame-size); $inner-height: var(--wp-block-editor-iframe-zoom-out-inner-height); $content-height: var(--wp-block-editor-iframe-zoom-out-content-height); - $prev-container-width: var(--wp-block-editor-iframe-zoom-out-prev-container-width); - - transform: scale(#{$scale}); - + $outer-container-width: var(--wp-block-editor-iframe-zoom-out-outer-container-width); + $container-width: var(--wp-block-editor-iframe-zoom-out-container-width, 100vw); + // Apply an X translation to center the scaled content within the available space. + transform: translateX(calc(( #{$outer-container-width} - #{ $container-width }) / 2 / #{$scale})); + scale: #{$scale}; background-color: $gray-300; // Chrome seems to respect that transform scale shouldn't affect the layout size of the element, diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index d234339909a5c..b72d86ef8e0f8 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -122,7 +122,7 @@ function Iframe( { }, [] ); const { styles = '', scripts = '' } = resolvedAssets; const [ iframeDocument, setIframeDocument ] = useState(); - const prevContainerWidthRef = useRef(); + const initialContainerWidth = useRef(); const [ bodyClasses, setBodyClasses ] = useState( [] ); const clearerRef = useBlockSelectionClearer(); const [ before, writingFlowRef, after ] = useWritingFlow(); @@ -243,7 +243,7 @@ function Iframe( { useEffect( () => { if ( ! isZoomedOut ) { - prevContainerWidthRef.current = containerWidth; + initialContainerWidth.current = containerWidth; } }, [ containerWidth, isZoomedOut ] ); @@ -298,14 +298,49 @@ function Iframe( { useEffect( () => cleanup, [ cleanup ] ); + const zoomOutAnimationClassnameRef = useRef( null ); + const handleZoomOutAnimationClassname = () => { + clearTimeout( zoomOutAnimationClassnameRef.current ); + + iframeDocument.documentElement.classList.add( 'zoom-out-animation' ); + + zoomOutAnimationClassnameRef.current = setTimeout( () => { + iframeDocument.documentElement.classList.remove( + 'zoom-out-animation' + ); + }, 400 ); // 400ms should match the animation speed used in components/iframe/content.scss + }; + + // Toggle zoom out CSS Classes only when zoom out mode changes. We could add these into the useEffect + // that controls settings the CSS variables, but then we would need to do more work to ensure we're + // only toggling these when the zoom out mode changes, as that useEffect is also triggered by a large + // number of dependencies. useEffect( () => { if ( ! iframeDocument || ! isZoomedOut ) { return; } + handleZoomOutAnimationClassname(); iframeDocument.documentElement.classList.add( 'is-zoomed-out' ); + return () => { + handleZoomOutAnimationClassname(); + iframeDocument.documentElement.classList.remove( 'is-zoomed-out' ); + }; + }, [ iframeDocument, isZoomedOut ] ); + + // Calculate the scaling and CSS variables for the zoom out canvas + useEffect( () => { + if ( ! iframeDocument || ! isZoomedOut ) { + return; + } + const maxWidth = 750; + // Note: When we initialize the zoom out when the canvas is smaller (sidebars open), + // initialContainerWidth will be smaller than the full page, and reflow will happen + // when the canvas area becomes larger due to sidebars closing. This is a known but + // minor divergence for now. + // This scaling calculation has to happen within the JS because CSS calc() can // only divide and multiply by a unitless value. I.e. calc( 100px / 2 ) is valid // but calc( 100px / 2px ) is not. @@ -314,7 +349,10 @@ function Iframe( { scale === 'default' ? ( Math.min( containerWidth, maxWidth ) - parseInt( frameSize ) * 2 ) / - prevContainerWidthRef.current + Math.max( + initialContainerWidth.current, + containerWidth + ) : scale ); @@ -336,13 +374,16 @@ function Iframe( { `${ containerWidth }px` ); iframeDocument.documentElement.style.setProperty( - '--wp-block-editor-iframe-zoom-out-prev-container-width', - `${ prevContainerWidthRef.current }px` + '--wp-block-editor-iframe-zoom-out-outer-container-width', + `${ Math.max( initialContainerWidth.current, containerWidth ) }px` ); - return () => { - iframeDocument.documentElement.classList.remove( 'is-zoomed-out' ); + // iframeDocument.documentElement.style.setProperty( + // '--wp-block-editor-iframe-zoom-out-outer-container-width', + // `${ Math.max( initialContainerWidth.current, containerWidth ) }px` + // ); + return () => { iframeDocument.documentElement.style.removeProperty( '--wp-block-editor-iframe-zoom-out-scale' ); @@ -359,7 +400,7 @@ function Iframe( { '--wp-block-editor-iframe-zoom-out-container-width' ); iframeDocument.documentElement.style.removeProperty( - '--wp-block-editor-iframe-zoom-out-prev-container-width' + '--wp-block-editor-iframe-zoom-out-outer-container-width' ); }; }, [ @@ -460,10 +501,12 @@ function Iframe( { isZoomedOut && 'is-zoomed-out' ) } style={ { - '--wp-block-editor-iframe-zoom-out-container-width': - isZoomedOut && `${ containerWidth }px`, - '--wp-block-editor-iframe-zoom-out-prev-container-width': - isZoomedOut && `${ prevContainerWidthRef.current }px`, + '--wp-block-editor-iframe-zoom-out-outer-container-width': + isZoomedOut && + `${ Math.max( + initialContainerWidth.current, + containerWidth + ) }px`, } } > { iframe } diff --git a/packages/block-editor/src/components/iframe/style.scss b/packages/block-editor/src/components/iframe/style.scss index dcddcdf0950a4..d05be2f3977b9 100644 --- a/packages/block-editor/src/components/iframe/style.scss +++ b/packages/block-editor/src/components/iframe/style.scss @@ -9,9 +9,10 @@ } .block-editor-iframe__scale-container.is-zoomed-out { - $container-width: var(--wp-block-editor-iframe-zoom-out-container-width, 100vw); - $prev-container-width: var(--wp-block-editor-iframe-zoom-out-prev-container-width, 100vw); - width: $prev-container-width; - // This is to offset the movement of the iframe when we open sidebars - margin-left: calc(-1 * (#{$prev-container-width} - #{$container-width}) / 2); + $outer-container-width: var(--wp-block-editor-iframe-zoom-out-outer-container-width, 100vw); + width: $outer-container-width; + // Position the iframe so that it is always aligned with the right side so that + // the scrollbar is always visible on the right side + position: absolute; + right: 0; } diff --git a/test/e2e/specs/site-editor/zoom-out.spec.js b/test/e2e/specs/site-editor/zoom-out.spec.js index 6ede9014d2a30..2aabee07d1878 100644 --- a/test/e2e/specs/site-editor/zoom-out.spec.js +++ b/test/e2e/specs/site-editor/zoom-out.spec.js @@ -27,8 +27,8 @@ test.describe( 'Zoom Out', () => { // Check that the html is scaled. await expect( html ).toHaveCSS( - 'transform', - 'matrix(0.67, 0, 0, 0.67, 0, 0)' + 'scale', + new RegExp( /0\.[5-8][0-9]*/, 'i' ) ); const iframeRect = await iframe.boundingBox(); const htmlRect = await html.boundingBox(); From 7590b3d0122322368ee7bc1f46a4034fcf2fe297 Mon Sep 17 00:00:00 2001 From: Alex Lende Date: Tue, 15 Oct 2024 07:23:07 -0700 Subject: [PATCH 08/23] Fix duotone on parallax/repeated featured image cover blocks (#65929) Co-authored-by: ajlende Co-authored-by: jeryj Co-authored-by: mrfoxtalbot Co-authored-by: jordesign Co-authored-by: jasmussen Co-authored-by: iamtakashi Co-authored-by: annezazu Co-authored-by: henriqueiamarino --- packages/block-library/src/cover/index.php | 57 ++++++++++++++-------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/packages/block-library/src/cover/index.php b/packages/block-library/src/cover/index.php index 2fca0b0374dd2..1ffe7ab3f4dbc 100644 --- a/packages/block-library/src/cover/index.php +++ b/packages/block-library/src/cover/index.php @@ -20,29 +20,22 @@ function render_block_core_cover( $attributes, $content ) { return $content; } + $object_position = isset( $attributes['focalPoint'] ) + ? round( $attributes['focalPoint']['x'] * 100 ) . '% ' . round( $attributes['focalPoint']['y'] * 100 ) . '%' + : null; + if ( ! ( $attributes['hasParallax'] || $attributes['isRepeated'] ) ) { $attr = array( 'class' => 'wp-block-cover__image-background', 'data-object-fit' => 'cover', ); - if ( isset( $attributes['focalPoint'] ) ) { - $object_position = round( $attributes['focalPoint']['x'] * 100 ) . '% ' . round( $attributes['focalPoint']['y'] * 100 ) . '%'; + if ( $object_position ) { $attr['data-object-position'] = $object_position; - $attr['style'] = 'object-position: ' . $object_position; + $attr['style'] = 'object-position:' . $object_position . ';'; } $image = get_the_post_thumbnail( null, 'post-thumbnail', $attr ); - - /* - * Inserts the featured image between the (1st) cover 'background' `span` and 'inner_container' `div`, - * and removes eventual whitespace characters between the two (typically introduced at template level) - */ - $inner_container_start = '/]+wp-block-cover__inner-container[\s|"][^>]*>/U'; - if ( 1 === preg_match( $inner_container_start, $content, $matches, PREG_OFFSET_CAPTURE ) ) { - $offset = $matches[0][1]; - $content = substr( $content, 0, $offset ) . $image . substr( $content, $offset ); - } } else { if ( in_the_loop() ) { update_post_thumbnail_cache(); @@ -52,15 +45,41 @@ function render_block_core_cover( $attributes, $content ) { return $content; } - $processor = new WP_HTML_Tag_Processor( $content ); + $current_thumbnail_id = get_post_thumbnail_id(); + + $processor = new WP_HTML_Tag_Processor( '
' ); $processor->next_tag(); - $styles = $processor->get_attribute( 'style' ); - $merged_styles = ! empty( $styles ) ? $styles . ';' : ''; - $merged_styles .= 'background-image:url(' . esc_url( $current_featured_image ) . ');'; + $current_alt = trim( strip_tags( get_post_meta( $current_thumbnail_id, '_wp_attachment_image_alt', true ) ) ); + if ( $current_alt ) { + $processor->set_attribute( 'role', 'img' ); + $processor->set_attribute( 'aria-label', $current_alt ); + } + + $processor->add_class( 'wp-block-cover__image-background' ); + $processor->add_class( 'wp-image-' . $current_thumbnail_id ); + if ( $attributes['hasParallax'] ) { + $processor->add_class( 'has-parallax' ); + } + if ( $attributes['isRepeated'] ) { + $processor->add_class( 'is-repeated' ); + } + + $styles = 'background-position:' . ( $object_position ?? '50% 50%' ) . ';'; + $styles .= 'background-image:url(' . esc_url( $current_featured_image ) . ');'; + $processor->set_attribute( 'style', $styles ); + + $image = $processor->get_updated_html(); + } - $processor->set_attribute( 'style', $merged_styles ); - $content = $processor->get_updated_html(); + /* + * Inserts the featured image between the (1st) cover 'background' `span` and 'inner_container' `div`, + * and removes eventual whitespace characters between the two (typically introduced at template level) + */ + $inner_container_start = '/]+wp-block-cover__inner-container[\s|"][^>]*>/U'; + if ( 1 === preg_match( $inner_container_start, $content, $matches, PREG_OFFSET_CAPTURE ) ) { + $offset = $matches[0][1]; + $content = substr( $content, 0, $offset ) . $image . substr( $content, $offset ); } return $content; From ec71b324b27f5498f22374f5954aa3ee1e0a3e25 Mon Sep 17 00:00:00 2001 From: Lee Pak <159242502+leemyongpakva@users.noreply.github.com> Date: Tue, 15 Oct 2024 21:28:52 +0700 Subject: [PATCH 09/23] data-basics/4-building-a-create-page-form is ready now (#66100) * Update 4-building-a-create-page-form.md consistency: other parts don't have "Part" in Heading * Update 3-building-an-edit-form.md Part 4 is ready now * Update 3-building-an-edit-form.md remove a space * Run docs:build --------- Unlinked contributors: leemyongpakva. Co-authored-by: t-hamano --- docs/how-to-guides/data-basics/3-building-an-edit-form.md | 2 +- docs/how-to-guides/data-basics/4-building-a-create-page-form.md | 2 +- docs/manifest.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/how-to-guides/data-basics/3-building-an-edit-form.md b/docs/how-to-guides/data-basics/3-building-an-edit-form.md index 65c4d0a5486a0..5f4e30357798d 100644 --- a/docs/how-to-guides/data-basics/3-building-an-edit-form.md +++ b/docs/how-to-guides/data-basics/3-building-an-edit-form.md @@ -539,5 +539,5 @@ function EditPageForm( { pageId, onCancel, onSaveFinished } ) { ## What's next? * **Previous part:** [Building a list of pages](/docs/how-to-guides/data-basics/2-building-a-list-of-pages.md) -* **Next part:** Building a *New Page* form (coming soon) +* **Next part:** [Building a Create Page form](/docs/how-to-guides/data-basics/4-building-a-create-page-form.md) * (optional) Review the [finished app](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/data-basics-59c8f8) in the block-development-examples repository diff --git a/docs/how-to-guides/data-basics/4-building-a-create-page-form.md b/docs/how-to-guides/data-basics/4-building-a-create-page-form.md index 33c6e9a5ccff5..9011596e0fedd 100644 --- a/docs/how-to-guides/data-basics/4-building-a-create-page-form.md +++ b/docs/how-to-guides/data-basics/4-building-a-create-page-form.md @@ -1,4 +1,4 @@ -# Part 4: Building a Create page form +# Building a Create page form In the [previous part](/docs/how-to-guides/data-basics/3-building-an-edit-form.md) we created an *Edit page* feature, and in this part we will add a *Create page* feature. Here's a glimpse of what we're going to build: diff --git a/docs/manifest.json b/docs/manifest.json index 8387b9079694c..5e94c6b83b70d 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -198,7 +198,7 @@ "parent": "data-basics" }, { - "title": "Part 4: Building a Create page form", + "title": "Building a Create page form", "slug": "4-building-a-create-page-form", "markdown_source": "../docs/how-to-guides/data-basics/4-building-a-create-page-form.md", "parent": "data-basics" From d32b15d972e8f38e6cebfa59fb339135e2b98b61 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Tue, 15 Oct 2024 07:49:40 -0700 Subject: [PATCH 10/23] Polish zoom out inserter (#66110) * Hide overflow; add delay to prompt; revise font scale * Tweak prompt text transition --- .../src/components/block-list/content.scss | 5 +++-- .../components/block-list/zoom-out-separator.js | 14 ++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/block-editor/src/components/block-list/content.scss b/packages/block-editor/src/components/block-list/content.scss index 6a88813b0c604..b42ebe349d599 100644 --- a/packages/block-editor/src/components/block-list/content.scss +++ b/packages/block-editor/src/components/block-list/content.scss @@ -404,14 +404,15 @@ _::-webkit-full-page-media, _:future, :root .has-multi-selection .block-editor-b display: flex; align-items: center; justify-content: center; + overflow: hidden; font-size: $default-font-size; font-family: $default-font; color: $black; font-weight: normal; .is-zoomed-out & { - // Scale the font size based on the zoom level. - font-size: calc(#{$default-font-size} * ( 2 - var(--wp-block-editor-iframe-zoom-out-scale) )); + // Maintains an absolute font-size by counter-scaling based on the zoom level. + font-size: calc(#{$default-font-size} / var(--wp-block-editor-iframe-zoom-out-scale)); } &.is-dragged-over { diff --git a/packages/block-editor/src/components/block-list/zoom-out-separator.js b/packages/block-editor/src/components/block-list/zoom-out-separator.js index 9e0d087c2267c..f2e6d050141fb 100644 --- a/packages/block-editor/src/components/block-list/zoom-out-separator.js +++ b/packages/block-editor/src/components/block-list/zoom-out-separator.js @@ -101,14 +101,15 @@ export function ZoomOutSeparator( { { isVisible && ( { __( 'Drop pattern.' ) } From 26e108deabcb6f58ffdf424f06887efb9f88333c Mon Sep 17 00:00:00 2001 From: Mario Santos <34552881+SantosGuillamot@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:01:14 +0200 Subject: [PATCH 11/23] Add missing `post.slug` dependency to `useMemo` (#66125) - Add missing `post.slug` dependency to `useMemo` Co-authored-by: SantosGuillamot Co-authored-by: youknowriad --- packages/editor/src/components/provider/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index 0c45dbc5e7199..51b54979cb34a 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -222,6 +222,7 @@ export const ExperimentalEditorProvider = withRegistryProvider( shouldRenderTemplate, post.id, post.type, + post.slug, rootLevelPost.type, rootLevelPost.slug, postTypes, From cd8d4dc12b874b32695ac0c52e186d8ae6f35054 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 15 Oct 2024 18:56:51 -0700 Subject: [PATCH 12/23] Leverage scheduler.yield in splitTask when available (#66001) * Leverage scheduler.yield in splitTask when available * Eliminate extra Promise when using scheduler.yield() Co-authored-by: swissspidy * Avoid needlessly re-checking whether scheduler.yield() is defined * Remove needless function wrapper * Ensure that scheduler is context object to yield function * Remove needless async * Reduce verbosity of scheduler.yield() existence check * Fix return type for scheduler.yield() Co-authored-by: Brendan Kenny * Define scheduler and scheduler.yield as optional Co-authored-by: Brendan Kenny --------- Unlinked contributors: brendankenny, LeszekSwirski. Co-authored-by: felixarntz --- packages/interactivity/src/utils.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/interactivity/src/utils.ts b/packages/interactivity/src/utils.ts index 4f10ed517de50..9cd6f8bebb0d1 100644 --- a/packages/interactivity/src/utils.ts +++ b/packages/interactivity/src/utils.ts @@ -22,6 +22,14 @@ interface Flusher { readonly dispose: () => void; } +declare global { + interface Window { + scheduler?: { + readonly yield?: () => Promise< void >; + }; + } +} + /** * Executes a callback function after the next frame is rendered. * @@ -48,12 +56,14 @@ const afterNextFrame = ( callback: () => void ) => { * * @return Promise */ -export const splitTask = () => { - return new Promise( ( resolve ) => { - // TODO: Use scheduler.yield() when available. - setTimeout( resolve, 0 ); - } ); -}; +export const splitTask = + typeof window.scheduler?.yield === 'function' + ? window.scheduler.yield.bind( window.scheduler ) + : () => { + return new Promise( ( resolve ) => { + setTimeout( resolve, 0 ); + } ); + }; /** * Creates a Flusher object that can be used to flush computed values and notify listeners. From 0ee8ce4536a2fc57ddeef10b9ada4bf9ceb07cdd Mon Sep 17 00:00:00 2001 From: Shail Mehta Date: Wed, 16 Oct 2024 07:36:11 +0530 Subject: [PATCH 13/23] Updated several typos in client-assets.php file (#66084) * Updated several typos in client-assets.php file * updated in backport changelog * moved changelog file in 6.7 folder --------- Co-authored-by: shail-mehta Co-authored-by: mukeshpanchal27 Co-authored-by: t-hamano --- backport-changelog/6.7/7561.md | 3 +++ lib/client-assets.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 backport-changelog/6.7/7561.md diff --git a/backport-changelog/6.7/7561.md b/backport-changelog/6.7/7561.md new file mode 100644 index 0000000000000..43fcffa41c02a --- /dev/null +++ b/backport-changelog/6.7/7561.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/7561 + +* https://github.com/WordPress/gutenberg/pull/66084 diff --git a/lib/client-assets.php b/lib/client-assets.php index 2343530e5595a..3e4f79cd3c1f7 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -630,7 +630,7 @@ function gutenberg_default_script_modules() { switch ( $script_module_id ) { /* * Interactivity exposes two entrypoints, "/index" and "/debug". - * "/debug" should replalce "/index" in devlopment. + * "/debug" should replace "/index" in development. */ case '@wordpress/interactivity/debug': if ( ! SCRIPT_DEBUG ) { From f809836298f82fe0303a5da1f7f938af459f7cb8 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 16 Oct 2024 08:46:17 +0300 Subject: [PATCH 14/23] Edit Site: Remove redundant state in StyleVariationsContainer (#66130) Co-authored-by: tyxla Co-authored-by: Mamaduka --- .../global-styles/style-variations-container.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/style-variations-container.js b/packages/edit-site/src/components/global-styles/style-variations-container.js index 18fc6f6ad9056..759303bcf5fa7 100644 --- a/packages/edit-site/src/components/global-styles/style-variations-container.js +++ b/packages/edit-site/src/components/global-styles/style-variations-container.js @@ -3,7 +3,7 @@ */ import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; -import { useContext, useEffect, useMemo, useState } from '@wordpress/element'; +import { useContext, useMemo } from '@wordpress/element'; import { __experimentalGrid as Grid } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; @@ -20,12 +20,7 @@ const { GlobalStylesContext } = unlock( blockEditorPrivateApis ); export default function StyleVariationsContainer( { gap = 2 } ) { const { user } = useContext( GlobalStylesContext ); - const [ currentUserStyles, setCurrentUserStyles ] = useState( user ); - const userStyles = currentUserStyles?.styles; - - useEffect( () => { - setCurrentUserStyles( user ); - }, [ user ] ); + const userStyles = user?.styles; const variations = useSelect( ( select ) => { return select( From 83ba78aac0f41a9adc2454ac16178967b2163bdc Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 16 Oct 2024 08:46:26 +0300 Subject: [PATCH 15/23] Edit Site: Avoid recomputing variations when no theme variations (#66137) Co-authored-by: tyxla Co-authored-by: Mamaduka --- .../use-theme-style-variations-by-property.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js b/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js index e6e3a1ccc3aa6..adf98b0d89b01 100644 --- a/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js +++ b/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js @@ -13,6 +13,7 @@ import { __ } from '@wordpress/i18n'; */ import { unlock } from '../../lock-unlock'; +const EMPTY_ARRAY = []; const { GlobalStylesContext, areGlobalStyleConfigsEqual } = unlock( blockEditorPrivateApis ); @@ -82,7 +83,7 @@ export function useCurrentMergeThemeStyleVariationsWithUserConfig( ).__experimentalGetCurrentThemeGlobalStylesVariations(); return { - variationsFromTheme: _variationsFromTheme || [], + variationsFromTheme: _variationsFromTheme || EMPTY_ARRAY, }; }, [] ); const { user: userVariation } = useContext( GlobalStylesContext ); From 3fd7c8a2cbdb321611c447d64d6fe4b79dfdefd0 Mon Sep 17 00:00:00 2001 From: Ramon Date: Wed, 16 Oct 2024 17:05:40 +1100 Subject: [PATCH 16/23] Theme JSON: remove redundant check and relocate $selectors assignment (#66154) Remove redundant check for `$theme_json['styles']`,` which means `WP_Theme_JSON::get_blocks_metadata()` is only called if necessary. This also has the potential to slightly improve the performance of functions that call WP_Theme_JSON::get_styles_block_nodes(), where no block styles exist Co-authored-by: ramonjd Co-authored-by: aaronrobertshaw --- backport-changelog/6.8/7575.md | 3 +++ lib/class-wp-theme-json-gutenberg.php | 8 ++------ 2 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 backport-changelog/6.8/7575.md diff --git a/backport-changelog/6.8/7575.md b/backport-changelog/6.8/7575.md new file mode 100644 index 0000000000000..f1c6a84da1368 --- /dev/null +++ b/backport-changelog/6.8/7575.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/7575 + +* https://github.com/WordPress/gutenberg/pull/66154 diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 8cd83e007f37c..98d0719cd24a9 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -2732,17 +2732,13 @@ private static function update_separator_declarations( $declarations ) { * @return array The block nodes in theme.json. */ private static function get_block_nodes( $theme_json, $selectors = array(), $options = array() ) { - $selectors = empty( $selectors ) ? static::get_blocks_metadata() : $selectors; - $nodes = array(); - if ( ! isset( $theme_json['styles'] ) ) { - return $nodes; - } + $nodes = array(); - // Blocks. if ( ! isset( $theme_json['styles']['blocks'] ) ) { return $nodes; } + $selectors = empty( $selectors ) ? static::get_blocks_metadata() : $selectors; $include_variations = $options['include_block_style_variations'] ?? false; $include_node_paths_only = $options['include_node_paths_only'] ?? false; From 6d84740fb33ae906469b35f4f8d74c4902f78b1a Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Wed, 16 Oct 2024 10:36:27 +0200 Subject: [PATCH 17/23] Memoize pattern objects returned from getAllowedPatterns (#66159) Co-authored-by: jsnajdr Co-authored-by: youknowriad Co-authored-by: andrewserong Co-authored-by: ndiego Co-authored-by: colorful-tones Co-authored-by: kevin940726 Co-authored-by: ramonjd Co-authored-by: madhusudhand Co-authored-by: getdave Co-authored-by: annezazu --- packages/block-editor/src/store/selectors.js | 24 +++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index a81d88de33466..2bfc52118d7ac 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2385,6 +2385,21 @@ const getAllowedPatternsDependants = ( select ) => ( state, rootClientId ) => [ ...getInsertBlockTypeDependants( state, rootClientId ), ]; +const patternsWithParsedBlocks = new WeakMap(); +function enhancePatternWithParsedBlocks( pattern ) { + let enhancedPattern = patternsWithParsedBlocks.get( pattern ); + if ( ! enhancedPattern ) { + enhancedPattern = { + ...pattern, + get blocks() { + return getParsedPattern( pattern ).blocks; + }, + }; + patternsWithParsedBlocks.set( pattern, enhancedPattern ); + } + return enhancedPattern; +} + /** * Returns the list of allowed patterns for inner blocks children. * @@ -2406,14 +2421,7 @@ export const __experimentalGetAllowedPatterns = createRegistrySelector( const { allowedBlockTypes } = getSettings( state ); const parsedPatterns = patterns .filter( ( { inserter = true } ) => !! inserter ) - .map( ( pattern ) => { - return { - ...pattern, - get blocks() { - return getParsedPattern( pattern ).blocks; - }, - }; - } ); + .map( enhancePatternWithParsedBlocks ); const availableParsedPatterns = parsedPatterns.filter( ( pattern ) => From 5c18dc781c47b4f072b6a26a8eea2b1a4d1c75fa Mon Sep 17 00:00:00 2001 From: Django Date: Wed, 16 Oct 2024 03:30:30 -0600 Subject: [PATCH 18/23] Remove clip for screen-reader-text CSS (#66145) Co-authored-by: mediaformat Co-authored-by: afercia Co-authored-by: getdave --- .../src/components/responsive-block-control/style.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-editor/src/components/responsive-block-control/style.scss b/packages/block-editor/src/components/responsive-block-control/style.scss index 7f90e12dd87c6..e840a3bfcf436 100644 --- a/packages/block-editor/src/components/responsive-block-control/style.scss +++ b/packages/block-editor/src/components/responsive-block-control/style.scss @@ -1,6 +1,5 @@ @mixin screen-reader-text() { border: 0; - clip: rect(1px, 1px, 1px, 1px); clip-path: inset(50%); height: 1px; margin: -1px; From 3948575f8940231faa63fe883caa66dd948d83cc Mon Sep 17 00:00:00 2001 From: Michal Date: Wed, 16 Oct 2024 10:37:07 +0100 Subject: [PATCH 19/23] Update the changelog (#66120) Co-authored-by: michalczaplinski Co-authored-by: cbravobernal --- packages/interactivity-router/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/interactivity-router/CHANGELOG.md b/packages/interactivity-router/CHANGELOG.md index 4f79489909668..10294b3a7e620 100644 --- a/packages/interactivity-router/CHANGELOG.md +++ b/packages/interactivity-router/CHANGELOG.md @@ -7,8 +7,10 @@ - Improvements to the experimental full-page navigation ([#64067](https://github.com/WordPress/gutenberg/pull/64067)): - Remove the `src` attributes from prefetched script tags. - Use `.textContent` instead of `.innerText` to set `