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/12326.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
https://github.com/WordPress/wordpress-develop/pull/12326

* https://github.com/WordPress/gutenberg/pull/79520
56 changes: 42 additions & 14 deletions lib/block-supports/layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -472,13 +472,16 @@ function gutenberg_get_child_layout_style_rules( $selector, $child_layout, $pare
* @return string CSS styles, or empty string.
*/
function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null, $should_skip_gap_serialization = false, $fallback_gap_value = '0.5em', $block_spacing = null, $options = array() ) {
$base_layout = is_array( $layout ) ? $layout : array();
$viewport_overrides = $options['viewport_overrides'] ?? null;
$layout_for_styles = null === $viewport_overrides ? $base_layout : array_replace( $base_layout, $viewport_overrides );
$layout_type = $base_layout['type'] ?? 'default';
$rules_group = $options['rules_group'] ?? null;
$has_block_gap_override = ! empty( $options['has_block_gap_override'] );
$should_output_block_gap = null === $viewport_overrides || $has_block_gap_override;
$base_layout = is_array( $layout ) ? $layout : array();
$viewport_overrides = $options['viewport_overrides'] ?? null;
$layout_for_styles = null === $viewport_overrides ? $base_layout : array_replace( $base_layout, $viewport_overrides );
$layout_type = $base_layout['type'] ?? 'default';
$rules_group = $options['rules_group'] ?? null;
$has_block_gap_override = ! empty( $options['has_block_gap_override'] );
$should_output_block_gap = null === $viewport_overrides || $has_block_gap_override;
// Viewport styles only store changed fields. If a field is present with null,
// the user cleared a value inherited from the default viewport, so check
// whether the key exists rather than whether the value is truthy.
$has_viewport_property_override = static function ( $property ) use ( $viewport_overrides ) {
return array_key_exists( $property, $viewport_overrides );
};
Expand Down Expand Up @@ -521,8 +524,28 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
$wide_size = $layout_for_styles['wideSize'] ?? '';
$justify_content = $layout_for_styles['justifyContent'] ?? 'center';

$all_max_width_value = $content_size ? $content_size : $wide_size;
$wide_max_width_value = $wide_size ? $wide_size : $content_size;
// Check if viewport-specific ("override") values exist. Null values are valid and mean the user cleared a value inherited from the default viewport.
$has_justify_content_override = null !== $viewport_overrides && $has_viewport_property_override( 'justifyContent' );
$has_content_size_override = null !== $viewport_overrides && $has_viewport_property_override( 'contentSize' );
$has_wide_size_override = null !== $viewport_overrides && $has_viewport_property_override( 'wideSize' );

/* Styles should be output either if there are no viewport overrides (this is the default case), or if the user has set a new viewport-specific
* value for contentSize or wideSize. If a viewport clears a custom constrained size, reset to the global layout variable.
*/
$should_output_constrained_sizes = null === $viewport_overrides || $has_content_size_override || $has_wide_size_override;
$is_resetting_constrained_sizes = null !== $viewport_overrides &&
(
( $has_content_size_override && ! $content_size ) ||
( $has_wide_size_override && ! $wide_size )
);

// If a viewport clears a custom constrained size, reset to the global layout variable.
$all_max_width_value = $content_size
? $content_size
: ( $wide_size && ! $has_content_size_override ? $wide_size : 'var(--wp--style--global--content-size, none)' );
$wide_max_width_value = $wide_size
? $wide_size
: ( $content_size && ! $has_wide_size_override ? $content_size : 'var(--wp--style--global--wide-size, none)' );

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Don't know how I missed all this in the first pass 🤦

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It would be good to add some code comments because the code is quite mysterious. I'm not really sure what it all means.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's fair! I added some comments on this change and the similar logic in other layout types. Hopefully they make things a bit clearer.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks 😄


// Make sure there is a single CSS rule, and all tags are stripped for security.
$all_max_width_value = safecss_filter_attr( explode( ';', $all_max_width_value )[0] );
Expand All @@ -531,9 +554,7 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
$margin_left = 'left' === $justify_content ? '0 !important' : 'auto !important';
$margin_right = 'right' === $justify_content ? '0 !important' : 'auto !important';

$has_justify_content_override = null !== $viewport_overrides && $has_viewport_property_override( 'justifyContent' );
$should_output_constrained_sizes = null === $viewport_overrides || $has_viewport_property_override( 'contentSize' ) || $has_viewport_property_override( 'wideSize' );
if ( $should_output_constrained_sizes && ( $content_size || $wide_size ) ) {
if ( $should_output_constrained_sizes && ( $content_size || $wide_size || $is_resetting_constrained_sizes ) ) {
$content_size_declarations = array(
'max-width' => $all_max_width_value,
);
Expand Down Expand Up @@ -673,6 +694,9 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
$vertical_alignment_options += array( 'space-between' => 'space-between' );
}

/* Styles should be output either if there are no viewport overrides (this is the default case), or if the user has set a new viewport-specific
* value for any of the flex properties.
*/
$should_output_flex_wrap = null === $viewport_overrides || $has_viewport_property_override( 'flexWrap' );
$should_output_flex_orientation = null === $viewport_overrides || $has_viewport_property_override( 'orientation' );
$should_output_flex_justification = null === $viewport_overrides || $has_viewport_property_override( 'justifyContent' ) || $has_viewport_property_override( 'orientation' );
Expand Down Expand Up @@ -803,6 +827,9 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
$responsive_gap_value = '0px';
}

/* Styles should be output either if there are no viewport overrides (this is the default case), or if the user has set a new viewport-specific
* value for any of the grid properties.
*/
$should_output_grid_columns = null === $viewport_overrides || $has_viewport_property_override( 'minimumColumnWidth' ) || $has_viewport_property_override( 'columnCount' ) || $has_viewport_property_override( 'autoFit' );
$uses_gap_in_grid_columns = ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['minimumColumnWidth'] );
if ( $has_block_gap_override && $uses_gap_in_grid_columns ) {
Expand All @@ -812,8 +839,9 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
$should_output_grid_rows = ( null === $viewport_overrides || $has_viewport_property_override( 'rowCount' ) ) && ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['rowCount'] );
$grid_declarations = array();

// When enabled, columns stretch to fill the available space using
// `auto-fit`; otherwise empty tracks are preserved with `auto-fill`.
/* When enabled, columns stretch to fill the available space using
* `auto-fit`; otherwise empty tracks are preserved with `auto-fill`.
*/
$auto_placement = ! empty( $layout_for_styles['autoFit'] ) ? 'auto-fit' : 'auto-fill';

if ( $should_output_grid_columns && ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['minimumColumnWidth'] ) ) {
Expand Down
8 changes: 5 additions & 3 deletions packages/block-editor/src/hooks/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function getResetLayout( layoutBlockSupport = {}, blockVariation ) {
} );
}

function getLayoutStateOverrides(
export function getLayoutStateOverrides(
layout = {},
baseLayout = {},
existingLayout = {}
Expand All @@ -98,11 +98,13 @@ function getLayoutStateOverrides(
);

Object.entries( layout || {} ).forEach( ( [ key, value ] ) => {
const baseHasValue = Object.hasOwn( baseLayout || {}, key );
if (
! CHILD_LAYOUT_KEYS.includes( key ) &&
value !== baseLayout?.[ key ]
) {
overrides[ key ] = value;
overrides[ key ] =
value === undefined && baseHasValue ? null : value;
}
} );

Expand Down Expand Up @@ -466,7 +468,7 @@ function LayoutPanelPure( {
const nextStateStyle = cleanEmptyObject( {
...stateStyle,
layout: getLayoutStateOverrides(
cleanEmptyObject( newLayout ),
newLayout,
baseLayout,
stateStyle?.layout
),
Expand Down
76 changes: 75 additions & 1 deletion packages/block-editor/src/hooks/test/layout.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/**
* Internal dependencies
*/
import { getResetLayout, getResponsiveLayoutStyles } from '../layout';
import {
getLayoutStateOverrides,
getResetLayout,
getResponsiveLayoutStyles,
} from '../layout';

describe( 'layout', () => {
describe( 'getResetLayout()', () => {
Expand Down Expand Up @@ -46,6 +50,28 @@ describe( 'layout', () => {
} );
} );

describe( 'getLayoutStateOverrides()', () => {
it( 'preserves explicit unsets for layout values inherited from the default state', () => {
expect(
getLayoutStateOverrides(
{ type: 'grid', columnCount: undefined },
{ type: 'grid', columnCount: 3 }
)
).toEqual( {
columnCount: null,
} );
} );

it( 'removes undefined layout values that are not inherited from the default state', () => {
expect(
getLayoutStateOverrides(
{ type: 'grid', columnCount: undefined },
{ type: 'grid' }
)
).toBeUndefined();
} );
} );

describe( 'getResponsiveLayoutStyles()', () => {
it( 'generates responsive block gap styles for flow layouts', () => {
expect(
Expand Down Expand Up @@ -135,6 +161,54 @@ describe( 'layout', () => {
);
} );

it( 'generates responsive auto grid columns when column count is unset', () => {
expect(
getResponsiveLayoutStyles( {
attributes: {
style: {
'@mobile': {
layout: {
columnCount: null,
},
},
},
},
blockName: 'core/group',
selector: '.wp-container-test',
layout: { type: 'grid', columnCount: 3 },
hasBlockGapSupport: true,
} )
).toBe(
'@media (width <= 480px){.wp-container-test { grid-template-columns: repeat(auto-fill, minmax(min(12rem, 100%), 1fr)); container-type: inline-size; }}'
);
} );

it( 'generates responsive constrained size resets when content width is unset', () => {
const result = getResponsiveLayoutStyles( {
attributes: {
style: {
'@mobile': {
layout: {
contentSize: null,
},
},
},
},
blockName: 'core/group',
selector: '.wp-container-test',
layout: { type: 'constrained', contentSize: '800px' },
hasBlockGapSupport: true,
} );

expect( result ).toContain( '@media (width <= 480px)' );
expect( result ).toContain(
'max-width: var(--wp--style--global--content-size, none);'
);
expect( result ).toContain(
'max-width: var(--wp--style--global--wide-size, none);'
);
} );

it( 'keeps responsive grid column overrides when block gap is also changed', () => {
expect(
getResponsiveLayoutStyles( {
Expand Down
76 changes: 51 additions & 25 deletions packages/block-editor/src/layouts/constrained.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ import { BlockControls, JustifyContentControl } from '../components';
import { cleanEmptyObject, shouldSkipSerialization } from '../hooks/utils';
import { LAYOUT_DEFINITIONS } from './definitions';

const GLOBAL_CONTENT_SIZE = 'var(--wp--style--global--content-size, none)';
const GLOBAL_WIDE_SIZE = 'var(--wp--style--global--wide-size, none)';

export default {
name: 'constrained',
label: __( 'Constrained' ),
Expand Down Expand Up @@ -115,21 +118,23 @@ export default {
__next40pxDefaultSize
label={ __( 'Content width' ) }
labelPosition="top"
value={ contentSize || wideSize || '' }
value={
contentSize === null
? ''
: contentSize || wideSize || ''
}
onChange={ ( nextWidth ) => {
nextWidth =
0 > parseFloat( nextWidth )
? '0'
: nextWidth;
onChange(
cleanEmptyObject( {
...layout,
contentSize:
nextWidth !== ''
? nextWidth
: undefined,
} )
);
onChange( {
...layout,
contentSize:
nextWidth !== ''
? nextWidth
: undefined,
} );
} }
units={ units }
prefix={
Expand All @@ -149,21 +154,23 @@ export default {
__next40pxDefaultSize
label={ __( 'Wide width' ) }
labelPosition="top"
value={ wideSize || contentSize || '' }
value={
wideSize === null
? ''
: wideSize || contentSize || ''
}
onChange={ ( nextWidth ) => {
nextWidth =
0 > parseFloat( nextWidth )
? '0'
: nextWidth;
onChange(
cleanEmptyObject( {
...layout,
wideSize:
nextWidth !== ''
? nextWidth
: undefined,
} )
);
onChange( {
...layout,
wideSize:
nextWidth !== ''
? nextWidth
: undefined,
} );
} }
units={ units }
prefix={
Expand Down Expand Up @@ -273,12 +280,30 @@ export default {

const hasJustificationOverride =
hasViewportOverrides && hasViewportOverride( 'justifyContent' );
const hasContentSizeOverride =
hasViewportOverrides && hasViewportOverride( 'contentSize' );
const hasWideSizeOverride =
hasViewportOverrides && hasViewportOverride( 'wideSize' );
const shouldOutputConstrainedSizes =
! hasViewportOverrides ||
hasViewportOverride( 'contentSize' ) ||
hasViewportOverride( 'wideSize' );
hasContentSizeOverride ||
hasWideSizeOverride;
const isResettingConstrainedSizes =
hasViewportOverrides &&
( ( hasContentSizeOverride && ! contentSize ) ||
( hasWideSizeOverride && ! wideSize ) );
const contentMaxWidth =
contentSize ||
( wideSize && ! hasContentSizeOverride
? wideSize
: GLOBAL_CONTENT_SIZE );
const wideMaxWidth =
wideSize ||
( contentSize && ! hasWideSizeOverride
? contentSize
: GLOBAL_WIDE_SIZE );
const constrainedSizeDeclarations = [
`max-width: ${ contentSize ?? wideSize }`,
`max-width: ${ contentMaxWidth }`,
];
if ( ! hasViewportOverrides || hasJustificationOverride ) {
constrainedSizeDeclarations.push(
Expand All @@ -287,7 +312,8 @@ export default {
);
}
let output =
shouldOutputConstrainedSizes && ( !! contentSize || !! wideSize )
shouldOutputConstrainedSizes &&
( !! contentSize || !! wideSize || isResettingConstrainedSizes )
? `
${ appendSelectors(
selector,
Expand All @@ -296,7 +322,7 @@ export default {
${ constrainedSizeDeclarations.join( '; ' ) };
}
${ appendSelectors( selector, '> .alignwide' ) } {
max-width: ${ wideSize ?? contentSize };
max-width: ${ wideMaxWidth };
}
${ appendSelectors( selector, '> .alignfull' ) } {
max-width: none;
Expand Down
2 changes: 1 addition & 1 deletion packages/block-editor/src/layouts/grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ function GridLayoutColumnsAndRowsControl( {
columnCount: newColumnCount,
} );
} }
value={ columnCount }
value={ columnCount ?? '' }
min={ 1 }
label={ __( 'Columns' ) }
hideLabelFromVision={ ! isManualPlacement }
Expand Down
Loading
Loading