Skip to content
Open
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
34 changes: 34 additions & 0 deletions src/wp-includes/block-supports/block-style-variations.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,33 @@ function wp_render_block_style_variation_support_styles( $parsed_block ) {
return $parsed_block;
}

/*
* A block style variation resolves to the same data every time it is
* encountered, so consecutive blocks using the same variation generate
* byte-identical CSS. Reuse the previous instance's class name in that case
* so the stylesheet is only generated and enqueued once.
*
* Reuse is deliberately limited to the immediately preceding instance.
* Variation selectors are wrapped in `:where()`, so every variation rule has
* the same specificity and source order alone decides which one wins. Sharing
* a class across an intervening variation would move it in the cascade and
* reintroduce the conflicting styles fixed in #61877.
*/
static $last_variation_key = null;
static $last_variation_class = null;

$variation_key = $parsed_block['blockName'] . '|' . $variation;

if ( null !== $last_variation_class && $last_variation_key === $variation_key ) {
_wp_array_set(
$parsed_block,
array( 'attrs', 'className' ),
$parsed_block['attrs']['className'] . " $last_variation_class"
);

return $parsed_block;
}

/*
* Recursively resolve any ref values with the appropriate value within the
* theme_json data.
Expand Down Expand Up @@ -194,6 +221,13 @@ function wp_render_block_style_variation_support_styles( $parsed_block ) {
wp_register_style( 'block-style-variation-styles', false, array( 'wp-block-library', 'global-styles' ) );
wp_add_inline_style( 'block-style-variation-styles', $variation_styles );

/*
* Record the instance so an immediately following block using the same
* variation can reuse it instead of emitting a duplicate stylesheet.
*/
$last_variation_key = $variation_key;
$last_variation_class = $class_name;

/*
* Add variation instance class name to block's className string so it can
* be enforced in the block markup via render_block filter.
Expand Down
86 changes: 86 additions & 0 deletions tests/phpunit/tests/block-supports/block-style-variations.php
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,92 @@ public function test_block_style_variation_ref_values() {
$this->assertSameSetsWithIndex( $expected, $variation_data, 'Variation data with resolved ref values does not match' );
}

/**
* Renders a group block with the given block style variation applied.
*
* @param string $variation Block style variation slug.
*
* @return array The parsed block after the block style variation support has run.
*/
private function render_group_with_variation( $variation ) {
return wp_render_block_style_variation_support_styles(
array(
'blockName' => 'core/group',
'attrs' => array( 'className' => "is-style-$variation" ),
)
);
}

/**
* Tests that consecutive blocks using the same block style variation share a
* single instance class, so the identical stylesheet is only emitted once.
*
* @ticket 65876
*
* @covers ::wp_render_block_style_variation_support_styles
*/
public function test_block_style_variation_reuses_instance_for_consecutive_blocks() {
switch_theme( 'block-theme' );

/*
* Render a different variation first so the reuse cache is primed with a
* known value regardless of what earlier tests left behind.
*/
$this->render_group_with_variation( 'block-style-variation-b' );

// Start counting emitted stylesheets from here.
$GLOBALS['wp_styles'] = null;

$first = $this->render_group_with_variation( 'block-style-variation-a' );
$second = $this->render_group_with_variation( 'block-style-variation-a' );

$this->assertSame(
$first['attrs']['className'],
$second['attrs']['className'],
'Consecutive blocks using the same variation should share one instance class'
);

$this->assertCount(
1,
wp_styles()->get_data( 'block-style-variation-styles', 'after' ),
'The variation stylesheet should only be emitted once for consecutive identical variations'
);
}

/**
* Tests that a variation interrupted by a different one gets a fresh instance
* class.
*
* Variation selectors are wrapped in `:where()`, so they all share the same
* specificity and source order alone decides which rule wins. Sharing a class
* across an intervening variation would move it in the cascade and
* reintroduce the conflicting styles fixed in #61877.
*
* @ticket 65876
* @ticket 61877
*
* @covers ::wp_render_block_style_variation_support_styles
*/
public function test_block_style_variation_does_not_reuse_instance_across_a_different_variation() {
switch_theme( 'block-theme' );

$first = $this->render_group_with_variation( 'block-style-variation-a' );
$second = $this->render_group_with_variation( 'block-style-variation-b' );
$third = $this->render_group_with_variation( 'block-style-variation-a' );

$this->assertNotSame(
$first['attrs']['className'],
$third['attrs']['className'],
'A variation separated by a different variation should get a fresh instance class'
);

$this->assertNotSame(
$first['attrs']['className'],
$second['attrs']['className'],
'Different variations should never share an instance class'
);
}

/**
* Tests that a non-string `className` attribute does not cause a fatal
* error and the block content is returned unmodified.
Expand Down
Loading