diff --git a/packages/editor/src/hooks/align.js b/packages/editor/src/hooks/align.js
index 7b80924c433e2..7b7062ba558f5 100644
--- a/packages/editor/src/hooks/align.js
+++ b/packages/editor/src/hooks/align.js
@@ -2,20 +2,72 @@
* External dependencies
*/
import classnames from 'classnames';
-import { assign, get, has, includes } from 'lodash';
+import { assign, get, has, includes, without } from 'lodash';
/**
* WordPress dependencies
*/
-import { createHigherOrderComponent } from '@wordpress/compose';
+import { compose, createHigherOrderComponent } from '@wordpress/compose';
import { addFilter } from '@wordpress/hooks';
-import { hasBlockSupport, getBlockSupport, getBlockType } from '@wordpress/blocks';
+import { getBlockSupport, getBlockType, hasBlockSupport } from '@wordpress/blocks';
+import { withSelect } from '@wordpress/data';
/**
* Internal dependencies
*/
import { BlockControls, BlockAlignmentToolbar } from '../components';
+/**
+ * An array which includes all possible valid alignments,
+ * used to validate if an alignment is valid or not.
+ *
+ * @constant
+ * @type {string[]}
+*/
+const ALL_ALIGNMENTS = [ 'left', 'center', 'right', 'wide', 'full' ];
+
+/**
+ * An array which includes all wide alignments.
+ * In order for this alignments to be valid they need to be supported by the block,
+ * and by the theme.
+ *
+ * @constant
+ * @type {string[]}
+*/
+const WIDE_ALIGNMENTS = [ 'wide', 'full' ];
+
+/**
+ * Returns the valid alignments.
+ * Takes into consideration the aligns supported by a block, if the block supports wide controls or not and if theme supports wide controls or not.
+ * Exported just for testing purposes, not exported outside the module.
+ *
+ * @param {?boolean|string[]} blockAlign Aligns supported by the block.
+ * @param {?boolean} hasWideBlockSupport True if block supports wide alignments. And False otherwise.
+ * @param {?boolean} hasWideEnabled True if theme supports wide alignments. And False otherwise.
+ *
+ * @return {string[]} Valid alignments.
+ */
+export function getValidAlignments( blockAlign, hasWideBlockSupport = true, hasWideEnabled = true ) {
+ let validAlignments;
+ if ( Array.isArray( blockAlign ) ) {
+ validAlignments = blockAlign;
+ } else if ( blockAlign === true ) {
+ // `true` includes all alignments...
+ validAlignments = ALL_ALIGNMENTS;
+ } else {
+ validAlignments = [];
+ }
+
+ if (
+ ! hasWideEnabled ||
+ ( blockAlign === true && ! hasWideBlockSupport )
+ ) {
+ return without( validAlignments, ...WIDE_ALIGNMENTS );
+ }
+
+ return validAlignments;
+}
+
/**
* Filters registered block settings, extending attributes to include `align`.
*
@@ -39,33 +91,6 @@ export function addAttribute( settings ) {
return settings;
}
-/**
- * Returns an array of valid alignments for a block type depending on its
- * defined supports. Returns an empty array if block does not support align.
- *
- * @param {string} blockName Block name to check
- * @return {string[]} Valid alignments for block
- */
-export function getBlockValidAlignments( blockName ) {
- // Explicitly defined array set of valid alignments
- const blockAlign = getBlockSupport( blockName, 'align' );
- if ( Array.isArray( blockAlign ) ) {
- return blockAlign;
- }
-
- const validAlignments = [];
- if ( true === blockAlign ) {
- // `true` includes all alignments...
- validAlignments.push( 'left', 'center', 'right' );
-
- if ( hasBlockSupport( blockName, 'alignWide', true ) ) {
- validAlignments.push( 'wide', 'full' );
- }
- }
-
- return validAlignments;
-}
-
/**
* Override the default edit UI to include new toolbar controls for block
* alignment, if block defines support.
@@ -73,46 +98,57 @@ export function getBlockValidAlignments( blockName ) {
* @param {Function} BlockEdit Original component
* @return {Function} Wrapped component
*/
-export const withToolbarControls = createHigherOrderComponent( ( BlockEdit ) => {
- return ( props ) => {
- const validAlignments = getBlockValidAlignments( props.name );
-
- const updateAlignment = ( nextAlign ) => {
- if ( ! nextAlign ) {
- const blockType = getBlockType( props.name );
- const blockDefaultAlign = get( blockType, [ 'attributes', 'align', 'default' ] );
- if ( blockDefaultAlign ) {
- nextAlign = '';
+export const withToolbarControls = createHigherOrderComponent(
+ ( BlockEdit ) => (
+ ( props ) => {
+ const { name: blockName } = props;
+ // Compute valid alignments without taking into account,
+ // if the theme supports wide alignments or not.
+ // BlockAlignmentToolbar takes into account the theme support.
+ const validAlignments = getValidAlignments(
+ getBlockSupport( blockName, 'align' ),
+ hasBlockSupport( blockName, 'alignWide', true ),
+ );
+
+ const updateAlignment = ( nextAlign ) => {
+ if ( ! nextAlign ) {
+ const blockType = getBlockType( props.name );
+ const blockDefaultAlign = get( blockType, [ 'attributes', 'align', 'default' ] );
+ if ( blockDefaultAlign ) {
+ nextAlign = '';
+ }
}
- }
- props.setAttributes( { align: nextAlign } );
- };
-
- return [
- validAlignments.length > 0 && props.isSelected && (
-
-
-
- ),
- ,
- ];
- };
-}, 'withToolbarControls' );
-
-/**
- * Override the default block element to add alignment wrapper props.
- *
- * @param {Function} BlockListBlock Original component
- * @return {Function} Wrapped component
- */
-export const withDataAlign = createHigherOrderComponent( ( BlockListBlock ) => {
- return ( props ) => {
- const { align } = props.block.attributes;
- const validAlignments = getBlockValidAlignments( props.block.name );
+ props.setAttributes( { align: nextAlign } );
+ };
+
+ return [
+ validAlignments.length > 0 && props.isSelected && (
+
+
+
+ ),
+ ,
+ ];
+ }
+ ),
+ 'withToolbarControls'
+);
+
+// Exported just for testing purposes, not exported outside the module.
+export const insideSelectWithDataAlign = ( BlockListBlock ) => (
+ ( props ) => {
+ const { block, hasWideEnabled } = props;
+ const { name: blockName } = block;
+ const { align } = block.attributes;
+ const validAlignments = getValidAlignments(
+ getBlockSupport( blockName, 'align' ),
+ hasBlockSupport( blockName, 'alignWide', true ),
+ hasWideEnabled
+ );
let wrapperProps = props.wrapperProps;
if ( includes( validAlignments, align ) ) {
@@ -120,8 +156,28 @@ export const withDataAlign = createHigherOrderComponent( ( BlockListBlock ) => {
}
return ;
- };
-}, 'withDataAlign' );
+ }
+);
+
+/**
+ * Override the default block element to add alignment wrapper props.
+ *
+ * @param {Function} BlockListBlock Original component
+ * @return {Function} Wrapped component
+ */
+export const withDataAlign = createHigherOrderComponent(
+ compose( [
+ withSelect(
+ ( select ) => {
+ const { getEditorSettings } = select( 'core/editor' );
+ return {
+ hasWideEnabled: !! getEditorSettings().alignWide,
+ };
+ }
+ ),
+ insideSelectWithDataAlign,
+ ] )
+);
/**
* Override props assigned to save component to inject alignment class name if
@@ -134,8 +190,16 @@ export const withDataAlign = createHigherOrderComponent( ( BlockListBlock ) => {
*/
export function addAssignedAlign( props, blockType, attributes ) {
const { align } = attributes;
-
- if ( includes( getBlockValidAlignments( blockType ), align ) ) {
+ const blockAlign = getBlockSupport( blockType, 'align' );
+ const hasWideBlockSupport = hasBlockSupport( blockType, 'alignWide', true );
+ const isAlignValid = includes(
+ // Compute valid alignments without taking into account,
+ // if the theme supports wide alignments or not.
+ // This way changing themes does not impacts the block save.
+ getValidAlignments( blockAlign, hasWideBlockSupport ),
+ align
+ );
+ if ( isAlignValid ) {
props.className = classnames( `align${ align }`, props.className );
}
diff --git a/packages/editor/src/hooks/test/align.js b/packages/editor/src/hooks/test/align.js
index ccc678eee8d77..5d3f3dba87c28 100644
--- a/packages/editor/src/hooks/test/align.js
+++ b/packages/editor/src/hooks/test/align.js
@@ -18,9 +18,9 @@ import {
* Internal dependencies
*/
import {
- getBlockValidAlignments,
+ getValidAlignments,
withToolbarControls,
- withDataAlign,
+ insideSelectWithDataAlign,
addAssignedAlign,
} from '../align';
@@ -58,49 +58,61 @@ describe( 'align', () => {
} );
} );
- describe( 'getBlockValidAlignments()', () => {
+ describe( 'getValidAlignments()', () => {
it( 'should return an empty array if block does not define align support', () => {
- registerBlockType( 'core/foo', blockSettings );
- const validAlignments = getBlockValidAlignments( 'core/foo' );
+ expect( getValidAlignments() ).toEqual( [] );
+ } );
- expect( validAlignments ).toEqual( [] );
+ it( 'should return all custom aligns set', () => {
+ expect(
+ getValidAlignments( [ 'left', 'right' ] )
+ ).toEqual(
+ [ 'left', 'right' ]
+ );
} );
- it( 'should return all custom align set', () => {
- registerBlockType( 'core/foo', {
- ...blockSettings,
- supports: {
- align: [ 'left', 'right' ],
- },
- } );
- const validAlignments = getBlockValidAlignments( 'core/foo' );
+ it( 'should return all aligns if block defines align support as true', () => {
+ expect(
+ getValidAlignments( true )
+ ).toEqual(
+ [ 'left', 'center', 'right', 'wide', 'full' ]
+ );
+ } );
- expect( validAlignments ).toEqual( [ 'left', 'right' ] );
+ it( 'should return all aligns except wide if wide align explicitly false on the block', () => {
+ expect(
+ getValidAlignments( true, false, true )
+ ).toEqual( [ 'left', 'center', 'right' ] );
+
+ expect(
+ getValidAlignments( true, false, false )
+ ).toEqual( [ 'left', 'center', 'right' ] );
} );
- it( 'should return all aligns if block defines align support', () => {
- registerBlockType( 'core/foo', {
- ...blockSettings,
- supports: {
- align: true,
- },
- } );
- const validAlignments = getBlockValidAlignments( 'core/foo' );
+ it( 'should return all aligns except wide if wide align is not supported by the theme', () => {
+ expect(
+ getValidAlignments( true, true, false )
+ ).toEqual( [ 'left', 'center', 'right' ] );
- expect( validAlignments ).toEqual( [ 'left', 'center', 'right', 'wide', 'full' ] );
+ expect(
+ getValidAlignments( true, false, false )
+ ).toEqual( [ 'left', 'center', 'right' ] );
} );
- it( 'should return all aligns except wide if wide align explicitly false', () => {
- registerBlockType( 'core/foo', {
- ...blockSettings,
- supports: {
- align: true,
- alignWide: false,
- },
- } );
- const validAlignments = getBlockValidAlignments( 'core/foo' );
+ it( 'should not remove wide aligns if they are not supported by the block and were set using an array in supports align', () => {
+ expect(
+ getValidAlignments( [ 'left', 'right', 'wide', 'full' ], false, true )
+ ).toEqual( [ 'left', 'right', 'wide', 'full' ] );
+ } );
- expect( validAlignments ).toEqual( [ 'left', 'center', 'right' ] );
+ it( 'should remove wide aligns if they are not supported by the theme and were set using an array in supports align', () => {
+ expect(
+ getValidAlignments( [ 'left', 'right', 'wide', 'full' ], true, false )
+ ).toEqual( [ 'left', 'right' ] );
+
+ expect(
+ getValidAlignments( [ 'left', 'right', 'wide', 'full' ], false, false )
+ ).toEqual( [ 'left', 'right' ] );
} );
} );
@@ -153,11 +165,11 @@ describe( 'align', () => {
...blockSettings,
supports: {
align: true,
- alignWide: false,
+ alignWide: true,
},
} );
- const EnhancedComponent = withDataAlign( ( { wrapperProps } ) => (
+ const EnhancedComponent = insideSelectWithDataAlign( ( { wrapperProps } ) => (
) );
@@ -166,16 +178,43 @@ describe( 'align', () => {
block={ {
name: 'core/foo',
attributes: {
- align: 'left',
+ align: 'wide',
},
} }
/>
);
expect( wrapper.toTree().rendered.props.wrapperProps ).toEqual( {
- 'data-align': 'left',
+ 'data-align': 'wide',
} );
} );
+ it( 'should not render wide/full wrapper props if wide controls are not enabled', () => {
+ registerBlockType( 'core/foo', {
+ ...blockSettings,
+ supports: {
+ align: true,
+ alignWide: true,
+ },
+ } );
+
+ const EnhancedComponent = insideSelectWithDataAlign( ( { wrapperProps } ) => (
+
+ ) );
+
+ const wrapper = renderer.create(
+
+ );
+ expect( wrapper.toTree().rendered.props.wrapperProps ).toEqual( undefined );
+ } );
+
it( 'should not render invalid align', () => {
registerBlockType( 'core/foo', {
...blockSettings,
@@ -185,7 +224,7 @@ describe( 'align', () => {
},
} );
- const EnhancedComponent = withDataAlign( ( { wrapperProps } ) => (
+ const EnhancedComponent = insideSelectWithDataAlign( ( { wrapperProps } ) => (
) );