diff --git a/packages/editor/src/components/document-outline/index.js b/packages/editor/src/components/document-outline/index.js
index 6c498ccc79990..351d11f6d19c8 100644
--- a/packages/editor/src/components/document-outline/index.js
+++ b/packages/editor/src/components/document-outline/index.js
@@ -1,3 +1,8 @@
+/**
+ * External dependencies
+ */
+import clsx from 'clsx';
+
/**
* WordPress dependencies
*/
@@ -73,25 +78,55 @@ function EmptyOutlineIllustration() {
);
}
+const incorrectMainTag = [
+
,
+
+ { __(
+ '(Your template should have exactly one main element. Adjust the HTML element in the Advanced panel of the block.)'
+ ) }
+ ,
+];
+
/**
- * Returns an array of heading blocks enhanced with the following properties:
- * level - An integer with the heading level.
- * isEmpty - Flag indicating if the heading has no content.
+ * Returns an array of heading blocks and blocks with the main tagName.
*
- * @param {?Array} blocks An array of blocks.
+ * @param {?Array} blocks An array of blocks.
+ * @param {boolean} isShowingTemplate Indicates if a template is being edited or previewed.
*
- * @return {Array} An array of heading blocks enhanced with the properties described above.
+ * @return {Array} An array of heading blocks and blocks with the main tagName.
*/
-const computeOutlineHeadings = ( blocks = [] ) => {
+const computeOutlineElements = ( blocks = [], isShowingTemplate = false ) => {
return blocks.flatMap( ( block = {} ) => {
- if ( block.name === 'core/heading' ) {
+ const isHeading = block.name === 'core/heading';
+ const isMain =
+ isShowingTemplate && block.attributes?.tagName === 'main';
+
+ if ( isHeading ) {
return {
...block,
+ type: 'heading',
level: block.attributes.level,
isEmpty: isEmptyHeading( block ),
};
}
- return computeOutlineHeadings( block.innerBlocks );
+
+ if ( isMain ) {
+ const children = computeOutlineElements(
+ block.innerBlocks || [],
+ isShowingTemplate
+ );
+ return [
+ {
+ ...block,
+ type: 'main',
+ children,
+ },
+ // Flatten the children so that they are not nested under the main.
+ ...children,
+ ];
+ }
+
+ return computeOutlineElements( block.innerBlocks, isShowingTemplate );
} );
};
@@ -113,104 +148,145 @@ export default function DocumentOutline( {
hasOutlineItemsDisabled,
} ) {
const { selectBlock } = useDispatch( blockEditorStore );
- const { blocks, title, isTitleSupported } = 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 { blocks, title, isTitleSupported, isShowingTemplate } = useSelect(
+ ( select ) => {
+ const { getBlocks } = select( blockEditorStore );
+ const { getEditedPostAttribute, getRenderingMode } =
+ select( editorStore );
+ const { getPostType } = select( coreStore );
+ const postType = getPostType( getEditedPostAttribute( 'type' ) );
+ return {
+ title: getEditedPostAttribute( 'title' ),
+ blocks: getBlocks(),
+ isTitleSupported: postType?.supports?.title ?? false,
+ isShowingTemplate:
+ postType?.slug === 'wp_template' ||
+ getRenderingMode() === 'template-locked',
+ };
+ }
+ );
const prevHeadingLevelRef = useRef( 1 );
+ const outlineElements = computeOutlineElements( blocks, isShowingTemplate );
+ const headings = outlineElements.filter(
+ ( item ) => item.type === 'heading'
+ );
+ const mainElements = outlineElements.filter(
+ ( item ) => item.type === 'main'
+ );
+ const headingsByLevel = headings.reduce( ( acc, heading ) => {
+ acc[ heading.level ] = ( acc[ heading.level ] || 0 ) + 1;
+ return acc;
+ }, {} );
+ const hasMultipleH1 = headingsByLevel[ 1 ] > 1;
+
+ /**
+ * TODO: Update hasTitle to work with the iframe in the block editor.
+ * The titleNode is not found with the querySelector when the editor is iframed.
+ * See https://github.com/WordPress/gutenberg/issues/47624
+ */
+ const titleNode = document.querySelector( '.editor-post-title__input' );
+ const hasTitle = isTitleSupported && title && titleNode;
- const headings = computeOutlineHeadings( blocks );
- if ( headings.length < 1 ) {
- return (
-
+ { __( + 'Navigate the structure of your document and address issues like empty or incorrect heading levels.' + ) } +
+ > + ) } + { isShowingTemplate && mainElements.length === 0 && ({ __( - 'Navigate the structure of your document and address issues like empty or incorrect heading levels.' + 'The main element is missing. Select the block that contains your most important content and add the main HTML element in the Advanced panel.' ) }
-