From 60306c572b8eab2d235632bbab0546ee4bbda9d3 Mon Sep 17 00:00:00 2001 From: Mario Santos <34552881+SantosGuillamot@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:00:12 +0200 Subject: [PATCH] Block bindings: allow only string field types in bindings. (#66174) * Filter fields by type in post meta * Add e2e test * Allow only strings * Update test to expect numbers and integers to be hidden * Update tests to check for label instead of key * Adapt object custom field * Adapt e2e tests * Add `type` prop to `getFieldsList` * Adapt testing source * Reduce diff * Add `attribute` to dependencies --------- Co-authored-by: SantosGuillamot Co-authored-by: cbravobernal Co-authored-by: artemiomorales Co-authored-by: gziolo --- .../block-editor/src/hooks/block-bindings.js | 63 +++++++++----- packages/e2e-tests/plugins/block-bindings.php | 86 +++++++++++++++++++ packages/editor/src/bindings/post-meta.js | 1 + .../various/block-bindings/post-meta.spec.js | 49 +++++++++++ 4 files changed, 176 insertions(+), 23 deletions(-) diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js index 5c002613831ce1..694c9c1c0bb3cc 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, attribute ] + ); 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/e2e-tests/plugins/block-bindings.php b/packages/e2e-tests/plugins/block-bindings.php index ffbe50ab3cc902..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', ), ); @@ -106,6 +109,89 @@ 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', + array( + 'label' => 'Object custom field', + 'show_in_rest' => array( + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'foo' => array( + 'type' => 'string', + ), + ), + ), + ), + 'single' => true, + 'type' => 'object', + ) + ); + 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/packages/editor/src/bindings/post-meta.js b/packages/editor/src/bindings/post-meta.js index 9198ac0fe41e1e..a3602ce7d62076 100644 --- a/packages/editor/src/bindings/post-meta.js +++ b/packages/editor/src/bindings/post-meta.js @@ -60,6 +60,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, }; } } ); 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..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 @@ -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: 'String custom field', + } ) + ).toBeVisible(); + await expect( + page.getByRole( 'menuitemradio', { + name: 'Number custom field', + } ) + ).toBeHidden(); + await expect( + page.getByRole( 'menuitemradio', { + name: 'Integer custom field', + } ) + ).toBeHidden(); + 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(); + } ); } ); } );