Skip to content

Commit

Permalink
Dimensions: Add Aspect Ratio block support (#56897)
Browse files Browse the repository at this point in the history
* Try: Dimensions Aspect Ratio block support

* Try server-rendering output

* Try outputting has-aspect-ratio classname

* Ensure block support is checked properly

* Try adding global styles support

* Update docs, add appropriate checks

* Try unsetting minHeight

* Try unsetting min-height when applying aspect ratio and vice versa

* Allow Cover block to expand when the content extends beyond the boundaries of the aspect-ratio

* Hide cover block resize handle when aspect ratio is set, clear aspect ratio when updating cover block min height

* Clear aspect-ratio if minHeight is set on the site frontend

* Attempt to get unset rules playing nicely in the editor with global styles

* Hide aspectRatio control from global styles for the group block

* Skip output of has-aspect-ratio if value isn't set
  • Loading branch information
andrewserong committed Jan 24, 2024
1 parent 323359c commit 2e01f22
Show file tree
Hide file tree
Showing 27 changed files with 340 additions and 26 deletions.
2 changes: 2 additions & 0 deletions docs/how-to-guides/themes/global-settings-and-styles.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ The settings section has the following structure:
},
"custom": {},
"dimensions": {
"aspectRatio": false,
"minHeight": false,
},
"layout": {
Expand Down Expand Up @@ -773,6 +774,7 @@ Each block declares which style properties it exposes via the [block supports me
"text": "value"
},
"dimensions": {
"aspectRatio": "value",
"minHeight": "value"
},
"filter": {
Expand Down
2 changes: 1 addition & 1 deletion docs/how-to-guides/themes/theme-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ Use this setting to enable the following Global Styles settings:
- color: link
- spacing: blockGap, margin, padding
- typography: lineHeight
- dimensions: minHeight
- dimensions: aspectRatio, minHeight
- position: sticky

```php
Expand Down
4 changes: 3 additions & 1 deletion docs/reference-guides/block-api/block-supports.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,19 +442,21 @@ This value signals that a block supports some of the CSS style properties relate
```js
supports: {
dimensions: {
aspectRatio: true // Enable aspect ratio control.
minHeight: true // Enable min height control.
}
}
```

When a block declares support for a specific dimensions property, its attributes definition is extended to include the `style` attribute.

- `style`: attribute of `object` type with no default assigned. This is added when `minHeight` support is declared. It stores the custom values set by the user, e.g.:
- `style`: attribute of `object` type with no default assigned. This is added when `aspectRatio` or `minHeight` support is declared. It stores the custom values set by the user, e.g.:

```js
attributes: {
style: {
dimensions: {
aspectRatio: "16/9",
minHeight: "50vh"
}
}
Expand Down
4 changes: 2 additions & 2 deletions docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ Add an image or video with a text overlay. ([Source](https://github.com/WordPres

- **Name:** core/cover
- **Category:** media
- **Supports:** align, anchor, color (heading, text, ~~background~~, ~~enableContrastChecker~~), layout (~~allowJustification~~), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Supports:** align, anchor, color (heading, text, ~~background~~, ~~enableContrastChecker~~), dimensions (aspectRatio), layout (~~allowJustification~~), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** allowedBlocks, alt, backgroundType, contentPosition, customGradient, customOverlayColor, dimRatio, focalPoint, gradient, hasParallax, id, isDark, isRepeated, isUserOverlayColor, minHeight, minHeightUnit, overlayColor, tagName, templateLock, url, useFeaturedImage

## Details
Expand Down Expand Up @@ -341,7 +341,7 @@ Gather blocks in a layout container. ([Source](https://github.com/WordPress/gute

- **Name:** core/group
- **Category:** design
- **Supports:** align (full, wide), anchor, ariaLabel, background (backgroundImage, backgroundSize), color (background, button, gradients, heading, link, text), dimensions (minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Supports:** align (full, wide), anchor, ariaLabel, background (backgroundImage, backgroundSize), color (background, button, gradients, heading, link, text), dimensions (aspectRatio, minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** allowedBlocks, tagName, templateLock

## Heading
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Setting that enables the following UI tools:
- background: backgroundImage
- border: color, radius, style, width
- color: link
- dimensions: minHeight
- dimensions: aspectRatio, minHeight
- position: sticky
- spacing: blockGap, margin, padding
- typography: lineHeight
Expand Down Expand Up @@ -116,6 +116,7 @@ Settings related to dimensions.

| Property | Type | Default | Props |
| --- | --- | --- |--- |
| aspectRatio | boolean | false | |
| minHeight | boolean | false | |

---
Expand Down Expand Up @@ -237,6 +238,7 @@ Dimensions styles

| Property | Type | Props |
| --- | --- |--- |
| aspectRatio | string, object | |
| minHeight | string, object | |

---
Expand Down
77 changes: 77 additions & 0 deletions lib/block-supports/dimensions.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,83 @@ function gutenberg_apply_dimensions_support( $block_type, $block_attributes ) {
return $attributes;
}

/**
* Renders server-side dimensions styles to the block wrapper.
* This block support uses the `render_block` hook to ensure that
* it is also applied to non-server-rendered blocks.
*
* @param string $block_content Rendered block content.
* @param array $block Block object.
* @return string Filtered block content.
*/
function gutenberg_render_dimensions_support( $block_content, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$block_attributes = ( isset( $block['attrs'] ) && is_array( $block['attrs'] ) ) ? $block['attrs'] : array();
$has_aspect_ratio_support = block_has_support( $block_type, array( 'dimensions', 'aspectRatio' ), false );

if (
! $has_aspect_ratio_support ||
wp_should_skip_block_supports_serialization( $block_type, 'dimensions', 'aspectRatio' )
) {
return $block_content;
}

$dimensions_block_styles = array();
$dimensions_block_styles['aspectRatio'] = $block_attributes['style']['dimensions']['aspectRatio'] ?? null;

// To ensure the aspect ratio does not get overridden by `minHeight` unset any existing rule.
if (
isset( $dimensions_block_styles['aspectRatio'] )
) {
$dimensions_block_styles['minHeight'] = 'unset';
} elseif (
isset( $block_attributes['style']['dimensions']['minHeight'] ) ||
isset( $block_attributes['minHeight'] )
) {
$dimensions_block_styles['aspectRatio'] = 'unset';
}

$styles = gutenberg_style_engine_get_styles( array( 'dimensions' => $dimensions_block_styles ) );

if ( ! empty( $styles['css'] ) ) {
// Inject dimensions styles to the first element, presuming it's the wrapper, if it exists.
$tags = new WP_HTML_Tag_Processor( $block_content );

if ( $tags->next_tag() ) {
$existing_style = $tags->get_attribute( 'style' );
$updated_style = '';

if ( ! empty( $existing_style ) ) {
$updated_style = $existing_style;
if ( ! str_ends_with( $existing_style, ';' ) ) {
$updated_style .= ';';
}
}

$updated_style .= $styles['css'];
$tags->set_attribute( 'style', $updated_style );

if ( ! empty( $styles['classnames'] ) ) {
foreach ( explode( ' ', $styles['classnames'] ) as $class_name ) {
if (
str_contains( $class_name, 'aspect-ratio' ) &&
! isset( $block_attributes['style']['dimensions']['aspectRatio'] )
) {
continue;
}
$tags->add_class( $class_name );
}
}
}

return $tags->get_updated_html();
}

return $block_content;
}

add_filter( 'render_block', 'gutenberg_render_dimensions_support', 10, 2 );

// Register the block support.
WP_Block_Supports::get_instance()->register(
'dimensions',
Expand Down
17 changes: 15 additions & 2 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ class WP_Theme_JSON_Gutenberg {
* @var array
*/
const PROPERTIES_METADATA = array(
'aspect-ratio' => array( 'dimensions', 'aspectRatio' ),
'background' => array( 'color', 'gradient' ),
'background-color' => array( 'color', 'background' ),
'border-radius' => array( 'border', 'radius' ),
Expand Down Expand Up @@ -381,7 +382,8 @@ class WP_Theme_JSON_Gutenberg {
),
'custom' => null,
'dimensions' => array(
'minHeight' => null,
'aspectRatio' => null,
'minHeight' => null,
),
'layout' => array(
'contentSize' => null,
Expand Down Expand Up @@ -486,7 +488,8 @@ class WP_Theme_JSON_Gutenberg {
'text' => null,
),
'dimensions' => array(
'minHeight' => null,
'aspectRatio' => null,
'minHeight' => null,
),
'filter' => array(
'duotone' => null,
Expand Down Expand Up @@ -661,6 +664,7 @@ public static function get_element_class_name( $element ) {
array( 'color', 'heading' ),
array( 'color', 'button' ),
array( 'color', 'caption' ),
array( 'dimensions', 'aspectRatio' ),
array( 'dimensions', 'minHeight' ),
// BEGIN EXPERIMENTAL.
// Allow `position.fixed` to be opted-in by default.
Expand Down Expand Up @@ -2093,6 +2097,15 @@ protected static function compute_style_properties( $styles, $settings = array()
$value = gutenberg_get_typography_font_size_value( array( 'size' => $value ) );
}

if ( 'aspect-ratio' === $css_property ) {
// For aspect ratio to work, other dimensions rules must be unset.
// This ensures that a fixed height does not override the aspect ratio.
$declarations[] = array(
'name' => 'min-height',
'value' => 'unset',
);
}

$declarations[] = array(
'name' => $css_property,
'value' => $value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,17 @@ export default function AspectRatioTool( {
onChange = () => {},
options = DEFAULT_ASPECT_RATIO_OPTIONS,
defaultValue = DEFAULT_ASPECT_RATIO_OPTIONS[ 0 ].value,
hasValue,
isShownByDefault = true,
} ) {
// Match the CSS default so if the value is used directly in CSS it will look correct in the control.
const displayValue = value ?? 'auto';

return (
<ToolsPanelItem
hasValue={ () => displayValue !== defaultValue }
hasValue={
hasValue ? hasValue : () => displayValue !== defaultValue
}
label={ __( 'Aspect ratio' ) }
onDeselect={ () => onChange( undefined ) }
isShownByDefault={ isShownByDefault }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
import SpacingSizesControl from '../spacing-sizes-control';
import HeightControl from '../height-control';
import ChildLayoutControl from '../child-layout-control';
import AspectRatioTool from '../dimensions-tool/aspect-ratio-tool';
import { cleanEmptyObject } from '../../hooks/utils';
import { setImmutably } from '../../utils/object';

Expand All @@ -39,6 +40,7 @@ export function useHasDimensionsPanel( settings ) {
const hasMargin = useHasMargin( settings );
const hasGap = useHasGap( settings );
const hasMinHeight = useHasMinHeight( settings );
const hasAspectRatio = useHasAspectRatio( settings );
const hasChildLayout = useHasChildLayout( settings );

return (
Expand All @@ -49,6 +51,7 @@ export function useHasDimensionsPanel( settings ) {
hasMargin ||
hasGap ||
hasMinHeight ||
hasAspectRatio ||
hasChildLayout )
);
}
Expand Down Expand Up @@ -77,6 +80,10 @@ function useHasMinHeight( settings ) {
return settings?.dimensions?.minHeight;
}

function useHasAspectRatio( settings ) {
return settings?.dimensions?.aspectRatio;
}

function useHasChildLayout( settings ) {
const {
type: parentLayoutType = 'default',
Expand Down Expand Up @@ -192,6 +199,7 @@ const DEFAULT_CONTROLS = {
margin: true,
blockGap: true,
minHeight: true,
aspectRatio: true,
childLayout: true,
};

Expand Down Expand Up @@ -346,15 +354,43 @@ export default function DimensionsPanel( {
const showMinHeightControl = useHasMinHeight( settings );
const minHeightValue = decodeValue( inheritedValue?.dimensions?.minHeight );
const setMinHeightValue = ( newValue ) => {
const tempValue = setImmutably(
value,
[ 'dimensions', 'minHeight' ],
newValue
);
// Apply min-height, while removing any applied aspect ratio.
onChange(
setImmutably( value, [ 'dimensions', 'minHeight' ], newValue )
setImmutably(
tempValue,
[ 'dimensions', 'aspectRatio' ],
undefined
)
);
};
const resetMinHeightValue = () => {
setMinHeightValue( undefined );
};
const hasMinHeightValue = () => !! value?.dimensions?.minHeight;

// Aspect Ratio
const showAspectRatioControl = useHasAspectRatio( settings );
const aspectRatioValue = decodeValue(
inheritedValue?.dimensions?.aspectRatio
);
const setAspectRatioValue = ( newValue ) => {
const tempValue = setImmutably(
value,
[ 'dimensions', 'aspectRatio' ],
newValue
);
// Apply aspect-ratio, while removing any applied min-height.
onChange(
setImmutably( tempValue, [ 'dimensions', 'minHeight' ], undefined )
);
};
const hasAspectRatioValue = () => !! value?.dimensions?.aspectRatio;

// Child Layout
const showChildLayoutControl = useHasChildLayout( settings );
const childLayout = inheritedValue?.layout;
Expand Down Expand Up @@ -397,6 +433,7 @@ export default function DimensionsPanel( {
dimensions: {
...previousValue?.dimensions,
minHeight: undefined,
aspectRatio: undefined,
},
};
}, [] );
Expand Down Expand Up @@ -617,6 +654,18 @@ export default function DimensionsPanel( {
/>
</ToolsPanelItem>
) }
{ showAspectRatioControl && (
<AspectRatioTool
hasValue={ hasAspectRatioValue }
value={ aspectRatioValue }
onChange={ setAspectRatioValue }
panelId={ panelId }
isShownByDefault={
defaultControls.aspectRatio ??
DEFAULT_CONTROLS.aspectRatio
}
/>
) }
{ showChildLayoutControl && (
<VStack
as={ ToolsPanelItem }
Expand Down
15 changes: 9 additions & 6 deletions packages/block-editor/src/components/global-styles/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const VALID_SETTINGS = [
'color.palette',
'color.text',
'custom',
'dimensions.aspectRatio',
'dimensions.minHeight',
'layout.contentSize',
'layout.definitions',
Expand Down Expand Up @@ -344,12 +345,14 @@ export function useSettingsForBlockElement(
}
} );

if ( ! supportedStyles.includes( 'minHeight' ) ) {
updatedSettings.dimensions = {
...updatedSettings.dimensions,
minHeight: false,
};
}
[ 'aspectRatio', 'minHeight' ].forEach( ( key ) => {
if ( ! supportedStyles.includes( key ) ) {
updatedSettings.dimensions = {
...updatedSettings.dimensions,
[ key ]: false,
};
}
} );

[ 'radius', 'color', 'style', 'width' ].forEach( ( key ) => {
if (
Expand Down
Loading

1 comment on commit 2e01f22

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in 2e01f22.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7646878568
📝 Reported issues:

Please sign in to comment.