diff --git a/lib/block-supports/dimensions.php b/lib/block-supports/dimensions.php
index b51682a34e0161..0c380c18feb46f 100644
--- a/lib/block-supports/dimensions.php
+++ b/lib/block-supports/dimensions.php
@@ -21,10 +21,10 @@ function gutenberg_register_dimensions_support( $block_type ) {
return;
}
- $has_spacing_support = gutenberg_block_has_support( $block_type, array( 'spacing' ), false );
- // Future block supports such as height & width will be added here.
+ $has_dimensions_support = gutenberg_block_has_support( $block_type, array( '__experimentalDimensions' ), false );
+ $has_spacing_support = gutenberg_block_has_support( $block_type, array( 'spacing' ), false );
- if ( $has_spacing_support ) {
+ if ( $has_dimensions_support || $has_spacing_support ) {
$block_type->attributes['style'] = array(
'type' => 'object',
);
@@ -41,22 +41,53 @@ function gutenberg_register_dimensions_support( $block_type ) {
* @return array Block spacing CSS classes and inline styles.
*/
function gutenberg_apply_dimensions_support( $block_type, $block_attributes ) {
- $spacing_styles = gutenberg_apply_spacing_support( $block_type, $block_attributes );
- // Future block supports such as height and width will be added here.
+ $dimensions_styles = gutenberg_get_dimensions_styles( $block_type, $block_attributes );
+ $spacing_styles = gutenberg_get_spacing_styles( $block_type, $block_attributes );
+ $styles = $dimensions_styles . $spacing_styles;
- return $spacing_styles;
+ return empty( $styles ) ? array() : array( 'style' => $styles );
}
/**
- * Add CSS classes for block spacing to the incoming attributes array.
+ * Add inline styles for block dimensions to the incoming attributes array.
* This will be applied to the block markup in the front-end.
*
* @param WP_Block_Type $block_type Block Type.
* @param array $block_attributes Block attributes.
*
- * @return array Block spacing CSS classes and inline styles.
+ * @return array Block dimensions inline styles.
*/
-function gutenberg_apply_spacing_support( $block_type, $block_attributes ) {
+function gutenberg_get_dimensions_styles( $block_type, $block_attributes ) {
+ if ( gutenberg_skip_dimensions_serialization( $block_type ) ) {
+ return array();
+ }
+
+ $has_height_support = gutenberg_block_has_support( $block_type, array( '__experimentalDimensions', 'height' ), false );
+ $styles = array();
+
+ if ( $has_height_support ) {
+ $height_value = _wp_array_get( $block_attributes, array( 'style', 'dimensions', 'height' ), null );
+
+ if ( null !== $height_value ) {
+ $styles[] = sprintf( 'height: %s;', $height_value );
+ }
+ }
+
+ // Width support to be added in near future.
+
+ return empty( $styles ) ? null : implode( ' ', $styles );
+}
+
+/**
+ * Add inline styles for block spacing to the incoming attributes array.
+ * This will be applied to the block markup in the front-end.
+ *
+ * @param WP_Block_Type $block_type Block Type.
+ * @param array $block_attributes Block attributes.
+ *
+ * @return array Block spacing inline styles.
+ */
+function gutenberg_get_spacing_styles( $block_type, $block_attributes ) {
if ( gutenberg_skip_spacing_serialization( $block_type ) ) {
return array();
}
@@ -89,7 +120,23 @@ function gutenberg_apply_spacing_support( $block_type, $block_attributes ) {
}
}
- return empty( $styles ) ? array() : array( 'style' => implode( ' ', $styles ) );
+ return empty( $styles ) ? null : implode( ' ', $styles );
+}
+
+/**
+ * Checks whether serialization of the current block's dimensions properties
+ * should occur.
+ *
+ * @param WP_Block_type $block_type Block type.
+ *
+ * @return boolean Whether to serialize dimensions support styles & classes.
+ */
+function gutenberg_skip_dimensions_serialization( $block_type ) {
+ $dimensions_support = _wp_array_get( $block_type->supports, array( '__experimentalDimensions' ), false );
+
+ return is_array( $dimensions_support ) &&
+ array_key_exists( '__experimentalSkipSerialization', $dimensions_support ) &&
+ $dimensions_support['__experimentalSkipSerialization'];
}
/**
diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php
index b3ee6d1f8b9d3e..af0716477c9fbf 100644
--- a/lib/class-wp-theme-json-gutenberg.php
+++ b/lib/class-wp-theme-json-gutenberg.php
@@ -60,6 +60,9 @@ class WP_Theme_JSON_Gutenberg {
'gradient' => null,
'text' => null,
),
+ 'dimensions' => array(
+ 'height' => null,
+ ),
'spacing' => array(
'margin' => null,
'padding' => null,
@@ -93,6 +96,9 @@ class WP_Theme_JSON_Gutenberg {
'palette' => null,
),
'custom' => null,
+ 'dimensions' => array(
+ 'customHeight' => null,
+ ),
'layout' => array(
'contentSize' => null,
'wideSize' => null,
@@ -221,6 +227,7 @@ class WP_Theme_JSON_Gutenberg {
'font-size' => array( 'typography', 'fontSize' ),
'font-style' => array( 'typography', 'fontStyle' ),
'font-weight' => array( 'typography', 'fontWeight' ),
+ 'height' => array( 'dimensions', 'height' ),
'letter-spacing' => array( 'typography', 'letterSpacing' ),
'line-height' => array( 'typography', 'lineHeight' ),
'margin' => array( 'spacing', 'margin' ),
diff --git a/lib/experimental-default-theme.json b/lib/experimental-default-theme.json
index 9e751dc939118b..90c2c3e175ce19 100644
--- a/lib/experimental-default-theme.json
+++ b/lib/experimental-default-theme.json
@@ -210,6 +210,9 @@
}
]
},
+ "dimensions": {
+ "customHeight": false
+ },
"spacing": {
"customMargin": false,
"customPadding": false,
diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js
index 8351676640b177..960698371040ed 100644
--- a/packages/block-editor/src/hooks/dimensions.js
+++ b/packages/block-editor/src/hooks/dimensions.js
@@ -13,6 +13,13 @@ import { getBlockSupport } from '@wordpress/blocks';
* Internal dependencies
*/
import InspectorControls from '../components/inspector-controls';
+import {
+ HeightEdit,
+ hasHeightSupport,
+ hasHeightValue,
+ resetHeight,
+ useIsHeightDisabled,
+} from './height';
import {
MarginEdit,
hasMarginSupport,
@@ -29,18 +36,19 @@ import {
} from './padding';
import { cleanEmptyObject } from './utils';
+export const DIMENSIONS_SUPPORT_KEY = '__experimentalDimensions';
export const SPACING_SUPPORT_KEY = 'spacing';
/**
* Inspector controls for dimensions support.
*
* @param {Object} props Block props.
- *
- * @return {WPElement} Inspector controls for spacing support features.
+ * @return {WPElement} Inspector controls for dimensions support features.
*/
export function DimensionsPanel( props ) {
const isPaddingDisabled = useIsPaddingDisabled( props );
const isMarginDisabled = useIsMarginDisabled( props );
+ const isHeightDisabled = useIsHeightDisabled( props );
const isDisabled = useIsDimensionsDisabled( props );
const isSupported = hasDimensionsSupport( props.name );
@@ -48,6 +56,11 @@ export function DimensionsPanel( props ) {
return null;
}
+ const defaultDimensionsControls = getBlockSupport( props.name, [
+ DIMENSIONS_SUPPORT_KEY,
+ '__experimentalDefaultControls',
+ ] );
+
const defaultSpacingControls = getBlockSupport( props.name, [
SPACING_SUPPORT_KEY,
'__experimentalDefaultControls',
@@ -60,6 +73,10 @@ export function DimensionsPanel( props ) {
props.setAttributes( {
style: cleanEmptyObject( {
...style,
+ dimensions: {
+ ...style?.dimensions,
+ height: undefined,
+ },
spacing: {
...style?.spacing,
margin: undefined,
@@ -76,6 +93,17 @@ export function DimensionsPanel( props ) {
header={ __( 'Dimensions' ) }
resetAll={ resetAll }
>
+ { ! isHeightDisabled && (
+ hasHeightValue( props ) }
+ label={ __( 'Height' ) }
+ onDeselect={ () => resetHeight( props ) }
+ isShownByDefault={ defaultDimensionsControls?.height }
+ >
+
+
+ ) }
{ ! isPaddingDisabled && (
hasPaddingValue( props ) }
@@ -113,21 +141,25 @@ export function hasDimensionsSupport( blockName ) {
return false;
}
- return hasPaddingSupport( blockName ) || hasMarginSupport( blockName );
+ return (
+ hasHeightSupport( blockName ) ||
+ hasPaddingSupport( blockName ) ||
+ hasMarginSupport( blockName )
+ );
}
/**
* Determines whether dimensions support has been disabled.
*
* @param {Object} props Block properties.
- *
- * @return {boolean} If spacing support is completely disabled.
+ * @return {boolean} If dimensions support is completely disabled.
*/
const useIsDimensionsDisabled = ( props = {} ) => {
+ const heightDisabled = useIsHeightDisabled( props );
const paddingDisabled = useIsPaddingDisabled( props );
const marginDisabled = useIsMarginDisabled( props );
- return paddingDisabled && marginDisabled;
+ return heightDisabled && paddingDisabled && marginDisabled;
};
/**
diff --git a/packages/block-editor/src/hooks/height.js b/packages/block-editor/src/hooks/height.js
new file mode 100644
index 00000000000000..302556d231a7b6
--- /dev/null
+++ b/packages/block-editor/src/hooks/height.js
@@ -0,0 +1,121 @@
+/**
+ * WordPress dependencies
+ */
+import { getBlockSupport } from '@wordpress/blocks';
+import {
+ __experimentalUseCustomUnits as useCustomUnits,
+ __experimentalUnitControl as UnitControl,
+} from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import useSetting from '../components/use-setting';
+import { DIMENSIONS_SUPPORT_KEY } from './dimensions';
+import { cleanEmptyObject } from './utils';
+
+/**
+ * Determines if there is height support.
+ *
+ * @param {string|Object} blockType Block name or Block Type object.
+ * @return {boolean} Whether there is support.
+ */
+export function hasHeightSupport( blockType ) {
+ const support = getBlockSupport( blockType, DIMENSIONS_SUPPORT_KEY );
+ return !! ( true === support || support?.height );
+}
+
+/**
+ * Checks if there is a current value in the height block support attributes.
+ *
+ * @param {Object} props Block props.
+ * @return {boolean} Whether or not the block has a height value set.
+ */
+export function hasHeightValue( props ) {
+ return props.attributes.style?.dimensions?.height !== undefined;
+}
+
+/**
+ * Resets the height block support attributes. This can be used when
+ * disabling the height support controls for a block via a progressive
+ * discovery panel.
+ *
+ * @param {Object} props Block props.
+ * @param {Object} props.attributes Block's attributes.
+ * @param {Object} props.setAttributes Function to set block's attributes.
+ */
+export function resetHeight( { attributes = {}, setAttributes } ) {
+ const { style } = attributes;
+
+ setAttributes( {
+ style: {
+ ...style,
+ dimensions: {
+ ...style?.dimensions,
+ height: undefined,
+ },
+ },
+ } );
+}
+
+/**
+ * Custom hook that checks if height controls have been disabled.
+ *
+ * @param {string} name The name of the block.
+ * @return {boolean} Whether height control is disabled.
+ */
+export function useIsHeightDisabled( { name: blockName } = {} ) {
+ const isDisabled = ! useSetting( 'dimensions.customHeight' );
+ return ! hasHeightSupport( blockName ) || isDisabled;
+}
+
+/**
+ * Inspector control panel containing the height related configuration.
+ *
+ * @param {Object} props Block props.
+ * @return {WPElement} Edit component for height.
+ */
+export function HeightEdit( props ) {
+ const {
+ attributes: { style },
+ setAttributes,
+ } = props;
+
+ const units = useCustomUnits( {
+ availableUnits: useSetting( 'dimensions.units' ) || [
+ '%',
+ 'px',
+ 'em',
+ 'rem',
+ 'vh',
+ 'vw',
+ ],
+ } );
+
+ if ( useIsHeightDisabled( props ) ) {
+ return null;
+ }
+
+ const onChange = ( next ) => {
+ const newStyle = {
+ ...style,
+ dimensions: {
+ ...style?.dimensions,
+ height: next,
+ },
+ };
+
+ setAttributes( { style: cleanEmptyObject( newStyle ) } );
+ };
+
+ return (
+
+ );
+}
diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js
index 87bf4685d50d42..2c3fff303ec402 100644
--- a/packages/block-editor/src/hooks/style.js
+++ b/packages/block-editor/src/hooks/style.js
@@ -37,13 +37,18 @@ import {
TYPOGRAPHY_SUPPORT_KEY,
TYPOGRAPHY_SUPPORT_KEYS,
} from './typography';
-import { SPACING_SUPPORT_KEY, DimensionsPanel } from './dimensions';
+import {
+ DIMENSIONS_SUPPORT_KEY,
+ SPACING_SUPPORT_KEY,
+ DimensionsPanel,
+} from './dimensions';
import useDisplayBlockControls from '../components/use-display-block-controls';
const styleSupportKeys = [
...TYPOGRAPHY_SUPPORT_KEYS,
BORDER_SUPPORT_KEY,
COLOR_SUPPORT_KEY,
+ DIMENSIONS_SUPPORT_KEY,
SPACING_SUPPORT_KEY,
];
@@ -152,8 +157,11 @@ const skipSerializationPaths = {
[ `${ TYPOGRAPHY_SUPPORT_KEY }.__experimentalSkipSerialization` ]: [
TYPOGRAPHY_SUPPORT_KEY,
],
+ [ `${ DIMENSIONS_SUPPORT_KEY }.__experimentalSkipSerialization` ]: [
+ DIMENSIONS_SUPPORT_KEY,
+ ],
[ `${ SPACING_SUPPORT_KEY }.__experimentalSkipSerialization` ]: [
- 'spacing',
+ SPACING_SUPPORT_KEY,
],
};
diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js
index 706d9a93ed0ba4..12e369ceb592f7 100644
--- a/packages/block-editor/src/hooks/test/style.js
+++ b/packages/block-editor/src/hooks/test/style.js
@@ -23,6 +23,9 @@ describe( 'getInlineStyles', () => {
style: 'dotted',
color: '#21759b',
},
+ dimensions: {
+ height: '500px',
+ },
spacing: {
padding: { top: '10px' },
margin: { bottom: '15px' },
@@ -37,6 +40,7 @@ describe( 'getInlineStyles', () => {
color: 'red',
lineHeight: 1.5,
fontSize: 10,
+ height: '500px',
marginBottom: '15px',
paddingTop: '10px',
} );
diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js
index 13d97020aca924..2da96b88da54ab 100644
--- a/packages/blocks/src/api/constants.js
+++ b/packages/blocks/src/api/constants.js
@@ -72,6 +72,10 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = {
value: [ 'typography', 'fontWeight' ],
support: [ 'typography', '__experimentalFontWeight' ],
},
+ height: {
+ value: [ 'dimensions', 'height' ],
+ support: [ '__experimentalDimensions', 'height' ],
+ },
lineHeight: {
value: [ 'typography', 'lineHeight' ],
support: [ 'typography', 'lineHeight' ],
diff --git a/packages/edit-site/src/components/editor/global-styles-renderer.js b/packages/edit-site/src/components/editor/global-styles-renderer.js
index 5d64dfabac0ad0..bb1ed4720f544a 100644
--- a/packages/edit-site/src/components/editor/global-styles-renderer.js
+++ b/packages/edit-site/src/components/editor/global-styles-renderer.js
@@ -186,7 +186,13 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => {
const pickStyleKeys = ( treeToPickFrom ) =>
pickBy( treeToPickFrom, ( value, key ) =>
- [ 'border', 'color', 'spacing', 'typography' ].includes( key )
+ [
+ 'border',
+ 'color',
+ 'dimensions',
+ 'spacing',
+ 'typography',
+ ].includes( key )
);
// Top-level.
diff --git a/packages/edit-site/src/components/sidebar/dimensions-panel.js b/packages/edit-site/src/components/sidebar/dimensions-panel.js
index 4f19f7e19cae6d..55dec0ec3e1b67 100644
--- a/packages/edit-site/src/components/sidebar/dimensions-panel.js
+++ b/packages/edit-site/src/components/sidebar/dimensions-panel.js
@@ -7,6 +7,7 @@ import {
__experimentalToolsPanelItem as ToolsPanelItem,
__experimentalBoxControl as BoxControl,
__experimentalUseCustomUnits as useCustomUnits,
+ __experimentalUnitControl as UnitControl,
} from '@wordpress/components';
import { __experimentalUseCustomSides as useCustomSides } from '@wordpress/block-editor';
@@ -16,10 +17,17 @@ import { __experimentalUseCustomSides as useCustomSides } from '@wordpress/block
import { useSetting } from '../editor/utils';
export function useHasDimensionsPanel( context ) {
+ const hasHeight = useHasHeight( context );
const hasPadding = useHasPadding( context );
const hasMargin = useHasMargin( context );
- return hasPadding || hasMargin;
+ return hasHeight || hasPadding || hasMargin;
+}
+
+function useHasHeight( { name, supports } ) {
+ const settings = useSetting( 'dimensions.customHeight', name );
+
+ return settings && supports.includes( 'height' );
}
function useHasPadding( { name, supports } ) {
@@ -64,6 +72,7 @@ function splitStyleValue( value ) {
export default function DimensionsPanel( { context, getStyle, setStyle } ) {
const { name } = context;
+ const showHeightControl = useHasHeight( context );
const showPaddingControl = useHasPadding( context );
const showMarginControl = useHasMargin( context );
const units = useCustomUnits( {
@@ -76,6 +85,13 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
],
} );
+ // Height.
+ const heightValue = getStyle( name, 'height' );
+ const setHeightValue = ( next ) => setStyle( name, 'height', next );
+ const resetHeightValue = () => setHeightValue( undefined );
+ const hasHeightValue = () => !! heightValue;
+
+ // Padding.
const paddingValues = splitStyleValue( getStyle( name, 'padding' ) );
const paddingSides = useCustomSides( name, 'padding' );
@@ -87,6 +103,7 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
const hasPaddingValue = () =>
paddingValues && Object.keys( paddingValues ).length;
+ // Margin.
const marginValues = splitStyleValue( getStyle( name, 'margin' ) );
const marginSides = useCustomSides( name, 'margin' );
@@ -99,6 +116,7 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
marginValues && Object.keys( marginValues ).length;
const resetAll = () => {
+ resetHeightValue();
resetPaddingValue();
resetMarginValue();
};
@@ -109,6 +127,23 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
header={ __( 'Dimensions' ) }
resetAll={ resetAll }
>
+ { showHeightControl && (
+
+
+
+ ) }
{ showPaddingControl && (
array(
'core/group' => array(
- 'border' => array(
+ 'border' => array(
'radius' => '10px',
),
- 'elements' => array(
+ 'elements' => array(
'link' => array(
'color' => array(
'text' => '#111',
),
),
),
- 'spacing' => array(
+ 'dimensions' => array(
+ 'height' => '200px',
+ ),
+ 'spacing' => array(
'padding' => '24px',
),
),
@@ -352,11 +355,11 @@ function test_get_stylesheet() {
);
$this->assertEquals(
- 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}',
+ 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;height: 200px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}',
$theme_json->get_stylesheet()
);
$this->assertEquals(
- 'body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}',
+ 'body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;height: 200px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}',
$theme_json->get_stylesheet( 'block_styles' )
);
$this->assertEquals(