diff --git a/packages/editor/src/components/document-outline/check.js b/packages/editor/src/components/document-outline/check.js index 0ae0b3435e1e46..9338ac6d5cf419 100644 --- a/packages/editor/src/components/document-outline/check.js +++ b/packages/editor/src/components/document-outline/check.js @@ -1,21 +1,19 @@ /** * WordPress dependencies */ -import { withSelect } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; import { store as blockEditorStore } from '@wordpress/block-editor'; -function DocumentOutlineCheck( { blocks, children } ) { - const headings = blocks.filter( - ( block ) => block.name === 'core/heading' - ); +export default function DocumentOutlineCheck( { children } ) { + const hasHeadings = useSelect( ( select ) => { + const { getGlobalBlockCount } = select( blockEditorStore ); - if ( headings.length < 1 ) { + return getGlobalBlockCount( 'core/heading' ) > 0; + } ); + + if ( hasHeadings ) { return null; } return children; } - -export default withSelect( ( select ) => ( { - blocks: select( blockEditorStore ).getBlocks(), -} ) )( DocumentOutlineCheck ); diff --git a/packages/editor/src/components/document-outline/index.js b/packages/editor/src/components/document-outline/index.js index bbddf220115406..20410959a17210 100644 --- a/packages/editor/src/components/document-outline/index.js +++ b/packages/editor/src/components/document-outline/index.js @@ -2,8 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { compose } from '@wordpress/compose'; -import { withSelect, useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { create, getTextContent } from '@wordpress/rich-text'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { store as coreStore } from '@wordpress/core-data'; @@ -98,15 +97,26 @@ const computeOutlineHeadings = ( blocks = [] ) => { const isEmptyHeading = ( heading ) => ! heading.attributes.content || heading.attributes.content.length === 0; -export const DocumentOutline = ( { - blocks = [], - title, +export default function DocumentOutline( { onSelect, isTitleSupported, hasOutlineItemsDisabled, -} ) => { - const headings = computeOutlineHeadings( blocks ); +} ) { const { selectBlock } = useDispatch( blockEditorStore ); + const { blocks, title } = useSelect( ( select ) => { + const { getBlocks } = select( blockEditorStore ); + const { getEditedPostAttribute } = select( editorStore ); + const { getPostType } = select( coreStore ); + const postType = getPostType( getEditedPostAttribute( 'type' ) ); + + return { + title: getEditedPostAttribute( 'title' ), + blocks: getBlocks(), + isTitleSupported: postType?.supports?.title ?? false, + }; + } ); + + const headings = computeOutlineHeadings( blocks ); if ( headings.length < 1 ) { return (
@@ -194,19 +204,4 @@ export const DocumentOutline = ( {
); -}; - -export default compose( - withSelect( ( select ) => { - const { getBlocks } = select( blockEditorStore ); - const { getEditedPostAttribute } = select( editorStore ); - const { getPostType } = select( coreStore ); - const postType = getPostType( getEditedPostAttribute( 'type' ) ); - - return { - title: getEditedPostAttribute( 'title' ), - blocks: getBlocks(), - isTitleSupported: postType?.supports?.title ?? false, - }; - } ) -)( DocumentOutline ); +} diff --git a/packages/editor/src/components/document-outline/test/index.js b/packages/editor/src/components/document-outline/test/index.js index 21c258c9a65df7..b396d7cc78349f 100644 --- a/packages/editor/src/components/document-outline/test/index.js +++ b/packages/editor/src/components/document-outline/test/index.js @@ -11,15 +11,27 @@ import { registerBlockType, unregisterBlockType, } from '@wordpress/blocks'; +import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ -import { DocumentOutline } from '../'; +import DocumentOutline from '../'; jest.mock( '@wordpress/block-editor', () => ( { BlockTitle: () => 'Block Title', } ) ); +jest.mock( '@wordpress/data/src/components/use-select', () => jest.fn() ); + +function setupMockSelect( blocks ) { + useSelect.mockImplementation( ( mapSelect ) => { + return mapSelect( () => ( { + getBlocks: () => blocks, + getEditedPostAttribute: () => null, + getPostType: () => null, + } ) ); + } ); +} describe( 'DocumentOutline', () => { let paragraph, headingH1, headingH2, headingH3, nestedHeading; @@ -77,6 +89,7 @@ describe( 'DocumentOutline', () => { describe( 'no header blocks present', () => { it( 'should not render when no blocks provided', () => { + setupMockSelect( [] ); render( ); expect( screen.queryByRole( 'list' ) ).not.toBeInTheDocument(); @@ -87,7 +100,8 @@ describe( 'DocumentOutline', () => { // Set client IDs to a predictable value. return { ...block, clientId: `clientId_${ index }` }; } ); - render( ); + setupMockSelect( blocks ); + render( ); expect( screen.queryByRole( 'list' ) ).not.toBeInTheDocument(); } ); @@ -99,14 +113,16 @@ describe( 'DocumentOutline', () => { // Set client IDs to a predictable value. return { ...block, clientId: `clientId_${ index }` }; } ); - render( ); + setupMockSelect( blocks ); + render( ); expect( screen.getByRole( 'list' ) ).toMatchSnapshot(); } ); it( 'should render an item when only one heading provided', () => { const blocks = [ headingH2 ]; - render( ); + setupMockSelect( blocks ); + render( ); const tableOfContentItem = within( screen.getByRole( 'list' ) @@ -123,7 +139,8 @@ describe( 'DocumentOutline', () => { headingH3, paragraph, ]; - render( ); + setupMockSelect( blocks ); + render( ); expect( within( screen.getByRole( 'list' ) ).getAllByRole( 'listitem' ) @@ -137,7 +154,8 @@ describe( 'DocumentOutline', () => { return { ...block, clientId: `clientId_${ index }` }; } ); - render( ); + setupMockSelect( blocks ); + render( ); expect( screen.getByRole( 'list' ) ).toMatchSnapshot(); } ); @@ -146,7 +164,8 @@ describe( 'DocumentOutline', () => { describe( 'nested headings', () => { it( 'should render even if the heading is nested', () => { const blocks = [ headingH2, nestedHeading ]; - render( ); + setupMockSelect( blocks ); + render( ); // Unnested heading and nested heading should appear as items. const tableOfContentItems = within(