From 54e664ce69ab7af7fc0626ff7f2d44b00cc04c8c Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 2 Mar 2026 13:17:31 +0100 Subject: [PATCH 1/7] PHP-only Blocks: Reflect bound attribute values in block inspector controls --- .../src/hooks/auto-inspector-controls.js | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/hooks/auto-inspector-controls.js b/packages/block-editor/src/hooks/auto-inspector-controls.js index 8e15d56f9f1efa..ee38558ecb83a2 100644 --- a/packages/block-editor/src/hooks/auto-inspector-controls.js +++ b/packages/block-editor/src/hooks/auto-inspector-controls.js @@ -1,11 +1,11 @@ /** * WordPress dependencies */ -import { getBlockType } from '@wordpress/blocks'; +import { getBlockType, store as blocksStore } from '@wordpress/blocks'; import { PanelBody } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { DataForm } from '@wordpress/dataviews'; -import { useMemo } from '@wordpress/element'; +import { useContext, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** @@ -14,6 +14,8 @@ import { __ } from '@wordpress/i18n'; import InspectorControls from '../components/inspector-controls'; import { useBlockEditingMode } from '../components/block-editing-mode'; import { store as blockEditorStore } from '../store'; +import { unlock } from '../lock-unlock'; +import BlockContext from '../components/block-context'; import { generateFieldsFromAttributes } from './generate-fields-from-attributes'; /** @@ -45,9 +47,34 @@ function hasAutoGenerateControl( blockTypeAttributes ) { function AutoRegisterControls( { name, clientId, setAttributes } ) { const blockEditingMode = useBlockEditingMode(); + const blockContext = useContext( BlockContext ); + const attributes = useSelect( - ( select ) => select( blockEditorStore ).getBlockAttributes( clientId ), - [ clientId ] + ( select ) => { + const _attributes = + select( blockEditorStore ).getBlockAttributes( clientId ); + if ( ! _attributes?.metadata?.bindings ) { + return _attributes; + } + + const { getBlockBindingsSource } = unlock( select( blocksStore ) ); + return Object.entries( _attributes.metadata.bindings ).reduce( + ( acc, [ attribute, binding ] ) => { + const source = getBlockBindingsSource( binding.source ); + if ( ! source ) { + return acc; + } + const values = source.getValues( { + select, + context: blockContext, + bindings: { [ attribute ]: binding }, + } ); + return { ...acc, ...values }; + }, + _attributes + ); + }, + [ blockContext, clientId ] ); const blockType = getBlockType( name ); From 92544a67fd3fcf6f1c4f8dd0c6e4bfca9e6283a8 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 2 Mar 2026 13:41:57 +0100 Subject: [PATCH 2/7] Add e2e test coverage --- .../plugins/server-side-rendered-block.php | 8 ++++ .../server-side-rendered-block.spec.js | 47 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/packages/e2e-tests/plugins/server-side-rendered-block.php b/packages/e2e-tests/plugins/server-side-rendered-block.php index 5f34a703fb5a90..28dc70af756080 100644 --- a/packages/e2e-tests/plugins/server-side-rendered-block.php +++ b/packages/e2e-tests/plugins/server-side-rendered-block.php @@ -87,6 +87,14 @@ static function () { ) ); + // Add binding support for the auto-register-with-controls block. + add_filter( + 'block_bindings_supported_attributes_test/auto-register-with-controls', + static function () { + return array( 'title', 'count', 'spacing', 'showEmojis', 'emoji' ); + } + ); + // PHP-only block with auto-generated controls from various attribute types register_block_type( 'test/auto-register-with-controls', diff --git a/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js b/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js index 8975a7691e0169..9f1a13e9396944 100644 --- a/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js +++ b/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js @@ -264,3 +264,50 @@ test.describe( 'PHP-only auto-register blocks', () => { ).toBeVisible(); } ); } ); + +test.describe( 'PHP-only auto-register blocks with bindings', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activatePlugin( + 'gutenberg-test-server-side-rendered-block' + ); + await requestUtils.activatePlugin( 'gutenberg-test-block-bindings' ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.deactivatePlugin( 'gutenberg-test-block-bindings' ); + await requestUtils.deactivatePlugin( + 'gutenberg-test-server-side-rendered-block' + ); + } ); + + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'should reflect bound attribute values in auto-generated inspector controls', async ( { + editor, + page, + } ) => { + // Insert the block with a binding on the title attribute. + await editor.insertBlock( { + name: 'test/auto-register-with-controls', + attributes: { + metadata: { + bindings: { + title: { + source: 'testing/complete-source', + args: { key: 'text_field' }, + }, + }, + }, + }, + } ); + + await editor.openDocumentSettingsSidebar(); + + // The Title control should show the bound value from the source, + // not the block attribute default ("My Emoji Collection"). + const titleInput = page.getByLabel( 'Title' ); + await expect( titleInput ).toHaveValue( 'Text Field Value' ); + } ); +} ); From 8d14a7bdbe26ec128722a6f5a0cfdc4b4364589b Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 2 Mar 2026 14:03:47 +0100 Subject: [PATCH 3/7] Absorb test into existing fixture --- .../server-side-rendered-block.spec.js | 70 +++++++++---------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js b/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js index 9f1a13e9396944..0855cf2d2dc8bd 100644 --- a/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js +++ b/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js @@ -263,51 +263,45 @@ test.describe( 'PHP-only auto-register blocks', () => { page.getByLabel( 'Emoji', { exact: true } ) ).toBeVisible(); } ); -} ); - -test.describe( 'PHP-only auto-register blocks with bindings', () => { - test.beforeAll( async ( { requestUtils } ) => { - await requestUtils.activatePlugin( - 'gutenberg-test-server-side-rendered-block' - ); - await requestUtils.activatePlugin( 'gutenberg-test-block-bindings' ); - } ); - test.afterAll( async ( { requestUtils } ) => { - await requestUtils.deactivatePlugin( 'gutenberg-test-block-bindings' ); - await requestUtils.deactivatePlugin( - 'gutenberg-test-server-side-rendered-block' - ); - } ); + test.describe( 'with block bindings', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activatePlugin( + 'gutenberg-test-block-bindings' + ); + } ); - test.beforeEach( async ( { admin } ) => { - await admin.createNewPost(); - } ); + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.deactivatePlugin( + 'gutenberg-test-block-bindings' + ); + } ); - test( 'should reflect bound attribute values in auto-generated inspector controls', async ( { - editor, - page, - } ) => { - // Insert the block with a binding on the title attribute. - await editor.insertBlock( { - name: 'test/auto-register-with-controls', - attributes: { - metadata: { - bindings: { - title: { - source: 'testing/complete-source', - args: { key: 'text_field' }, + test( 'should reflect bound attribute values in auto-generated inspector controls', async ( { + editor, + page, + } ) => { + // Insert the block with a binding on the title attribute. + await editor.insertBlock( { + name: 'test/auto-register-with-controls', + attributes: { + metadata: { + bindings: { + title: { + source: 'testing/complete-source', + args: { key: 'text_field' }, + }, }, }, }, - }, - } ); + } ); - await editor.openDocumentSettingsSidebar(); + await editor.openDocumentSettingsSidebar(); - // The Title control should show the bound value from the source, - // not the block attribute default ("My Emoji Collection"). - const titleInput = page.getByLabel( 'Title' ); - await expect( titleInput ).toHaveValue( 'Text Field Value' ); + // Controls should show bound values from the source, + // not the block attribute defaults. + const titleInput = page.getByLabel( 'Title' ); + await expect( titleInput ).toHaveValue( 'Text Field Value' ); + } ); } ); } ); From 344e62f42d28f4318053cb3e2076ec5e00d64097 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 2 Mar 2026 14:28:22 +0100 Subject: [PATCH 4/7] Don't bind emoji (yet) --- packages/e2e-tests/plugins/server-side-rendered-block.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/e2e-tests/plugins/server-side-rendered-block.php b/packages/e2e-tests/plugins/server-side-rendered-block.php index 28dc70af756080..29962e90798318 100644 --- a/packages/e2e-tests/plugins/server-side-rendered-block.php +++ b/packages/e2e-tests/plugins/server-side-rendered-block.php @@ -91,7 +91,7 @@ static function () { add_filter( 'block_bindings_supported_attributes_test/auto-register-with-controls', static function () { - return array( 'title', 'count', 'spacing', 'showEmojis', 'emoji' ); + return array( 'title', 'count', 'spacing', 'showEmojis' ); } ); From 30fa0ac35c11dc52dfe996a37696bf37fb7746cf Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 2 Mar 2026 14:28:42 +0100 Subject: [PATCH 5/7] Test multiple attribute bindings --- .../server-side-rendered-block.spec.js | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js b/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js index 0855cf2d2dc8bd..95c6ad2f512038 100644 --- a/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js +++ b/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js @@ -281,15 +281,27 @@ test.describe( 'PHP-only auto-register blocks', () => { editor, page, } ) => { - // Insert the block with a binding on the title attribute. + // Insert the block with bindings on multiple attributes. await editor.insertBlock( { name: 'test/auto-register-with-controls', attributes: { metadata: { bindings: { title: { - source: 'testing/complete-source', - args: { key: 'text_field' }, + source: 'core/post-meta', + args: { key: 'text_custom_field' }, + }, + count: { + source: 'core/post-meta', + args: { key: 'integer' }, + }, + spacing: { + source: 'core/post-meta', + args: { key: 'number_custom_field' }, + }, + showEmojis: { + source: 'core/post-meta', + args: { key: 'boolean' }, }, }, }, @@ -300,8 +312,12 @@ test.describe( 'PHP-only auto-register blocks', () => { // Controls should show bound values from the source, // not the block attribute defaults. - const titleInput = page.getByLabel( 'Title' ); - await expect( titleInput ).toHaveValue( 'Text Field Value' ); + await expect( page.getByLabel( 'Title' ) ).toHaveValue( + 'Value of the text custom field' + ); + await expect( page.getByLabel( 'Count' ) ).toHaveValue( '5' ); + await expect( page.getByLabel( 'Spacing' ) ).toHaveValue( '5.5' ); + await expect( page.getByLabel( 'Show Emojis' ) ).toBeChecked(); } ); } ); } ); From 336d4335d34aa894c660fbd0b478feaef1291c28 Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 2 Mar 2026 14:29:52 +0100 Subject: [PATCH 6/7] Tweak test description --- .../e2e/specs/editor/plugins/server-side-rendered-block.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js b/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js index 95c6ad2f512038..399a340a5ab3ee 100644 --- a/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js +++ b/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js @@ -277,7 +277,7 @@ test.describe( 'PHP-only auto-register blocks', () => { ); } ); - test( 'should reflect bound attribute values in auto-generated inspector controls', async ( { + test( 'generated inspector controls should reflect bound attribute values', async ( { editor, page, } ) => { From 8e8ad86b84d132eb9a6a51213980a12b569882ac Mon Sep 17 00:00:00 2001 From: Bernie Reiter Date: Mon, 2 Mar 2026 14:34:24 +0100 Subject: [PATCH 7/7] Tweak test values --- packages/e2e-tests/plugins/block-bindings.php | 4 ++-- .../specs/editor/plugins/server-side-rendered-block.spec.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/e2e-tests/plugins/block-bindings.php b/packages/e2e-tests/plugins/block-bindings.php index 92331d2b654d27..1d51c8c6bff15c 100644 --- a/packages/e2e-tests/plugins/block-bindings.php +++ b/packages/e2e-tests/plugins/block-bindings.php @@ -172,7 +172,7 @@ function gutenberg_test_block_bindings_registration() { 'type' => 'number', 'show_in_rest' => true, 'single' => true, - 'default' => 5.5, + 'default' => 0.5, ) ); register_meta( @@ -183,7 +183,7 @@ function gutenberg_test_block_bindings_registration() { 'type' => 'integer', 'show_in_rest' => true, 'single' => true, - 'default' => 5, + 'default' => 3, ) ); register_meta( diff --git a/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js b/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js index 399a340a5ab3ee..cf5bbb7bdbd6fa 100644 --- a/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js +++ b/test/e2e/specs/editor/plugins/server-side-rendered-block.spec.js @@ -315,8 +315,8 @@ test.describe( 'PHP-only auto-register blocks', () => { await expect( page.getByLabel( 'Title' ) ).toHaveValue( 'Value of the text custom field' ); - await expect( page.getByLabel( 'Count' ) ).toHaveValue( '5' ); - await expect( page.getByLabel( 'Spacing' ) ).toHaveValue( '5.5' ); + await expect( page.getByLabel( 'Count' ) ).toHaveValue( '3' ); + await expect( page.getByLabel( 'Spacing' ) ).toHaveValue( '0.5' ); await expect( page.getByLabel( 'Show Emojis' ) ).toBeChecked(); } ); } );