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

* https://github.com/WordPress/gutenberg/pull/79073
2 changes: 1 addition & 1 deletion docs/reference-guides/block-api/block-supports.md
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,7 @@ For the `flow` layout type only, determines display of the "Inner blocks use con
- Type: `boolean`
- Default value: `false`

For the `flex` layout type only, determines display of sizing controls (Fit/Fill/Fixed) on all child blocks of the flex block.
For the `flex` layout type only, determines display of sizing controls (Fit/Grow/Max width/Fixed) on all child blocks of the flex block.

### layout.allowVerticalAlignment

Expand Down
38 changes: 35 additions & 3 deletions lib/block-supports/layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,13 +317,45 @@ function gutenberg_get_child_layout_style_rules( $selector, $child_layout, $pare
return array_key_exists( $property, $viewport_overrides );
};

$self_stretch = $child_layout['selfStretch'] ?? null;
$self_stretch = $child_layout['selfStretch'] ?? null;
$base_self_stretch = $base_child_layout['selfStretch'] ?? null;

/*
* These are the serialized `selfStretch` values. `max` used to be called
* "Fixed" in the UI, but was renamed and replaced by `fixedNoShrink`.
*/
$flex_child_layout_values = array(
'fit' => 'fit',
'grow' => 'fill',
'max' => 'fixed',
'fixed' => 'fixedNoShrink',
);
$flex_size_values = array(
$flex_child_layout_values['max'],
$flex_child_layout_values['fixed'],
);

if ( null === $viewport_overrides || $has_viewport_property_override( 'selfStretch' ) || $has_viewport_property_override( 'flexSize' ) ) {
if ( 'fixed' === $self_stretch && isset( $child_layout['flexSize'] ) ) {
if (
null !== $viewport_overrides &&
( $flex_child_layout_values['fit'] === $self_stretch || $flex_child_layout_values['grow'] === $self_stretch ) &&
in_array( $base_self_stretch, $flex_size_values, true ) &&
isset( $base_child_layout['flexSize'] )
) {
$child_layout_declarations['flex-basis'] = 'unset';
if ( $flex_child_layout_values['fixed'] === $base_self_stretch ) {
$child_layout_declarations['flex-shrink'] = 'unset';
}
}
if ( in_array( $self_stretch, $flex_size_values, true ) && isset( $child_layout['flexSize'] ) ) {
$child_layout_declarations['flex-basis'] = $child_layout['flexSize'];
if ( $flex_child_layout_values['fixed'] === $self_stretch ) {
$child_layout_declarations['flex-shrink'] = '0';
} elseif ( null !== $viewport_overrides && $flex_child_layout_values['fixed'] === $base_self_stretch ) {
$child_layout_declarations['flex-shrink'] = 'unset';
}
$child_layout_declarations['box-sizing'] = 'border-box';
} elseif ( 'fill' === $self_stretch ) {
} elseif ( $flex_child_layout_values['grow'] === $self_stretch ) {
$child_layout_declarations['flex-grow'] = '1';
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,51 @@ import { useGetNumberOfBlocksBeforeCell } from '../grid/use-get-number-of-blocks
import { store as blockEditorStore } from '../../store';
import { useSettings } from '../use-settings';

function helpText( selfStretch, parentLayout ) {
const { orientation = 'horizontal' } = parentLayout;
// These are the serialized `selfStretch` values. `max` used to be called
// "Fixed" in the UI, but was renamed and replaced by `fixedNoShrink`.
const FLEX_CHILD_LAYOUT_VALUES = {
fit: 'fit',
grow: 'fill',
max: 'fixed',
fixed: 'fixedNoShrink',
};

if ( selfStretch === 'fill' ) {
const FLEX_SIZE_VALUES = [
FLEX_CHILD_LAYOUT_VALUES.max,
FLEX_CHILD_LAYOUT_VALUES.fixed,
];

function isFlexSizeValue( value ) {
return FLEX_SIZE_VALUES.includes( value );
}

function maxSizeLabel( parentLayout ) {
const { orientation = 'horizontal' } = parentLayout ?? {};
return orientation === 'horizontal'
? _x( 'Max', 'Block with maximum width in flex layout' )
: _x( 'Max', 'Block with maximum height in flex layout' );
}

function helpText( flexControlValue, parentLayout ) {
const { orientation = 'horizontal' } = parentLayout ?? {};

if ( flexControlValue === FLEX_CHILD_LAYOUT_VALUES.grow ) {
return __( 'Stretch to fill available space.' );
}
if ( selfStretch === 'fixed' && orientation === 'horizontal' ) {
if (
flexControlValue === FLEX_CHILD_LAYOUT_VALUES.max &&
orientation === 'horizontal'
) {
return __( 'Specify a maximum width.' );
} else if ( flexControlValue === FLEX_CHILD_LAYOUT_VALUES.max ) {
return __( 'Specify a maximum height.' );
}
if (
flexControlValue === FLEX_CHILD_LAYOUT_VALUES.fixed &&
orientation === 'horizontal'
) {
return __( 'Specify a fixed width.' );
} else if ( selfStretch === 'fixed' ) {
} else if ( flexControlValue === FLEX_CHILD_LAYOUT_VALUES.fixed ) {
return __( 'Specify a fixed height.' );
}
return __( 'Fit contents.' );
Expand Down Expand Up @@ -99,6 +135,8 @@ function FlexControls( {
} ) {
const { selfStretch, flexSize } = childLayout;
const { orientation = 'horizontal' } = parentLayout ?? {};
const flexControlValue = selfStretch || FLEX_CHILD_LAYOUT_VALUES.fit;
const hasFlexSizeValue = isFlexSizeValue( flexControlValue );
const hasFlexValue = () => !! selfStretch;
const flexResetLabel =
orientation === 'horizontal' ? __( 'Width' ) : __( 'Height' );
Expand All @@ -121,13 +159,13 @@ function FlexControls( {
};

useEffect( () => {
if ( selfStretch === 'fixed' && ! flexSize ) {
if ( isFlexSizeValue( selfStretch ) && ! flexSize ) {
onChange( {
...childLayout,
selfStretch: 'fit',
selfStretch: FLEX_CHILD_LAYOUT_VALUES.fit,
} );
}
}, [] );
}, [] ); // eslint-disable-line react-hooks/exhaustive-deps

return (
<VStack
Expand All @@ -142,10 +180,12 @@ function FlexControls( {
<ToggleGroupControl
size="__unstable-large"
label={ childLayoutOrientation( parentLayout ) }
value={ selfStretch || 'fit' }
help={ helpText( selfStretch, parentLayout ) }
value={ flexControlValue }
help={ helpText( flexControlValue, parentLayout ) }
onChange={ ( value ) => {
const newFlexSize = value !== 'fixed' ? null : flexSize;
const newFlexSize = isFlexSizeValue( value )
? flexSize
: null;
onChange( {
selfStretch: value,
flexSize: newFlexSize,
Expand All @@ -154,37 +194,42 @@ function FlexControls( {
isBlock
>
<ToggleGroupControlOption
key="fit"
value="fit"
key={ FLEX_CHILD_LAYOUT_VALUES.fit }
value={ FLEX_CHILD_LAYOUT_VALUES.fit }
label={ _x(
'Fit',
'Intrinsic block width in flex layout'
) }
/>
<ToggleGroupControlOption
key="fill"
value="fill"
key={ FLEX_CHILD_LAYOUT_VALUES.grow }
value={ FLEX_CHILD_LAYOUT_VALUES.grow }
label={ _x(
'Grow',
'Block with expanding width in flex layout'
) }
/>
<ToggleGroupControlOption
key="fixed"
value="fixed"
key={ FLEX_CHILD_LAYOUT_VALUES.max }
value={ FLEX_CHILD_LAYOUT_VALUES.max }
label={ maxSizeLabel( parentLayout ) }
/>
<ToggleGroupControlOption
key={ FLEX_CHILD_LAYOUT_VALUES.fixed }
value={ FLEX_CHILD_LAYOUT_VALUES.fixed }
label={ _x(
'Fixed',
'Block with fixed width in flex layout'
) }
/>
</ToggleGroupControl>
{ selfStretch === 'fixed' && (
{ hasFlexSizeValue && (
<UnitControl
size="__unstable-large"
units={ units }
onChange={ ( value ) => {
onChange( {
selfStretch,
selfStretch: flexControlValue,
flexSize: value,
} );
} }
Expand All @@ -199,7 +244,7 @@ function FlexControls( {
}

export function childLayoutOrientation( parentLayout ) {
const { orientation = 'horizontal' } = parentLayout;
const { orientation = 'horizontal' } = parentLayout ?? {};
return orientation === 'horizontal' ? __( 'Width' ) : __( 'Height' );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import { __experimentalToolsPanel as ToolsPanel } from '@wordpress/components';
*/
import ChildLayoutControl from '../';

jest.mock( '../../use-settings', () => ( {
useSettings: () => [ undefined ],
} ) );

jest.mock( '@wordpress/data/src/components/use-select', () =>
jest.fn( ( mapSelect ) => {
if ( typeof mapSelect === 'function' ) {
Expand Down Expand Up @@ -123,4 +127,64 @@ describe( 'ChildLayoutControl', () => {
columnSpan: 1,
} );
} );

it( 'shows legacy fixed flex sizing as max', () => {
renderControl( {
parentLayout: {
type: 'flex',
orientation: 'horizontal',
},
value: {
selfStretch: 'fixed',
flexSize: '320px',
},
} );

expect( screen.getByRole( 'radio', { name: 'Max' } ) ).toBeChecked();
} );

it( 'sets fixedNoShrink when selecting fixed flex sizing', async () => {
const user = userEvent.setup();
const onChange = jest.fn();

renderControl( {
parentLayout: {
type: 'flex',
orientation: 'horizontal',
},
value: {},
onChange,
} );

await user.click( screen.getByRole( 'radio', { name: 'Fixed' } ) );

expect( onChange ).toHaveBeenCalledWith( {
selfStretch: 'fixedNoShrink',
flexSize: undefined,
} );
} );

it( 'sets legacy fixed when selecting max sizing', async () => {
const user = userEvent.setup();
const onChange = jest.fn();

renderControl( {
parentLayout: {
type: 'flex',
orientation: 'horizontal',
},
value: {
selfStretch: 'fixedNoShrink',
flexSize: '320px',
},
onChange,
} );

await user.click( screen.getByRole( 'radio', { name: 'Max' } ) );

expect( onChange ).toHaveBeenCalledWith( {
selfStretch: 'fixed',
flexSize: '320px',
} );
} );
} );
43 changes: 41 additions & 2 deletions packages/block-editor/src/hooks/layout-child.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ const RESPONSIVE_BREAKPOINTS = {
tablet: '@media (480px < width <= 782px)',
};

// These are the serialized `selfStretch` values. `max` used to be called
// "Fixed" in the UI, but was renamed and replaced by `fixedNoShrink`.
const FLEX_CHILD_LAYOUT_VALUES = {
fit: 'fit',
grow: 'fill',
max: 'fixed',
fixed: 'fixedNoShrink',
};

const FLEX_SIZE_VALUES = [
FLEX_CHILD_LAYOUT_VALUES.max,
FLEX_CHILD_LAYOUT_VALUES.fixed,
];

function isFlexSizeValue( value ) {
return FLEX_SIZE_VALUES.includes( value );
}

function serializeRule( { selector, declarations } ) {
return `${ selector } {
${ Object.entries( declarations )
Expand Down Expand Up @@ -60,6 +78,7 @@ export function getChildLayoutStyleRules( {
columnSpan,
rowSpan,
} = effectiveLayout;
const baseSelfStretch = layout.selfStretch;
const { columnCount, minimumColumnWidth } = parentLayout;
const rules = [];

Expand All @@ -69,10 +88,30 @@ export function getChildLayoutStyleRules( {
hasViewportOverride( 'selfStretch' ) ||
hasViewportOverride( 'flexSize' )
) {
if ( selfStretch === 'fixed' && flexSize ) {
if (
hasViewportOverrides &&
( selfStretch === FLEX_CHILD_LAYOUT_VALUES.fit ||
selfStretch === FLEX_CHILD_LAYOUT_VALUES.grow ) &&
isFlexSizeValue( baseSelfStretch ) &&
layout.flexSize
) {
declarations[ 'flex-basis' ] = 'unset';
if ( baseSelfStretch === FLEX_CHILD_LAYOUT_VALUES.fixed ) {
declarations[ 'flex-shrink' ] = 'unset';
}
}
if ( isFlexSizeValue( selfStretch ) && flexSize ) {
declarations[ 'flex-basis' ] = flexSize;
if ( selfStretch === FLEX_CHILD_LAYOUT_VALUES.fixed ) {
declarations[ 'flex-shrink' ] = '0';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I noticed one thing while testing with responsive styles, if a responsive override switches to max width and the default is fixed, the responsive rule only includes flex-basis and it doesn’t reset flex-shrink.

I guess we need to explicitly include flex-shrink: 1;?

Is that something to look at in this PR or a follow up?

@media (width <= 480px) {
    .wp-container-content-067f0074 {
        flex-basis: 244px;
        // inherits  flex-shrink: 0; from default/desktop
    }
}

.wp-container-content-067f0074 {
    flex-basis: 260px;
    flex-shrink: 0;
}

Maybe some tests could cover the final cascade behavior

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.

Good catch! Should be fixed now.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice. I can see flex-shrink: unset; now. 🌮

} else if (
hasViewportOverrides &&
baseSelfStretch === FLEX_CHILD_LAYOUT_VALUES.fixed
) {
declarations[ 'flex-shrink' ] = 'unset';
}
declarations[ 'box-sizing' ] = 'border-box';
} else if ( selfStretch === 'fill' ) {
} else if ( selfStretch === FLEX_CHILD_LAYOUT_VALUES.grow ) {
declarations[ 'flex-grow' ] = '1';
}
}
Expand Down
Loading
Loading