Skip to content

Commit 8ed68ea

Browse files
author
Gerardo Pacheco
authored
[Mobile] - Spacer block - Add initial support for spacing presets (#47258)
* Mobile - Spacer block - Add initial support for spacing presets * Mobile - getPxFromCssUnit - Updates evalMathExpression, before it was splitting by math symbols, this would break for complex values like nested math expressions. Now it will split the string by every number value with a unit, that way it will match all values and convert them to pixel values, to then trigger the math calculation. It will also take into account math expressions wrapped within CSS expressions and take into account this cases. * Mobile - parse-css-unit-to-px - Update logic to take into account nested calc(). It also brings back the usage of the for loop to keep consistency instead of using replace directly from the string.
1 parent 5e2269a commit 8ed68ea

File tree

9 files changed

+222
-16
lines changed

9 files changed

+222
-16
lines changed

packages/block-editor/src/components/index.native.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ export {
7474
} from './block-settings';
7575
export { default as VideoPlayer, VIDEO_ASPECT_RATIO } from './video-player';
7676

77+
export {
78+
getSpacingPresetCssVar,
79+
getCustomValueFromPreset,
80+
isValueSpacingPreset,
81+
} from './spacing-sizes-control/utils';
7782
// Content Related Components.
7883
export { default as BlockList } from './block-list';
7984
export { default as BlockMover } from './block-mover';

packages/block-editor/src/utils/parse-css-unit-to-px.js

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ function parseUnit( cssUnit ) {
2424
* @return {number} evaluated expression.
2525
*/
2626
function calculate( expression ) {
27-
return Function( `'use strict'; return (${ expression })` )();
27+
try {
28+
return Function( `'use strict'; return (${ expression })` )();
29+
} catch ( err ) {
30+
return null;
31+
}
2832
}
2933

3034
/**
@@ -117,9 +121,9 @@ function isMathExpression( cssUnit ) {
117121
function evalMathExpression( cssUnit ) {
118122
let errorFound = false;
119123
// Convert every part of the expression to px values.
120-
const cssUnitsBits = cssUnit
121-
.split( /(?!^-)[+*\/-](\s?-)?/g )
122-
.filter( Boolean );
124+
// The following regex matches numbers that have a following unit
125+
// E.g. 5.25rem, 1vw
126+
const cssUnitsBits = cssUnit.match( /\d+\.?\d*[a-zA-Z]+|\.\d+[a-zA-Z]+/g );
123127
for ( const unit of cssUnitsBits ) {
124128
// Standardize the unit to px and extract the value.
125129
const parsedUnit = parseUnit( getPxFromCssUnit( unit ) );
@@ -131,7 +135,33 @@ function evalMathExpression( cssUnit ) {
131135
cssUnit = cssUnit.replace( unit, parsedUnit.value );
132136
}
133137

134-
return errorFound ? null : calculate( cssUnit ).toFixed( 0 ) + 'px';
138+
// For mixed math expressions wrapped within CSS expressions
139+
if ( ! errorFound && cssUnit.match( /(max|min|clamp)/g ) ) {
140+
const values = cssUnit.split( ',' );
141+
for ( const currentValue of values ) {
142+
// Check for nested calc() and remove them to calculate the value.
143+
const rawCurrentValue = currentValue.replace( /\s|calc/g, '' );
144+
145+
if ( isMathExpression( rawCurrentValue ) ) {
146+
const calculatedExpression = calculate( rawCurrentValue );
147+
148+
if ( calculatedExpression ) {
149+
const calculatedValue =
150+
calculatedExpression.toFixed( 0 ) + 'px';
151+
cssUnit = cssUnit.replace( currentValue, calculatedValue );
152+
}
153+
}
154+
}
155+
const parsedValue = parseUnitFunction( cssUnit );
156+
return ! parsedValue ? null : parsedValue.value + parsedValue.unit;
157+
}
158+
159+
if ( errorFound ) {
160+
return null;
161+
}
162+
163+
const calculatedResult = calculate( cssUnit );
164+
return calculatedResult ? calculatedResult.toFixed( 0 ) + 'px' : null;
135165
}
136166

137167
/**

packages/block-editor/src/utils/test/parse-css-unit-to-px.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,18 @@ describe( 'getPxFromCssUnit', () => {
116116
[ 'console.log("howdy"); + 10px', null ],
117117
[ 'calc(12vw * 10px', null ], // Missing closing bracket.
118118
[ 'calc( 1em + 0.875rem )', '30px' ], // Decimals
119+
[
120+
'clamp(1.8rem, 1.8rem + ((1vw / 0.48rem + 1rem) * 2.885), 3rem)',
121+
'48px',
122+
],
123+
[
124+
'clamp(5rem, 5.25rem + ((1vw - 0.48rem) * 9.096), 8rem)',
125+
'80px',
126+
],
127+
[
128+
'clamp(2.625rem, calc(2.625rem + ((1vw - 0.48rem) * 8.4135)), 3.25rem)',
129+
'42px',
130+
],
119131
];
120132

121133
test.each( testData )( 'getPxFromCssUnit( %s )', ( unit, expected ) => {

packages/block-library/src/spacer/controls.native.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,24 @@ import { __ } from '@wordpress/i18n';
1717
import { MIN_SPACER_SIZE } from './constants';
1818
import styles from './style.scss';
1919

20-
const DEFAULT_VALUES = { px: 100, em: 10, rem: 10, vw: 10, vh: 25 };
20+
export const DEFAULT_VALUES = { px: 100, em: 10, rem: 10, vw: 10, vh: 25 };
2121

22-
function Controls( { attributes, context, setAttributes } ) {
22+
function Controls( {
23+
attributes,
24+
context,
25+
setAttributes,
26+
presetWidth,
27+
presetHeight,
28+
} ) {
2329
const { orientation } = context;
2430
const label = orientation !== 'horizontal' ? __( 'Height' ) : __( 'Width' );
2531

26-
const { height, width } = attributes;
32+
const width = presetWidth || attributes.width;
33+
const height = presetHeight || attributes.height;
2734
const { valueToConvert, valueUnit: unit } =
2835
getValueAndUnit( orientation !== 'horizontal' ? height : width ) || {};
2936
const value = Number( valueToConvert );
37+
const currentUnit = unit || 'px';
3038

3139
const setNewDimensions = ( nextValue, nextUnit ) => {
3240
const valueWithUnit = `${ nextValue }${ nextUnit }`;
@@ -39,7 +47,7 @@ function Controls( { attributes, context, setAttributes } ) {
3947

4048
const handleChange = useCallback(
4149
( nextValue ) => {
42-
setNewDimensions( nextValue, unit );
50+
setNewDimensions( nextValue, currentUnit );
4351
},
4452
[ height, width ]
4553
);
@@ -72,7 +80,7 @@ function Controls( { attributes, context, setAttributes } ) {
7280
onChange={ handleChange }
7381
onUnitChange={ handleUnitChange }
7482
units={ units }
75-
unit={ unit }
83+
unit={ currentUnit }
7684
style={ styles.rangeCellContainer }
7785
/>
7886
</PanelBody>

packages/block-library/src/spacer/edit.native.js

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,48 @@
11
/**
22
* External dependencies
33
*/
4-
import { View } from 'react-native';
4+
import { View, useWindowDimensions } from 'react-native';
55

66
/**
77
* WordPress dependencies
88
*/
99
import { useConvertUnitToMobile } from '@wordpress/components';
1010
import { withPreferredColorScheme } from '@wordpress/compose';
11-
import { InspectorControls } from '@wordpress/block-editor';
11+
import {
12+
InspectorControls,
13+
isValueSpacingPreset,
14+
useSetting,
15+
getCustomValueFromPreset,
16+
getPxFromCssUnit,
17+
} from '@wordpress/block-editor';
1218
import { useEffect } from '@wordpress/element';
1319

1420
/**
1521
* Internal dependencies
1622
*/
17-
import Controls from './controls';
23+
import Controls, { DEFAULT_VALUES } from './controls';
1824
import styles from './editor.native.scss';
1925

26+
const DEFAULT_FONT_SIZE = 16;
27+
2028
const Spacer = ( {
2129
attributes,
2230
context,
2331
setAttributes,
2432
isSelected,
2533
getStylesFromColorScheme,
2634
} ) => {
35+
const { height: screenHeight, width: screenWidth } = useWindowDimensions();
36+
const cssUnitOptions = {
37+
height: screenHeight,
38+
width: screenWidth,
39+
fontSize: DEFAULT_FONT_SIZE,
40+
};
2741
const { height, width } = attributes;
28-
42+
const spacingSizes = [
43+
{ name: 0, slug: '0', size: 0 },
44+
...( useSetting( 'spacing.spacingSizes' ) || [] ),
45+
];
2946
const { orientation } = context;
3047
const defaultStyle = getStylesFromColorScheme(
3148
styles.staticSpacer,
@@ -41,8 +58,29 @@ const Spacer = ( {
4158
}
4259
}, [] );
4360

44-
const convertedHeight = useConvertUnitToMobile( height );
45-
const convertedWidth = useConvertUnitToMobile( width );
61+
let convertedHeight = useConvertUnitToMobile( height );
62+
let convertedWidth = useConvertUnitToMobile( width );
63+
const presetValues = {};
64+
65+
if ( isValueSpacingPreset( height ) ) {
66+
const heightValue = getCustomValueFromPreset( height, spacingSizes );
67+
const parsedPresetHeightValue = parseFloat(
68+
getPxFromCssUnit( heightValue, cssUnitOptions )
69+
);
70+
71+
convertedHeight = parsedPresetHeightValue || DEFAULT_VALUES.px;
72+
presetValues.presetHeight = convertedHeight;
73+
}
74+
75+
if ( isValueSpacingPreset( width ) ) {
76+
const widthValue = getCustomValueFromPreset( width, spacingSizes );
77+
const parsedPresetWidthValue = parseFloat(
78+
getPxFromCssUnit( widthValue, cssUnitOptions )
79+
);
80+
81+
convertedWidth = parsedPresetWidthValue || DEFAULT_VALUES.px;
82+
presetValues.presetWidth = convertedWidth;
83+
}
4684

4785
return (
4886
<View
@@ -58,6 +96,7 @@ const Spacer = ( {
5896
attributes={ attributes }
5997
context={ context }
6098
setAttributes={ setAttributes }
99+
{ ...presetValues }
61100
/>
62101
</InspectorControls>
63102
) }
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { useBlockProps, getSpacingPresetCssVar } from '@wordpress/block-editor';
5+
6+
export default function save( { attributes: { height, width } } ) {
7+
return (
8+
<div
9+
{ ...useBlockProps.save( {
10+
style: {
11+
height: getSpacingPresetCssVar( height ),
12+
width: getSpacingPresetCssVar( width ),
13+
},
14+
'aria-hidden': true,
15+
} ) }
16+
/>
17+
);
18+
}

packages/block-library/src/spacer/test/__snapshots__/index.native.js.snap

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ exports[`Spacer block inserts block 1`] = `
1818
<!-- /wp:spacer -->"
1919
`;
2020
21+
exports[`Spacer block inserts block with spacingSizes preset 1`] = `
22+
"<!-- wp:spacer {\\"height\\":\\"70px\\"} -->
23+
<div style=\\"height:70px\\" aria-hidden=\\"true\\" class=\\"wp-block-spacer\\"></div>
24+
<!-- /wp:spacer -->"
25+
`;
26+
27+
exports[`Spacer block inserts block with spacingSizes preset without matching global styles values 1`] = `
28+
"<!-- wp:spacer {\\"height\\":\\"120px\\"} -->
29+
<div style=\\"height:120px\\" aria-hidden=\\"true\\" class=\\"wp-block-spacer\\"></div>
30+
<!-- /wp:spacer -->"
31+
`;
32+
2133
exports[`Spacer block updates height to 25vh 1`] = `
2234
"<!-- wp:spacer {\\"height\\":\\"25vh\\"} -->
2335
<div style=\\"height:25vh\\" aria-hidden=\\"true\\" class=\\"wp-block-spacer\\"></div>

packages/block-library/src/spacer/test/index.native.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,85 @@ describe( 'Spacer block', () => {
169169

170170
expect( getEditorHtml() ).toMatchSnapshot();
171171
} );
172+
173+
it( 'inserts block with spacingSizes preset', async () => {
174+
// Mock spacingSizes presets
175+
const RAW_STYLES = {
176+
typography: {
177+
fontSize: 16,
178+
},
179+
};
180+
const RAW_FEATURES = {
181+
spacing: {
182+
spacingSizes: {
183+
theme: [
184+
{
185+
size: '3.125rem',
186+
slug: '100',
187+
name: '100',
188+
},
189+
{
190+
size: '3.75rem',
191+
slug: '110',
192+
name: '110',
193+
},
194+
],
195+
},
196+
},
197+
};
198+
199+
const initialHtml = `<!-- wp:spacer {"height":"var:preset|spacing|110"} -->
200+
<div style="height:var(--wp--preset--spacing--110)" aria-hidden="true" class="wp-block-spacer"></div>
201+
<!-- /wp:spacer -->`;
202+
const screen = await initializeEditor( {
203+
initialHtml,
204+
rawStyles: JSON.stringify( RAW_STYLES ),
205+
rawFeatures: JSON.stringify( RAW_FEATURES ),
206+
} );
207+
208+
// Select Spacer block
209+
const [ spacerBlock ] =
210+
screen.getAllByLabelText( /Spacer Block\. Row 1/ );
211+
fireEvent.press( spacerBlock );
212+
213+
// Open block settings
214+
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
215+
await waitFor(
216+
() => screen.getByTestId( 'block-settings-modal' ).props.isVisible
217+
);
218+
219+
// Update height attribute
220+
fireEvent.press( screen.getByText( '60' ) );
221+
const heightTextInput = screen.getByDisplayValue( '60' );
222+
fireEvent.changeText( heightTextInput, '70' );
223+
224+
expect( getEditorHtml() ).toMatchSnapshot();
225+
} );
226+
227+
it( 'inserts block with spacingSizes preset without matching global styles values', async () => {
228+
const initialHtml = `<!-- wp:spacer {"height":"var:preset|spacing|30"} -->
229+
<div style="height:var(--wp--preset--spacing--30)" aria-hidden="true" class="wp-block-spacer"></div>
230+
<!-- /wp:spacer -->`;
231+
const screen = await initializeEditor( {
232+
initialHtml,
233+
} );
234+
235+
// Select Spacer block
236+
const [ spacerBlock ] =
237+
screen.getAllByLabelText( /Spacer Block\. Row 1/ );
238+
fireEvent.press( spacerBlock );
239+
240+
// Open block settings
241+
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
242+
await waitFor(
243+
() => screen.getByTestId( 'block-settings-modal' ).props.isVisible
244+
);
245+
246+
// Update height attribute
247+
fireEvent.press( screen.getByText( '100' ) );
248+
const heightTextInput = screen.getByDisplayValue( '100' );
249+
fireEvent.changeText( heightTextInput, '120' );
250+
251+
expect( getEditorHtml() ).toMatchSnapshot();
252+
} );
172253
} );

packages/components/src/mobile/global-styles-context/utils.native.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ export function getGlobalStyles( rawStyles, rawFeatures ) {
416416
fontSizes,
417417
customLineHeight: features?.custom?.[ 'line-height' ],
418418
},
419+
spacing: features?.spacing,
419420
},
420421
__experimentalGlobalStylesBaseStyles: globalStyles,
421422
};

0 commit comments

Comments
 (0)