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(