diff --git a/assets/src/amp-story-editor-blocks.js b/assets/src/amp-story-editor-blocks.js index bfd71d423e9..7e33581c949 100644 --- a/assets/src/amp-story-editor-blocks.js +++ b/assets/src/amp-story-editor-blocks.js @@ -29,7 +29,7 @@ import { Shortcuts, } from './components'; import { ALLOWED_BLOCKS, ALLOWED_CHILD_BLOCKS } from './constants'; -import { maybeEnqueueFontStyle, setBlockParent, addAMPAttributes, addAMPExtraProps } from './helpers'; +import { maybeEnqueueFontStyle, setBlockParent, addAMPAttributes, addAMPExtraProps, getTagName } from './helpers'; import store from './stores/amp-story'; const { getBlockOrder, getBlock, getBlocksByClientId, getClientIdsWithDescendants, getBlockRootClientId } = select( 'core/editor' ); @@ -187,6 +187,33 @@ function maybeSetInitialPositioning( clientId ) { } } +/** + * Determines the HTML tag name that should be used for text blocks. + * + * This is based on the block's attributes, as well as the surrounding context. + * + * For example, there can only be one

tag on a page. + * Also, font size takes precedence over text length as it's a stronger signal for semantic meaning. + * + * @param {string} clientId Block ID. + */ +function maybeSetTagName( clientId ) { + const block = getBlock( clientId ); + + if ( ! block || 'amp/amp-story-text' !== block.name ) { + return; + } + + const siblings = getBlocksByClientId( getBlockOrder( clientId ) ).filter( ( { clientId: blockId } ) => blockId !== clientId ); + const canUseH1 = ! siblings.some( ( { attributes } ) => attributes.tagName === 'h1' ); + + const tagName = getTagName( block.attributes, canUseH1 ); + + if ( block.attributes.tagName !== tagName ) { + updateBlockAttributes( clientId, { tagName } ); + } +} + let blockOrder = getBlockOrder(); let allBlocksWithChildren = getClientIdsWithDescendants(); @@ -232,6 +259,7 @@ subscribe( () => { for ( const block of allBlocksWithChildren ) { maybeSetInitialPositioning( block ); + maybeSetTagName( block ); } allBlocksWithChildren = getClientIdsWithDescendants(); diff --git a/assets/src/blocks/amp-story-text/get-tag-name.js b/assets/src/blocks/amp-story-text/get-tag-name.js deleted file mode 100644 index b066f1e5325..00000000000 --- a/assets/src/blocks/amp-story-text/get-tag-name.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * WordPress dependencies - */ -import { count } from '@wordpress/wordcount'; -import { _x } from '@wordpress/i18n'; - -// Todo: Make these customizable? -const H1_FONT_SIZE = 40; -const H2_FONT_SIZE = 24; -const H1_TEXT_LENGTH = 4; -const H2_TEXT_LENGTH = 10; - -/* - * translators: If your word count is based on single characters (e.g. East Asian characters), - * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'. - * Do not translate into your own language. - */ -const wordCountType = _x( 'words', 'Word count type. Do not translate!', 'amp' ); - -/** - * Determines the HTML tag name that should be used given on the block's attributes. - * - * Font size takes precedence over text length as it's a stronger signal for semantic meaning. - * - * @param {Object} attributes Block attributes. - * @return {string} HTML tag name. Either p, h1, or h2. - */ -export default function( attributes ) { - const { type, fontSize, customFontSize } = attributes; - - if ( -1 !== [ 'h1', 'h2', 'p' ].indexOf( type ) ) { - return type; - } - - if ( 'huge' === fontSize || ( customFontSize && customFontSize > H1_FONT_SIZE ) ) { - return 'h1'; - } - - if ( 'large' === fontSize || ( customFontSize && customFontSize > H2_FONT_SIZE ) ) { - return 'h2'; - } - - const textLength = count( attributes.content, wordCountType, {} ); - - if ( H1_TEXT_LENGTH >= textLength ) { - return 'h1'; - } - - if ( H2_TEXT_LENGTH >= textLength ) { - return 'h2'; - } - - return 'p'; -} diff --git a/assets/src/blocks/amp-story-text/index.js b/assets/src/blocks/amp-story-text/index.js index e20098e0f3f..7c3398f02aa 100644 --- a/assets/src/blocks/amp-story-text/index.js +++ b/assets/src/blocks/amp-story-text/index.js @@ -18,7 +18,6 @@ import { registerBlockType } from '@wordpress/blocks'; * Internal dependencies */ import edit from './edit'; -import getTagName from './get-tag-name'; export const name = 'amp/amp-story-text'; @@ -41,6 +40,10 @@ const schema = { type: 'string', default: 'auto', }, + tagName: { + type: 'string', + default: 'p', + }, fontSize: { type: 'string', }, @@ -94,14 +97,13 @@ export const settings = { textColor, customBackgroundColor, customTextColor, + tagName, } = attributes; const textClass = getColorClassName( 'color', textColor ); const backgroundClass = getColorClassName( 'background-color', backgroundColor ); const fontSizeClass = getFontSizeClass( fontSize ); - const tagName = getTagName( attributes ); - const className = classnames( { 'has-text-color': textColor || customTextColor, 'has-background': backgroundColor || customBackgroundColor, diff --git a/assets/src/helpers.js b/assets/src/helpers.js index a0aecc334b5..be1d3572cd9 100644 --- a/assets/src/helpers.js +++ b/assets/src/helpers.js @@ -5,6 +5,12 @@ */ import uuid from 'uuid/v4'; +/** + * WordPress dependencies + */ +import { count } from '@wordpress/wordcount'; +import { _x } from '@wordpress/i18n'; + /** * Internal dependencies */ @@ -217,3 +223,55 @@ export const addAMPExtraProps = ( props, blockType, attributes ) => { ...ampAttributes, }; }; + +// Todo: Make these customizable? +const H1_FONT_SIZE = 40; +const H2_FONT_SIZE = 24; +const H1_TEXT_LENGTH = 4; +const H2_TEXT_LENGTH = 10; + +/* + * translators: If your word count is based on single characters (e.g. East Asian characters), + * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'. + * Do not translate into your own language. + */ +const wordCountType = _x( 'words', 'Word count type. Do not translate!', 'amp' ); + +/** + * Determines the HTML tag name that should be used given on the block's attributes. + * + * Font size takes precedence over text length as it's a stronger signal for semantic meaning. + * + * @param {Object} attributes Block attributes. + * @param {boolean} canUseH1 Whether an H1 tag is allowed. + * + * @return {string} HTML tag name. Either p, h1, or h2. + */ +export const getTagName = ( attributes, canUseH1 ) => { + const { fontSize, customFontSize, positionTop } = attributes; + + // Elements positioned that low on a page are unlikely to be headings. + if ( positionTop > 80 ) { + return 'p'; + } + + if ( 'huge' === fontSize || ( customFontSize && customFontSize > H1_FONT_SIZE ) ) { + return canUseH1 ? 'h1' : 'h2'; + } + + if ( 'large' === fontSize || ( customFontSize && customFontSize > H2_FONT_SIZE ) ) { + return 'h2'; + } + + const textLength = count( attributes.content, wordCountType, {} ); + + if ( H1_TEXT_LENGTH >= textLength ) { + return canUseH1 ? 'h1' : 'h2'; + } + + if ( H2_TEXT_LENGTH >= textLength ) { + return 'h2'; + } + + return 'p'; +};