Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backport-changelog/7.1/12077.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
https://github.com/WordPress/wordpress-develop/pull/12077

* https://github.com/WordPress/gutenberg/pull/78795
20 changes: 18 additions & 2 deletions lib/block-supports/dimensions.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,22 @@ function gutenberg_apply_dimensions_support( $block_type, $block_attributes ) {
return $attributes;
}

/**
* Checks whether an aspectRatio block-support value is explicitly set.
*
* @param mixed $aspect_ratio Aspect-ratio value.
* @return bool Whether the value is an explicit aspect ratio.
*/
function gutenberg_is_explicit_aspect_ratio_value( $aspect_ratio ) {
if ( ! is_string( $aspect_ratio ) && ! is_numeric( $aspect_ratio ) ) {
return false;
}

$aspect_ratio = strtolower( trim( (string) $aspect_ratio ) );

return '' !== $aspect_ratio && 'auto' !== $aspect_ratio;
}

/**
* Renders server-side dimensions styles to the block wrapper.
* This block support uses the `render_block` hook to ensure that
Expand All @@ -105,7 +121,7 @@ function gutenberg_render_dimensions_support( $block_content, $block ) {

// To ensure the aspect ratio does not get overridden by `minHeight` or `height` unset any existing rule.
if (
isset( $dimensions_block_styles['aspectRatio'] )
gutenberg_is_explicit_aspect_ratio_value( $dimensions_block_styles['aspectRatio'] )
) {
$dimensions_block_styles['minHeight'] = 'unset';
$dimensions_block_styles['height'] = 'unset';
Expand Down Expand Up @@ -142,7 +158,7 @@ function gutenberg_render_dimensions_support( $block_content, $block ) {
foreach ( explode( ' ', $styles['classnames'] ) as $class_name ) {
if (
str_contains( $class_name, 'aspect-ratio' ) &&
! isset( $block_attributes['style']['dimensions']['aspectRatio'] )
! gutenberg_is_explicit_aspect_ratio_value( $block_attributes['style']['dimensions']['aspectRatio'] ?? null )
) {
continue;
}
Expand Down
55 changes: 52 additions & 3 deletions lib/block-supports/states.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,54 @@ function gutenberg_get_state_declarations_with_fallback_border_styles( $declarat
return $declarations;
}

/**
* Adds fallback dimension styles for aspectRatio and height block-support values.
*
* @param array $state_style State style object.
* @return array State style object with fallback dimension styles applied where needed.
*/
function gutenberg_get_state_style_with_fallback_dimension_styles( $state_style ) {
if ( ! is_array( $state_style ) ) {
return $state_style;
}

$dimensions = isset( $state_style['dimensions'] ) && is_array( $state_style['dimensions'] )
? $state_style['dimensions']
: array();

if ( empty( $dimensions ) ) {
return $state_style;
}

if ( gutenberg_is_explicit_aspect_ratio_value( $dimensions['aspectRatio'] ?? null ) ) {
return array_replace_recursive(
$state_style,
array(
'dimensions' => array(
'minHeight' => 'unset',
'height' => 'unset',
),
)
);
}

$has_min_height = isset( $dimensions['minHeight'] ) && ( is_string( $dimensions['minHeight'] ) || is_numeric( $dimensions['minHeight'] ) ) && '' !== trim( (string) $dimensions['minHeight'] );
$has_height = isset( $dimensions['height'] ) && ( is_string( $dimensions['height'] ) || is_numeric( $dimensions['height'] ) ) && '' !== trim( (string) $dimensions['height'] );

if ( $has_min_height || $has_height ) {
return array_replace_recursive(
$state_style,
array(
'dimensions' => array(
'aspectRatio' => 'unset',
),
)
);
}

return $state_style;
}

/**
* Adds a style fragment to a selector-keyed state style group.
*
Expand Down Expand Up @@ -216,8 +264,9 @@ function gutenberg_get_block_state_style_rules( $state_styles, $block_type, $rul
}

foreach ( gutenberg_get_state_style_groups( $state_style, $block_selectors ) as $group ) {
$style = gutenberg_get_state_style_with_fallback_dimension_styles( $group['style'] );
$compiled = gutenberg_style_engine_get_styles(
gutenberg_normalize_state_style_for_css_output( $group['style'] )
gutenberg_normalize_state_style_for_css_output( $style )
);

if ( ! empty( $compiled['declarations'] ) ) {
Expand Down Expand Up @@ -431,8 +480,8 @@ function gutenberg_render_block_states_support( $block_content, $block ) {
*/
$style_rules = array();
foreach ( $css_rules as $rule ) {
$declarations = array();
foreach ( $rule['declarations'] as $property => $value ) {
$declarations = $rule['declarations'];
foreach ( $declarations as $property => $value ) {
$declarations[ $property ] = is_string( $value ) && str_contains( $value, '!important' )
? $value
: $value . ' !important';
Expand Down
13 changes: 4 additions & 9 deletions packages/block-editor/src/components/dimensions-tool/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function DimensionsTool( {
aspectRatioOptions, // Default options handled by AspectRatioTool.
defaultAspectRatio = 'auto', // Match CSS default value for aspect-ratio.
scaleOptions, // Default options handled by ScaleTool.
defaultScale = 'fill', // Match CSS default value for object-fit.
defaultScale = 'cover',
unitsOptions, // Default options handled by UnitControl.
tools = [ 'aspectRatio', 'widthHeight', 'scale' ],
} ) {
Expand All @@ -73,21 +73,19 @@ function DimensionsTool( {
value.aspectRatio === undefined || value.aspectRatio === 'auto'
? null
: value.aspectRatio;
const scale =
value.scale === undefined || value.scale === 'fill'
? null
: value.scale;
const scale = value.scale === undefined ? null : value.scale;

// Keep track of state internally, so when the value is cleared by means
// other than directly editing that field, it's easier to restore the
// previous value.
const [ lastScale, setLastScale ] = useState( scale );
const [ lastAspectRatio, setLastAspectRatio ] = useState( aspectRatio );
const hasCustomAspectRatio = !! ( width && height );

// 'custom' is not a valid value for CSS aspect-ratio, but it is used in the
// dropdown to indicate that setting both the width and height is the same
// as a custom aspect ratio.
const aspectRatioValue = width && height ? 'custom' : lastAspectRatio;
const aspectRatioValue = hasCustomAspectRatio ? 'custom' : aspectRatio;

const showScaleControl = aspectRatio || ( width && height );

Expand Down Expand Up @@ -197,9 +195,6 @@ function DimensionsTool( {
onChange={ ( nextScale ) => {
const nextValue = { ...value };

// 'fill' is CSS default, so it gets treated as null.
nextScale = nextScale === 'fill' ? null : nextScale;

setLastScale( nextScale );

// Update scale.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ export default function ScaleTool( {
defaultValue = DEFAULT_SCALE_OPTIONS[ 0 ].value,
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 ?? 'fill';
const displayValue = value ?? defaultValue;

const scaleHelp = useMemo( () => {
return options.reduce( ( acc, option ) => {
Expand Down
100 changes: 89 additions & 11 deletions packages/block-editor/src/components/dimensions-tool/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ import { useState } from '@wordpress/element';
import DimensionsTool from '../';

const EMPTY_OBJECT = {};
const ASPECT_RATIO_OPTIONS = [
{ label: 'Original', value: 'auto' },
{ label: '16/9', value: '16/9' },
{ label: '4/3', value: '4/3' },
{
label: 'Custom',
value: 'custom',
disabled: true,
hidden: true,
},
];

function Example( { initialValue, onChange, ...props } ) {
const [ value, setValue ] = useState( initialValue );
Expand All @@ -33,16 +44,27 @@ function Example( { initialValue, onChange, ...props } ) {
} }
defaultScale="cover"
defaultAspectRatio="auto"
aspectRatioOptions={ [
{ label: 'Original', value: 'auto' },
{ label: '16/9', value: '16/9' },
{
label: 'Custom',
value: 'custom',
disabled: true,
hidden: true,
},
] }
aspectRatioOptions={ ASPECT_RATIO_OPTIONS }
value={ value }
{ ...props }
/>
</ToolsPanel>
);
}

function ControlledExample( { value, onChange, ...props } ) {
return (
<ToolsPanel
label="Dimensions"
panelId="panel-id"
resetAll={ () => onChange( EMPTY_OBJECT ) }
>
<DimensionsTool
panelId="panel-id"
onChange={ onChange }
defaultScale="cover"
defaultAspectRatio="auto"
aspectRatioOptions={ ASPECT_RATIO_OPTIONS }
value={ value }
{ ...props }
/>
Expand All @@ -60,6 +82,39 @@ function Example( { initialValue, onChange, ...props } ) {
// properties are treated differently from missing properties.

describe( 'DimensionsTool', () => {
describe( 'controlled values', () => {
it( 'updates the aspect ratio control when the value prop changes', () => {
const onChange = jest.fn();
const { rerender } = render(
<ControlledExample
value={ { aspectRatio: '16/9' } }
onChange={ onChange }
/>
);
const aspectRatioSelect = screen.getByRole( 'combobox', {
name: 'Aspect ratio',
} );

expect( aspectRatioSelect ).toHaveValue( '16/9' );

rerender(
<ControlledExample
value={ { aspectRatio: '4/3' } }
onChange={ onChange }
/>
);
expect( aspectRatioSelect ).toHaveValue( '4/3' );

rerender(
<ControlledExample
value={ EMPTY_OBJECT }
onChange={ onChange }
/>
);
expect( aspectRatioSelect ).toHaveValue( 'auto' );
} );
} );

describe( 'updating aspectRatio', () => {
it( 'when starting with empty initial state, setting aspectRatio also sets scale (0000) -> (1100)', async () => {
const user = userEvent.setup();
Expand Down Expand Up @@ -323,7 +378,30 @@ describe( 'DimensionsTool', () => {
} );

describe( 'updating scale', () => {
// No custom interactions here. Things should just update normally.
it( 'when default scale is cover, setting scale to fill preserves the fill value', async () => {
const user = userEvent.setup();
const onChange = jest.fn();

const initialValue = {
aspectRatio: '16/9',
scale: 'cover',
};

render(
<Example initialValue={ initialValue } onChange={ onChange } />
);

const scaleFillRadio = screen.getByRole( 'radio', {
name: 'Fill',
} );

await user.click( scaleFillRadio );
expect( scaleFillRadio ).toBeChecked();

expect( onChange.mock.calls ).toStrictEqual( [
[ { aspectRatio: '16/9', scale: 'fill' } ],
] );
} );
} );

describe( 'updating dimensions', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import { setImmutably } from '../../utils/object';
import {
DEFAULT_BLOCK_STYLE_STATE,
hasPseudoBlockStyleState,
isDefaultBlockStyleState,
hasViewportBlockStyleState,
} from '../../hooks/block-style-state';

Expand Down Expand Up @@ -96,7 +95,7 @@ function hasWidth( settings ) {

function hasAspectRatio( settings, styleState = DEFAULT_BLOCK_STYLE_STATE ) {
return (
isDefaultBlockStyleState( styleState ) &&
! hasPseudoBlockStyleState( styleState ) &&
settings?.dimensions?.aspectRatio
);
}
Expand Down
17 changes: 14 additions & 3 deletions packages/block-editor/src/hooks/dimensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@ export function hasDimensionsSupport( blockName, feature = 'any' ) {
return !! support?.[ feature ];
}

export function isExplicitAspectRatio( aspectRatio ) {
if ( ! aspectRatio ) {
return false;
}

return `${ aspectRatio }`.trim().toLowerCase() !== 'auto';
}

export default {
useBlockProps,
attributeKeys: [ 'height', 'minHeight', 'width', 'style' ],
Expand All @@ -210,21 +218,24 @@ function useBlockProps( { name, height, minHeight, style } ) {
return {};
}

const hasExplicitAspectRatio = isExplicitAspectRatio(
style?.dimensions?.aspectRatio
);
const className = clsx( {
'has-aspect-ratio': !! style?.dimensions?.aspectRatio,
'has-aspect-ratio': hasExplicitAspectRatio,
} );

// Allow dimensions-based inline style overrides to override any global styles rules that
// might be set for the block, and therefore affect the display of the aspect ratio.
const inlineStyleOverrides = {};

// Apply rules to unset incompatible styles.
// Note that a set `aspectRatio` will win out if both an aspect ratio and height-related properties are set.
// Note that an explicit `aspectRatio` will win out if both an aspect ratio and height-related properties are set.
// This is because the aspect ratio is a newer block support, so (in theory) any aspect ratio
// that is set should be intentional and should override any existing height properties. The Cover block
// and dimensions controls have logic that will manually clear the aspect ratio if height properties
// are set.
if ( style?.dimensions?.aspectRatio ) {
if ( hasExplicitAspectRatio ) {
// To ensure the aspect ratio does not get overridden by `minHeight` or `height` unset any existing rule.
inlineStyleOverrides.minHeight = 'unset';
inlineStyleOverrides.height = 'unset';
Expand Down
Loading
Loading