Skip to content

Commit

Permalink
Blocks: Implement block insertion functions.
Browse files Browse the repository at this point in the history
For #59313, we need to implement functions to insert a given parsed block into another parsed block's inner blocks, and to prepend and append to that array, respectively.

We will use those functions in combination with `traverse_and_serialize_blocks` (see #59327) to implement automatic insertion of hooked blocks into block templates and patterns.

Props gziolo.
Fixes #59385.
  • Loading branch information
ockham committed Sep 19, 2023
1 parent 1cd9877 commit 13aa0cf
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 3 deletions.
78 changes: 78 additions & 0 deletions src/wp-includes/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,84 @@ function get_hooked_blocks( $name ) {
return $hooked_blocks;
}

/**
* Insert a parsed block into a parent block's inner blocks.
*
* Given a parsed block, a block index, and a chunk index, insert another parsed block
* into the parent block at the given indices.
*
* Note that the this mutates the parent block by inserting into the parent's `innerBlocks`
* array, and by updating the parent's `innerContent` array accordingly.
*
* @since 6.4.0
*
* @param array $parent_block The parent block.
* @param int $block_index The index specifying the insertion position among the parent block's inner blocks.
* @param int $chunk_index The index specifying the insertion position among the parent block's inner content chunks.
* @param array $inserted_block The block to insert.
* @return void
*/
function insert_inner_block( &$parent_block, $block_index, $chunk_index, $inserted_block ) {
array_splice( $parent_block['innerBlocks'], $block_index, 0, array( $inserted_block ) );

/*
* Since WP_Block::render() iterates over `inner_content` (rather than `inner_blocks`)
* when rendering blocks, we also need to insert a value (`null`, to mark a block
* location) into that array.
*/
array_splice( $parent_block['innerContent'], $chunk_index, 0, array( null ) );
}

/**
* Prepend a parsed block to a parent block's inner blocks.
*
* Given a parsed block, prepend another parsed block to the parent block's inner blocks.
*
* Note that the this mutates the parent block by inserting into the parent's `innerBlocks`
* array, and by updating the parent's `innerContent` array accordingly.
*
* @since 6.4.0
*
* @param array $parent_block The parent block.
* @param array $inserted_block The block to insert.
* @return void
*/
function prepend_inner_block( &$parent_block, $inserted_block ) {
$chunk_index = 0;
for ( $index = 0; $index < count( $parent_block['innerContent'] ); $index++ ) {
if ( is_null( $parent_block['innerContent'][ $index ] ) ) {
$chunk_index = $index;
break;
}
}
insert_inner_block( $parent_block, 0, $chunk_index, $inserted_block );
}

/**
* Append a parsed block to a parent block's inner blocks.
*
* Given a parsed block, append another parsed block to the parent block's inner blocks.
*
* Note that the this mutates the parent block by inserting into the parent's `innerBlocks`
* array, and by updating the parent's `innerContent` array accordingly.
*
* @since 6.4.0
*
* @param array $parent_block The parent block.
* @param array $inserted_block The block to insert.
* @return void
*/
function append_inner_block( &$parent_block, $inserted_block ) {
$chunk_index = count( $parent_block['innerContent'] );
for ( $index = count( $parent_block['innerContent'] ); $index > 0; $index-- ) {
if ( is_null( $parent_block['innerContent'][ $index - 1 ] ) ) {
$chunk_index = $index;
break;
}
}
insert_inner_block( $parent_block, count( $parent_block['innerBlocks'] ), $chunk_index, $inserted_block );
}

/**
* Given an array of attributes, returns a string in the serialized attributes
* format prepared for post content.
Expand Down
162 changes: 159 additions & 3 deletions tests/phpunit/tests/blocks/blockHooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ class Tests_Blocks_BlockHooks extends WP_UnitTestCase {
* @since 6.4.0
*/
public function tear_down() {
$registry = WP_Block_Type_Registry::get_instance();

foreach ( array( 'tests/my-block', 'tests/my-container-block' ) as $block_name ) {
$registry = WP_Block_Type_Registry::get_instance();
$block_names = array(
'tests/injected-one',
'tests/injected-two',
);
foreach ( $block_names as $block_name ) {
if ( $registry->is_registered( $block_name ) ) {
$registry->unregister( $block_name );
}
Expand Down Expand Up @@ -97,4 +100,157 @@ public function test_get_hooked_blocks_matches_found() {
'block hooked at the last child position'
);
}

/**
* @ticket 59385
*
* @covers ::insert_inner_block
*
* @dataProvider data_insert_inner_block
*
* @param string $block_index Block index to insert the block at.
* @param string $expected_markup Expected markup after the block is inserted.
*/
public function test_insert_inner_block( $block_index, $expected_markup ) {
$original_markup = <<<HTML
<!-- wp:tests/group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
<!-- wp:paragraph -->
<p>Foo</p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:tests/group -->
HTML;

$inserted_block = array(
'blockName' => 'tests/hooked-block',
'attrs' => array(),
'innerBlocks' => array(),
'innerHTML' => '',
'innerContent' => array(),
);

$expected = parse_blocks( $expected_markup )[0];
$block = parse_blocks( $original_markup )[0];
insert_inner_block( $block, $block_index, 1, $inserted_block );
$this->assertSame( $expected, $block );
}

/**
* Data provider.
*
* @return array[]
*/
public function data_insert_inner_block() {
$expected_before_markup = <<<HTML
<!-- wp:tests/group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
<!-- wp:tests/hooked-block /--><!-- wp:paragraph -->
<p>Foo</p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:tests/group -->
HTML;

$expected_after_markup = <<<HTML
<!-- wp:tests/group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
<!-- wp:paragraph -->
<p>Foo</p>
<!-- /wp:paragraph --><!-- wp:tests/hooked-block /-->
</div>
<!-- /wp:tests/group -->
HTML;

return array(
'insert before given block' => array(
'block_index' => 0,
'expected_markup' => $expected_before_markup,
),
'insert after given block' => array(
'block_index' => 1,
'expected_markup' => $expected_after_markup,
),
);
}

/**
* @ticket 59385
*
* @covers ::prepend_inner_block
*/
public function test_prepend_inner_block() {
$original_markup = <<<HTML
<!-- wp:tests/group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
<!-- wp:paragraph -->
<p>Foo</p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:tests/group -->
HTML;

$inserted_block = array(
'blockName' => 'tests/hooked-block',
'attrs' => array(),
'innerBlocks' => array(),
'innerHTML' => '',
'innerContent' => array(),
);

$expected_markup = <<<HTML
<!-- wp:tests/group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
<!-- wp:tests/hooked-block /--><!-- wp:paragraph -->
<p>Foo</p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:tests/group -->
HTML;

$expected = parse_blocks( $expected_markup )[0];
$block = parse_blocks( $original_markup )[0];
prepend_inner_block( $block, $inserted_block );
$this->assertSame( $expected, $block );
}

/**
* @ticket 59385
*
* @covers ::append_inner_block
*/
public function test_append_inner_block() {
$original_markup = <<<HTML
<!-- wp:tests/group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
<!-- wp:paragraph -->
<p>Foo</p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:tests/group -->
HTML;

$inserted_block = array(
'blockName' => 'tests/hooked-block',
'attrs' => array(),
'innerBlocks' => array(),
'innerHTML' => '',
'innerContent' => array(),
);

$expected_markup = <<<HTML
<!-- wp:tests/group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
<!-- wp:paragraph -->
<p>Foo</p>
<!-- /wp:paragraph --><!-- wp:tests/hooked-block /-->
</div>
<!-- /wp:tests/group -->
HTML;

$expected = parse_blocks( $expected_markup )[0];
$block = parse_blocks( $original_markup )[0];
append_inner_block( $block, $inserted_block );
$this->assertSame( $expected, $block );
}
}

0 comments on commit 13aa0cf

Please sign in to comment.