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

Post Template Block: Set block context via filter #50313

Merged
merged 3 commits into from
May 30, 2023

Conversation

ockham
Copy link
Contributor

@ockham ockham commented May 4, 2023

What?

In the Post Template block's render callback, instead of creating a new WP_Block instance with postId and postType context set, use the render_block_context filter to set that context and render the existing WP_Block instance.

Why?

This approach arguably follows our established patterns with regard to handling block context better. Notably, with the previous approach, we were only setting block context for the Post Template block itself. In this PR, we extend it to apply to all child blocks, including ones that are dynamically inserted, e.g. via the render_block filter. This is relevant for Auto-inserting blocks (see #50103).

This follows the precedent of the Comment Template block, see #50279.

How?

See "What".

Testing Instructions

Verify that the Post Template block (and children) are still working as expected on the frontend:

  1. Use a block theme that uses them (e.g. Twenty Twenty-Three).
  2. On trunk, create a number of posts. View on the frontend, and take note (and a screenshot) of what the posts look like.
  3. Switch to this branch, and rebuild GB.
  4. Reload the page, and verify that it looks the same as before.

Testing Instructions for Keyboard

N/A.

Screenshots or screencast

N/A.

@ockham ockham self-assigned this May 4, 2023
@ockham ockham added the [Block] Post Template Affects the Post Template Block label May 4, 2023
// Set the block name to one that does not correspond to an existing registered block.
// This ensures that for the inner instances of the Post Template block, we do not render any block supports.
$block_instance['blockName'] = 'core/null';

$block->name = 'core/null';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ockham ockham marked this pull request as ready for review May 4, 2023 09:15
@ockham ockham requested a review from ajitbohra as a code owner May 4, 2023 09:15
)
)
)->render( array( 'dynamic' => false ) );
$block_content = $block->render( array( 'dynamic' => false ) );
Copy link
Member

Choose a reason for hiding this comment

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

I left a similar comment under the PR that updates the Comment Template block. Have you considered passing the original context $block->context together with the locally modified context? I guess both approaches will work. The only question is why we landed on using the approach with a separate instance of WP_Block in this block. @ntsekouras, any insights we could miss?

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 discovered something on the Comments Template block PR that might be relevant here: https://github.com/WordPress/gutenberg/pull/50279/files#r1184916607

Copy link
Contributor

Choose a reason for hiding this comment

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

The only question is why we landed on using the approach with a separate instance of WP_Block in this block. @ntsekouras, any insights we could miss?

I'm not really sure, but maybe even the render_block_context filter wasn't even in code yet. It seems both Query and this filter were introduced in the same WP version(5.5). There is a discussion about the context in the original PR, but nothing regarding the new instance.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still not sure why the issue with the layout classes is happening, but the culprit is definitely this change. Logging out $block_content shows the classes are already present in the nested block. This happens independently of whether the block in question supports layout or not: I can see the classes locally being added to Post Title.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My current (and somewhat uninformed) hunch is that prior to this PR, we were setting the block name to core/null for the copy of the block that we created on the fly, whereas now, we're setting it directly on the block itself. Maybe setting the block name back to its original value after rendering could fix this. I'll give that a try.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, I think I was slightly off. Judging by this comment

// Set the block name to one that does not correspond to an existing registered block.
// This ensures that for the inner instances of the Post Template block, we do not render any block supports.

...the whole point of setting the block name to core/null is to prevent the application of block supports -- which would lead to the duplication of class names that we're seeing here. So it's basically not working as it's supposed to.

My new hunch is that setting only the ->name isn't enough: Previously, we were setting the parsed block's blockName field, which is used by the WP_Block constructor to set the ->name but also the ->block_type.

So I think it's the latter that's missing here.

@gziolo gziolo added the [Type] Bug An existing feature does not function as intended label May 4, 2023
Copy link
Contributor

@andrewserong andrewserong left a comment

Choose a reason for hiding this comment

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

Thanks for the ping and for the explanation surrounding using render_block_context 👍

In testing, I noticed that this PR appears to result in an unexpected classname appearing in children of the li item. Specifically the inner wrapper classname for the post template block appears to be duplicated in the child of the li item, where it shouldn't occur.

On trunk, for example, wp-block-post-template-is-layout-flow appears on the wrapper ul element, and not in the Group block that is a direct child of the Post Template block:

image

With this PR, wp-block-post-template-is-layout-flow appears to be duplicated, that is, it's appears in the wrapper ul element, and is also appearing in the rendered markup for the Group block that is a direct child of the Post Template block:

image

From observing that, my guess is that by re-using $block instead of a new WP_Block, additional data is possibly being passed along / process / generated, that shouldn't be?

CC: @tellthemachines since this relates to layout classes.

@tellthemachines
Copy link
Contributor

I can reproduce @andrewserong's issue above in the editor - wp-block-post-template-is-layout-flow is duplicated in the next block down - but in the front end I'm seeing the duplication of both classnames - is-layout-flow wp-block-post-template-is-layout-flow - two blocks down instead.

I'm not at all sure what's happening at first glance 😕 but will try to look into it a bit more.

@gziolo
Copy link
Member

gziolo commented May 8, 2023

You can achieve a similar goal by correctly propagating the available context to the rendered inner blocks with:

diff --git a/packages/block-library/src/post-template/index.php b/packages/block-library/src/post-template/index.php
index 3a3c207cf9..4797faf69a 100644
--- a/packages/block-library/src/post-template/index.php
+++ b/packages/block-library/src/post-template/index.php
@@ -90,9 +90,12 @@ function render_block_core_post_template( $attributes, $content, $block ) {
 		$block_content = (
 			new WP_Block(
 				$block_instance,
-				array(
-					'postType' => get_post_type(),
-					'postId'   => get_the_ID(),
+				array_merge(
+					$block->context,
+					array(
+						'postType' => get_post_type(),
+						'postId'   => get_the_ID(),
+					)
 				)
 			)
 		)->render( array( 'dynamic' => false ) );

In the case of the Post Template Block it shouldn't make any difference because the only additional context available is the query by default. However, it might resolve some other issues when custom blocks are used.

@jdm-web
Copy link

jdm-web commented May 15, 2023

Glad you have this subject opened.

I opened a track ticket recently with some code I've used to propagate the context based on the provides_context attribute.

Here's the link to the ticket : https://core.trac.wordpress.org/ticket/58278#ticket

And here's the proposed implementation if that helps :


                // Render the inner blocks of the Post Template block with `dynamic` set to `false` to prevent calling
                // `render_callback` and ensure that no wrapper markup is included.
        $context = array(
            'postType' => get_post_type(),
            'postId'   => get_the_ID(),
        );
        
        //Check if additional context is provided by ancestor blocks
        if (!empty($block->block_type->provides_context)) {
            foreach ($block->block_type->provides_context as $context_name) {
                if (array_key_exists($context_name, $block->context)) {
                    $context[$context_name] = $block->context[$context_name];
                }
            }
        }

                $block_content = (
                        new WP_Block(
                                $block_instance,
                                $context
                        )
                )->render( array( 'dynamic' => false ) );

@gziolo
Copy link
Member

gziolo commented May 16, 2023

I left some feedback #50279 (comment) based on my explorations on the changes that landed in trunk for the Post Comment block.

@ockham
Copy link
Contributor Author

ockham commented May 22, 2023

As next steps for this PR, I'd like to:

  • add automated test coverage for the class name (to make sure it's not duplicated), and
  • test my theory.

@ockham
Copy link
Contributor Author

ockham commented May 24, 2023

You can achieve a similar goal by correctly propagating the available context to the rendered inner blocks with:

diff --git a/packages/block-library/src/post-template/index.php b/packages/block-library/src/post-template/index.php
index 3a3c207cf9..4797faf69a 100644
--- a/packages/block-library/src/post-template/index.php
+++ b/packages/block-library/src/post-template/index.php
@@ -90,9 +90,12 @@ function render_block_core_post_template( $attributes, $content, $block ) {
 		$block_content = (
 			new WP_Block(
 				$block_instance,
-				array(
-					'postType' => get_post_type(),
-					'postId'   => get_the_ID(),
+				array_merge(
+					$block->context,
+					array(
+						'postType' => get_post_type(),
+						'postId'   => get_the_ID(),
+					)
 				)
 			)
 		)->render( array( 'dynamic' => false ) );

Unfortunately, that's not quite equivalent to the solution proposed by this PR's branch; see #50279 (comment), where I explain the difference (for the Comment Template block in that case, but the same applies to the Post Template block).

ockham added 2 commits May 24, 2023 11:49
In the Post Template block's render callback, instead of creating a new `WP_Block` instance with `postId` and `postType` context set, use the `render_block_context` filter to set that context and render the existing `WP_Block` instance.

This approach arguably follows our established patterns with regard to handling block context better. Notably, with the previous approach, we were only setting block context for the Post Template block itself. In this PR, we extend it to apply to all child blocks, including ones that are dynamically inserted, e.g. via the `render_block` filter. This is relevant for Auto-inserting blocks (see #50103).

This follows the precedent of the Comment Template block, see #50279.
@ockham ockham force-pushed the update/post-template-render-context branch from e22af2d to 8695bd7 Compare May 24, 2023 09:49
@ockham
Copy link
Contributor Author

ockham commented May 24, 2023

As next steps for this PR, I'd like to:

  • add automated test coverage for the class name (to make sure it's not duplicated), and

Done in 8695bd7, which passes on trunk but fails on this branch.

(I also rebased.)

Edit: Aside, we might want to include this test in wordpress-develop for some baseline coverage of the Post Template block 🤔

@ockham
Copy link
Contributor Author

ockham commented May 25, 2023

With the test coverage in 8695bd7, and the fix I just posted in 8add1e6, I think this PR should be ready for another look 😊

@github-actions
Copy link

Flaky tests detected in 8add1e6.
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/5079572629
📝 Reported issues:

@gziolo
Copy link
Member

gziolo commented May 29, 2023

My new discovery is that we operate with two different states for the WP_Block instances.

The context that to the current block opted to with uses_context:

public $context = array();

https://github.com/WordPress/wordpress-develop/blob/bf38bd326c90fbe0a46ecc2253ee8ad00a8d7b52/src/wp-includes/class-wp-block.php#L50

The accumulated context exposed by all parent blocks with provides_context that child blocks can opt in to:

protected $available_context;

https://github.com/WordPress/wordpress-develop/blob/bf38bd326c90fbe0a46ecc2253ee8ad00a8d7b52/src/wp-includes/class-wp-block.php#L59

I realized that my attempt shared in #50313 (comment) is incorrect because we should be passing $block->available_context when instantiating WP_Block, but it isn't possible as of today because it's a protected class field.


Here is the test case that I copied and modified for the Post Template block from WordPress core that covers the block context functionality:

https://github.com/WordPress/wordpress-develop/blob/bf38bd326c90fbe0a46ecc2253ee8ad00a8d7b52/tests/phpunit/tests/blocks/context.php#L74-L135

It's going to be necessary to apply additional modifications for Post Template and Comment Template blocks to ensure that the available context is correctly passed from parent blocks:

diff --git a/phpunit/blocks/render-post-template-test.php b/phpunit/blocks/render-post-template-test.php
index 13b5623cdd..2718126b8f 100644
--- a/phpunit/blocks/render-post-template-test.php
+++ b/phpunit/blocks/render-post-template-test.php
@@ -13,6 +13,13 @@ class Tests_Blocks_RenderPostTemplateBlock extends WP_UnitTestCase {
 	private static $post;
 	private static $other_post;
 
+	/**
+	 * Registered block names.
+	 *
+	 * @var string[]
+	 */
+	private $registered_block_names = array();
+
 	public function set_up() {
 		parent::set_up();
 
@@ -39,6 +46,37 @@ class Tests_Blocks_RenderPostTemplateBlock extends WP_UnitTestCase {
 		);
 	}
 
+	/**
+	 * Tear down each test method.
+	 */
+	public function tear_down() {
+		while ( ! empty( $this->registered_block_names ) ) {
+			$block_name = array_pop( $this->registered_block_names );
+			unregister_block_type( $block_name );
+		}
+
+		parent::tear_down();
+	}
+
+	/**
+	 * Registers a block type.
+	 *
+	 * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a
+	 *                                   complete WP_Block_Type instance. In case a WP_Block_Type
+	 *                                   is provided, the $args parameter will be ignored.
+	 * @param array                $args {
+	 *     Optional. Array of block type arguments. Any arguments may be defined, however the
+	 *     ones described below are supported by default. Default empty array.
+	 *
+	 *     @type callable $render_callback Callback used to render blocks of this block type.
+	 * }
+	 */
+	private function register_block_type( $name, $args ) {
+		register_block_type( $name, $args );
+
+		$this->registered_block_names[] = $name;
+	}
+
 	public function test_rendering_post_template() {
 		$parsed_blocks = parse_blocks(
 			'<!-- wp:post-template --><!-- wp:post-title /--><!-- wp:post-excerpt /--><!-- /wp:post-template -->'
@@ -70,4 +108,62 @@ END;
 			str_replace( array( "\n", "\t" ), '', $markup )
 		);
 	}
+
+	public function test_provides_block_context() {
+		$provided_context = array();
+
+		$this->register_block_type(
+			'gutenberg/test-context-provider',
+			array(
+				'attributes'       => array(
+					'foo' => array(
+						'type' => 'string',
+					),
+				),
+				'provides_context' => array(
+					'gutenberg/foo' => 'foo',
+				),
+			)
+		);
+
+		$this->register_block_type(
+			'gutenberg/test-context-consumer',
+			array(
+				'uses_context'    => array(
+					'gutenberg/foo',
+				),
+				'render_callback' => static function( $attributes, $content, $block ) use ( &$provided_context ) {
+					$provided_context[] = $block->context;
+
+					return '';
+				},
+			)
+		);
+
+		$parsed_blocks = parse_blocks(
+			'<!-- wp:gutenberg/test-context-provider {"foo":"bar"} -->' .
+			'<!-- wp:post-template -->' .
+			'<!-- wp:gutenberg/test-context-consumer /-->' .
+			'<!-- /wp:post-template -->' .
+			'<!-- /wp:gutenberg/test-context-provider -->'
+		);
+
+		render_block( $parsed_blocks[0] );
+
+		$this->assertSame(
+			array(
+				array(
+					'gutenberg/foo' => 'bar',
+					'postType'      => self::$other_post->post_type,
+					'postId'        => self::$other_post->ID,
+				),
+				array(
+					'gutenberg/foo' => 'bar',
+					'postType'      => self::$post->post_type,
+					'postId'        => self::$post->ID,
+				),
+			),
+			$provided_context
+		);
+	}
 }

At the moment, the test fails with the following error:

Screenshot 2023-05-29 at 13 45 09

It confirms that this PR resolves one of the existing issues for passing down postId and postType with the block context, but it is still impossible to access other values from the available context to avoid issues like the one reported in https://core.trac.wordpress.org/ticket/58278 by @jdm-web. See also his comment in #50313 (comment).

Copy link
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

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

This PR is an improvement compared to what we have today, but there is still room for improvement, as noted in my previous comment. However, we can tackle the other issue separately.

@ockham
Copy link
Contributor Author

ockham commented May 30, 2023

Here is the test case that I copied and modified for the Post Template block from WordPress core that covers the block context functionality:

https://github.com/WordPress/wordpress-develop/blob/bf38bd326c90fbe0a46ecc2253ee8ad00a8d7b52/tests/phpunit/tests/blocks/context.php#L74-L135

Ah, nice find! I was trying to find test coverage for the Post Template block in Core and was surprised that I couldn't find any 😅

This PR is an improvement compared to what we have today, but there is still room for improvement, as noted in my previous comment. However, we can tackle the other issue separately.

Thanks a lot for investigating, and for posting the unit test and fix!

Happy to land this, and to iterate on the additional fixes separately 😊

@ockham ockham merged commit 31ddc1f into trunk May 30, 2023
@ockham ockham deleted the update/post-template-render-context branch May 30, 2023 13:12
@github-actions github-actions bot added this to the Gutenberg 16.0 milestone May 30, 2023
@gziolo
Copy link
Member

gziolo commented May 30, 2023

Thanks a lot for investigating, and for posting the unit test and fix!

There is no fix yet. We can't access $block->available_context because this field is protected.

sethrubenstein pushed a commit to pewresearch/gutenberg that referenced this pull request Jul 13, 2023
* Post Template Block: Set block context via filter

In the Post Template block's render callback, use the `render_block_context` filter to set `postId` and `postType` context, rather than passing that context directly to the `WP_Block` constructor.

This approach arguably follows our established patterns with regard to handling block context better. Notably, with the previous approach, we were only setting block context for the Post Template block itself. In this PR, we extend it to apply to all child blocks, including ones that are dynamically inserted, e.g. via the `render_block` filter. This is relevant for Auto-inserting blocks (see WordPress#50103).

This follows the precedent of the Comment Template block, see WordPress#50279.

Furthermore, add some test coverage to guard against duplicated block-supports class names, which was an issue in a previous iteration of this PR.
@jdm-web
Copy link

jdm-web commented Aug 29, 2023

I've seen this PR landed in WP6.3.

With this code, is it possible to tell the post template to provide the queryId as context to the block below it?
If yes, would you mind sharing an example?

By reading the render_block_core_post_template, I don't see how to use the render_block_context filter to alter the context, and I don't see where $filter_block_context nor $context are used.
Furthermore, before the change, the context was passed as a second WP_Block parameter, it's not the case anymore so I'm a little confused.

Thank you for your explanations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Post Template Affects the Post Template Block [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants