From 945356bdf19eaf96f210e8733d8144d303c4c361 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Mon, 10 May 2021 15:47:41 +1000 Subject: [PATCH 1/9] Update theme json for per corner border radii This updates the theme.json generated stylesheet to handle individual border radius values per corner. It maintains single string value support for the shorthand expression for all corners at once. This allows backwards compatibility for blocks that have already adopted border radius support. --- lib/class-wp-theme-json-gutenberg.php | 66 +++++++++++++++++---------- phpunit/class-wp-theme-json-test.php | 17 +++++-- 2 files changed, 56 insertions(+), 27 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 7ac18b4cd2c9ee..e8504b398e1a0d 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -213,57 +213,70 @@ class WP_Theme_JSON_Gutenberg { * - 'value': path to the value in theme.json and block attributes. */ const PROPERTIES_METADATA = array( - 'background' => array( + 'background' => array( 'value' => array( 'color', 'gradient' ), ), - 'background-color' => array( + 'background-color' => array( 'value' => array( 'color', 'background' ), ), - 'border-radius' => array( + 'border-radius' => array( 'value' => array( 'border', 'radius' ), ), - 'border-color' => array( + // This is deliberately separate to the border-radius entry above. + // It allows for backwards compatible shorthand e.g. border-radius: 10px + // while below provides a means to explicitly set the CSS property name + // where the flat array configuration approach builds an incorrect name. + 'corner-border-radius' => array( + 'value' => array( 'border', 'radius' ), + 'properties' => array( + 'border-top-left-radius' => 'topLeft', + 'border-top-right-radius' => 'topRight', + 'border-bottom-left-radius' => 'bottomLeft', + 'border-bottom-right-radius' => 'bottomRight', + ), + ), + 'border-color' => array( 'value' => array( 'border', 'color' ), ), - 'border-width' => array( + 'border-width' => array( 'value' => array( 'border', 'width' ), ), - 'border-style' => array( + 'border-style' => array( 'value' => array( 'border', 'style' ), ), - 'color' => array( + 'color' => array( 'value' => array( 'color', 'text' ), ), - 'font-family' => array( + 'font-family' => array( 'value' => array( 'typography', 'fontFamily' ), ), - 'font-size' => array( + 'font-size' => array( 'value' => array( 'typography', 'fontSize' ), ), - 'font-style' => array( + 'font-style' => array( 'value' => array( 'typography', 'fontStyle' ), ), - 'font-weight' => array( + 'font-weight' => array( 'value' => array( 'typography', 'fontWeight' ), ), - 'letter-spacing' => array( + 'letter-spacing' => array( 'value' => array( 'typography', 'letterSpacing' ), ), - 'line-height' => array( + 'line-height' => array( 'value' => array( 'typography', 'lineHeight' ), ), - 'margin' => array( + 'margin' => array( 'value' => array( 'spacing', 'margin' ), 'properties' => array( 'top', 'right', 'bottom', 'left' ), ), - 'padding' => array( + 'padding' => array( 'value' => array( 'spacing', 'padding' ), 'properties' => array( 'top', 'right', 'bottom', 'left' ), ), - 'text-decoration' => array( + 'text-decoration' => array( 'value' => array( 'typography', 'textDecoration' ), ), - 'text-transform' => array( + 'text-transform' => array( 'value' => array( 'typography', 'textTransform' ), ), ); @@ -388,8 +401,12 @@ private static function to_property( $css_name ) { foreach ( self::PROPERTIES_METADATA as $key => $metadata ) { $to_property[ $key ] = $key; if ( self::has_properties( $metadata ) ) { - foreach ( $metadata['properties'] as $property ) { - $to_property[ $key . '-' . $property ] = $key; + foreach ( $metadata['properties'] as $name => $property ) { + if ( is_numeric( $name ) ) { + $to_property[ $key . '-' . $property ] = $key; + } else { + $to_property[ $name ] = $key; + } } } } @@ -556,7 +573,7 @@ private static function flatten_tree( $tree, $prefix = '', $token = '--' ) { private static function get_property_value( $styles, $path ) { $value = _wp_array_get( $styles, $path, '' ); - if ( '' === $value ) { + if ( '' === $value || is_array( $value ) ) { return $value; } @@ -618,9 +635,10 @@ private static function compute_style_properties( $styles ) { // they contain multiple values instead of a single one. // An example of this is the padding property. if ( self::has_properties( $metadata ) ) { - foreach ( $metadata['properties'] as $property ) { - $properties[] = array( - 'name' => $name . '-' . $property, + foreach ( $metadata['properties'] as $key => $property ) { + $property_name = is_numeric( $key ) ? $name . '-' . $property : $key; + $properties[] = array( + 'name' => $property_name, 'value' => array_merge( $metadata['value'], array( $property ) ), ); } @@ -634,7 +652,7 @@ private static function compute_style_properties( $styles ) { foreach ( $properties as $prop ) { $value = self::get_property_value( $styles, $prop['value'] ); - if ( empty( $value ) ) { + if ( empty( $value ) || is_array( $value ) ) { continue; } diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 5b9cc20c3233d2..0f3cdd6eca2648 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -236,6 +236,9 @@ function test_get_stylesheet() { ), 'blocks' => array( 'core/group' => array( + 'border' => array( + 'radius' => '10px', + ), 'elements' => array( 'link' => array( 'color' => array( @@ -279,6 +282,14 @@ function test_get_stylesheet() { ), ), ), + 'core/image' => array( + 'border' => array( + 'radius' => array( + 'topLeft' => '10px', + 'bottomRight' => '1em', + ), + ), + ), ), ), 'misc' => 'value', @@ -286,11 +297,11 @@ function test_get_stylesheet() { ); $this->assertEquals( - 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', + 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', $theme_json->get_stylesheet() ); $this->assertEquals( - 'body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', + 'body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', $theme_json->get_stylesheet( 'block_styles' ) ); $this->assertEquals( @@ -1004,7 +1015,7 @@ function test_remove_insecure_properties_removes_unsafe_preset_settings() { array( 'name' => 'Blue', 'slug' => 'blue', - 'color' => 'var(--color, var(--unsafe--falback))', + 'color' => 'var(--color, var(--unsafe--fallback))', ), array( 'name' => 'Pink', From 265a81221bdb5f0e48bdefdb247139cfebaf2b00 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Mon, 10 May 2021 15:48:33 +1000 Subject: [PATCH 2/9] Update border radius block support for per corner radii Maintains support for flat string value for setting all border radii at once. --- lib/block-supports/border.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php index 9d8d97c690a7b8..c29bfd666efdc7 100644 --- a/lib/block-supports/border.php +++ b/lib/block-supports/border.php @@ -58,12 +58,21 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) { ) { $border_radius = $block_attributes['style']['border']['radius']; - // This check handles original unitless implementation. - if ( is_numeric( $border_radius ) ) { - $border_radius .= 'px'; + if ( is_array( $border_radius ) ) { + // We have individual border radius corner values. + foreach ( $border_radius as $key => $radius ) { + // Convert CamelCase corner name to kebab-case. + $corner = strtolower( preg_replace( '/(? Date: Mon, 10 May 2021 16:14:37 +1000 Subject: [PATCH 3/9] Update block support style generation for named subpaths --- packages/block-editor/src/hooks/style.js | 26 ++++++++++++++----- packages/block-editor/src/hooks/test/style.js | 20 ++++++++++++++ packages/blocks/src/api/constants.js | 6 +++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index f177ed5da8c9bc..dff2545ef6154f 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -8,6 +8,7 @@ import { get, has, isEmpty, + isString, kebabCase, map, omit, @@ -78,12 +79,25 @@ export function getInlineStyles( styles = {} ) { const subPaths = STYLE_PROPERTY[ propKey ].properties; // Ignore styles on elements because they are handled on the server. if ( has( styles, path ) && 'elements' !== first( path ) ) { - if ( !! subPaths ) { - subPaths.forEach( ( suffix ) => { - output[ - propKey + capitalize( suffix ) - ] = compileStyleValue( get( styles, [ ...path, suffix ] ) ); - } ); + // Checking if style value is a string allows for shorthand css + // option and backwards compatibility for border radius support. + const styleValue = get( styles, path ); + + if ( !! subPaths && ! isString( styleValue ) ) { + if ( Array.isArray( subPaths ) ) { + subPaths.forEach( ( suffix ) => { + output[ + propKey + capitalize( suffix ) + ] = compileStyleValue( get( styleValue, [ suffix ] ) ); + } ); + } else { + Object.entries( subPaths ).forEach( ( entry ) => { + const [ name, suffix ] = entry; + output[ name ] = compileStyleValue( + get( styleValue, [ suffix ] ) + ); + } ); + } } else { output[ propKey ] = compileStyleValue( get( styles, path ) ); } diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js index 85adb56c802e19..9efdb3e939b91d 100644 --- a/packages/block-editor/src/hooks/test/style.js +++ b/packages/block-editor/src/hooks/test/style.js @@ -41,4 +41,24 @@ describe( 'getInlineStyles', () => { paddingTop: '10px', } ); } ); + + it( 'should return individual border radius styles', () => { + expect( + getInlineStyles( { + border: { + radius: { + topLeft: '10px', + topRight: '0.5rem', + bottomLeft: '0.5em', + bottomRight: '1em', + }, + }, + } ) + ).toEqual( { + borderTopLeftRadius: '10px', + borderTopRightRadius: '0.5rem', + borderBottomLeftRadius: '0.5em', + borderBottomRightRadius: '1em', + } ); + } ); } ); diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index 3a827f444974ea..fad0b9b7eee8d1 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -33,6 +33,12 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { borderRadius: { value: [ 'border', 'radius' ], support: [ '__experimentalBorder', 'radius' ], + properties: { + borderTopLeftRadius: 'topLeft', + borderTopRightRadius: 'topRight', + borderBottomLeftRadius: 'bottomLeft', + borderBottomRightRadius: 'bottomRight', + }, }, borderStyle: { value: [ 'border', 'style' ], From 114c3d760892cdc8ba6df45d0ecfe332d6fbc332 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Mon, 10 May 2021 19:50:01 +1000 Subject: [PATCH 4/9] Update global styles renderer for custom named css properties --- .../editor/global-styles-renderer.js | 57 +++++++++++++------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/packages/edit-site/src/components/editor/global-styles-renderer.js b/packages/edit-site/src/components/editor/global-styles-renderer.js index bdc46662fb62fc..2148d7448bca4d 100644 --- a/packages/edit-site/src/components/editor/global-styles-renderer.js +++ b/packages/edit-site/src/components/editor/global-styles-renderer.js @@ -7,6 +7,7 @@ import { forEach, get, isEmpty, + isString, kebabCase, pickBy, reduce, @@ -134,24 +135,44 @@ function getStylesDeclarations( blockStyles = {} ) { if ( first( pathToValue ) === 'elements' ) { return declarations; } - if ( !! properties ) { - properties.forEach( ( prop ) => { - if ( - ! get( blockStyles, [ ...pathToValue, prop ], false ) - ) { - // Do not create a declaration - // for sub-properties that don't have any value. - return; - } - const cssProperty = kebabCase( - `${ key }${ capitalize( prop ) }` - ); - declarations.push( - `${ cssProperty }: ${ compileStyleValue( - get( blockStyles, [ ...pathToValue, prop ] ) - ) }` - ); - } ); + + const styleValue = get( blockStyles, pathToValue ); + + if ( !! properties && ! isString( styleValue ) ) { + if ( Array.isArray( properties ) ) { + properties.forEach( ( prop ) => { + if ( ! get( styleValue, [ prop ], false ) ) { + // Do not create a declaration + // for sub-properties that don't have any value. + return; + } + const cssProperty = kebabCase( + `${ key }${ capitalize( prop ) }` + ); + declarations.push( + `${ cssProperty }: ${ compileStyleValue( + get( styleValue, [ prop ] ) + ) }` + ); + } ); + } else { + Object.entries( properties ).forEach( ( entry ) => { + const [ name, prop ] = entry; + + if ( ! get( styleValue, [ prop ], false ) ) { + // Do not create a declaration + // for sub-properties that don't have any value. + return; + } + + const cssProperty = kebabCase( name ); + declarations.push( + `${ cssProperty }: ${ compileStyleValue( + get( styleValue, [ prop ] ) + ) }` + ); + } ); + } } else if ( get( blockStyles, pathToValue, false ) ) { const cssProperty = key.startsWith( '--' ) ? key From 4cdeee1ec9dfa055724fb96a47b4b4c9f009c7c2 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Tue, 15 Jun 2021 18:16:59 +1000 Subject: [PATCH 5/9] Make border radius, margin and padding config consistent --- lib/block-supports/spacing.php | 10 +- lib/class-wp-theme-json-gutenberg.php | 108 +++++++++--------- lib/compat.php | 4 + packages/block-editor/src/hooks/style.js | 21 +--- packages/block-editor/src/hooks/test/style.js | 44 +++++++ packages/blocks/src/api/constants.js | 14 ++- .../editor/global-styles-renderer.js | 51 +++------ .../editor/test/global-styles-renderer.js | 25 +++- phpunit/class-wp-theme-json-test.php | 92 +++++++++++++-- 9 files changed, 253 insertions(+), 116 deletions(-) diff --git a/lib/block-supports/spacing.php b/lib/block-supports/spacing.php index ada54a6b76eeb3..592949f0473b86 100644 --- a/lib/block-supports/spacing.php +++ b/lib/block-supports/spacing.php @@ -45,19 +45,25 @@ function gutenberg_apply_spacing_support( $block_type, $block_attributes ) { if ( $has_padding_support ) { $padding_value = _wp_array_get( $block_attributes, array( 'style', 'spacing', 'padding' ), null ); - if ( null !== $padding_value ) { + + if ( is_array( $padding_value ) ) { foreach ( $padding_value as $key => $value ) { $styles[] = sprintf( 'padding-%s: %s;', $key, $value ); } + } elseif ( null !== $padding_value ) { + $styles[] = sprintf( 'padding: %s;', $padding_value ); } } if ( $has_margin_support ) { $margin_value = _wp_array_get( $block_attributes, array( 'style', 'spacing', 'margin' ), null ); - if ( null !== $margin_value ) { + + if ( is_array( $margin_value ) ) { foreach ( $margin_value as $key => $value ) { $styles[] = sprintf( 'margin-%s: %s;', $key, $value ); } + } elseif ( null !== $margin_value ) { + $styles[] = sprintf( 'margin: %s;', $margin_value ); } } diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index e8504b398e1a0d..2f725a4a1e44d3 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -61,18 +61,8 @@ class WP_Theme_JSON_Gutenberg { 'text' => null, ), 'spacing' => array( - 'margin' => array( - 'top' => null, - 'right' => null, - 'bottom' => null, - 'left' => null, - ), - 'padding' => array( - 'bottom' => null, - 'left' => null, - 'right' => null, - 'top' => null, - ), + 'margin' => null, + 'padding' => null, ), 'typography' => array( 'fontFamily' => null, @@ -213,20 +203,13 @@ class WP_Theme_JSON_Gutenberg { * - 'value': path to the value in theme.json and block attributes. */ const PROPERTIES_METADATA = array( - 'background' => array( + 'background' => array( 'value' => array( 'color', 'gradient' ), ), - 'background-color' => array( + 'background-color' => array( 'value' => array( 'color', 'background' ), ), - 'border-radius' => array( - 'value' => array( 'border', 'radius' ), - ), - // This is deliberately separate to the border-radius entry above. - // It allows for backwards compatible shorthand e.g. border-radius: 10px - // while below provides a means to explicitly set the CSS property name - // where the flat array configuration approach builds an incorrect name. - 'corner-border-radius' => array( + 'border-radius' => array( 'value' => array( 'border', 'radius' ), 'properties' => array( 'border-top-left-radius' => 'topLeft', @@ -235,48 +218,58 @@ class WP_Theme_JSON_Gutenberg { 'border-bottom-right-radius' => 'bottomRight', ), ), - 'border-color' => array( + 'border-color' => array( 'value' => array( 'border', 'color' ), ), - 'border-width' => array( + 'border-width' => array( 'value' => array( 'border', 'width' ), ), - 'border-style' => array( + 'border-style' => array( 'value' => array( 'border', 'style' ), ), - 'color' => array( + 'color' => array( 'value' => array( 'color', 'text' ), ), - 'font-family' => array( + 'font-family' => array( 'value' => array( 'typography', 'fontFamily' ), ), - 'font-size' => array( + 'font-size' => array( 'value' => array( 'typography', 'fontSize' ), ), - 'font-style' => array( + 'font-style' => array( 'value' => array( 'typography', 'fontStyle' ), ), - 'font-weight' => array( + 'font-weight' => array( 'value' => array( 'typography', 'fontWeight' ), ), - 'letter-spacing' => array( + 'letter-spacing' => array( 'value' => array( 'typography', 'letterSpacing' ), ), - 'line-height' => array( + 'line-height' => array( 'value' => array( 'typography', 'lineHeight' ), ), - 'margin' => array( + 'margin' => array( 'value' => array( 'spacing', 'margin' ), - 'properties' => array( 'top', 'right', 'bottom', 'left' ), + 'properties' => array( + 'margin-top' => 'top', + 'margin-right' => 'right', + 'margin-bottom' => 'bottom', + 'margin-left' => 'left', + ), ), - 'padding' => array( + 'padding' => array( 'value' => array( 'spacing', 'padding' ), - 'properties' => array( 'top', 'right', 'bottom', 'left' ), + 'properties' => array( + 'padding-top' => 'top', + 'padding-right' => 'right', + 'padding-bottom' => 'bottom', + 'padding-left' => 'left', + ), ), - 'text-decoration' => array( + 'text-decoration' => array( 'value' => array( 'typography', 'textDecoration' ), ), - 'text-transform' => array( + 'text-transform' => array( 'value' => array( 'typography', 'textTransform' ), ), ); @@ -402,11 +395,7 @@ private static function to_property( $css_name ) { $to_property[ $key ] = $key; if ( self::has_properties( $metadata ) ) { foreach ( $metadata['properties'] as $name => $property ) { - if ( is_numeric( $name ) ) { - $to_property[ $key . '-' . $property ] = $key; - } else { - $to_property[ $name ] = $key; - } + $to_property[ $name ] = $key; } } } @@ -631,22 +620,21 @@ private static function compute_style_properties( $styles ) { $properties = array(); foreach ( self::PROPERTIES_METADATA as $name => $metadata ) { + $properties[] = array( + 'name' => $name, + 'value' => $metadata['value'], + ); + // Some properties can be shorthand properties, meaning that // they contain multiple values instead of a single one. // An example of this is the padding property. if ( self::has_properties( $metadata ) ) { foreach ( $metadata['properties'] as $key => $property ) { - $property_name = is_numeric( $key ) ? $name . '-' . $property : $key; - $properties[] = array( - 'name' => $property_name, + $properties[] = array( + 'name' => $key, 'value' => array_merge( $metadata['value'], array( $property ) ), ); } - } else { - $properties[] = array( - 'name' => $name, - 'value' => $metadata['value'], - ); } } @@ -1272,11 +1260,23 @@ private static function remove_insecure_styles( $input ) { if ( self::is_safe_css_declaration( $declaration['name'], $declaration['value'] ) ) { $property = self::to_property( $declaration['name'] ); $path = self::PROPERTIES_METADATA[ $property ]['value']; + + // Add shorthand declaration e.g. `margin`. + $value = _wp_array_get( $input, $path, array() ); + if ( ! is_array( $value ) ) { + gutenberg_experimental_set( $output, $path, $value ); + } + + // Handle longhand css properties e.g. `margin-left`, `border-top-left-radius` etc. if ( self::has_properties( self::PROPERTIES_METADATA[ $property ] ) ) { - $declaration_divided = explode( '-', $declaration['name'] ); - $path[] = $declaration_divided[1]; + $properties = self::PROPERTIES_METADATA[ $property ]['properties']; + $sub_path = _wp_array_get( $properties, array( $declaration['name'] ), null ); + + if ( $sub_path ) { + $path[] = $sub_path; + gutenberg_experimental_set( $output, $path, _wp_array_get( $input, $path, array() ) ); + } } - gutenberg_experimental_set( $output, $path, _wp_array_get( $input, $path, array() ) ); } } return $output; diff --git a/lib/compat.php b/lib/compat.php index 72f39e15a42983..917d22fe733704 100644 --- a/lib/compat.php +++ b/lib/compat.php @@ -182,6 +182,10 @@ function gutenberg_override_reusable_block_post_type_labels() { */ function gutenberg_safe_style_attrs( $attrs ) { $attrs[] = 'object-position'; + $attrs[] = 'border-top-left-radius'; + $attrs[] = 'border-top-right-radius'; + $attrs[] = 'border-bottom-right-radius'; + $attrs[] = 'border-bottom-left-radius'; return $attrs; } diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index dff2545ef6154f..66bc3e4f6478bf 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -2,7 +2,6 @@ * External dependencies */ import { - capitalize, first, forEach, get, @@ -84,20 +83,12 @@ export function getInlineStyles( styles = {} ) { const styleValue = get( styles, path ); if ( !! subPaths && ! isString( styleValue ) ) { - if ( Array.isArray( subPaths ) ) { - subPaths.forEach( ( suffix ) => { - output[ - propKey + capitalize( suffix ) - ] = compileStyleValue( get( styleValue, [ suffix ] ) ); - } ); - } else { - Object.entries( subPaths ).forEach( ( entry ) => { - const [ name, suffix ] = entry; - output[ name ] = compileStyleValue( - get( styleValue, [ suffix ] ) - ); - } ); - } + Object.entries( subPaths ).forEach( ( entry ) => { + const [ name, suffix ] = entry; + output[ name ] = compileStyleValue( + get( styleValue, [ suffix ] ) + ); + } ); } else { output[ propKey ] = compileStyleValue( get( styles, path ) ); } diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js index 9efdb3e939b91d..706d9a93ed0ba4 100644 --- a/packages/block-editor/src/hooks/test/style.js +++ b/packages/block-editor/src/hooks/test/style.js @@ -61,4 +61,48 @@ describe( 'getInlineStyles', () => { borderBottomRightRadius: '1em', } ); } ); + + it( 'should support longhand spacing styles', () => { + expect( + getInlineStyles( { + spacing: { + margin: { + top: '10px', + right: '0.5rem', + bottom: '0.5em', + left: '1em', + }, + padding: { + top: '20px', + right: '25px', + bottom: '30px', + left: '35px', + }, + }, + } ) + ).toEqual( { + marginTop: '10px', + marginRight: '0.5rem', + marginBottom: '0.5em', + marginLeft: '1em', + paddingTop: '20px', + paddingRight: '25px', + paddingBottom: '30px', + paddingLeft: '35px', + } ); + } ); + + it( 'should support shorthand spacing styles', () => { + expect( + getInlineStyles( { + spacing: { + margin: '10px', + padding: '20px', + }, + } ) + ).toEqual( { + margin: '10px', + padding: '20px', + } ); + } ); } ); diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index fad0b9b7eee8d1..13d97020aca924 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -79,12 +79,22 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { margin: { value: [ 'spacing', 'margin' ], support: [ 'spacing', 'margin' ], - properties: [ 'top', 'right', 'bottom', 'left' ], + properties: { + marginTop: 'top', + marginRight: 'right', + marginBottom: 'bottom', + marginLeft: 'left', + }, }, padding: { value: [ 'spacing', 'padding' ], support: [ 'spacing', 'padding' ], - properties: [ 'top', 'right', 'bottom', 'left' ], + properties: { + paddingTop: 'top', + paddingRight: 'right', + paddingBottom: 'bottom', + paddingLeft: 'left', + }, }, textDecoration: { value: [ 'typography', 'textDecoration' ], diff --git a/packages/edit-site/src/components/editor/global-styles-renderer.js b/packages/edit-site/src/components/editor/global-styles-renderer.js index 2148d7448bca4d..38d3e06a12b6d9 100644 --- a/packages/edit-site/src/components/editor/global-styles-renderer.js +++ b/packages/edit-site/src/components/editor/global-styles-renderer.js @@ -2,7 +2,6 @@ * External dependencies */ import { - capitalize, first, forEach, get, @@ -139,40 +138,22 @@ function getStylesDeclarations( blockStyles = {} ) { const styleValue = get( blockStyles, pathToValue ); if ( !! properties && ! isString( styleValue ) ) { - if ( Array.isArray( properties ) ) { - properties.forEach( ( prop ) => { - if ( ! get( styleValue, [ prop ], false ) ) { - // Do not create a declaration - // for sub-properties that don't have any value. - return; - } - const cssProperty = kebabCase( - `${ key }${ capitalize( prop ) }` - ); - declarations.push( - `${ cssProperty }: ${ compileStyleValue( - get( styleValue, [ prop ] ) - ) }` - ); - } ); - } else { - Object.entries( properties ).forEach( ( entry ) => { - const [ name, prop ] = entry; - - if ( ! get( styleValue, [ prop ], false ) ) { - // Do not create a declaration - // for sub-properties that don't have any value. - return; - } - - const cssProperty = kebabCase( name ); - declarations.push( - `${ cssProperty }: ${ compileStyleValue( - get( styleValue, [ prop ] ) - ) }` - ); - } ); - } + Object.entries( properties ).forEach( ( entry ) => { + const [ name, prop ] = entry; + + if ( ! get( styleValue, [ prop ], false ) ) { + // Do not create a declaration + // for sub-properties that don't have any value. + return; + } + + const cssProperty = kebabCase( name ); + declarations.push( + `${ cssProperty }: ${ compileStyleValue( + get( styleValue, [ prop ] ) + ) }` + ); + } ); } else if ( get( blockStyles, pathToValue, false ) ) { const cssProperty = key.startsWith( '--' ) ? key diff --git a/packages/edit-site/src/components/editor/test/global-styles-renderer.js b/packages/edit-site/src/components/editor/test/global-styles-renderer.js index 56250ebf201bc3..2644ac724a8c18 100644 --- a/packages/edit-site/src/components/editor/test/global-styles-renderer.js +++ b/packages/edit-site/src/components/editor/test/global-styles-renderer.js @@ -293,6 +293,10 @@ describe( 'global styles renderer', () => { }, }, styles: { + spacing: { + margin: '10px', + padding: '10px', + }, color: { background: 'red', }, @@ -304,6 +308,22 @@ describe( 'global styles renderer', () => { }, }, blocks: { + 'core/group': { + spacing: { + margin: { + top: '10px', + right: '20px', + bottom: '30px', + left: '40px', + }, + padding: { + top: '11px', + right: '22px', + bottom: '33px', + left: '44px', + }, + }, + }, 'core/heading': { color: { text: 'orange', @@ -321,6 +341,9 @@ describe( 'global styles renderer', () => { }; const blockSelectors = { + 'core/group': { + selector: '.wp-block-group', + }, 'core/heading': { selector: 'h1,h2,h3,h4,h5,h6', elements: { @@ -342,7 +365,7 @@ describe( 'global styles renderer', () => { }; expect( toStyles( tree, blockSelectors ) ).toEqual( - 'body{background-color: red;}h1{font-size: 42px;}h1,h2,h3,h4,h5,h6{color: orange;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: hotpink;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}' + 'body{background-color: red;margin: 10px;padding: 10px;}h1{font-size: 42px;}.wp-block-group{margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}h1,h2,h3,h4,h5,h6{color: orange;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: hotpink;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}' ); } ); } ); diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 0f3cdd6eca2648..02c7dea4921e71 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -181,6 +181,52 @@ function test_get_settings_presets_are_keyed_by_origin() { $this->assertEqualSetsWithIndex( $expected_no_origin, $actual_no_origin ); } + function test_get_stylesheet_support_for_shorthand_and_longhand_values() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/group' => array( + 'border' => array( + 'radius' => '10px', + ), + 'spacing' => array( + 'padding' => '24px', + 'margin' => '1em', + ), + ), + 'core/image' => array( + 'border' => array( + 'radius' => array( + 'topLeft' => '10px', + 'bottomRight' => '1em', + ), + ), + 'spacing' => array( + 'padding' => array( + 'top' => '15px', + ), + 'margin' => array( + 'bottom' => '30px', + ), + ), + ), + ), + ), + ) + ); + + $this->assertEquals( + '.wp-block-group{border-radius: 10px;margin: 1em;padding: 24px;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;padding-top: 15px;}', + $theme_json->get_stylesheet() + ); + $this->assertEquals( + '.wp-block-group{border-radius: 10px;margin: 1em;padding: 24px;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;padding-top: 15px;}', + $theme_json->get_stylesheet( 'block_styles' ) + ); + } + function test_get_stylesheet() { $theme_json = new WP_Theme_JSON_Gutenberg( array( @@ -247,10 +293,7 @@ function test_get_stylesheet() { ), ), 'spacing' => array( - 'padding' => array( - 'top' => '12px', - 'bottom' => '24px', - ), + 'padding' => '24px', ), ), 'core/heading' => array( @@ -283,12 +326,17 @@ function test_get_stylesheet() { ), ), 'core/image' => array( - 'border' => array( + 'border' => array( 'radius' => array( 'topLeft' => '10px', 'bottomRight' => '1em', ), ), + 'spacing' => array( + 'margin' => array( + 'bottom' => '30px', + ), + ), ), ), ), @@ -297,11 +345,11 @@ function test_get_stylesheet() { ); $this->assertEquals( - 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', + 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', $theme_json->get_stylesheet() ); $this->assertEquals( - 'body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding-top: 12px;padding-bottom: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', + 'body{color: var(--wp--preset--color--grey);}a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', $theme_json->get_stylesheet( 'block_styles' ) ); $this->assertEquals( @@ -739,6 +787,14 @@ function test_remove_insecure_properties_removes_unsafe_styles_sub_properties() array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, 'styles' => array( + 'border' => array( + 'radius' => array( + 'topLeft' => '6px', + 'topRight' => 'var(--top-right, var(--unsafe-fallback))', + 'bottomRight' => '6px', + 'bottomLeft' => '6px', + ), + ), 'spacing' => array( 'padding' => array( 'top' => '1px', @@ -761,6 +817,14 @@ function test_remove_insecure_properties_removes_unsafe_styles_sub_properties() ), 'blocks' => array( 'core/group' => array( + 'border' => array( + 'radius' => array( + 'topLeft' => '5px', + 'topRight' => 'var(--top-right, var(--unsafe-fallback))', + 'bottomRight' => '5px', + 'bottomLeft' => '5px', + ), + ), 'spacing' => array( 'padding' => array( 'top' => '3px', @@ -791,6 +855,13 @@ function test_remove_insecure_properties_removes_unsafe_styles_sub_properties() $expected = array( 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, 'styles' => array( + 'border' => array( + 'radius' => array( + 'topLeft' => '6px', + 'bottomRight' => '6px', + 'bottomLeft' => '6px', + ), + ), 'spacing' => array( 'padding' => array( 'top' => '1px', @@ -811,6 +882,13 @@ function test_remove_insecure_properties_removes_unsafe_styles_sub_properties() ), 'blocks' => array( 'core/group' => array( + 'border' => array( + 'radius' => array( + 'topLeft' => '5px', + 'bottomRight' => '5px', + 'bottomLeft' => '5px', + ), + ), 'spacing' => array( 'padding' => array( 'top' => '3px', From 6a2a1a1be3561dadda5e2eea0374877a27489d6c Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 16 Jun 2021 11:59:42 +1000 Subject: [PATCH 6/9] Flatten theme.json properties metadata --- lib/class-wp-theme-json-gutenberg.php | 169 ++++++-------------------- 1 file changed, 38 insertions(+), 131 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 2f725a4a1e44d3..6b6135fd76b66b 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -198,80 +198,39 @@ class WP_Theme_JSON_Gutenberg { /** * Metadata for style properties. * - * Each property declares: - * - * - 'value': path to the value in theme.json and block attributes. + * Each element is a direct mapping from the CSS property name to the + * path to the value in theme.json & block attributes. */ const PROPERTIES_METADATA = array( - 'background' => array( - 'value' => array( 'color', 'gradient' ), - ), - 'background-color' => array( - 'value' => array( 'color', 'background' ), - ), - 'border-radius' => array( - 'value' => array( 'border', 'radius' ), - 'properties' => array( - 'border-top-left-radius' => 'topLeft', - 'border-top-right-radius' => 'topRight', - 'border-bottom-left-radius' => 'bottomLeft', - 'border-bottom-right-radius' => 'bottomRight', - ), - ), - 'border-color' => array( - 'value' => array( 'border', 'color' ), - ), - 'border-width' => array( - 'value' => array( 'border', 'width' ), - ), - 'border-style' => array( - 'value' => array( 'border', 'style' ), - ), - 'color' => array( - 'value' => array( 'color', 'text' ), - ), - 'font-family' => array( - 'value' => array( 'typography', 'fontFamily' ), - ), - 'font-size' => array( - 'value' => array( 'typography', 'fontSize' ), - ), - 'font-style' => array( - 'value' => array( 'typography', 'fontStyle' ), - ), - 'font-weight' => array( - 'value' => array( 'typography', 'fontWeight' ), - ), - 'letter-spacing' => array( - 'value' => array( 'typography', 'letterSpacing' ), - ), - 'line-height' => array( - 'value' => array( 'typography', 'lineHeight' ), - ), - 'margin' => array( - 'value' => array( 'spacing', 'margin' ), - 'properties' => array( - 'margin-top' => 'top', - 'margin-right' => 'right', - 'margin-bottom' => 'bottom', - 'margin-left' => 'left', - ), - ), - 'padding' => array( - 'value' => array( 'spacing', 'padding' ), - 'properties' => array( - 'padding-top' => 'top', - 'padding-right' => 'right', - 'padding-bottom' => 'bottom', - 'padding-left' => 'left', - ), - ), - 'text-decoration' => array( - 'value' => array( 'typography', 'textDecoration' ), - ), - 'text-transform' => array( - 'value' => array( 'typography', 'textTransform' ), - ), + 'background' => array( 'color', 'gradient' ), + 'background-color' => array( 'color', 'background' ), + 'border-radius' => array( 'border', 'radius' ), + 'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ), + 'border-top-right-radius' => array( 'border', 'radius', 'topRight' ), + 'border-bottom-left-radius' => array( 'border', 'radius', 'bottomLeft' ), + 'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ), + 'border-color' => array( 'border', 'color' ), + 'border-width' => array( 'border', 'width' ), + 'border-style' => array( 'border', 'style' ), + 'color' => array( 'color', 'text' ), + 'font-family' => array( 'typography', 'fontFamily' ), + 'font-size' => array( 'typography', 'fontSize' ), + 'font-style' => array( 'typography', 'fontStyle' ), + 'font-weight' => array( 'typography', 'fontWeight' ), + 'letter-spacing' => array( 'typography', 'letterSpacing' ), + 'line-height' => array( 'typography', 'lineHeight' ), + 'margin' => array( 'spacing', 'margin' ), + 'margin-top' => array( 'spacing', 'margin', 'top' ), + 'margin-right' => array( 'spacing', 'margin', 'right' ), + 'margin-bottom' => array( 'spacing', 'margin', 'bottom' ), + 'margin-left' => array( 'spacing', 'margin', 'left' ), + 'padding' => array( 'spacing', 'padding' ), + 'padding-top' => array( 'spacing', 'padding', 'top' ), + 'padding-right' => array( 'spacing', 'padding', 'right' ), + 'padding-bottom' => array( 'spacing', 'padding', 'bottom' ), + 'padding-left' => array( 'spacing', 'padding', 'left' ), + 'text-decoration' => array( 'typography', 'textDecoration' ), + 'text-transform' => array( 'typography', 'textTransform' ), ); const ELEMENTS = array( @@ -380,29 +339,6 @@ private static function sanitize( $input, $valid_block_names, $valid_element_nam return $output; } - /** - * Given a CSS property name, returns the property it belongs - * within the self::PROPERTIES_METADATA map. - * - * @param string $css_name The CSS property name. - * - * @return string The property name. - */ - private static function to_property( $css_name ) { - static $to_property; - if ( null === $to_property ) { - foreach ( self::PROPERTIES_METADATA as $key => $metadata ) { - $to_property[ $key ] = $key; - if ( self::has_properties( $metadata ) ) { - foreach ( $metadata['properties'] as $name => $property ) { - $to_property[ $name ] = $key; - } - } - } - } - return $to_property[ $css_name ]; - } - /** * Returns the metadata for each block. * @@ -618,34 +554,16 @@ private static function compute_style_properties( $styles ) { return $declarations; } - $properties = array(); - foreach ( self::PROPERTIES_METADATA as $name => $metadata ) { - $properties[] = array( - 'name' => $name, - 'value' => $metadata['value'], - ); - - // Some properties can be shorthand properties, meaning that - // they contain multiple values instead of a single one. - // An example of this is the padding property. - if ( self::has_properties( $metadata ) ) { - foreach ( $metadata['properties'] as $key => $property ) { - $properties[] = array( - 'name' => $key, - 'value' => array_merge( $metadata['value'], array( $property ) ), - ); - } - } - } + foreach ( self::PROPERTIES_METADATA as $css_property => $value_path ) { + $value = self::get_property_value( $styles, $value_path ); - foreach ( $properties as $prop ) { - $value = self::get_property_value( $styles, $prop['value'] ); + // Skip if empty or value represents array of longhand values. if ( empty( $value ) || is_array( $value ) ) { continue; } $declarations[] = array( - 'name' => $prop['name'], + 'name' => $css_property, 'value' => $value, ); } @@ -1258,25 +1176,14 @@ private static function remove_insecure_styles( $input ) { foreach ( $declarations as $declaration ) { if ( self::is_safe_css_declaration( $declaration['name'], $declaration['value'] ) ) { - $property = self::to_property( $declaration['name'] ); - $path = self::PROPERTIES_METADATA[ $property ]['value']; + $path = self::PROPERTIES_METADATA[ $declaration['name'] ]; - // Add shorthand declaration e.g. `margin`. + // Check the value isn't an array before adding so as to not + // double up shorthand and longhand styles. $value = _wp_array_get( $input, $path, array() ); if ( ! is_array( $value ) ) { gutenberg_experimental_set( $output, $path, $value ); } - - // Handle longhand css properties e.g. `margin-left`, `border-top-left-radius` etc. - if ( self::has_properties( self::PROPERTIES_METADATA[ $property ] ) ) { - $properties = self::PROPERTIES_METADATA[ $property ]['properties']; - $sub_path = _wp_array_get( $properties, array( $declaration['name'] ), null ); - - if ( $sub_path ) { - $path[] = $sub_path; - gutenberg_experimental_set( $output, $path, _wp_array_get( $input, $path, array() ) ); - } - } } } return $output; From aafe091a86ccbb3f134cf1c8768f2222d8c22853 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 16 Jun 2021 16:11:26 +1000 Subject: [PATCH 7/9] Update spacing panel to support shorthand theme styles --- .../src/components/sidebar/spacing-panel.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/edit-site/src/components/sidebar/spacing-panel.js b/packages/edit-site/src/components/sidebar/spacing-panel.js index 987d568aaabdcf..cf224ee63fc25d 100644 --- a/packages/edit-site/src/components/sidebar/spacing-panel.js +++ b/packages/edit-site/src/components/sidebar/spacing-panel.js @@ -46,6 +46,21 @@ function filterValuesBySides( values, sides ) { return filteredValues; } +function splitStyleValue( value ) { + // Check for shorthand value ( a string value ). + if ( value && typeof value === 'string' ) { + // Convert to value for individual sides for BoxControl. + return { + top: value, + right: value, + bottom: value, + left: value, + }; + } + + return value; +} + export default function SpacingPanel( { context, getStyle, setStyle } ) { const { name } = context; const showPaddingControl = useHasPadding( context ); @@ -60,7 +75,7 @@ export default function SpacingPanel( { context, getStyle, setStyle } ) { ], } ); - const paddingValues = getStyle( name, 'padding' ); + const paddingValues = splitStyleValue( getStyle( name, 'padding' ) ); const paddingSides = useCustomSides( name, 'padding' ); const setPaddingValues = ( newPaddingValues ) => { @@ -68,7 +83,7 @@ export default function SpacingPanel( { context, getStyle, setStyle } ) { setStyle( name, 'padding', padding ); }; - const marginValues = getStyle( name, 'margin' ); + const marginValues = splitStyleValue( getStyle( name, 'margin' ) ); const marginSides = useCustomSides( name, 'margin' ); const setMarginValues = ( newMarginValues ) => { From c6c55956751c621b1a2f854420cf79bbd0de196f Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 17 Jun 2021 12:15:08 +1000 Subject: [PATCH 8/9] Remove unused has_properties function --- lib/class-wp-theme-json-gutenberg.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 6b6135fd76b66b..2ca794bd2deeae 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -518,21 +518,6 @@ private static function get_property_value( $styles, $path ) { return $value; } - /** - * Whether the metadata contains a key named properties. - * - * @param array $metadata Description of the style property. - * - * @return boolean True if properties exists, false otherwise. - */ - private static function has_properties( $metadata ) { - if ( array_key_exists( 'properties', $metadata ) ) { - return true; - } - - return false; - } - /** * Given a styles array, it extracts the style properties * and adds them to the $declarations array following the format: From 9b8f2c74d05bbac019e0ce8dea7c797c03d0dac6 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 17 Jun 2021 15:17:23 +1000 Subject: [PATCH 9/9] Filter empty longhand CSS styles from inline style generation --- packages/block-editor/src/hooks/style.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index 66bc3e4f6478bf..8c5d6ce1872f97 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -84,10 +84,12 @@ export function getInlineStyles( styles = {} ) { if ( !! subPaths && ! isString( styleValue ) ) { Object.entries( subPaths ).forEach( ( entry ) => { - const [ name, suffix ] = entry; - output[ name ] = compileStyleValue( - get( styleValue, [ suffix ] ) - ); + const [ name, subPath ] = entry; + const value = get( styleValue, [ subPath ] ); + + if ( value ) { + output[ name ] = compileStyleValue( value ); + } } ); } else { output[ propKey ] = compileStyleValue( get( styles, path ) );