Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global Styles: Prevent duplicate block style variations CSS #62465

Merged
merged 8 commits into from
Jun 17, 2024
3 changes: 3 additions & 0 deletions backport-changelog/6.6/6827.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
https://github.com/WordPress/wordpress-develop/pull/6827

* https://github.com/WordPress/gutenberg/pull/62465
5 changes: 3 additions & 2 deletions lib/block-supports/block-style-variations.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,9 @@ function gutenberg_render_block_style_variation_support_styles( $parsed_block )
array( 'styles' ),
array( 'custom' ),
array(
'skip_root_layout_styles' => true,
'scope' => ".$class_name",
'include_block_style_variations' => true,
'skip_root_layout_styles' => true,
'scope' => ".$class_name",
)
);

Expand Down
23 changes: 16 additions & 7 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,7 @@ public function get_settings() {
*
* @since 5.8.0
* @since 5.9.0 Removed the `$type` parameter`, added the `$types` and `$origins` parameters.
* @since 6.6.0 Added option to skip root layout styles.
* @since 6.6.0 Added option to skip root layout or block style variation styles.
*
* @param array $types Types of styles to load. Will load all by default. It accepts:
* - `variables`: only the CSS Custom Properties for presets & custom ones.
Expand All @@ -1260,6 +1260,7 @@ public function get_settings() {
* - 'scope' that makes sure all style are scoped to a given selector
* - `root_selector` which overwrites and forces a given selector to be used on the root node
* - `skip_root_layout_styles` which omits root layout styles from the generated stylesheet.
* - `include_block_style_variations` which includes CSS for block style variations.
* @return string The resulting stylesheet.
*/
public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' ), $origins = null, $options = array() ) {
Expand All @@ -1280,7 +1281,7 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets'
}

$blocks_metadata = static::get_blocks_metadata();
$style_nodes = static::get_style_nodes( $this->theme_json, $blocks_metadata );
$style_nodes = static::get_style_nodes( $this->theme_json, $blocks_metadata, $options );
$setting_nodes = static::get_setting_nodes( $this->theme_json, $blocks_metadata );

$root_style_key = array_search( static::ROOT_BLOCK_SELECTOR, array_column( $style_nodes, 'selector' ), true );
Expand Down Expand Up @@ -2487,9 +2488,12 @@ protected static function get_setting_nodes( $theme_json, $selectors = array() )
*
* @param array $theme_json The tree to extract style nodes from.
* @param array $selectors List of selectors per block.
* @param array $options An array of options to facilitate filtering style node generation
* The options currently supported are:
* - `include_block_style_variations` which includes CSS for block style variations.
* @return array An array of style nodes metadata.
*/
protected static function get_style_nodes( $theme_json, $selectors = array() ) {
protected static function get_style_nodes( $theme_json, $selectors = array(), $options = array() ) {
$nodes = array();
if ( ! isset( $theme_json['styles'] ) ) {
return $nodes;
Expand Down Expand Up @@ -2533,7 +2537,7 @@ protected static function get_style_nodes( $theme_json, $selectors = array() ) {
return $nodes;
}

$block_nodes = static::get_block_nodes( $theme_json, $selectors );
$block_nodes = static::get_block_nodes( $theme_json, $selectors, $options );
foreach ( $block_nodes as $block_node ) {
$nodes[] = $block_node;
}
Expand Down Expand Up @@ -2608,9 +2612,12 @@ private static function update_separator_declarations( $declarations ) {
*
* @param array $theme_json The theme.json converted to an array.
* @param array $selectors Optional list of selectors per block.
* @param array $options An array of options to facilitate filtering node generation
* The options currently supported are:
* - `include_block_style_variations` which includes CSS for block style variations.
* @return array The block nodes in theme.json.
*/
private static function get_block_nodes( $theme_json, $selectors = array() ) {
private static function get_block_nodes( $theme_json, $selectors = array(), $options = array() ) {
$selectors = empty( $selectors ) ? static::get_blocks_metadata() : $selectors;
$nodes = array();
if ( ! isset( $theme_json['styles'] ) ) {
Expand Down Expand Up @@ -2639,7 +2646,8 @@ private static function get_block_nodes( $theme_json, $selectors = array() ) {
}

$variation_selectors = array();
if ( isset( $node['variations'] ) ) {
$include_variations = $options['include_block_style_variations'] ?? false;
if ( $include_variations && isset( $node['variations'] ) ) {
foreach ( $node['variations'] as $variation => $node ) {
$variation_selectors[] = array(
'path' => array( 'styles', 'blocks', $name, 'variations', $variation ),
Expand Down Expand Up @@ -3323,7 +3331,8 @@ public static function remove_insecure_properties( $theme_json, $origin = 'theme
$theme_json = static::sanitize( $theme_json, $valid_block_names, $valid_element_names, $valid_variations );

$blocks_metadata = static::get_blocks_metadata();
$style_nodes = static::get_style_nodes( $theme_json, $blocks_metadata );
$style_options = array( 'include_block_style_variations' => true ); // Allow variations data.
$style_nodes = static::get_style_nodes( $theme_json, $blocks_metadata, $style_options );

foreach ( $style_nodes as $metadata ) {
$input = _wp_array_get( $theme_json, $metadata['path'], array() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -565,9 +565,44 @@ describe( 'global styles renderer', () => {
},
};

expect( toStyles( Object.freeze( tree ), blockSelectors ) ).toEqual(
':where(body) {margin: 0;}.is-layout-flow > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }.is-layout-flow > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }.is-layout-flow > .aligncenter { margin-left: auto !important; margin-right: auto !important; }.is-layout-constrained > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }.is-layout-constrained > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }.is-layout-constrained > .aligncenter { margin-left: auto !important; margin-right: auto !important; }.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)) { max-width: var(--wp--style--global--content-size); margin-left: auto !important; margin-right: auto !important; }.is-layout-constrained > .alignwide { max-width: var(--wp--style--global--wide-size); }body .is-layout-flex { display:flex; }.is-layout-flex { flex-wrap: wrap; align-items: center; }.is-layout-flex > :is(*, div) { margin: 0; }body .is-layout-grid { display:grid; }.is-layout-grid > :is(*, div) { margin: 0; }' +
':root :where(.is-style-foo.wp-image.wp-image-spacing){padding-top: 2px;}:root :where(.is-style-foo.wp-image.wp-image-border-color){border-color: blue;}:root :where(.is-style-foo.wp-image){color: blue;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'
const hasBlockGapSupport = false;
const hasFallbackGapSupport = true;
const disableLayoutStyles = true;
const disableRootPadding = true;
const styleOptions = {
blockGap: false,
blockStyles: true,
layoutStyles: false,
marginReset: false,
presets: false,
rootPadding: false,
};

// Confirm no variation styles by default.
const withoutVariations = toStyles(
Object.freeze( tree ),
blockSelectors,
hasBlockGapSupport,
hasFallbackGapSupport,
disableLayoutStyles,
disableRootPadding,
styleOptions
);
expect( withoutVariations ).toEqual( '' );

// Includes variation styles when requested.
styleOptions.variationStyles = true;
const withVariations = toStyles(
Object.freeze( tree ),
blockSelectors,
hasBlockGapSupport,
hasFallbackGapSupport,
disableLayoutStyles,
disableRootPadding,
styleOptions
);
expect( withVariations ).toEqual(
':root :where(.is-style-foo.wp-image.wp-image-spacing){padding-top: 2px;}:root :where(.is-style-foo.wp-image.wp-image-border-color){border-color: blue;}:root :where(.is-style-foo.wp-image){color: blue;}'
);
} );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,7 @@ export const toStyles = (
marginReset: true,
presets: true,
rootPadding: true,
variationStyles: false,
...styleOptions,
};
const nodesWithStyles = getNodesWithStyles( tree, blockSelectors );
Expand Down Expand Up @@ -1010,7 +1011,7 @@ export const toStyles = (
);
}

if ( styleVariationSelectors ) {
if ( options.variationStyles && styleVariationSelectors ) {
Object.entries( styleVariationSelectors ).forEach(
( [ styleVariationName, styleVariationSelector ] ) => {
const styleVariations =
Expand Down
5 changes: 3 additions & 2 deletions packages/block-editor/src/hooks/block-style-variation.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,22 +116,23 @@ function useBlockProps( { name, className, clientId } ) {
const hasBlockGapSupport = false;
const hasFallbackGapSupport = true;
const disableLayoutStyles = true;
const isTemplate = true;
const disableRootPadding = true;

return toStyles(
variationConfig,
blockSelectors,
hasBlockGapSupport,
hasFallbackGapSupport,
disableLayoutStyles,
isTemplate,
disableRootPadding,
{
blockGap: false,
blockStyles: true,
layoutStyles: false,
marginReset: false,
presets: false,
rootPadding: false,
variationStyles: true,
}
);
}, [ variation, settings, styles, getBlockStyles, clientId ] );
Expand Down
77 changes: 77 additions & 0 deletions phpunit/class-wp-theme-json-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -5410,4 +5410,81 @@ public function test_scope_style_node_selectors() {

$this->assertEquals( $expected, $actual );
}

/**
* Block style variations styles aren't generated by default. This test covers
* the `get_block_nodes` does not include variations by default, preventing
* the inclusion of their styles.
*/
public function test_opt_out_of_block_style_variations_by_default() {
$theme_json = new ReflectionClass( 'WP_Theme_JSON_Gutenberg' );

$func = $theme_json->getMethod( 'get_block_nodes' );
$func->setAccessible( true );

$theme_json = array(
'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
'styles' => array(
'blocks' => array(
'core/button' => array(
'variations' => array(
'outline' => array(
'color' => array(
'background' => 'red',
),
),
),
),
),
),
);
$selectors = array();

$block_nodes = $func->invoke( null, $theme_json, $selectors );
$button_variations = $block_nodes[0]['variations'] ?? array();

$this->assertEquals( array(), $button_variations );
}

/**
* Block style variations styles aren't generated by default. This test ensures
* variations are included by `get_block_nodes` when requested.
*/
public function test_opt_in_to_block_style_variations() {
$theme_json = new ReflectionClass( 'WP_Theme_JSON_Gutenberg' );

$func = $theme_json->getMethod( 'get_block_nodes' );
$func->setAccessible( true );

$theme_json = array(
'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
'styles' => array(
'blocks' => array(
'core/button' => array(
'variations' => array(
'outline' => array(
'color' => array(
'background' => 'red',
),
),
),
),
),
),
);
$selectors = array();
$options = array( 'include_block_style_variations' => true );

$block_nodes = $func->invoke( null, $theme_json, $selectors, $options );
$button_variations = $block_nodes[0]['variations'] ?? array();

$expected = array(
array(
'path' => array( 'styles', 'blocks', 'core/button', 'variations', 'outline' ),
'selector' => '.wp-block-button.is-style-outline .wp-block-button__link',
),
);

$this->assertEquals( $expected, $button_variations );
}
}
Loading