From 3c8f33832634af9d4fabd887ae74a743bc20636c Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 16 Oct 2024 17:03:23 +0200 Subject: [PATCH 01/11] Filter fields by type in post meta --- packages/editor/src/bindings/post-meta.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/bindings/post-meta.js b/packages/editor/src/bindings/post-meta.js index 9198ac0fe41e1e..2ff722be2ab0f6 100644 --- a/packages/editor/src/bindings/post-meta.js +++ b/packages/editor/src/bindings/post-meta.js @@ -51,8 +51,16 @@ function getPostMetaFields( select, context ) { const registeredFields = getRegisteredPostMeta( context?.postType ); const metaFields = {}; Object.entries( registeredFields || {} ).forEach( ( [ key, props ] ) => { - // Don't include footnotes or private fields. - if ( key !== 'footnotes' && key.charAt( 0 ) !== '_' ) { + if ( + // Don't include footnotes. + key !== 'footnotes' && + // Don't include private fields. + key.charAt( 0 ) !== '_' && + // Don't support boolean, object, and array fields yet. + typeof entityMetaValues?.[ key ] !== 'boolean' && + typeof entityMetaValues?.[ key ] !== 'object' && + ! Array.isArray( entityMetaValues?.[ key ] ) + ) { metaFields[ key ] = { label: props.title || key, value: From c21e55a4ce1d72afc3b2e325c7bbe2c32fa50b0e Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 16 Oct 2024 17:11:30 +0200 Subject: [PATCH 02/11] Add e2e test --- packages/e2e-tests/plugins/block-bindings.php | 64 +++++++++++++++++++ .../various/block-bindings/post-meta.spec.js | 49 ++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/packages/e2e-tests/plugins/block-bindings.php b/packages/e2e-tests/plugins/block-bindings.php index ffbe50ab3cc902..4f46bb0b4c84ed 100644 --- a/packages/e2e-tests/plugins/block-bindings.php +++ b/packages/e2e-tests/plugins/block-bindings.php @@ -90,6 +90,7 @@ function gutenberg_test_block_bindings_registration() { 'post', 'text_custom_field', array( + 'label' => 'Text custom field', 'default' => 'Value of the text custom field', 'show_in_rest' => true, 'single' => true, @@ -106,6 +107,69 @@ function gutenberg_test_block_bindings_registration() { 'type' => 'string', ) ); + register_meta( + 'post', + 'object_custom_field', + array( + 'label' => 'Object custom field', + 'show_in_rest' => true, + 'single' => true, + 'type' => 'object', + 'default' => json_decode( json_encode( array( 'foo' => 'bar' ) ), false ), + ) + ); + register_meta( + 'post', + 'array_custom_field', + array( + 'label' => 'Array custom field', + 'show_in_rest' => array( + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'string', + ), + ), + ), + 'single' => true, + 'type' => 'array', + 'default' => array(), + ) + ); + register_meta( + 'post', + 'number', + array( + 'label' => 'Number custom field', + 'type' => 'number', + 'show_in_rest' => true, + 'single' => true, + 'default' => 5.5, + ) + ); + register_meta( + 'post', + 'integer', + array( + 'label' => 'Integer custom field', + 'type' => 'integer', + 'show_in_rest' => true, + 'single' => true, + 'default' => 5, + ) + ); + register_meta( + 'post', + 'boolean', + array( + 'label' => 'Boolean custom field', + 'type' => 'boolean', + 'show_in_rest' => true, + 'single' => true, + 'default' => true, + ) + ); + // Register CPT custom fields. register_meta( 'post', diff --git a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js index d82def6feb66bf..0a95e8107fd644 100644 --- a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js +++ b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js @@ -547,5 +547,54 @@ test.describe( 'Post Meta source', () => { .filter( { hasText: 'Movie field label' } ); await expect( movieField ).toBeVisible(); } ); + test( 'should not be possible to connect non-supported fields through the attributes panel', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + } ); + await page.getByLabel( 'Attributes options' ).click(); + await page + .getByRole( 'menuitemcheckbox', { + name: 'Show content', + } ) + .click(); + await page + .getByRole( 'button', { + name: 'content', + } ) + .click(); + await expect( + page.getByRole( 'menuitemradio', { + name: 'Text custom field', + } ) + ).toBeVisible(); + await expect( + page.getByRole( 'menuitemradio', { + name: 'Number custom field', + } ) + ).toBeVisible(); + await expect( + page.getByRole( 'menuitemradio', { + name: 'Integer custom field', + } ) + ).toBeVisible(); + await expect( + page.getByRole( 'menuitemradio', { + name: 'Boolean custom field', + } ) + ).toBeHidden(); + await expect( + page.getByRole( 'menuitemradio', { + name: 'Object custom field', + } ) + ).toBeHidden(); + await expect( + page.getByRole( 'menuitemradio', { + name: 'Array custom field', + } ) + ).toBeHidden(); + } ); } ); } ); From ece9a6895ef27ef6c4e3cf267150ecb442a7c02f Mon Sep 17 00:00:00 2001 From: Carlos Bravo <37012961+cbravobernal@users.noreply.github.com> Date: Wed, 16 Oct 2024 17:40:10 +0200 Subject: [PATCH 03/11] Allow only strings --- packages/editor/src/bindings/post-meta.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/bindings/post-meta.js b/packages/editor/src/bindings/post-meta.js index 2ff722be2ab0f6..81216e61ed54a7 100644 --- a/packages/editor/src/bindings/post-meta.js +++ b/packages/editor/src/bindings/post-meta.js @@ -56,10 +56,8 @@ function getPostMetaFields( select, context ) { key !== 'footnotes' && // Don't include private fields. key.charAt( 0 ) !== '_' && - // Don't support boolean, object, and array fields yet. - typeof entityMetaValues?.[ key ] !== 'boolean' && - typeof entityMetaValues?.[ key ] !== 'object' && - ! Array.isArray( entityMetaValues?.[ key ] ) + // Only support string types. + typeof entityMetaValues?.[ key ] === 'string' ) { metaFields[ key ] = { label: props.title || key, From 02bcdc4f0ac61efb1fa227015e3966f7a3827db7 Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Thu, 17 Oct 2024 00:54:01 -0400 Subject: [PATCH 04/11] Update test to expect numbers and integers to be hidden --- .../e2e/specs/editor/various/block-bindings/post-meta.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js index 0a95e8107fd644..6c852984412747 100644 --- a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js +++ b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js @@ -574,12 +574,12 @@ test.describe( 'Post Meta source', () => { page.getByRole( 'menuitemradio', { name: 'Number custom field', } ) - ).toBeVisible(); + ).toBeHidden(); await expect( page.getByRole( 'menuitemradio', { name: 'Integer custom field', } ) - ).toBeVisible(); + ).toBeHidden(); await expect( page.getByRole( 'menuitemradio', { name: 'Boolean custom field', From 6f8397c50a04f3c9faed651c7bdf43b63e644d6f Mon Sep 17 00:00:00 2001 From: Ricardo Artemio Morales Date: Thu, 17 Oct 2024 00:54:49 -0400 Subject: [PATCH 05/11] Update tests to check for label instead of key --- .../specs/editor/various/block-bindings/post-meta.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js index 6c852984412747..90395d6bb24308 100644 --- a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js +++ b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js @@ -236,14 +236,14 @@ test.describe( 'Post Meta source', () => { } ) => { const globalField = page .getByRole( 'menuitemradio' ) - .filter( { hasText: 'text_custom_field' } ); + .filter( { hasText: 'Text custom field' } ); await expect( globalField ).toBeVisible(); } ); test( 'should not include protected fields', async ( { page } ) => { // Ensure the fields have loaded by checking the field is visible. const globalField = page .getByRole( 'menuitemradio' ) - .filter( { hasText: 'text_custom_field' } ); + .filter( { hasText: 'Text custom field' } ); await expect( globalField ).toBeVisible(); // Check the protected fields are not visible. const protectedField = page @@ -322,7 +322,7 @@ test.describe( 'Post Meta source', () => { // Check the post meta fields are not visible. const globalField = page .getByRole( 'menuitemradio' ) - .filter( { hasText: 'text_custom_field' } ); + .filter( { hasText: 'Text custom field' } ); await expect( globalField ).toBeHidden(); const movieField = page .getByRole( 'menuitemradio' ) From 3ef22f9214738a63fb11e049e4b8a1af91ea45f4 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Thu, 17 Oct 2024 09:36:20 +0200 Subject: [PATCH 06/11] Adapt object custom field --- packages/e2e-tests/plugins/block-bindings.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/e2e-tests/plugins/block-bindings.php b/packages/e2e-tests/plugins/block-bindings.php index 4f46bb0b4c84ed..e3e07d22f20aaf 100644 --- a/packages/e2e-tests/plugins/block-bindings.php +++ b/packages/e2e-tests/plugins/block-bindings.php @@ -112,10 +112,18 @@ function gutenberg_test_block_bindings_registration() { 'object_custom_field', array( 'label' => 'Object custom field', - 'show_in_rest' => true, + 'show_in_rest' => array( + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'foo' => array( + 'type' => 'string', + ), + ), + ), + ), 'single' => true, 'type' => 'object', - 'default' => json_decode( json_encode( array( 'foo' => 'bar' ) ), false ), ) ); register_meta( From 48485b1a6f06946092010ec62164e1f816b0adea Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Thu, 17 Oct 2024 10:42:19 +0200 Subject: [PATCH 07/11] Adapt e2e tests --- packages/e2e-tests/plugins/block-bindings.php | 13 ++++++++++++- .../editor/various/block-bindings/post-meta.spec.js | 9 +++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/e2e-tests/plugins/block-bindings.php b/packages/e2e-tests/plugins/block-bindings.php index e3e07d22f20aaf..71681d4422fa27 100644 --- a/packages/e2e-tests/plugins/block-bindings.php +++ b/packages/e2e-tests/plugins/block-bindings.php @@ -90,7 +90,6 @@ function gutenberg_test_block_bindings_registration() { 'post', 'text_custom_field', array( - 'label' => 'Text custom field', 'default' => 'Value of the text custom field', 'show_in_rest' => true, 'single' => true, @@ -107,6 +106,18 @@ function gutenberg_test_block_bindings_registration() { 'type' => 'string', ) ); + // Register different types of custom fields for testing. + register_meta( + 'post', + 'string_custom_field', + array( + 'label' => 'String custom field', + 'default' => '', + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + ) + ); register_meta( 'post', 'object_custom_field', diff --git a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js index 90395d6bb24308..a3351ca1f7ac15 100644 --- a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js +++ b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js @@ -236,14 +236,14 @@ test.describe( 'Post Meta source', () => { } ) => { const globalField = page .getByRole( 'menuitemradio' ) - .filter( { hasText: 'Text custom field' } ); + .filter( { hasText: 'text_custom_field' } ); await expect( globalField ).toBeVisible(); } ); test( 'should not include protected fields', async ( { page } ) => { // Ensure the fields have loaded by checking the field is visible. const globalField = page .getByRole( 'menuitemradio' ) - .filter( { hasText: 'Text custom field' } ); + .filter( { hasText: 'text_custom_field' } ); await expect( globalField ).toBeVisible(); // Check the protected fields are not visible. const protectedField = page @@ -258,6 +258,7 @@ test.describe( 'Post Meta source', () => { test( 'should show the default value if it is defined', async ( { page, } ) => { + await page.pause(); const fieldButton = page .getByRole( 'menuitemradio' ) .filter( { hasText: 'Movie field label' } ); @@ -322,7 +323,7 @@ test.describe( 'Post Meta source', () => { // Check the post meta fields are not visible. const globalField = page .getByRole( 'menuitemradio' ) - .filter( { hasText: 'Text custom field' } ); + .filter( { hasText: 'text_custom_field' } ); await expect( globalField ).toBeHidden(); const movieField = page .getByRole( 'menuitemradio' ) @@ -567,7 +568,7 @@ test.describe( 'Post Meta source', () => { .click(); await expect( page.getByRole( 'menuitemradio', { - name: 'Text custom field', + name: 'String custom field', } ) ).toBeVisible(); await expect( From 98e41bd7fc895da5ab0c18357fa2844b939f6d00 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Thu, 17 Oct 2024 11:02:44 +0200 Subject: [PATCH 08/11] Add `type` prop to `getFieldsList` --- .../block-editor/src/hooks/block-bindings.js | 63 ++++++++++++------- packages/editor/src/bindings/post-meta.js | 5 +- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js index 5c002613831ce1..45e0a0fc8b3556 100644 --- a/packages/block-editor/src/hooks/block-bindings.js +++ b/packages/block-editor/src/hooks/block-bindings.js @@ -5,6 +5,7 @@ import { __ } from '@wordpress/i18n'; import { getBlockBindingsSource, getBlockBindingsSources, + getBlockType, } from '@wordpress/blocks'; import { __experimentalItemGroup as ItemGroup, @@ -29,6 +30,7 @@ import { import { unlock } from '../lock-unlock'; import InspectorControls from '../components/inspector-controls'; import BlockContext from '../components/block-context'; +import { useBlockEditContext } from '../components/block-edit'; import { useBlockBindingsUtils } from '../utils/block-bindings'; import { store as blockEditorStore } from '../store'; @@ -50,9 +52,20 @@ const useToolsPanelDropdownMenuProps = () => { }; function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) { + const { clientId } = useBlockEditContext(); const registeredSources = getBlockBindingsSources(); const { updateBlockBindings } = useBlockBindingsUtils(); const currentKey = binding?.args?.key; + const attributeType = useSelect( + ( select ) => { + const { name: blockName } = + select( blockEditorStore ).getBlock( clientId ); + const _attributeType = + getBlockType( blockName ).attributes?.[ attribute ]?.type; + return _attributeType === 'rich-text' ? 'string' : _attributeType; + }, + [ clientId ] + ); return ( <> { Object.entries( fieldsList ).map( ( [ name, fields ], i ) => ( @@ -63,29 +76,33 @@ function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) { { registeredSources[ name ].label } ) } - { Object.entries( fields ).map( ( [ key, args ] ) => ( - - updateBlockBindings( { - [ attribute ]: { - source: name, - args: { key }, - }, - } ) - } - name={ attribute + '-binding' } - value={ key } - checked={ key === currentKey } - > - - { args?.label } - - - { args?.value } - - - ) ) } + { Object.entries( fields ) + .filter( + ( [ , args ] ) => args?.type === attributeType + ) + .map( ( [ key, args ] ) => ( + + updateBlockBindings( { + [ attribute ]: { + source: name, + args: { key }, + }, + } ) + } + name={ attribute + '-binding' } + value={ key } + checked={ key === currentKey } + > + + { args?.label } + + + { args?.value } + + + ) ) } { i !== Object.keys( fieldsList ).length - 1 && ( diff --git a/packages/editor/src/bindings/post-meta.js b/packages/editor/src/bindings/post-meta.js index 81216e61ed54a7..f6756914042d1d 100644 --- a/packages/editor/src/bindings/post-meta.js +++ b/packages/editor/src/bindings/post-meta.js @@ -55,9 +55,7 @@ function getPostMetaFields( select, context ) { // Don't include footnotes. key !== 'footnotes' && // Don't include private fields. - key.charAt( 0 ) !== '_' && - // Only support string types. - typeof entityMetaValues?.[ key ] === 'string' + key.charAt( 0 ) !== '_' ) { metaFields[ key ] = { label: props.title || key, @@ -66,6 +64,7 @@ function getPostMetaFields( select, context ) { entityMetaValues?.[ key ] ?? // When using the default, an empty string IS NOT a valid value. ( props.default || undefined ), + type: props.type, }; } } ); From 346faed638eaa66750b33f8ec10791a0eedd2e6c Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Thu, 17 Oct 2024 12:01:02 +0200 Subject: [PATCH 09/11] Adapt testing source --- packages/e2e-tests/plugins/block-bindings.php | 3 +++ test/e2e/specs/editor/various/block-bindings/post-meta.spec.js | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/e2e-tests/plugins/block-bindings.php b/packages/e2e-tests/plugins/block-bindings.php index 71681d4422fa27..b86673c2c523d0 100644 --- a/packages/e2e-tests/plugins/block-bindings.php +++ b/packages/e2e-tests/plugins/block-bindings.php @@ -21,14 +21,17 @@ function gutenberg_test_block_bindings_registration() { 'text_field' => array( 'label' => 'Text Field Label', 'value' => 'Text Field Value', + 'type' => 'string', ), 'url_field' => array( 'label' => 'URL Field Label', 'value' => $testing_url, + 'type' => 'string', ), 'empty_field' => array( 'label' => 'Empty Field Label', 'value' => '', + 'type' => 'string', ), ); diff --git a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js index a3351ca1f7ac15..32334bfc777f2a 100644 --- a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js +++ b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js @@ -258,7 +258,6 @@ test.describe( 'Post Meta source', () => { test( 'should show the default value if it is defined', async ( { page, } ) => { - await page.pause(); const fieldButton = page .getByRole( 'menuitemradio' ) .filter( { hasText: 'Movie field label' } ); From 41d6aba1ee00207013d557756f008edb628ba719 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Thu, 17 Oct 2024 12:49:40 +0200 Subject: [PATCH 10/11] Reduce diff --- packages/editor/src/bindings/post-meta.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/editor/src/bindings/post-meta.js b/packages/editor/src/bindings/post-meta.js index f6756914042d1d..a3602ce7d62076 100644 --- a/packages/editor/src/bindings/post-meta.js +++ b/packages/editor/src/bindings/post-meta.js @@ -51,12 +51,8 @@ function getPostMetaFields( select, context ) { const registeredFields = getRegisteredPostMeta( context?.postType ); const metaFields = {}; Object.entries( registeredFields || {} ).forEach( ( [ key, props ] ) => { - if ( - // Don't include footnotes. - key !== 'footnotes' && - // Don't include private fields. - key.charAt( 0 ) !== '_' - ) { + // Don't include footnotes or private fields. + if ( key !== 'footnotes' && key.charAt( 0 ) !== '_' ) { metaFields[ key ] = { label: props.title || key, value: From 1512311cb388cbd3ba965bc7674a7e5ba744d573 Mon Sep 17 00:00:00 2001 From: Mario Santos <34552881+SantosGuillamot@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:23:35 +0200 Subject: [PATCH 11/11] Add `attribute` to dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Greg Ziółkowski --- packages/block-editor/src/hooks/block-bindings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js index 45e0a0fc8b3556..694c9c1c0bb3cc 100644 --- a/packages/block-editor/src/hooks/block-bindings.js +++ b/packages/block-editor/src/hooks/block-bindings.js @@ -64,7 +64,7 @@ function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) { getBlockType( blockName ).attributes?.[ attribute ]?.type; return _attributeType === 'rich-text' ? 'string' : _attributeType; }, - [ clientId ] + [ clientId, attribute ] ); return ( <>