-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add heading hierarchy checker notice #14889
Closed
Closed
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
ec509c3
Add headings hierarchy checker notice
Jackie6 3d28ae5
Remove duplicate function and use import instead
Jackie6 df75f91
Change the way to import computeOutlineHeadings
Jackie6 e997892
Remove selectedLevel passed to HeadingChecker
Jackie6 c2dcb6b
Rename HeadingChecker
Jackie6 3fa3845
Add checking for duplicate headings
Jackie6 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { countBy, flatMap, get } from 'lodash'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { speak } from '@wordpress/a11y'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { Notice } from '@wordpress/components'; | ||
import { useEffect } from '@wordpress/element'; | ||
import { compose } from '@wordpress/compose'; | ||
import { withSelect } from '@wordpress/data'; | ||
|
||
// copy from packages/editor/src/components/document-outline/index.js | ||
/** | ||
* Returns an array of heading blocks enhanced with the following properties: | ||
* path - An array of blocks that are ancestors of the heading starting from a top-level node. | ||
* Can be an empty array if the heading is a top-level node (is not nested inside another block). | ||
* level - An integer with the heading level. | ||
* isEmpty - Flag indicating if the heading has no content. | ||
* | ||
* @param {?Array} blocks An array of blocks. | ||
* @param {?Array} path An array of blocks that are ancestors of the blocks passed as blocks. | ||
* | ||
* @return {Array} An array of heading blocks enhanced with the properties described above. | ||
*/ | ||
export const computeOutlineHeadings = ( blocks = [], path = [] ) => { | ||
return flatMap( blocks, ( block = {} ) => { | ||
if ( block.name === 'core/heading' ) { | ||
return { | ||
...block, | ||
path, | ||
level: block.attributes.level, | ||
}; | ||
} | ||
return computeOutlineHeadings( block.innerBlocks, [ ...path, block ] ); | ||
} ); | ||
}; | ||
|
||
export const HeadingLevelChecker = ( { blocks = [], title, isTitleSupported, selectedHeadingId } ) => { | ||
const headings = computeOutlineHeadings( blocks ); | ||
|
||
// Iterate headings to find prevHeadingLevel and selectedLevel | ||
let prevHeadingLevel = 1; | ||
let selectedLevel = 1; | ||
let i = 0; | ||
for ( i = 0; i < headings.length; i++ ) { | ||
if ( headings[ i ].clientId === selectedHeadingId ) { | ||
selectedLevel = headings[ i ].level; | ||
if ( i >= 1 ) { | ||
prevHeadingLevel = headings[ i - 1 ].level; | ||
} | ||
} | ||
} | ||
|
||
const titleNode = document.querySelector( '.editor-post-title__input' ); | ||
const hasTitle = isTitleSupported && title && titleNode; | ||
const countByLevel = countBy( headings, 'level' ); | ||
const hasMultipleH1 = countByLevel[ 1 ] > 1; | ||
const isIncorrectLevel = selectedLevel > prevHeadingLevel + 1; | ||
|
||
let msg = ''; | ||
if ( isIncorrectLevel ) { | ||
msg = __( 'This heading level is incorrect.' ); | ||
} else if ( selectedLevel === 1 && hasMultipleH1 ) { | ||
msg = __( 'Multiple H1 headings found.' ); | ||
} else if ( selectedLevel === 1 && hasTitle && ! hasMultipleH1 ) { | ||
msg = __( 'H1 is already used for the post title.' ); | ||
} else { | ||
return null; | ||
} | ||
|
||
// For accessibility | ||
useEffect( () => { | ||
speak( msg ); | ||
}, [ selectedLevel ] ); | ||
|
||
return ( | ||
<div className="block-library-heading__heading-level-checker"> | ||
<Notice status="warning" isDismissible={ false }> | ||
{ msg } | ||
</Notice> | ||
</div> | ||
); | ||
}; | ||
|
||
export default compose( | ||
withSelect( ( select ) => { | ||
const { getBlocks } = select( 'core/block-editor' ); | ||
const { getEditedPostAttribute } = select( 'core/editor' ); | ||
const { getPostType } = select( 'core' ); | ||
const postType = getPostType( getEditedPostAttribute( 'type' ) ); | ||
|
||
return { | ||
blocks: getBlocks(), | ||
title: getEditedPostAttribute( 'title' ), | ||
isTitleSupported: get( postType, [ 'supports', 'title' ], false ), | ||
}; | ||
} ) | ||
)( HeadingLevelChecker ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,6 @@ | |
.blockText { | ||
min-height: $min-height-heading; | ||
} | ||
.block-library-heading__heading-level-checker { | ||
margin: 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -48,7 +48,7 @@ const multipleH1Headings = [ | |||||
* | ||||||
* @return {Array} An array of heading blocks enhanced with the properties described above. | ||||||
*/ | ||||||
const computeOutlineHeadings = ( blocks = [], path = [] ) => { | ||||||
export const computeOutlineHeadings = ( blocks = [], path = [] ) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It no longer should be exported:
Suggested change
|
||||||
return flatMap( blocks, ( block = {} ) => { | ||||||
if ( block.name === 'core/heading' ) { | ||||||
return { | ||||||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This style if really necessary should be applied to
style.scss
file which web version uses. This file is used by React Native app.