From ccc90db551f85ff23923352cd56e324dc2ccaf6f Mon Sep 17 00:00:00 2001 From: salvatorecorsi <86927009+salvatorecorsi@users.noreply.github.com> Date: Wed, 21 Oct 2026 09:20:42 +0200 Subject: [PATCH] Blocks: Preserve pre-rendered content when generating preset styles. --- src/wp-includes/block-supports/settings.php | 8 +- .../phpunit/tests/block-supports/settings.php | 172 ++++++++++++++++++ 2 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 tests/phpunit/tests/block-supports/settings.php diff --git a/src/wp-includes/block-supports/settings.php b/src/wp-includes/block-supports/settings.php index 6b1eac6defc7f..b4ab5dfdc1d18 100644 --- a/src/wp-includes/block-supports/settings.php +++ b/src/wp-includes/block-supports/settings.php @@ -71,19 +71,19 @@ function _wp_add_block_level_presets_class( $block_content, $block ) { * * @param string|null $pre_render The pre-rendered content. Default null. * @param array $block The block being rendered. - * @return null + * @return string|null The pre-rendered content. */ function _wp_add_block_level_preset_styles( $pre_render, $block ) { // Return early if the block has not support for descendent block styles. $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); if ( ! block_has_support( $block_type, '__experimentalSettings', false ) ) { - return null; + return $pre_render; } // return early if no settings are found on the block attributes. $block_settings = $block['attrs']['settings'] ?? null; if ( empty( $block_settings ) ) { - return null; + return $pre_render; } $class_name = '.' . _wp_get_presets_class_name( $block ); @@ -144,7 +144,7 @@ function _wp_add_block_level_preset_styles( $pre_render, $block ) { wp_enqueue_block_support_styles( $styles ); } - return null; + return $pre_render; } add_filter( 'render_block', '_wp_add_block_level_presets_class', 10, 2 ); diff --git a/tests/phpunit/tests/block-supports/settings.php b/tests/phpunit/tests/block-supports/settings.php new file mode 100644 index 0000000000000..08e8175012675 --- /dev/null +++ b/tests/phpunit/tests/block-supports/settings.php @@ -0,0 +1,172 @@ +test_block_name ) { + unregister_block_type( $this->test_block_name ); + } + $this->test_block_name = null; + parent::tear_down(); + } + + /** + * Preserves earlier render results without losing the block's preset styles. + * + * @ticket 59551 + * @dataProvider data_preserve_pre_rendered_content + * + * @param bool $supports_settings Whether the block supports preset settings. + * @param array $settings The block's preset settings. + * @param string|null $pre_render The earlier filter's render result. + */ + public function test_preserve_pre_rendered_content( $supports_settings, $settings, $pre_render ) { + $this->test_block_name = 'test/settings-pre-render'; + $render_calls = 0; + $filter_calls = 0; + + register_block_type( + $this->test_block_name, + array( + 'api_version' => 3, + 'attributes' => array( 'settings' => array( 'type' => 'object' ) ), + 'supports' => array( '__experimentalSettings' => $supports_settings ), + 'render_callback' => static function () use ( &$render_calls ) { + ++$render_calls; + return '
Rendered settings block
'; + }, + ) + ); + + $block = array( + 'blockName' => $this->test_block_name, + 'attrs' => $settings ? array( 'settings' => $settings ) : array(), + 'innerBlocks' => array(), + 'innerHTML' => '', + 'innerContent' => array(), + ); + + $pre_render_filter = function ( $content, $parsed_block ) use ( $pre_render, &$filter_calls ) { + if ( $this->test_block_name !== $parsed_block['blockName'] ) { + return $content; + } + ++$filter_calls; + $this->assertNull( $content ); + return $pre_render; + }; + add_filter( 'pre_render_block', $pre_render_filter, 9, 2 ); + + // Resolve the theme before measuring the block's newly enqueued styles. + wp_is_block_theme(); + $style_hooks = array(); + foreach ( array( 'wp_head', 'wp_footer' ) as $hook_name ) { + $style_hooks[ $hook_name ] = isset( $GLOBALS['wp_filter'][ $hook_name ] ) + ? clone $GLOBALS['wp_filter'][ $hook_name ] + : null; + } + + try { + $actual = do_blocks( serialize_block( $block ) ); + + if ( null === $pre_render ) { + $this->assertSame( 1, $render_calls ); + $this->assertStringContainsString( 'Rendered settings block', $actual ); + } else { + $this->assertSame( $pre_render, $actual ); + $this->assertSame( 0, $render_calls ); + } + $this->assertSame( 1, $filter_calls ); + + $styles = $this->get_added_style_output( $style_hooks ); + if ( $supports_settings && $settings ) { + $this->assertStringContainsString( '--wp--preset--color--test-settings-pre-render', $styles ); + $this->assertStringContainsString( '#123abc', $styles ); + $this->assertStringContainsString( '.has-test-settings-pre-render-color', $styles ); + } else { + $this->assertStringNotContainsString( 'test-settings-pre-render', $styles ); + } + } finally { + remove_filter( 'pre_render_block', $pre_render_filter, 9 ); + foreach ( $style_hooks as $hook_name => $original_hook ) { + if ( null === $original_hook ) { + unset( $GLOBALS['wp_filter'][ $hook_name ] ); + } else { + $GLOBALS['wp_filter'][ $hook_name ] = $original_hook; + } + } + } + } + + /** + * Captures the real style callbacks added by the rendered block only. + * + * @param array $original_hooks Style hooks before rendering. + * @return string Newly enqueued stylesheet output. + */ + private function get_added_style_output( $original_hooks ) { + ob_start(); + try { + foreach ( $original_hooks as $hook_name => $original_hook ) { + if ( ! isset( $GLOBALS['wp_filter'][ $hook_name ] ) ) { + continue; + } + foreach ( $GLOBALS['wp_filter'][ $hook_name ]->callbacks as $priority => $callbacks ) { + $original_callbacks = null === $original_hook ? array() : ( $original_hook->callbacks[ $priority ] ?? array() ); + foreach ( array_diff_key( $callbacks, $original_callbacks ) as $callback ) { + call_user_func( $callback['function'] ); + } + } + } + return ob_get_contents(); + } finally { + ob_end_clean(); + } + } + + /** + * @return array + */ + public static function data_preserve_pre_rendered_content() { + $settings = array( + 'color' => array( + 'palette' => array( + 'custom' => array( + array( + 'name' => 'Test settings pre-render', + 'slug' => 'test-settings-pre-render', + 'color' => '#123abc', + ), + ), + ), + ), + ); + $branches = array( + 'unsupported block' => array( false, $settings ), + 'no settings' => array( true, array() ), + 'preset settings' => array( true, $settings ), + ); + $results = array( + 'empty string' => '', + 'zero string' => '0', + 'cached markup' => '
Cached settings block
', + 'normal render' => null, + ); + $cases = array(); + foreach ( $branches as $branch_name => $branch ) { + foreach ( $results as $result_name => $pre_render ) { + $cases[ $branch_name . ', ' . $result_name ] = array( $branch[0], $branch[1], $pre_render ); + } + } + return $cases; + } +}