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: Fix for third-party blocks #3529

Closed

Conversation

ockham
Copy link
Contributor

@ockham ockham commented Oct 26, 2022

First reported in WordPress/gutenberg#40808.

Quoting that issue's instructions to reproduce the issue:

  • Install WooCommerce.
  • Open the FSE Editor.
  • Add a WC Blocks (e.g: Feature Product Block).
  • Customize the global styles related to the block.
  • Save.
  • Notice that the Global Styles is not saved.

The Gutenberg PR that fixed this was flagged for inclusion in WP 6.1 a month ago, but we missed that these are PHP changes, which thus require a manual backport 😞 This was recently brought up by @oandregal to us.

First impressions indicate that this is a regression that was introduced during the 6.1 cycle. I'll try to ascertain.

Trac ticket: https://core.trac.wordpress.org/ticket/56915


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@ockham ockham marked this pull request as ready for review October 26, 2022 13:40
@ndiego
Copy link
Member

ndiego commented Oct 26, 2022

I can confirm this issue using my custom Icon Block.

Reproducing the issue

To reproduce, set a custom border style in Global Styles.
image

Add an icon in the Editor and the border will appear correct.
image

However, on the Frontend, the global style is not applied.
image

After the fix is applied

With this fix, everything appears correctly on the frontend.
image

@gigitux
Copy link

gigitux commented Oct 26, 2022

I can confirm that this is a regression.

Global Styles - Editor Side Global Styles - Frontend Side - WP 6.1 Global Styles - Frontend Side - WP 6.0
image image image

Reproduce issue

  1. Install WooCommerce. Import products from the CSV that is attached at the bottom.
  2. Open the FSE Editor.
  3. Edit the Product Catalog template.
  4. Add a WC Blocks (e.g.: Product Categories List).
  5. Customize the global styles related to the block.
  6. Save.
  7. Go to on /shop page. See that the customizations aren't applied (on WP 6.1)

sample_products.csv

@ockham
Copy link
Contributor Author

ockham commented Oct 26, 2022

FWIW, I can also repro the issue and the fix with @ndiego's Icon block 😊 (using a 10px red border with 20px rounded edges).

On the frontend:

trunk This branch
image image

@ockham
Copy link
Contributor Author

ockham commented Oct 26, 2022

Noting here that the Gutenberg fix has been part of Gutenberg 14.1 and all subsequent GB releases:

image

@ockham
Copy link
Contributor Author

ockham commented Oct 26, 2022

Verifying that styling did work in 6.0.3 for the Icon block ✅

image

@cbravobernal
Copy link
Contributor

I can reproduce it also and check that is a regression with a really simple block plugin.
https://www.loom.com/share/72856d7db45b40ce9f12e225ea0f8f05

@ockham
Copy link
Contributor Author

ockham commented Oct 26, 2022

To recap:

What/who is impacted? This issue affects third-party blocks.
How are they impacted? The issue is that when using Global Styles to change the styling of a third-party block, that styling is not reflected on the frontend.
What's the severity? I'd consider this high severity, since it means that Global Styles will only work for blocks included with WordPress, but won't have any effect on the frontend for any third-party blocks.

Does this meet the criteria of being fixed this late into the cycle?

@desrosj
Copy link
Contributor

desrosj commented Oct 26, 2022

Could we please add some unit tests here? Especially this late in the cycle, it would be great to include these.

@ockham
Copy link
Contributor Author

ockham commented Oct 26, 2022

Could we please add some unit tests here? Especially this late in the cycle, it would be great to include these.

Yeah, makes sense! I’d have to look a bit where this would best fit in; I guess we probably have some unit test coverage for Global Styles already 🤔

I guess the test should be for wp_add_global_styles_for_blocks since that’s what we’re modifying here. And we'd need to check the output of wp_add_inline_style for the presence of the relevant styling 🤔

@ockham
Copy link
Contributor Author

ockham commented Oct 26, 2022

Looks like there's no test coverage for wp_add_global_styles_for_blocks yet. However, there's e.g. wp_get_global_stylesheet in the same file, which has coverage in a dedicated tests/phpunit/tests/theme/wpGetGlobalStylesheet.php file.

So I guess we need to add a new tests/phpunit/tests/theme/wpGetGlobalStylesForBlocks.php file.

@ockham
Copy link
Contributor Author

ockham commented Oct 26, 2022

I haven't figured out yet how to write a unit test for this; specifically, how to programmatically set some Global Styles for a block.

Pinging @oandregal @scruffian @ramonjd @andrewserong in case you're able to help with this 🙏 😊

On the assertions side, if I read the code correctly, we should ideally cover two different scenarios -- core/ and non-core/ blocks (see). In either case, they should check if wp_add_inline_style's output contains the expected styling.

(For non-core/ blocks, the assertion should fail without the fix in 1885efc.)

@ramonjd
Copy link
Member

ramonjd commented Oct 26, 2022

I haven't figured out yet how to write a unit test for this; specifically, how to programmatically set some Global Styles for a block.

Apologies in advanced if this is not anywhere near what you asked, but here's what I know without thinking too hard:

If you want to set styles for blocks in theme.json, there's the option of creating a test theme and styling your blocks in theme.json. Here's an example: https://github.com/WordPress/gutenberg/blob/963d836b290ce5ec002de2027a8011d492c20420/phpunit/block-supports/typography-test.php#L39

If it's user-defined global styles (those added in the site editor) we want to test, then maybe we could hook into wp_theme_json_data_user to update the underlying data and styles in a unit test?

In the function wp_add_global_styles_for_blocks() we're calling $tree = WP_Theme_JSON_Resolver::get_merged_data();, which gets the merged theme.json data, including styles, and including user customisations.

To get user data for the merge, get_user_data() is called. That's where wp_theme_json_data_user lives.

That is all theoretical, I haven't tried it yet. If I get time today I will, but the universe has conspired against me so leaving these notes here in case they're helpful.

@andrewserong
Copy link
Contributor

Thanks for the ping! Unfortunately I can't think of much else add other than what Ramon mentioned, where sometimes adding a test theme to themedir1 can be a good way to handle things that call global functions that gather theme data.

Although, maybe there's an option to filter the theme data via the wp_theme_json_data_blocks filter within a test (passing in some placeholder blocks in the structure there to match what needs to be tested), now that it exists? Then after calling wp_add_global_styles_for_blocks() look up the global $wp_styles to see if the inline style has been added as expected?

I'll be AFK for the rest of the week so unfortunately can't dig any further right now, but happy to help test / review on Monday if this is still being worked on.

@oandregal
Copy link
Member

oandregal commented Oct 27, 2022

👋 I've taken a look and this is the gist we need to do for a test:

  1. Add styles for a 3rd party block in any test theme. We can reuse one of the existing themes. Because the block won't be registered for the other tests, it'll be ignored. Something like this in the theme.json of any test theme:
{
    "styles" : {
        "blocks" :{
            "my/third-party-block": {
                "color": {
                    "background": "hotpink"
                }
            }
    }
}
  1. Write a test that retrieves those styles and previously registers the block called my/third-party-block. Something along these lines:
function test_third_party_styles( ) {
    $name     = 'my/third-party-block';
    $settings = array(
        'icon'            => 'text',
        'category'        => 'common',
        'render_callback' => 'foo',
    );
    register_block_type( $name, $settings ); // Register the block, so the theme.json styles are not ignored.
    wp_add_global_styles_for_blocks();
    unregister_block_type( $name ); // Unregister it, so it does not affect other tests.

    // TEST STYLES HAVE BEEN INLINED: HOW DO WE TEST THIS?
}

I'm looking at completing this (how to test inline styles?) but sharing in the interest of velocity, in case anyone else knows off the top of their head how to test the remaining things.

@ramonjd
Copy link
Member

ramonjd commented Oct 27, 2022

how to test inline styles?

Not sure either.

Could we assert against registered styles?

wp_styles()->registered['some-inline-styles']->extra['after'],

@oandregal
Copy link
Member

Added a single test 5521274 We can iterate from here.

@@ -69,6 +69,11 @@
"filter": {
"duotone": "var(--wp--preset--duotone--custom-duotone)"
}
},
"my/third-party-block": {
Copy link
Member

@oandregal oandregal Oct 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These styles will be ignored by any other tests because the block my/third-party-block won't be registered.

wp_enqueue_global_styles();
unregister_block_type( $name );

$this->assertStringContainsString( '.wp-block-my-third-party-block{background-color: hotpink;}', get_echo( 'wp_print_styles' ), '3rd party block styles are included' );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The styles added to theme.json for the my/third-party-block generate this style rule: .wp-block-my-third-party-block{background-color: hotpink;}.

'render_callback' => 'foo',
);
register_block_type( $name, $settings );
wp_enqueue_global_styles();
Copy link
Member

@oandregal oandregal Oct 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are interested in testing wp_add_global_styles_for_blocks. Though, because of how it's designed, I wasn't lucky using it directly. Hence, I had to resort to call this function (the one that orchestrates all the styles).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it'd be great if we could isolate the test to really just run wp_add_global_styles_for_blocks 🤔

@ockham
Copy link
Contributor Author

ockham commented Oct 27, 2022

Hmm, looks like the test is passing on trunk 🤔 (I've cherry-picked 5521274 to trunk locally and ran npm run test:php -- --group=themes.)

@oandregal
Copy link
Member

I've executed the test without the fix (npm run test:php -- --filter Tests_Theme_wpGetGlobalStylesForBlocks) and it's passing. I'm trying to understand why.

@oandregal
Copy link
Member

Pushed a change 1099bce that makes the test fail on trunk. According to the how the test passed without that, I understand this is the issue without this patch:

  • when block styles are loaded as part of the global-styles-inline-css stylesheet, 3rd party blocks styles worked fine. How to test: use a theme that disables separate block assets via add_filter( 'should_load_separate_core_block_assets', '__return_false' );.
  • when block styles are inlined to the own block stylesheet, this didn't work. My understanding is that block themes load assets separately, by default. This condition can be forced via add_filter( 'should_load_separate_core_block_assets', '__return_false' ).

@ockham
Copy link
Contributor Author

ockham commented Oct 27, 2022

Pushed a change 1099bce that makes the test fail on trunk.

Great, thank you very much!

According to the how the test passed without that, I understand this is the issue without this patch:

  • when block styles are loaded as part of the global-styles-inline-css stylesheet, 3rd party blocks styles worked fine. How to test: use a theme that disables separate block assets via add_filter( 'should_load_separate_core_block_assets', '__return_false' );.
  • when block styles are inlined to the own block stylesheet, this didn't work. My understanding is that block themes load assets separately, by default. This condition can be forced via add_filter( 'should_load_separate_core_block_assets', '__return_false' ).

Yeah, I think that matches this logic here:

if ( ! wp_should_load_separate_core_block_assets() ) {
wp_add_inline_style( 'global-styles', $block_css );
continue;
}

@oandregal
Copy link
Member

oandregal commented Oct 27, 2022

I understand this is the issue without this patch:

Yeah, the issue only happens in trunk when the styles are loaded per block. Tested by:

  • using trunk
  • using TwentyTwentyThree
  • added an icon block
  • go to the global styles sidebar and add a border

Following this steps, the border is not rendered in the front end.

Then, I did the following:

  • create a functions.php for the theme
  • add this:
<?php

add_filter( 'should_load_separate_core_block_assets', '__return_false' );

And the border is properly rendered.

So, yeah, the test is covering the issue we had.

@ockham
Copy link
Contributor Author

ockham commented Oct 27, 2022

Update, I'm working to isolate the test to wp_add_global_styles_for_blocks (see).

@ockham
Copy link
Contributor Author

ockham commented Oct 27, 2022

So I've replaced the call to wp_enqueue_global_styles with wp_add_global_styles_for_blocks:

diff --git a/tests/phpunit/tests/theme/wpGetGlobalStylesForBlocks.php b/tests/phpunit/tests/theme/wpGetGlobalStylesForBlocks.php
index 627c4fc82d..350f6a0e06 100644
--- a/tests/phpunit/tests/theme/wpGetGlobalStylesForBlocks.php
+++ b/tests/phpunit/tests/theme/wpGetGlobalStylesForBlocks.php
@@ -83,7 +83,7 @@ class Tests_Theme_wpGetGlobalStylesForBlocks extends WP_UnitTestCase {
                        'render_callback' => 'foo',
                );
                register_block_type( $name, $settings );
-               wp_enqueue_global_styles();
+               wp_add_global_styles_for_blocks();
                unregister_block_type( $name );
 
                $this->assertStringContainsString( '.wp-block-my-third-party-block{background-color: hotpink;}', get_echo( 'wp_print_styles' ), '3rd party block styles are included' );

I've then debugged what wp_add_inline_style( $handle, $data ) is doing. It's basically an alias for wp_styles()->add_inline_style( $handle, $data ). The wp_styles() function gives access to the global wp_styles object, which is of course of class WP_Styles. That class in turn is derived from WP_Dependencies.

Turns out that down in the call stack, this function is called :

public function add_data( $handle, $key, $value ) {
if ( ! isset( $this->registered[ $handle ] ) ) {
return false;
}
return $this->registered[ $handle ]->add_data( $key, $value );
}

with the following args:

$handle: global-styles
$key: after
$value: Array
(
    [0] => .wp-block-my-third-party-block{background-color: hotpink;}
)

Turns out that ! isset( $this->registered[ $handle ] ) returns false for $handle === 'after' $handle === 'global_styles'.

I'll try to find out why 'after' 'global-styles' is not registered (and what the heck that even means).

Edit: I had mixed up $handle and $key! We're looking for the global_styles handle, not the after handle!

@ockham
Copy link
Contributor Author

ockham commented Oct 27, 2022

I'll try to find out why 'after' 'global-styles' is not registered (and what the heck that even means).

If I'm not mistaken, the only way to register a handle with WP_Dependencies is via its add() method (rather than add_data()). So I'm thinking some other code must be doing that for the 'after' 'global-styles' handle somewhere in the callstack of wp_enqueue_global_styles, which would explain why the test works for that, but not with explicitly calling wp_add_global_styles_for_blocks.

Edit: I had mixed up $handle and $key! We're looking for the global_styles handle, not the after handle!

@ockham
Copy link
Contributor Author

ockham commented Oct 27, 2022

This looks promising:

wp_register_style( 'global-styles', false, array(), true, true );

@ockham
Copy link
Contributor Author

ockham commented Oct 27, 2022

Ah, or not. 😕 I think I need to step away from this for a moment. Upon re-running the test, I don't even see global_styles not being registered anymore. Maybe I was looking at the wrong thing 🙄

@ockham
Copy link
Contributor Author

ockham commented Oct 27, 2022

Oh, this actually does register global_styles in time for the add_data call 🤔

The test still isn't passing with this, though 😕

diff --git a/tests/phpunit/tests/theme/wpAddGlobalStylesForBlocks.php b/tests/phpunit/tests/theme/wpAddGlobalStylesForBlocks.php
index c3286f813e..92d8dfcddf 100644
--- a/tests/phpunit/tests/theme/wpAddGlobalStylesForBlocks.php
+++ b/tests/phpunit/tests/theme/wpAddGlobalStylesForBlocks.php
@@ -83,7 +83,8 @@ class Tests_Theme_wpAddGlobalStylesForBlocks extends WP_UnitTestCase {
                        'render_callback' => 'foo',
                );
                register_block_type( $name, $settings );
-               wp_enqueue_global_styles();
+               wp_register_style( 'global-styles', false, array(), true, true );
+               wp_add_global_styles_for_blocks();
                unregister_block_type( $name );
 
                $this->assertStringContainsString( '.wp-block-my-third-party-block{background-color: hotpink;}', get_echo( 'wp_print_styles' ), '3rd party block styles are included' );

@hellofromtonya
Copy link
Contributor

@ockham I tweaked your example:

	wp_register_style( 'global-styles', false, array(), true, true );

		$actual = wp_styles()->get_data( 'global-styles', 'after' );
		$actual = is_array( $actual ) ? $actual : array();
		$this->assertNotContains(
			'.wp-block-my-third-party-block{background-color: hotpink;}',
			$actual,
			'Third party block inline style should not be registered before running wp_add_global_styles_for_blocks()'
		);
		
		wp_add_global_styles_for_blocks();

		$actual = wp_styles()->get_data( 'global-styles', 'after' );
		$this->assertSame(
			'.wp-block-my-third-party-block{background-color: hotpink;}',
			$actual[0],
			'Third party block inline style should be registered after running wp_add_global_styles_for_blocks()'
		);

This fails before the fix and passes after the fix. I'll do a little cleanup and push changes shortly.

@ockham
Copy link
Contributor Author

ockham commented Oct 27, 2022

Ah, I guess it’s the argument to wp_styles()->get_data() that’s key! If I’m reading WP_Dependencies::do_items() (called from wp_print_styles), WP_Dependencies::all_deps(), and friends correctly, the class is a bit picky if no $handles arg is passed to those methods. Seems like without that arg, do_items only prints styling for handles that were previously enqueued:

public function do_items( $handles = false, $group = false ) {
/*
* If nothing is passed, print the queue. If a string is passed,
* print that item. If an array is passed, print those items.
*/
$handles = false === $handles ? $this->queue : (array) $handles;

@@ -0,0 +1,66 @@
<?php

abstract class WP_Theme_UnitTestCase extends WP_UnitTestCase {
Copy link
Contributor

@hellofromtonya hellofromtonya Oct 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add this base test case? Test classes in this group are repeating the same code. This base abstract test case encapsulates the repeating code, removing it from the test class itself. It's the starting point for improving the other tests in this same group.

Why in this PR? This "repeating set up and tear down code" is copied from the other tests in the same group. This means it's not part of the code review for the scope of this PR. By moving this copied logic to a base test case, it's signaling to focus the review on the new test class, not this copied setup/teardown code.

Note: A follow-up PR will address the other tests in this group.

@hellofromtonya
Copy link
Contributor

hellofromtonya commented Oct 27, 2022

19f6271 improves the tests to:

  • focus on the scope of this PR
  • isolate the integration tests to wp_add_global_styles_for_blocks()

Running locally:

  • Removing the bugfix, tests fail ✅
  • Adding the bugfix, tests pass ✅

These 2 states are the expected behavior ✅

/**
* @ticket 56915
*/
public function test_third_party_blocks_inline_styles_not_register_to_global_styles() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be

Suggested change
public function test_third_party_blocks_inline_styles_not_register_to_global_styles() {
public function test_third_party_blocks_inline_styles_not_registered_to_global_styles() {

?

Copy link
Contributor Author

@ockham ockham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are looking great 👍 Thank you very much for the coverage @hellofromtonya

@ockham
Copy link
Contributor Author

ockham commented Oct 27, 2022

Running locally:

  • Removing the bugfix, tests fail ✅
  • Adding the bugfix, tests pass ✅

These 2 states are the expected behavior ✅

Confirmed!

Copy link
Contributor

@hellofromtonya hellofromtonya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

More tests should be added to the new Tests_Theme_WpAddGlobalStylesForBlocks test class for all of the paths through the function. However, those tests are out-of-scope for this bug report.

@hellofromtonya hellofromtonya force-pushed the fix/global-styles-for-third-party-blocks branch from 37b6f74 to 25eaf26 Compare October 27, 2022 14:24
@hellofromtonya
Copy link
Contributor

Whoopsie, accidentally pushed my revert of the patch when testing the tests fail without the patch 🤦‍♀️ Reverted that temporary code and force pushed test only changes.

Copy link
Contributor

@dream-encode dream-encode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Tests passing locally for me as well. 👍

@dream-encode
Copy link
Contributor

Thanks everyone! Merged into Core in https://core.trac.wordpress.org/changeset/54703.

@ockham ockham deleted the fix/global-styles-for-third-party-blocks branch October 27, 2022 15:51
@ockham
Copy link
Contributor Author

ockham commented Oct 27, 2022

Merged into Core's 6.1 branch in https://core.trac.wordpress.org/changeset/54705.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

10 participants