From ad283c63c79e1188a8d27de1c2fff2d76e57b007 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 2 Oct 2024 11:11:41 +0200 Subject: [PATCH 1/3] Pass `clientId` to `useBlockBindingsUtils` --- packages/block-editor/README.md | 4 ++++ packages/block-editor/src/utils/block-bindings.js | 15 +++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index a6f3b8980cfd7a..36f3dc0ba73fa1 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 2deeb959371742..dcf80d985473b2 100644 --- a/packages/block-editor/src/utils/block-bindings.js +++ b/packages/block-editor/src/utils/block-bindings.js @@ -32,6 +32,8 @@ function isObjectEmpty( object ) { * * @since 6.7.0 Introduced in WordPress core. * + * @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 @@ -62,8 +64,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 ); @@ -98,7 +101,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 ] ) => { @@ -118,7 +121,7 @@ export function useBlockBindingsUtils() { delete newMetadata.bindings; } - updateBlockAttributes( clientId, { + updateBlockAttributes( blockClientId, { metadata: isObjectEmpty( newMetadata ) ? undefined : newMetadata, } ); }; @@ -136,8 +139,8 @@ export function useBlockBindingsUtils() { */ const removeAllBlockBindings = () => { const { metadata: { bindings, ...metadata } = {} } = - getBlockAttributes( clientId ); - updateBlockAttributes( clientId, { + getBlockAttributes( blockClientId ); + updateBlockAttributes( blockClientId, { metadata: isObjectEmpty( metadata ) ? undefined : metadata, } ); }; From 23ac64b3bc586032f19259ab57ccb5e57c2e6d66 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 2 Oct 2024 11:12:03 +0200 Subject: [PATCH 2/3] Add unit tests for `useBlockBindingsUtils` --- .../utils/test/use-block-bindings-utils.js | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 packages/block-editor/src/utils/test/use-block-bindings-utils.js 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 00000000000000..da3aab8b42898a --- /dev/null +++ b/packages/block-editor/src/utils/test/use-block-bindings-utils.js @@ -0,0 +1,176 @@ +/** + * 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 ).insertBlocks( block ); + clientId = block.clientId; + } ); + + // Clean up after each test by removing all blocks + afterEach( () => { + dispatch( blockEditorStore ).resetBlocks( [] ); + } ); + + afterAll( () => { + // 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', + } ); + } ); +} ); From e52bab20a76c2b6426e691ad70fa1a98d85cf77d Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Wed, 9 Oct 2024 16:07:04 +0200 Subject: [PATCH 3/3] Use `resetBlocks` instead of `insertBlocks` in unit test --- .../src/utils/test/use-block-bindings-utils.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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 index da3aab8b42898a..e5e26bd24c20d1 100644 --- a/packages/block-editor/src/utils/test/use-block-bindings-utils.js +++ b/packages/block-editor/src/utils/test/use-block-bindings-utils.js @@ -47,16 +47,14 @@ describe( 'useBlockBindingsUtils', () => { }, }, } ); - await dispatch( blockEditorStore ).insertBlocks( block ); + await dispatch( blockEditorStore ).resetBlocks( [ block ] ); clientId = block.clientId; } ); - // Clean up after each test by removing all blocks - afterEach( () => { + afterAll( () => { + // Remove blocks after all tests. dispatch( blockEditorStore ).resetBlocks( [] ); - } ); - afterAll( () => { // Clean up registered blocks getBlockTypes().forEach( ( block ) => { unregisterBlockType( block.name );