diff --git a/packages/block-editor/src/components/use-setting/index.js b/packages/block-editor/src/components/use-setting/index.js index 630909f48738c..add0b7ff6e08d 100644 --- a/packages/block-editor/src/components/use-setting/index.js +++ b/packages/block-editor/src/components/use-setting/index.js @@ -11,6 +11,7 @@ import { __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE, hasBlockSupport, } from '@wordpress/blocks'; +import { applyFilters } from '@wordpress/hooks'; /** * Internal dependencies @@ -122,7 +123,18 @@ export default function useSetting( path ) { return undefined; } - let result; + // 0. Allow third parties to filter the block's settings at runtime. + let result = applyFilters( + 'blockEditor.useSetting.before', + undefined, + path, + clientId, + blockName + ); + + if ( undefined !== result ) { + return result; + } const normalizedPath = removeCustomPrefixes( path ); diff --git a/packages/block-editor/src/components/use-setting/test/index.js b/packages/block-editor/src/components/use-setting/test/index.js new file mode 100644 index 0000000000000..1c625c8a1969c --- /dev/null +++ b/packages/block-editor/src/components/use-setting/test/index.js @@ -0,0 +1,99 @@ +/** + * WordPress dependencies + */ +import { addFilter, removeFilter } from '@wordpress/hooks'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import useSetting from '..'; +import * as BlockEditContext from '../../block-edit/context'; + +// Mock useSelect() functions used by useSetting() +jest.mock( '@wordpress/data/src/components/use-select' ); + +let selectMock = {}; +const setupSelectMock = () => { + selectMock = { + getSettings: () => ( {} ), + getBlockParents: () => [], + getBlockName: () => '', + }; +}; + +useSelect.mockImplementation( ( callback ) => callback( () => selectMock ) ); + +const mockSettings = ( settings ) => { + selectMock.getSettings = () => ( { + __experimentalFeatures: settings, + } ); +}; + +const mockCurrentBlockContext = ( + blockContext = { name: '', isSelected: false } +) => { + jest.spyOn( BlockEditContext, 'useBlockEditContext' ).mockReturnValue( + blockContext + ); +}; + +describe( 'useSetting', () => { + beforeEach( () => { + setupSelectMock(); + mockCurrentBlockContext(); + } ); + + it( 'uses block setting', () => { + mockSettings( { + blocks: { + 'core/test-block': { + layout: { + contentSize: '840px', + }, + }, + }, + } ); + + mockCurrentBlockContext( { + name: 'core/test-block', + } ); + + expect( useSetting( 'layout.contentSize' ) ).toBe( '840px' ); + } ); + + it( 'uses blockEditor.useSetting.before hook override', () => { + mockSettings( { + blocks: { + 'core/test-block': { + layout: { + contentSize: '840px', + }, + }, + }, + } ); + + mockCurrentBlockContext( { + name: 'core/test-block', + } ); + + addFilter( + 'blockEditor.useSetting.before', + 'test/useSetting.before', + ( result, path, clientId, blockName ) => { + if ( blockName === 'core/test-block' ) { + return '960px'; + } + + return result; + } + ); + + expect( useSetting( 'layout.contentSize' ) ).toBe( '960px' ); + + removeFilter( + 'blockEditor.useSetting.before', + 'test/useSetting.before' + ); + } ); +} );