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

Layout Support: Replace incremental IDs with hashes #68210

Open
wants to merge 11 commits into
base: trunk
Choose a base branch
from

Conversation

DAreRodz
Copy link
Contributor

@DAreRodz DAreRodz commented Dec 20, 2024

What?

Changes how class names for layout containers are generated, replacing per-block, incrementally-generated IDs with hashes obtained from the layout styles definition.

Will fix #67308.

Why?

This currently affects the style of elements outside of router regions, even when navigating between pages generated with the same template, because layout class names depend on the number of blocks rendered using them (see #67826), which can vary.

How?

The PR adds a new function named gutenberg_unique_id_from_values, which basically generates a short hash from the passed array.

This function is used to generate the layout container class, receiving all the data required to generate the style output. That guarantees hashes to be unique for each layout style combination.

Similar examples can be found in the WordPress codebase already:

Testing Instructions

  1. Create a new post.
  2. Add four different groups to test each of the different types: flow, constrained, flex and grid. The layout settings can be found in the right-side panel under the "block" tab. Follow these steps to configure each of the types:
    • Flow: disable "Inner blocks use content width".
    • Constrained: enable "Inner blocks use content width".
    • Flex: transform the Group into a Row or a Stack.
    • Grid: transform the Group into a Grid.
  3. For the flow and constrained types, you would have to add more settings in order to generate classes with hashes:
    • Flow: add a custom block spacing in the block styles.
    • Constrained: change justification to left or right.
  4. Save and visit the post.
  5. Ensure the div container for these groups has a class name similar to wp-container-core-group-is-layout-634f0b9d (a different hash is OK).
  6. Edit the post and duplicate all Group elements. Save and visit the post.
  7. Ensure the duplicated Group elements have the same hashes as the original Group elements.
  8. Edit the post again, modifying some layout settings in the duplicated elements.
  9. Ensure the duplicated Group elements now have different hashes.

Screenshots or screencast

Before After
Screen.Recording.2024-12-20.at.13.45.58.mov
Screen.Recording.2024-12-20.at.13.48.13.mov

@DAreRodz DAreRodz added [Type] Bug An existing feature does not function as intended [Feature] Layout Layout block support, its UI controls, and style output. labels Dec 20, 2024
Copy link

github-actions bot commented Dec 20, 2024

Flaky tests detected in eb456cb.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/13332123167
📝 Reported issues:

@talldan
Copy link
Contributor

talldan commented Dec 23, 2024

Added some potential reviewers for when this is ready.

@gziolo
Copy link
Member

gziolo commented Feb 12, 2025

There are more places where incremental IDs are used. Example for layouts in child blocks. Should the strategy change there as well?

$container_content_class = wp_unique_prefixed_id( 'wp-container-content-' );

@DAreRodz
Copy link
Contributor Author

DAreRodz commented Feb 12, 2025

There are more places where incremental IDs are used. Example for layouts in child blocks. Should the strategy change there as well?

$container_content_class = wp_unique_prefixed_id( 'wp-container-content-' );

@gziolo Good catch! Yes, gutenberg_incremental_id_per_prefix and wp_unique_prefixed_id have the same problem. We should also replace wp_unique_prefixed_id calls.

@Mamaduka
Copy link
Member

@westonruter, do you have any thoughts on this proposal? I remember you suggested switching to wp_unique_id, which improved performance in specific scenarios.

@westonruter
Copy link
Member

@Mamaduka I fully endorse this proposal! Long ago Gutenberg was using uniqid() which was problems because the CSS on the page could not be cached since it was different with every page load (#38889). So switching to wp_unique_id() fixed that problem in that the class names were then stable across page loads. However, creating a class name out of hashed data will be even more stable since changes to the block order then won't matter. So yeah, +1 for using a hash to compute the class name.

Related, the Embed Optimizer plugin in Performance Lab is also using md5() in a similar way, except to create an element ID instead of a class name.

*
* @return string The generated unique ID for the array.
*/
function gutenberg_unique_prefixed_id_from_array( array $data, string $prefix = '' ): string {
Copy link
Member

Choose a reason for hiding this comment

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

Nit (non-blocking): I think we could use a more generic name here, wp_unique_id_from_values. Since the prefix is optional, we could also remove it.

Copy link
Member

Choose a reason for hiding this comment

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

I see the name was changed to gutenberg_unique_id_from_values. @DAreRodz, can you clarify your intentions regarding the prefix?

@DAreRodz DAreRodz force-pushed the update/block-supports-layout-container-class-with-hash branch from eb70891 to eb456cb Compare February 14, 2025 15:19
@DAreRodz DAreRodz marked this pull request as ready for review February 17, 2025 13:22
Copy link

github-actions bot commented Feb 17, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: DAreRodz <darerodz@git.wordpress.org>
Co-authored-by: gziolo <gziolo@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>
Co-authored-by: fabiankaegy <fabiankaegy@git.wordpress.org>
Co-authored-by: talldan <talldanwp@git.wordpress.org>
Co-authored-by: luisherranz <luisherranz@git.wordpress.org>
Co-authored-by: Gulamdastgir-Momin <gulamdastgir04@git.wordpress.org>
Co-authored-by: Mayank-Tripathi32 <mayanktripathi32@git.wordpress.org>
Co-authored-by: kmanijak <karolmanijak@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@DAreRodz
Copy link
Contributor Author

Setting this PR ready for review.

The logic for generating layout styles is complex, so I've added some test cases instead of testing the feature extensively. More test cases are welcome. 🙂

@@ -501,7 +501,47 @@ public function data_layout_support_flag_renders_classnames_on_wrapper() {
),
),
),
'expected_output' => '<p class="wp-container-content-1">Some text.</p>', // The generated classname number assumes `wp_unique_prefixed_id( 'wp-container-content-' )` will not have run previously in this test.
'expected_output' => '/<p class="wp-container-content-[a-f0-9]{8}">Some text.<\\/p>/',
Copy link
Member

Choose a reason for hiding this comment

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

md5 is predictable so it should stay the same between the test runs. What is the reasoning behind using pattern matching here? I expected the tests would ensure that these values don't accidentally change as long as params provided remain the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No problem. I can change that. I wasn't sure if it would be a good idea to update the tests every time the passed parameters change, but that's not going to happen often, right? 😅

PS: I copied what is being done for the duotone classes (although, in that case, the IDs might not be predictable).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've just updated the tests to check the generated hashes with their expected values.

Copy link
Member

Choose a reason for hiding this comment

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

Cool, it seems like it's doable for the use cases in this PR. We could leave a note in the tests that the hash might change if the conditions provided get refined in the future.

@DAreRodz DAreRodz requested a review from gziolo February 18, 2025 12:44
@gziolo
Copy link
Member

gziolo commented Feb 18, 2025

In my opinion, this PR is shaping up nicely, and my recent comments are mostly stylistic. I don't think I have any major feedback left at this point. I would appreciate some additional review on how the hash is calculated by someone more familiar with layout processing.

@DAreRodz DAreRodz requested a review from luisherranz February 19, 2025 12:32
$fallback_gap_value,
$block_spacing,
),
'wp-container-' . sanitize_title( $block['blockName'] ) . '-is-layout-'
Copy link
Member

@gziolo gziolo Feb 26, 2025

Choose a reason for hiding this comment

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

Not a blocker: I have a general thought after looking at the generated CSS. The fact that the class name contains the name of the block might be no longer needed. It's currently a general-purpose class that groups similar styles. It might be interesting to explore in the follow-up PR whether the class name prefix can be simplified to the wp-container-block-is-layout-. That would be also similar to the child layouts and the wp-container-content- prefix.

Screenshot 2025-02-26 at 11 07 00

@gziolo
Copy link
Member

gziolo commented Feb 26, 2025

Overall, everything works as expected on the frontend. PHP code properly generates predictable hashes taking into account how the block is configured. I noticed that the editor uses different algorithm for generating class names. I would expect that JavaScript gets updated to mirror the changes in PHP. Example:

Markup:

<!-- wp:group {"borderColor":"contrast","layout":{"type":"constrained"}} -->
<div class="wp-block-group has-border-color has-contrast-border-color"><!-- wp:paragraph -->
<p>Group</p>
<!-- /wp:paragraph -->

<!-- wp:group {"borderColor":"accent-3","layout":{"type":"default"}} -->
<div class="wp-block-group has-border-color has-accent-3-border-color"><!-- wp:paragraph -->
<p>Nested Group</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"style":{"layout":{"columnSpan":2}}} -->
<p>Child layout</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group --></div>
<!-- /wp:group -->

<!-- wp:group {"borderColor":"contrast","layout":{"type":"flex","flexWrap":"nowrap"}} -->
<div class="wp-block-group has-border-color has-contrast-border-color"><!-- wp:paragraph -->
<p>Row</p>
<!-- /wp:paragraph -->

<!-- wp:group {"borderColor":"accent-3","layout":{"type":"flex","flexWrap":"nowrap","justifyContent":"space-between","orientation":"horizontal"}} -->
<div class="wp-block-group has-border-color has-accent-3-border-color"><!-- wp:paragraph -->
<p>Nested Row</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"style":{"layout":{"columnSpan":2}}} -->
<p>Child layout</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group --></div>
<!-- /wp:group -->

<!-- wp:group {"borderColor":"contrast","layout":{"type":"flex","orientation":"vertical"}} -->
<div class="wp-block-group has-border-color has-contrast-border-color"><!-- wp:paragraph -->
<p>Stack</p>
<!-- /wp:paragraph -->

<!-- wp:group {"borderColor":"accent-3","layout":{"type":"flex","flexWrap":"nowrap"}} -->
<div class="wp-block-group has-border-color has-accent-3-border-color"><!-- wp:paragraph -->
<p>Nested Row</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"style":{"layout":{"columnSpan":2}}} -->
<p>Child layout</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group --></div>
<!-- /wp:group -->

<!-- wp:group {"borderColor":"contrast","layout":{"type":"grid"}} -->
<div class="wp-block-group has-border-color has-contrast-border-color"><!-- wp:paragraph -->
<p>Grid</p>
<!-- /wp:paragraph -->

<!-- wp:group {"borderColor":"accent-3","layout":{"type":"grid","columnCount":null,"minimumColumnWidth":"23rem"}} -->
<div class="wp-block-group has-border-color has-accent-3-border-color"><!-- wp:paragraph -->
<p>Nested Grid</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph {"style":{"layout":{"columnSpan":2}}} -->
<p>Child layout</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group -->

<!-- wp:paragraph -->
<p></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p></p>
<!-- /wp:paragraph --></div>
<!-- /wp:group -->

Editor:

Screenshot 2025-02-26 at 11 22 11

Frontend:

Screenshot 2025-02-26 at 11 25 30

What are your thoughts on these differences wp-container-content-b5f5adbb vs wp-container-content-17?

Edit: it looks like there is also wp-elements-17 that has no styles assigned to it, which isn't present on the front end. I see a similar nuanced difference for the wp-container-core-group-is-layout-55 in the editor vs the version with hash on the front end.

Copy link
Member

@fabiankaegy fabiankaegy left a comment

Choose a reason for hiding this comment

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

From my POV this here is a great fix that works as advertised that we should get into WordPress 6.8.

I'm about to start the RC for 20.4 which is the last release to make it into 6.8 so ideally I would have wanted to merge this before then. But since it is a bugfix we should be able to still get it in after today. So I'll hold merging and wait a few more days for additional feedback 👍

@gziolo
Copy link
Member

gziolo commented Feb 28, 2025

Fabian, feel free to land it if you confirmed it works correctly. We can discuss my feedback separately as the more I think about it, the less it's of an issue that editors encapsulates the styles under different class names. It doesn't impact visual aspects and developers can't target them anyway.

@fabiankaegy
Copy link
Member

Fabian, feel free to land it if you confirmed it works correctly. We can discuss my feedback separately as the more I think about it, the less it's of an issue that editors encapsulates the styles under different class names. It doesn't impact visual aspects and developers can't target them anyway.

Yeah I agree we should align the class name generation in JS to match. But since they are auto generated already and users cannot target them for me the two are separate and one doesn't need to block the other :)

And yes in my testing this solves the issue it was intended to solve and I am not noticing any regressions on the sample content I have as testing data :)

@DAreRodz
Copy link
Contributor Author

@fabiankaegy, feel free to merge this PR once it's ready. 😊

@Mamaduka
Copy link
Member

Mamaduka commented Mar 3, 2025

I think aligning class name generation between JS and PHP would be a lovely addition. @DAreRodz, @gziolo, do you have time to work on this?

Considering the bug status of this PR, it should be safe to merge in release betas.

@joemcgill, @desrosj, what do you think?

@gziolo
Copy link
Member

gziolo commented Mar 3, 2025

I think aligning class name generation between JS and PHP would be a lovely addition. @DAreRodz, @gziolo, do you have time to work on this?

Unfortunately, I don't have the capacity to work on it during the Beta phase.

@luisherranz
Copy link
Member

Yeah I agree we should align the class name generation in JS to match. But since they are auto generated already and users cannot target them for me the two are separate and one doesn't need to block the other :)

I agree with Fabian that, although aligning JS class name generation would be a nice addition, it shouldn't block merging this PR.

@Mamaduka
Copy link
Member

Mamaduka commented Mar 6, 2025

What are the remaining items here? Just aligning JS class name generation? Also, is the core backport PR up to date?

@desrosj, @joemcgill, what do you think about shipping this fix in WP 6.8?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Layout Layout block support, its UI controls, and style output. [Type] Bug An existing feature does not function as intended
Projects
Status: 🔎 Needs Review
Development

Successfully merging this pull request may close these issues.

Layout styles are incompatible with Interactivity API client-side navigation
7 participants