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';
+};