From 373adccd3a00498c90514b61b7086c18e7d779d2 Mon Sep 17 00:00:00 2001 From: Mario Santos <34552881+SantosGuillamot@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:43:16 +0200 Subject: [PATCH] Block Bindings: Accept client ID as parameter for `useBlockBindingsUtils` (#65818) * Pass `clientId` to `useBlockBindingsUtils` * Add unit tests for `useBlockBindingsUtils` * Use `resetBlocks` instead of `insertBlocks` in unit test Co-authored-by: SantosGuillamot Co-authored-by: gziolo --- packages/block-editor/README.md | 4 + .../block-editor/src/utils/block-bindings.js | 15 +- .../utils/test/use-block-bindings-utils.js | 174 ++++++++++++++++++ 3 files changed, 187 insertions(+), 6 deletions(-) create mode 100644 packages/block-editor/src/utils/test/use-block-bindings-utils.js diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 32ba4598c4f92..02beaf47c61e0 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -877,6 +877,10 @@ updateBlockBindings( { url: undefined } ); removeAllBlockBindings(); ``` +_Parameters_ + +- _clientId_ `?string`: Optional block client ID. If not set, it will use the current block client ID from the context. + _Returns_ - `?WPBlockBindingsUtils`: Object containing the block bindings utils. diff --git a/packages/block-editor/src/utils/block-bindings.js b/packages/block-editor/src/utils/block-bindings.js index db4de9d39cb25..ed59115a7fdbb 100644 --- a/packages/block-editor/src/utils/block-bindings.js +++ b/packages/block-editor/src/utils/block-bindings.js @@ -30,6 +30,8 @@ function isObjectEmpty( object ) { * - `updateBlockBindings`: Updates the value of the bindings connected to block attributes. It can be used to remove a specific binding by setting the value to `undefined`. * - `removeAllBlockBindings`: Removes the bindings property of the `metadata` attribute. * + * @param {?string} clientId Optional block client ID. If not set, it will use the current block client ID from the context. + * * @return {?WPBlockBindingsUtils} Object containing the block bindings utils. * * @example @@ -60,8 +62,9 @@ function isObjectEmpty( object ) { * removeAllBlockBindings(); * ``` */ -export function useBlockBindingsUtils() { - const { clientId } = useBlockEditContext(); +export function useBlockBindingsUtils( clientId ) { + const { clientId: contextClientId } = useBlockEditContext(); + const blockClientId = clientId || contextClientId; const { updateBlockAttributes } = useDispatch( blockEditorStore ); const { getBlockAttributes } = useRegistry().select( blockEditorStore ); @@ -96,7 +99,7 @@ export function useBlockBindingsUtils() { */ const updateBlockBindings = ( bindings ) => { const { metadata: { bindings: currentBindings, ...metadata } = {} } = - getBlockAttributes( clientId ); + getBlockAttributes( blockClientId ); const newBindings = { ...currentBindings }; Object.entries( bindings ).forEach( ( [ attribute, binding ] ) => { @@ -116,7 +119,7 @@ export function useBlockBindingsUtils() { delete newMetadata.bindings; } - updateBlockAttributes( clientId, { + updateBlockAttributes( blockClientId, { metadata: isObjectEmpty( newMetadata ) ? undefined : newMetadata, } ); }; @@ -134,8 +137,8 @@ export function useBlockBindingsUtils() { */ const removeAllBlockBindings = () => { const { metadata: { bindings, ...metadata } = {} } = - getBlockAttributes( clientId ); - updateBlockAttributes( clientId, { + getBlockAttributes( blockClientId ); + updateBlockAttributes( blockClientId, { metadata: isObjectEmpty( metadata ) ? undefined : metadata, } ); }; diff --git a/packages/block-editor/src/utils/test/use-block-bindings-utils.js b/packages/block-editor/src/utils/test/use-block-bindings-utils.js new file mode 100644 index 0000000000000..e5e26bd24c20d --- /dev/null +++ b/packages/block-editor/src/utils/test/use-block-bindings-utils.js @@ -0,0 +1,174 @@ +/** + * External dependencies + */ +import { renderHook } from '@testing-library/react'; + +/** + * WordPress dependencies + */ +import { store as blockEditorStore } from '@wordpress/block-editor'; +import { dispatch, select } from '@wordpress/data'; +import { + createBlock, + getBlockTypes, + unregisterBlockType, +} from '@wordpress/blocks'; +import { registerCoreBlocks } from '@wordpress/block-library'; + +/** + * Internal dependencies + */ +import { useBlockBindingsUtils } from '../'; + +describe( 'useBlockBindingsUtils', () => { + beforeAll( () => { + // Register all core blocks + registerCoreBlocks(); + } ); + + let clientId; + beforeEach( async () => { + const block = createBlock( 'core/paragraph', { + metadata: { + name: 'Block name', + bindings: { + prop1: { + source: 'core/post-meta', + args: { + key: 'initial_key', + }, + }, + prop2: { + source: 'core/post-meta', + args: { + key: 'initial_key', + }, + }, + }, + }, + } ); + await dispatch( blockEditorStore ).resetBlocks( [ block ] ); + clientId = block.clientId; + } ); + + afterAll( () => { + // Remove blocks after all tests. + dispatch( blockEditorStore ).resetBlocks( [] ); + + // Clean up registered blocks + getBlockTypes().forEach( ( block ) => { + unregisterBlockType( block.name ); + } ); + } ); + + it( 'should be possible to update just one connection', async () => { + renderHook( () => { + const { updateBlockBindings } = useBlockBindingsUtils( clientId ); + updateBlockBindings( { + prop1: { + source: 'core/post-meta', + args: { + key: 'new_key', + }, + }, + } ); + } ); + const { metadata } = + await select( blockEditorStore ).getBlockAttributes( clientId ); + expect( metadata ).toMatchObject( { + // Other metadata properties shouldn't change. + name: 'Block name', + bindings: { + prop1: { + source: 'core/post-meta', + args: { + key: 'new_key', + }, + }, + prop2: { + source: 'core/post-meta', + args: { + key: 'initial_key', + }, + }, + }, + } ); + } ); + + it( 'should be possible to update multiple connections at once', async () => { + renderHook( () => { + const { updateBlockBindings } = useBlockBindingsUtils( clientId ); + updateBlockBindings( { + prop1: { + source: 'core/post-meta', + args: { + key: 'new_key', + }, + }, + prop2: { + source: 'core/post-meta', + args: { + key: 'new_key', + }, + }, + } ); + } ); + const { metadata } = + await select( blockEditorStore ).getBlockAttributes( clientId ); + expect( metadata ).toMatchObject( { + // Other metadata properties shouldn't change. + name: 'Block name', + bindings: { + prop1: { + source: 'core/post-meta', + args: { + key: 'new_key', + }, + }, + prop2: { + source: 'core/post-meta', + args: { + key: 'new_key', + }, + }, + }, + } ); + } ); + + it( 'should be possible to remove connections', async () => { + renderHook( () => { + const { updateBlockBindings } = useBlockBindingsUtils( clientId ); + updateBlockBindings( { + prop2: undefined, + } ); + } ); + const { metadata } = + await select( blockEditorStore ).getBlockAttributes( clientId ); + expect( metadata ).toMatchObject( { + // Other metadata properties shouldn't change. + name: 'Block name', + bindings: { + prop1: { + source: 'core/post-meta', + args: { + key: 'initial_key', + }, + }, + }, + } ); + } ); + + it( 'should be possible to remove all connections', async () => { + renderHook( () => { + const { removeAllBlockBindings } = + useBlockBindingsUtils( clientId ); + removeAllBlockBindings(); + } ); + const { metadata } = + await select( blockEditorStore ).getBlockAttributes( clientId ); + expect( metadata ).toMatchObject( { + // Other metadata properties shouldn't change. + name: 'Block name', + } ); + } ); +} );