diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml
index 746e22c..aca92bc 100644
--- a/.github/workflows/phpcs.yml
+++ b/.github/workflows/phpcs.yml
@@ -29,7 +29,7 @@ jobs:
- name: Cache Composer packages
id: composer-cache
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
diff --git a/src/parser/block-additions/core-block.php b/src/parser/block-additions/core-block.php
index 14d5092..c76fd74 100644
--- a/src/parser/block-additions/core-block.php
+++ b/src/parser/block-additions/core-block.php
@@ -10,10 +10,12 @@
defined( 'ABSPATH' ) || die();
use WP_Block;
-use WP_Block_Supports;
-use function add_action;
+use WP_Post;
+use WPCOMVIP\BlockDataApi\ContentParser;
+
use function add_filter;
-use function remove_filter;
+use function get_post;
+use function parse_blocks;
/**
* Enhance core/block block by capturing its inner blocks.
@@ -26,59 +28,22 @@ class CoreBlock {
*/
private static $block_name = 'core/block';
- /**
- * A store of captured inner blocks. See `capture_inner_blocks`.
- *
- * @var array
- *
- * @access private
- */
- protected static $captured_inner_blocks = [];
-
/**
* Initialize the CoreBlock class.
*
* @access private
*/
public static function init(): void {
- add_action( 'vip_block_data_api__before_block_render', [ __CLASS__, 'setup_before_render' ], 10, 0 );
- add_action( 'vip_block_data_api__after_block_render', [ __CLASS__, 'cleanup_after_render' ], 10, 0 );
- add_filter( 'vip_block_data_api__sourced_block_inner_blocks', [ __CLASS__, 'get_inner_blocks' ], 5, 4 );
+ add_filter( 'vip_block_data_api__sourced_block_inner_blocks', [ __CLASS__, 'get_inner_blocks' ], 5, 5 );
add_filter( 'vip_block_data_api__sourced_block_result', [ __CLASS__, 'remove_content_array' ], 5, 2 );
}
/**
- * Setup before render.
- */
- public static function setup_before_render(): void {
- /**
- * Hook into the `render_block` filter, which is near the end of WP_Block#render().
- * This allows us to capture the inner blocks of synced patterns ("core/block").
- * See `capture_inner_blocks`.
- */
- add_filter( 'render_block', [ __CLASS__, 'capture_inner_blocks' ], 10, 3 );
- }
-
- /**
- * Cleanup after render.
- */
- public static function cleanup_after_render() {
- self::$captured_inner_blocks = [];
- remove_filter( 'render_block', [ __CLASS__, 'capture_inner_blocks' ], 10 );
- }
-
- /**
- * Capture the inner blocks of synced patterns during block rendering. Intended
- * for use with the `render_block` filter.
- *
- * We have no intention of filtering the rendered block content, but this hook
- * is conveniently located near the end of WP_Block#render() after block
- * processing is finished. We get access to the parent block via the global
- * static class `WP_Block_Supports`.
+ * Get the inner blocks of a synced pattern / reusable block. Intended for use
+ * with the `vip_block_data_api__sourced_block_inner_blocks` filter.
*
- * This approach is necessary because synced patterns (core/block) are dynamic
- * blocks, and core's method of rendering dynamic blocks severs the connection
- * between the parent block and its inner blocks:
+ * Synced patterns are dynamic blocks, and core's method of rendering dynamic
+ * blocks severs the connection between the parent block and its inner blocks:
*
* https://github.com/WordPress/WordPress/blob/6.6.1/wp-includes/class-wp-block.php#L519
*
@@ -88,105 +53,47 @@ public static function cleanup_after_render() {
* missing from the Block Data API. Capturing synced pattern content as inner
* blocks is extremely useful and avoids the need for additional API calls.
*
- * @param string $block_content Rendered block content.
- * @param array $parsed_block Parsed block data.
- * @param WP_Block $block Block instance.
- * @return string
- */
- public static function capture_inner_blocks( string $block_content, array $parsed_block, WP_Block $block ): string {
- // If this block is a synced pattern, that means it is finished rendering.
- // Lock its inner blocks to prevent further captures in case it is rendered
- // elsewhere in the tree.
- if ( self::$block_name === $block->name ) {
- $store_key = self::get_store_key( $parsed_block );
- if ( isset( self::$captured_inner_blocks[ $store_key ] ) ) {
- self::$captured_inner_blocks[ $store_key ]['locked'] = true;
- }
- }
-
- // Get the parent block that is currently being rendered. This is fragile,
- // but is currently the only way we can get access to the parent block from
- // inside a dynamic block's render callback function.
- //
- // https://github.com/WordPress/WordPress/blob/6.6.1/wp-includes/class-wp-block.php#L517
- $parent_block = isset( WP_Block_Supports::$block_to_render ) ? WP_Block_Supports::$block_to_render : [];
-
- // If the parent block is not a synced pattern, do nothing.
- if ( ! isset( $parent_block['attrs']['ref'] ) || self::$block_name !== $parent_block['blockName'] ) {
- return $block_content;
- }
-
- // Capture the inner block for this synced pattern.
- self::capture_inner_block( $parent_block, $block );
-
- return $block_content;
- }
-
- /**
- * Get captured inner blocks for synced patterns. Intended for use with
- * the `vip_block_data_api__sourced_block_inner_blocks` filter.
+ * This requires us to reimplement some logic from `render_block_core_block()`:
+ *
+ * https://github.com/WordPress/WordPress/blob/6.6.1/wp-includes/blocks/block.php#L19
*
* @param array $inner_blocks Inner blocks.
* @param string $block_name Block name.
- * @param int|null $_post_id Post ID (unused).
+ * @param int|null $post_id Post ID.
* @param array $parsed_block Parsed block data.
* @return array
*/
- public static function get_inner_blocks( array $inner_blocks, string $block_name, int|null $_post_id, array $parsed_block ): array {
+ public static function get_inner_blocks( array $inner_blocks, string $block_name, int|null $post_id, array $parsed_block ): array {
+ // Not a synced pattern? Return the inner blocks unchanged.
if ( self::$block_name !== $block_name || ! isset( $parsed_block['attrs']['ref'] ) ) {
return $inner_blocks;
}
- $store_key = self::get_store_key( $parsed_block );
+ $context = [];
- if ( ! isset( self::$captured_inner_blocks[ $store_key ] ) ) {
- return $inner_blocks;
+ // Support synced pattern overrides. Copied and adapted from core:
+ // https://github.com/WordPress/WordPress/blob/6.6.1/wp-includes/blocks/block.php#L81
+ //
+ // In our case, we don't need to filter the context since we can pass it in.
+ if ( isset( $parsed_block['attrs']['content'] ) ) {
+ $context['pattern/overrides'] = $parsed_block['attrs']['content'];
}
- return self::$captured_inner_blocks[ $store_key ]['inner_blocks'];
- }
+ // Load, parse, and render the inner blocks of the synced pattern, passing
+ // along its block context. We intentionally do not recursively call
+ // ContentParser->parse() to avoid calling telemetry and filters again.
+ $parser = new ContentParser();
+ $post = get_post( $parsed_block['attrs']['ref'] );
- /**
- * Create a unique key that can be used to identify a synced pattern. This
- * allows us to store and retrieve inner blocks for synced patterns and avoid
- * duplication when they are used multiple times within the same tree.
- *
- * Using a hash of attributes is important because they may contain synced
- * pattern overrides, which can change the inner block content. The attributes
- * contain the synced pattern post ID, so uniqueness is built-in.
- *
- * @param array $parsed_block Parsed block data.
- * @return string
- */
- protected static function get_store_key( array $parsed_block ): string {
- // Include the synced pattern ID in the key just for legibility.
- $synced_pattern_id = $parsed_block['attrs']['ref'] ?? null;
- $attribute_json = wp_json_encode( $parsed_block['attrs'] );
-
- return sprintf( '%s_%s', strval( $synced_pattern_id ), sha1( $attribute_json ) );
- }
-
- /**
- * Capture inner block for a synced pattern.
- *
- * @param array $synced_pattern Synced pattern block (parsed block).
- * @param WP_Block $block Inner block.
- */
- protected static function capture_inner_block( array $synced_pattern, WP_Block $block ): void {
- $store_key = self::get_store_key( $synced_pattern );
- if ( ! isset( self::$captured_inner_blocks[ $store_key ] ) ) {
- self::$captured_inner_blocks[ $store_key ] = [
- 'inner_blocks' => [],
- 'locked' => false,
- ];
+ if ( ! $post instanceof WP_Post ) {
+ return [];
}
- // This pattern has already been rendered somewhere in the tree and is now locked.
- if ( self::$captured_inner_blocks[ $store_key ]['locked'] ) {
- return;
- }
+ $blocks = parse_blocks( $post->post_content );
- self::$captured_inner_blocks[ $store_key ]['inner_blocks'][] = $block;
+ return array_map( function ( array $block ) use ( $parser, $context, $post_id ): WP_Block {
+ return $parser->render_parsed_block( $block, $post_id, $context );
+ }, $blocks );
}
/**
diff --git a/src/parser/content-parser.php b/src/parser/content-parser.php
index 7c8535b..3ddf068 100644
--- a/src/parser/content-parser.php
+++ b/src/parser/content-parser.php
@@ -180,10 +180,10 @@ public function parse( $post_content, $post_id = null, $filter_options = [] ) {
*/
do_action( 'vip_block_data_api__before_block_render', $blocks, $post_id );
- $sourced_blocks = array_map( function ( $block ) use ( $filter_options ) {
+ $sourced_blocks = array_map( function ( $block ) use ( $filter_options, $post_id ) {
// Render the block, then walk the tree using source_block to apply our
// sourced attribute logic.
- $rendered_block = $this->render_parsed_block( $block );
+ $rendered_block = $this->render_parsed_block( $block, $post_id );
return $this->source_block( $rendered_block, $filter_options );
}, $blocks );
@@ -252,14 +252,15 @@ public function parse( $post_content, $post_id = null, $filter_options = [] ) {
*
* https://github.com/WordPress/WordPress/blob/6.6.1/wp-includes/blocks.php#L1959
*
- * @param array $parsed_block Parsed block (result of `parse_blocks`).
+ * @param array $parsed_block Parsed block (result of `parse_blocks`).
+ * @param int|null $post_id Post ID.
+ * @param array $context Context to be passed to the block.
* @return WP_Block
*/
- protected function render_parsed_block( array $parsed_block ): WP_Block {
- $context = [];
- if ( is_int( $this->post_id ) ) {
- $context['postId'] = $this->post_id;
- $context['postType'] = get_post_type( $this->post_id );
+ public function render_parsed_block( array $parsed_block, int|null $post_id, array $context = [] ): WP_Block {
+ if ( is_int( $post_id ) ) {
+ $context['postId'] = $post_id;
+ $context['postType'] = get_post_type( $post_id );
}
$context = apply_filters( 'render_block_context', $context, $parsed_block, null );
@@ -280,7 +281,7 @@ protected function render_parsed_block( array $parsed_block ): WP_Block {
*
* @access private
*/
- protected function source_block( WP_Block $block, array $filter_options ) {
+ protected function source_block( WP_Block $block, array $filter_options ): array|null {
$block_name = $block->name;
if ( ! $this->should_block_be_included( $block, $filter_options ) ) {
@@ -309,7 +310,7 @@ protected function source_block( WP_Block $block, array $filter_options ) {
* @param array $inner_blocks An array of inner block (WP_Block) instances.
* @param string $block_name Name of the parsed block, e.g. 'core/paragraph'.
* @param int $post_id Post ID associated with the parsed block.
- * @param array $block Result of parse_blocks() for this block.
+ * @param array $parsed_block Result of parse_blocks() for this block.
*/
$inner_blocks = apply_filters( 'vip_block_data_api__sourced_block_inner_blocks', $inner_blocks, $block_name, $this->post_id, $block->parsed_block );
diff --git a/tests/parser/test-synced-patterns.php b/tests/parser/test-synced-patterns.php
index 8379a68..9636551 100644
--- a/tests/parser/test-synced-patterns.php
+++ b/tests/parser/test-synced-patterns.php
@@ -165,6 +165,28 @@ public function test_multiple_synced_patterns() {
$this->assertEquals( $expected_blocks, $blocks['blocks'], sprintf( 'Blocks not equal: %s', wp_json_encode( $blocks['blocks'] ) ) );
}
+ /* Missing synced pattern */
+
+ public function test_missing_synced_pattern() {
+ $html = sprintf( '', -1 );
+
+ $expected_blocks = [
+ [
+ 'name' => 'core/block',
+ 'attributes' => [
+ 'ref' => -1,
+ ],
+ // inner_blocks is omitted when empty for backwards compatibility with earlier release
+ ],
+ ];
+
+ $content_parser = new ContentParser( $this->get_block_registry() );
+ $blocks = $content_parser->parse( $html );
+
+ $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) );
+ $this->assertEquals( $expected_blocks, $blocks['blocks'], sprintf( 'Blocks not equal: %s', wp_json_encode( $blocks['blocks'] ) ) );
+ }
+
/* Synced pattern with override */
public function test_synced_pattern_with_override() {
@@ -226,6 +248,273 @@ public function test_synced_pattern_with_override() {
$this->assertEquals( 1, count( $blocks['blocks'][0]['innerBlocks'] ), 'Too many inner blocks in synced pattern' );
}
+ /* Synced pattern with deeply nested content */
+
+ public function test_synced_pattern_with_deeply_nested_content() {
+ $synced_pattern_content = '
+
+
+
+
+
+
Nested synced pattern content
+
+
+
+
+
+
+
Deeply nested synced pattern content
+
+
+
+
+
+
+
+
+
+ ';
+
+ $synced_pattern = $this->factory()->post->create_and_get( [
+ 'post_content' => $synced_pattern_content,
+ 'post_status' => 'publish',
+ 'post_type' => 'wp_block',
+ ] );
+
+ $html = sprintf( '', $synced_pattern->ID );
+
+ $expected_blocks = [
+ [
+ 'name' => 'core/block',
+ 'attributes' => [
+ 'ref' => $synced_pattern->ID,
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'core/group',
+ 'attributes' => [
+ 'tagName' => 'div',
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'core/group',
+ 'attributes' => [
+ 'tagName' => 'div',
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'core/paragraph',
+ 'attributes' => [
+ 'content' => 'Nested synced pattern content',
+ ],
+ ],
+ [
+ 'name' => 'core/group',
+ 'attributes' => [
+ 'tagName' => 'div',
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'core/group',
+ 'attributes' => [
+ 'tagName' => 'div',
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'core/paragraph',
+ 'attributes' => [
+ 'content' => 'Deeply nested synced pattern content',
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ $content_parser = new ContentParser( $this->get_block_registry() );
+ $blocks = $content_parser->parse( $html );
+
+ $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) );
+
+ // Core block attributes can change, so we use assertArraySubset to avoid
+ // brittle assertions.
+ $this->assertArraySubset( $expected_blocks, $blocks['blocks'], false, wp_json_encode( $blocks['blocks'] ) );
+ $this->assertEquals( 1, count( $blocks['blocks'] ), 'Too many blocks in result set' );
+ $this->assertEquals( 1, count( $blocks['blocks'][0]['innerBlocks'] ), 'Too many inner blocks in synced pattern' );
+ }
+
+ /* Synced pattern with sourced attribute in nested content */
+
+ public function test_synced_pattern_with_sourced_attribute_in_nested_content() {
+ $this->register_block_with_attributes( 'test/custom-block', [
+ 'content' => [
+ 'type' => 'rich-text',
+ 'source' => 'rich-text',
+ 'selector' => 'p',
+ '__experimentalRole' => 'content',
+ ],
+ 'bing' => [
+ 'type' => 'string',
+ 'source' => 'attribute',
+ 'selector' => 'p',
+ 'attribute' => 'data-bing',
+ ],
+ ] );
+
+ $synced_pattern_content = '
+
+
+
+
+
+
My nested synced pattern content
+
+
+
+
+
+ ';
+
+ $synced_pattern = $this->factory()->post->create_and_get( [
+ 'post_content' => $synced_pattern_content,
+ 'post_status' => 'publish',
+ 'post_type' => 'wp_block',
+ ] );
+
+ $html = sprintf( '', $synced_pattern->ID );
+
+ $expected_blocks = [
+ [
+ 'name' => 'core/block',
+ 'attributes' => [
+ 'ref' => $synced_pattern->ID,
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'core/group',
+ 'attributes' => [
+ 'tagName' => 'div',
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'core/group',
+ 'attributes' => [
+ 'tagName' => 'div',
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'test/custom-block',
+ 'attributes' => [
+ 'content' => 'My nested synced pattern content',
+ 'bing' => 'bong',
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ $content_parser = new ContentParser( $this->get_block_registry() );
+ $blocks = $content_parser->parse( $html );
+
+ $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) );
+ $this->assertEquals( $expected_blocks, $blocks['blocks'], sprintf( 'Blocks not equal: %s', wp_json_encode( $blocks['blocks'] ) ) );
+ }
+
+ /* Synced pattern with override in nested content */
+
+ public function test_synced_pattern_with_override_in_nested_content() {
+ $synced_pattern_content = '
+
+
+
+ ';
+
+ $synced_pattern = $this->factory()->post->create_and_get( [
+ 'post_content' => $synced_pattern_content,
+ 'post_status' => 'publish',
+ 'post_type' => 'wp_block',
+ ] );
+
+ $html = sprintf( '
+
+ ', $synced_pattern->ID );
+
+ $expected_blocks = [
+ [
+ 'name' => 'core/block',
+ 'attributes' => [
+ 'ref' => $synced_pattern->ID,
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'core/group',
+ 'attributes' => [
+ 'tagName' => 'div',
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'core/group',
+ 'attributes' => [
+ 'tagName' => 'div',
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'core/paragraph',
+ 'attributes' => [
+ 'content' => 'Overridden content', // Overridden by synced pattern override
+ 'metadata' => [
+ 'bindings' => [
+ 'content' => [
+ 'source' => 'core/pattern-overrides',
+ ],
+ ],
+ 'name' => 'my-override',
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ $content_parser = new ContentParser( $this->get_block_registry() );
+ $blocks = $content_parser->parse( $html );
+
+ $this->assertArrayHasKey( 'blocks', $blocks, sprintf( 'Unexpected parser output: %s', wp_json_encode( $blocks ) ) );
+
+ // Block bindings are currently only supported for specific core blocks.
+ // https://make.wordpress.org/core/2024/03/06/new-feature-the-block-bindings-api/
+ //
+ // Core block attributes can change, so we use assertArraySubset to avoid
+ // brittle assertions.
+ $this->assertArraySubset( $expected_blocks, $blocks['blocks'], false, wp_json_encode( $blocks['blocks'] ) );
+ $this->assertEquals( 1, count( $blocks['blocks'] ), 'Too many blocks in result set' );
+ $this->assertEquals( 1, count( $blocks['blocks'][0]['innerBlocks'] ), 'Too many inner blocks in synced pattern' );
+ }
+
/* Multiple nested synced patterns with block bindings -- FINAL BOSS! */
public function test_multiple_nested_synced_patterns_with_block_bindings() {
@@ -254,6 +543,9 @@ public function test_multiple_nested_synced_patterns_with_block_bindings() {
] );
$synced_pattern_content_1 = '
+
+
+
My first synced pattern content
@@ -265,6 +557,9 @@ public function test_multiple_nested_synced_patterns_with_block_bindings() {
Default content
+
+
+
';
$synced_pattern_1 = $this->factory()->post->create_and_get( [
@@ -274,6 +569,9 @@ public function test_multiple_nested_synced_patterns_with_block_bindings() {
] );
$synced_pattern_content_2 = sprintf( '
+
+
+
My second synced pattern content which contains the first
@@ -283,6 +581,9 @@ public function test_multiple_nested_synced_patterns_with_block_bindings() {
Another block to "wrap" the nested pattern
+
+
+
', $synced_pattern_1->ID );
$synced_pattern_2 = $this->factory()->post->create_and_get( [
@@ -340,37 +641,45 @@ public function test_multiple_nested_synced_patterns_with_block_bindings() {
],
'innerBlocks' => [
[
- 'name' => 'test/custom-block',
- 'attributes' => [
- 'content' => 'My first synced pattern content',
- 'bing' => 'bong',
+ 'name' => 'core/group',
+ 'attributes' => [
+ 'tagName' => 'div',
],
- ],
- [
- 'name' => 'core/paragraph',
- 'attributes' => [
- 'content' => sprintf( 'Block binding for core/paragraph with arg foo=bar in post %d', $post->ID ),
- 'metadata' => [
- 'bindings' => [
- 'content' => [
- 'source' => 'test/synced-pattern-block-binding',
- 'args' => [ 'foo' => 'bar' ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'test/custom-block',
+ 'attributes' => [
+ 'content' => 'My first synced pattern content',
+ 'bing' => 'bong',
+ ],
+ ],
+ [
+ 'name' => 'core/paragraph',
+ 'attributes' => [
+ 'content' => sprintf( 'Block binding for core/paragraph with arg foo=bar in post %d', $post->ID ),
+ 'metadata' => [
+ 'bindings' => [
+ 'content' => [
+ 'source' => 'test/synced-pattern-block-binding',
+ 'args' => [ 'foo' => 'bar' ],
+ ],
+ ],
],
],
],
- ],
- ],
- [
- 'name' => 'core/paragraph',
- 'attributes' => [
- 'content' => 'Default content',
- 'metadata' => [
- 'bindings' => [
- 'content' => [
- 'source' => 'core/pattern-overrides',
+ [
+ 'name' => 'core/paragraph',
+ 'attributes' => [
+ 'content' => 'Default content',
+ 'metadata' => [
+ 'bindings' => [
+ 'content' => [
+ 'source' => 'core/pattern-overrides',
+ ],
+ ],
+ 'name' => 'my-override',
],
],
- 'name' => 'my-override',
],
],
],
@@ -391,37 +700,45 @@ public function test_multiple_nested_synced_patterns_with_block_bindings() {
],
'innerBlocks' => [
[
- 'name' => 'test/custom-block',
- 'attributes' => [
- 'content' => 'My first synced pattern content',
- 'bing' => 'bong',
+ 'name' => 'core/group',
+ 'attributes' => [
+ 'tagName' => 'div',
],
- ],
- [
- 'name' => 'core/paragraph',
- 'attributes' => [
- 'content' => sprintf( 'Block binding for core/paragraph with arg foo=bar in post %d', $post->ID ),
- 'metadata' => [
- 'bindings' => [
- 'content' => [
- 'source' => 'test/synced-pattern-block-binding',
- 'args' => [ 'foo' => 'bar' ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'test/custom-block',
+ 'attributes' => [
+ 'content' => 'My first synced pattern content',
+ 'bing' => 'bong',
+ ],
+ ],
+ [
+ 'name' => 'core/paragraph',
+ 'attributes' => [
+ 'content' => sprintf( 'Block binding for core/paragraph with arg foo=bar in post %d', $post->ID ),
+ 'metadata' => [
+ 'bindings' => [
+ 'content' => [
+ 'source' => 'test/synced-pattern-block-binding',
+ 'args' => [ 'foo' => 'bar' ],
+ ],
+ ],
],
],
],
- ],
- ],
- [
- 'name' => 'core/paragraph',
- 'attributes' => [
- 'content' => 'Overridden content', // Overridden by synced pattern override
- 'metadata' => [
- 'bindings' => [
- 'content' => [
- 'source' => 'core/pattern-overrides',
+ [
+ 'name' => 'core/paragraph',
+ 'attributes' => [
+ 'content' => 'Overridden content', // Overridden by synced pattern override
+ 'metadata' => [
+ 'bindings' => [
+ 'content' => [
+ 'source' => 'core/pattern-overrides',
+ ],
+ ],
+ 'name' => 'my-override',
],
],
- 'name' => 'my-override',
],
],
],
@@ -442,62 +759,78 @@ public function test_multiple_nested_synced_patterns_with_block_bindings() {
],
'innerBlocks' => [
[
- 'name' => 'test/custom-block',
- 'attributes' => [
- 'content' => 'My second synced pattern content which contains the first',
- 'bing' => 'bang',
- ],
- ],
- [
- 'name' => 'core/block',
+ 'name' => 'core/group',
'attributes' => [
- 'ref' => $synced_pattern_1->ID,
+ 'tagName' => 'div',
],
'innerBlocks' => [
[
'name' => 'test/custom-block',
'attributes' => [
- 'content' => 'My first synced pattern content',
- 'bing' => 'bong',
+ 'content' => 'My second synced pattern content which contains the first',
+ 'bing' => 'bang',
],
],
[
- 'name' => 'core/paragraph',
- 'attributes' => [
- 'content' => sprintf( 'Block binding for core/paragraph with arg foo=bar in post %d', $post->ID ),
- 'metadata' => [
- 'bindings' => [
- 'content' => [
- 'source' => 'test/synced-pattern-block-binding',
- 'args' => [ 'foo' => 'bar' ],
+ 'name' => 'core/block',
+ 'attributes' => [
+ 'ref' => $synced_pattern_1->ID,
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'core/group',
+ 'attributes' => [
+ 'tagName' => 'div',
+ ],
+ 'innerBlocks' => [
+ [
+ 'name' => 'test/custom-block',
+ 'attributes' => [
+ 'content' => 'My first synced pattern content',
+ 'bing' => 'bong',
+ ],
+ ],
+ [
+ 'name' => 'core/paragraph',
+ 'attributes' => [
+ 'content' => sprintf( 'Block binding for core/paragraph with arg foo=bar in post %d', $post->ID ),
+ 'metadata' => [
+ 'bindings' => [
+ 'content' => [
+ 'source' => 'test/synced-pattern-block-binding',
+ 'args' => [ 'foo' => 'bar' ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ [
+ 'name' => 'core/paragraph',
+ 'attributes' => [
+ 'content' => 'Default content',
+ 'metadata' => [
+ 'bindings' => [
+ 'content' => [
+ 'source' => 'core/pattern-overrides',
+ ],
+ ],
+ 'name' => 'my-override',
+ ],
+ ],
],
],
],
],
],
[
- 'name' => 'core/paragraph',
+ 'name' => 'test/custom-block',
'attributes' => [
- 'content' => 'Default content',
- 'metadata' => [
- 'bindings' => [
- 'content' => [
- 'source' => 'core/pattern-overrides',
- ],
- ],
- 'name' => 'my-override',
- ],
+ 'content' => 'Another block to "wrap" the nested pattern',
+ 'bing' => 'bang',
],
],
],
],
- [
- 'name' => 'test/custom-block',
- 'attributes' => [
- 'content' => 'Another block to "wrap" the nested pattern',
- 'bing' => 'bang',
- ],
- ],
],
],
],
@@ -519,15 +852,19 @@ public function test_multiple_nested_synced_patterns_with_block_bindings() {
// First synced pattern
$this->assertEquals( 1, count( $blocks['blocks'][0]['innerBlocks'] ), 'Too many inner blocks in first container block' );
- $this->assertEquals( 3, count( $blocks['blocks'][0]['innerBlocks'][0]['innerBlocks'] ), 'Too many inner blocks in first synced pattern' );
+ $this->assertEquals( 1, count( $blocks['blocks'][0]['innerBlocks'][0]['innerBlocks'] ), 'Too many inner blocks in first synced pattern' );
+ $this->assertEquals( 3, count( $blocks['blocks'][0]['innerBlocks'][0]['innerBlocks'][0]['innerBlocks'] ), 'Too many inner blocks in first synced pattern group block' );
// First synced pattern, repeated (contains pattern override)
$this->assertEquals( 1, count( $blocks['blocks'][1]['innerBlocks'] ), 'Too many inner blocks in first container block' );
- $this->assertEquals( 3, count( $blocks['blocks'][1]['innerBlocks'][0]['innerBlocks'] ), 'Too many inner blocks in first synced pattern' );
+ $this->assertEquals( 1, count( $blocks['blocks'][1]['innerBlocks'][0]['innerBlocks'] ), 'Too many inner blocks in first synced pattern' );
+ $this->assertEquals( 3, count( $blocks['blocks'][1]['innerBlocks'][0]['innerBlocks'][0]['innerBlocks'] ), 'Too many inner blocks in first synced pattern group block' );
// Second synced pattern
$this->assertEquals( 1, count( $blocks['blocks'][2]['innerBlocks'] ), 'Too many inner blocks in second container block' );
- $this->assertEquals( 3, count( $blocks['blocks'][2]['innerBlocks'][0]['innerBlocks'] ), 'Too many inner blocks in second synced pattern' );
- $this->assertEquals( 3, count( $blocks['blocks'][2]['innerBlocks'][0]['innerBlocks'][1]['innerBlocks'] ), 'Too many inner blocks in nested pattern in second synced pattern' );
+ $this->assertEquals( 1, count( $blocks['blocks'][2]['innerBlocks'][0]['innerBlocks'] ), 'Too many inner blocks in second synced pattern' );
+ $this->assertEquals( 3, count( $blocks['blocks'][2]['innerBlocks'][0]['innerBlocks'][0]['innerBlocks'] ), 'Too many inner blocks in second synced pattern group block' );
+ $this->assertEquals( 1, count( $blocks['blocks'][2]['innerBlocks'][0]['innerBlocks'][0]['innerBlocks'][1]['innerBlocks'] ), 'Too many inner blocks in nested pattern in second synced pattern' );
+ $this->assertEquals( 3, count( $blocks['blocks'][2]['innerBlocks'][0]['innerBlocks'][0]['innerBlocks'][1]['innerBlocks'][0]['innerBlocks'] ), 'Too many inner blocks in nested pattern in second synced pattern group block' );
}
}