Make WordPress Core

Opened 5 months ago

Last modified 3 months ago

#63919 assigned enhancement

Block Bindings: Conditional rendering of HTML elements corresponding to block attrs

Reported by: bernhard-reiter's profile Bernhard Reiter Owned by:
Milestone: Future Release Priority: normal
Severity: normal Version:
Component: General Keywords: needs-patch
Focuses: Cc:

Description

An Image block will only include a <figcaption> HTML element in its saved markup if the block's caption attribute is not empty; the same applies to the Pullquote block's citation attribute, which corresponds to a <cite> element.

This poses a problem when adding Block Bindings support for those attributes, as the Block Bindings logic depends on the presence of those HTML elements in order to replace their inner HTML with the value obtained from the respective Block Bindings source.

One possible solution is to change the editor-side block logic so that includes those HTML elements whenever the corresponding attribute is bound to a Block Bindings source. This change needs to be made to the block JavaScript code, i.e. inside of Gutenberg.

This approach needs to be complemented by server-side logic that will remove those HTML elements prior to rendering if their value is empty. (E.g. if the Block Bindings source did not return a non-empty value for the given attribute after all.)

Change History (10)

This ticket was mentioned in PR #9702 on WordPress/wordpress-develop by @Bernhard Reiter.


5 months ago
#1

  • Keywords has-patch has-unit-tests added

Implement "conditional" Block Bindings, using the example of the Image block's caption attribute. The following cases need to be supported when that attribute is bound to a Block Bindings source:

  • [x] If the block was saved with a caption, and the Block Bindings source value is not empty, the caption is replaced with the latter.
  • [x] If the block was saved with a caption, and the Block Bindings source value is empty, the <figcaption> element is removed.
  • [x] If the block was saved without a caption, and the Block Bindings source value is not empty, a <figcaption> element is added (with the caption set to the Block Bindings source value).
    • We can implement this by changing save.js to include a <figcaption>. This is implemented by https://github.com/WordPress/gutenberg/pull/71483. If the caption attribute is empty _after_ binding, the empty <figcaption> element will be removed by the logic that handles the case described by the second item in this list.

## Testing Instructions
The first two cases from the above list are covered by unit tests.

To test manually, follow these instructions:

Create a new post, and paste the following markup:

<div class="wp-block-group">
<figure class="wp-block-image size-large is-resized">[[Image(https://img.pokemondb.net/artwork/large/bulbasaur.jpg)]]<figcaption class="wp-element-caption">Replace</figcaption></figure>



<figure class="wp-block-image size-large is-resized">[[Image(https://img.pokemondb.net/artwork/large/charmander.jpg)]]<figcaption class="wp-element-caption"></figcaption></figure>



<figure class="wp-block-image size-large is-resized">[[Image(https://img.pokemondb.net/artwork/large/squirtle.jpg)]]<figcaption class="wp-element-caption">Remove</figcaption></figure>
</div>

The resulting content should look like this:

https://github.com/user-attachments/assets/2e8d8390-719d-478f-b382-400a10bff47c

In the Visual Editor, select all three image blocks. Then, from the block toolbar, select "Create Pattern". Assign a name of your choice to the pattern (e.g. "Pokemons").

Below the three image blocks that now form a pattern, insert another instance of the "Pokemons" pattern. Then, edit the newly inserted instance as follows:

  1. Change the caption of the first image to "Bulbasaur".
  2. Add a caption to the second image ("Charmander").
  3. Remove the caption of third image.

Publish the post, and verify that the result looks like this:

https://github.com/user-attachments/assets/97baa824-a21f-4b47-b48d-98135094c1e2

(Testing instructions largely copied from https://github.com/WordPress/gutenberg/pull/70642.)

Supersedes https://github.com/ockham/wordpress-develop/pull/5.

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

#2 @dmsnell
5 months ago

  • Keywords has-patch has-unit-tests removed

Thanks for raising this important point of tension! And this only highlights the tip of the iceberg when it comes to blocks supporting block bindings. The <figcaption> example is a good case to review, but any and every block that does anything other than outputting a static HTML structure independent of its attributes will face this same problem and many will have no clear automatic resolution.

One thing worth keeping in mind is the cost of adding new complexity in Core to try and automate this or fix blocks. With every resolution we dissociate the rendering of the block from the JS code in save() and we do so by adding compound conditionals.

At some point it may be easier, substantially less cognitive overhead, and easier to maintain due to the parallel construction of writing a rendering function in PHP for blocks with this characteristic. That way the JS and PHP code can be discussed, people already know how to write JS and PHP, and Core doesn’t need the machinery to make expensive guesses.

#3 @Bernhard Reiter
5 months ago

Yes, that's a very valid point. There's a real risk of adding too much implicit magic to the logic that processes block markup before rendering.

Earlier approaches (e.g.) attempted to avoid this by adding directives (such as wp-data-maybe-hide) to instruct WP what to do and to make the process thus more explicit. However, TBH I didn't like that too much either, since the directive seemed largely redundant (the infomation's available from the block's metadata.bindings attribute), yet required pervasive updates to existing block markup (as seen in the block markup snapshot test updates in that PR); plus I wasn't quite sure if this approach would scale well to other logic constructs (e.g. for loops) which we will eventually need (think Gallery blocks).

But I agree that there's a risk in saying, "oh yeah, this element is empty, and it corresponds to a rich-text attribute whose selector in block.json is a tag name, so that means we'll remove it".

One alternative I can think of is a more block-supports inspired approach, where we provide utils on both the editor (JS) and render (PHP) side. "Just" opting a block attribute into Block Bindings seems like a bit of a pipe dream anyway right now; there's always some block-specific logic that needs tweaking. (For example, the Image caption needed changes to its corresponding UI control's visibility).

So maybe we can provide block authors with tools such as replace_block_attribute_value() (which would be basically this thing) or remove_node. One downside is that it would mean that even blocks that have so far been static will need to add PHP code to do so if any of their attributes is to be supported by Block Bindings (even e.g. core/paragraph!)


As a way forward, how about we start by landing the editor-only changes first (once that PR is unblocked)? From there, we can see if we prefer conditionally removing the <figcaption> based on "generic" criteria, or to do it from the block's render method (needs a PR).

#4 @Bernhard Reiter
4 months ago

We recently landed this PR which saves an Image block's <figcaption> as part of the block markup if its caption attribute is bound to a block bindings source. (Conversely, if the attribute is empty and isn't bound, the <figcaption> won't be included in the markup.)

This still leaves us with the case where the block bindings source returns an empty value for caption, in which case the <figcaption> should be removed prior to rendering.

Based on the discussion on this ticket and some conversations with @gziolo and @cbravobernal, we're now thinking that this should be the individual block's responsibility. This seems like a reasonable requirement, especially since a "generic" solution at WP Core level that removes those elements based on some criteria seems too fragile and error-prone (unless enabled by a special placeholder syntax, which would also come at a cost).

@Bernhard Reiter commented on PR #9702:


4 months ago
#5

Closing this due to reasons laid out in this comment.

I'll follow-up with a trimmed-down PR that _only_ adds support for the Image block's caption attribute (but doesn't remove <figcaption> if it's empty).

This ticket was mentioned in PR #9949 on WordPress/wordpress-develop by @Bernhard Reiter.


4 months ago
#6

  • Keywords has-patch has-unit-tests added

Supersedes https://github.com/WordPress/wordpress-develop/pull/9702 for reasons stated here.

TODO:

Trac ticket:

@gziolo commented on PR #9990:


4 months ago
#8

After talking with @mtias, I'm going to close this PR as we are going to deprecate the Pullquote block soon:

This ticket was mentioned in Slack in #core by welcher. View the logs.


3 months ago

#10 @welcher
3 months ago

  • Keywords needs-patch added; has-patch has-unit-tests removed
  • Milestone changed from 6.9 to Future Release
Note: See TracTickets for help on using tickets.