diff --git a/packages/block-editor/src/components/off-canvas-editor/appender.js b/packages/block-editor/src/components/off-canvas-editor/appender.js index 0fb19df664f016..5571db2816382d 100644 --- a/packages/block-editor/src/components/off-canvas-editor/appender.js +++ b/packages/block-editor/src/components/off-canvas-editor/appender.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; import { forwardRef, useState } from '@wordpress/element'; /** * Internal dependencies @@ -10,6 +10,7 @@ import { store as blockEditorStore } from '../../store'; import Inserter from '../inserter'; import { LinkUI } from './link-ui'; import { updateAttributes } from './update-attributes'; +import { useInsertedBlock } from './use-inserted-block'; const BLOCKS_WITH_LINK_UI_SUPPORT = [ 'core/navigation-link', @@ -36,27 +37,11 @@ export const Appender = forwardRef( ( props, ref ) => { }; }, [] ); - const { insertedBlockAttributes, insertedBlockName } = useSelect( - ( select ) => { - const { getBlockName, getBlockAttributes } = - select( blockEditorStore ); - - return { - insertedBlockAttributes: getBlockAttributes( - insertedBlockClientId - ), - insertedBlockName: getBlockName( insertedBlockClientId ), - }; - }, - [ insertedBlockClientId ] - ); - - const { updateBlockAttributes } = useDispatch( blockEditorStore ); - - const setAttributes = - ( _insertedBlockClientId ) => ( _updatedAttributes ) => { - updateBlockAttributes( _insertedBlockClientId, _updatedAttributes ); - }; + const { + insertedBlockAttributes, + insertedBlockName, + setInsertedBlockAttributes, + } = useInsertedBlock( insertedBlockClientId ); const maybeSetInsertedBlockOnInsertion = ( _insertedBlock ) => { if ( ! _insertedBlock?.clientId ) { @@ -81,7 +66,7 @@ export const Appender = forwardRef( ( props, ref ) => { onChange={ ( updatedValue ) => { updateAttributes( updatedValue, - setAttributes( insertedBlockClientId ), + setInsertedBlockAttributes, insertedBlockAttributes ); setInsertedBlockClientId( null ); diff --git a/packages/block-editor/src/components/off-canvas-editor/test/use-inserted-block.js b/packages/block-editor/src/components/off-canvas-editor/test/use-inserted-block.js new file mode 100644 index 00000000000000..f4e6746581e008 --- /dev/null +++ b/packages/block-editor/src/components/off-canvas-editor/test/use-inserted-block.js @@ -0,0 +1,108 @@ +/** + * Internal dependencies + */ +import { useInsertedBlock } from '../use-inserted-block'; + +/** + * WordPress dependencies + */ +import { useDispatch, useSelect } from '@wordpress/data'; + +/** + * External dependencies + */ +import { act, renderHook } from '@testing-library/react'; + +jest.mock( '@wordpress/data/src/components/use-select', () => { + // This allows us to tweak the returned value on each test. + const mock = jest.fn(); + return mock; +} ); + +jest.mock( '@wordpress/data/src/components/use-dispatch', () => ( { + useDispatch: jest.fn(), +} ) ); + +describe( 'useInsertedBlock', () => { + const mockUpdateBlockAttributes = jest.fn(); + + it( 'returns undefined values when called without a block clientId', () => { + useSelect.mockImplementation( () => ( { + insertedBlockAttributes: { + 'some-attribute': 'some-value', + }, + insertedBlockName: 'core/navigation-link', + } ) ); + + useDispatch.mockImplementation( () => ( { + updateBlockAttributes: mockUpdateBlockAttributes, + } ) ); + + const { result } = renderHook( () => useInsertedBlock() ); + + const { + insertedBlockName, + insertedBlockAttributes, + setInsertedBlockAttributes, + } = result.current; + + expect( insertedBlockName ).toBeUndefined(); + expect( insertedBlockAttributes ).toBeUndefined(); + expect( + setInsertedBlockAttributes( { 'some-attribute': 'new-value' } ) + ).toBeUndefined(); + } ); + + it( 'returns name and attributes when called with a block clientId', () => { + useSelect.mockImplementation( () => ( { + insertedBlockAttributes: { + 'some-attribute': 'some-value', + }, + insertedBlockName: 'core/navigation-link', + } ) ); + + useDispatch.mockImplementation( () => ( { + updateBlockAttributes: mockUpdateBlockAttributes, + } ) ); + + const { result } = renderHook( () => + useInsertedBlock( 'some-client-id-here' ) + ); + + const { insertedBlockName, insertedBlockAttributes } = result.current; + + expect( insertedBlockName ).toBe( 'core/navigation-link' ); + expect( insertedBlockAttributes ).toEqual( { + 'some-attribute': 'some-value', + } ); + } ); + + it( 'dispatches updateBlockAttributes on provided client ID with new attributes when setInsertedBlockAttributes is called', () => { + useSelect.mockImplementation( () => ( { + insertedBlockAttributes: { + 'some-attribute': 'some-value', + }, + insertedBlockName: 'core/navigation-link', + } ) ); + + useDispatch.mockImplementation( () => ( { + updateBlockAttributes: mockUpdateBlockAttributes, + } ) ); + + const clientId = '123456789'; + + const { result } = renderHook( () => useInsertedBlock( clientId ) ); + + const { setInsertedBlockAttributes } = result.current; + + act( () => { + setInsertedBlockAttributes( { + 'some-attribute': 'new-value', + } ); + } ); + + expect( mockUpdateBlockAttributes ).toHaveBeenCalledWith( clientId, { + 'some-attribute': 'new-value', + } ); + } ); +} ); diff --git a/packages/block-editor/src/components/off-canvas-editor/use-inserted-block.js b/packages/block-editor/src/components/off-canvas-editor/use-inserted-block.js new file mode 100644 index 00000000000000..0e5a25c980a1c3 --- /dev/null +++ b/packages/block-editor/src/components/off-canvas-editor/use-inserted-block.js @@ -0,0 +1,47 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../../store'; + +export const useInsertedBlock = ( insertedBlockClientId ) => { + const { insertedBlockAttributes, insertedBlockName } = useSelect( + ( select ) => { + const { getBlockName, getBlockAttributes } = + select( blockEditorStore ); + + return { + insertedBlockAttributes: getBlockAttributes( + insertedBlockClientId + ), + insertedBlockName: getBlockName( insertedBlockClientId ), + }; + }, + [ insertedBlockClientId ] + ); + + const { updateBlockAttributes } = useDispatch( blockEditorStore ); + + const setInsertedBlockAttributes = ( _updatedAttributes ) => { + if ( ! insertedBlockClientId ) return; + updateBlockAttributes( insertedBlockClientId, _updatedAttributes ); + }; + + if ( ! insertedBlockClientId ) { + return { + insertedBlockAttributes: undefined, + insertedBlockName: undefined, + setInsertedBlockAttributes, + }; + } + + return { + insertedBlockAttributes, + insertedBlockName, + setInsertedBlockAttributes, + }; +};