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

Server directive processing: Process only root blocks #55739

Merged
merged 9 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 48 additions & 36 deletions lib/experimental/interactivity-api/directive-processing.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,66 @@
*/

/**
* Process directives in each block.
*
* @param string $block_content The block content.
* @param array $block The full block.
*
* @return string Filtered block content.
*/
function gutenberg_interactivity_process_directives_in_root_blocks( $block_content, $block ) {
// Don't process inner blocks or root blocks that don't contain directives.
if ( ! WP_Directive_Processor::is_root_block( $block ) || strpos( $block_content, 'data-wp-' ) === false ) {
return $block_content;
}

// TODO: Add some directive/components registration mechanism.
$directives = array(
'data-wp-bind' => 'gutenberg_interactivity_process_wp_bind',
'data-wp-context' => 'gutenberg_interactivity_process_wp_context',
'data-wp-class' => 'gutenberg_interactivity_process_wp_class',
'data-wp-style' => 'gutenberg_interactivity_process_wp_style',
'data-wp-text' => 'gutenberg_interactivity_process_wp_text',
);

$tags = new WP_Directive_Processor( $block_content );
$tags = gutenberg_interactivity_process_directives( $tags, 'data-wp-', $directives );
return $tags->get_updated_html();
}
add_filter( 'render_block', 'gutenberg_interactivity_process_directives_in_root_blocks', 10, 2 );

/**
* Mark the inner blocks with a temporary property so we can discard them later,
* and process only the root blocks.
* Process the Interactivity API directives using the root blocks of the
* outermost rendering, ignoring the root blocks of inner blocks like Patterns,
* Template Parts or Content.
*
* @param array $parsed_block The parsed block.
* @param array $source_block The source block.
* @param array $parent_block The parent block.
*
* @return array The parsed block.
*/
function gutenberg_interactivity_mark_inner_blocks( $parsed_block, $source_block, $parent_block ) {
if ( ! isset( $parent_block ) ) {
function gutenberg_interactivity_process_directives( $parsed_block, $source_block, $parent_block ) {
static $is_inside_root_block = false;
static $process_directives_in_root_blocks = null;

if ( ! isset( $process_directives_in_root_blocks ) ) {
/**
* Process directives in each root block.
*
* @param string $block_content The block content.
* @param array $block The full block.
*
* @return string Filtered block content.
*/
$process_directives_in_root_blocks = static function ( $block_content, $block ) use ( &$is_inside_root_block ) {

if ( WP_Directive_Processor::is_root_block( $block ) ) {

$directives = array(
'data-wp-bind' => 'gutenberg_interactivity_process_wp_bind',
'data-wp-context' => 'gutenberg_interactivity_process_wp_context',
'data-wp-class' => 'gutenberg_interactivity_process_wp_class',
'data-wp-style' => 'gutenberg_interactivity_process_wp_style',
'data-wp-text' => 'gutenberg_interactivity_process_wp_text',
);

$tags = new WP_Directive_Processor( $block_content );
$tags = gutenberg_interactivity_process_rendered_html( $tags, 'data-wp-', $directives );
$is_inside_root_block = false;
return $tags->get_updated_html();

}

return $block_content;
};
add_filter( 'render_block', $process_directives_in_root_blocks, 10, 2 );
}

if ( ! isset( $parent_block ) && ! $is_inside_root_block ) {
WP_Directive_Processor::add_root_block( $parsed_block );
$is_inside_root_block = true;
}

return $parsed_block;
}
add_filter( 'render_block_data', 'gutenberg_interactivity_mark_inner_blocks', 10, 3 );
add_filter( 'render_block_data', 'gutenberg_interactivity_process_directives', 10, 3 );


/**
* Process directives.
* Traverses the HTML searching for Interactivity API directives and processing
* them.
*
* @param WP_Directive_Processor $tags An instance of the WP_Directive_Processor.
* @param string $prefix Attribute prefix.
Expand All @@ -64,7 +76,7 @@ function gutenberg_interactivity_mark_inner_blocks( $parsed_block, $source_block
* @return WP_Directive_Processor The modified instance of the
* WP_Directive_Processor.
*/
function gutenberg_interactivity_process_directives( $tags, $prefix, $directives ) {
function gutenberg_interactivity_process_rendered_html( $tags, $prefix, $directives ) {
$context = new WP_Directive_Context();
$tag_stack = array();

Expand Down
4 changes: 2 additions & 2 deletions packages/e2e-tests/plugins/interactive-blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ function () {
// HTML is not correct or malformed.
if ( 'true' === $_GET['disable_directives_ssr'] ) {
remove_filter(
'render_block',
'gutenberg_interactivity_process_directives_in_root_blocks'
'render_block_data',
'gutenberg_interactivity_process_directives'
);
}
}
Expand Down
102 changes: 84 additions & 18 deletions phpunit/experimental/interactivity-api/directive-processing-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ function gutenberg_test_process_directives_helper_increment( $store ) {
}

/**
* Tests for the gutenberg_interactivity_process_directives function.
* Tests for the gutenberg_interactivity_process_rendered_html function.
*
* @group interactivity-api
* @covers gutenberg_interactivity_process_directives
* @covers gutenberg_interactivity_process_rendered_html
*/
class Tests_Process_Directives extends WP_UnitTestCase {
public function test_correctly_call_attribute_directive_processor_on_closing_tag() {
Expand All @@ -40,44 +40,110 @@ public function test_correctly_call_attribute_directive_processor_on_closing_tag
$test_helper = $this->createMock( Helper_Class::class );

$test_helper->expects( $this->exactly( 2 ) )
->method( 'process_foo_test' )
->with(
$this->callback(
function ( $p ) {
return 'DIV' === $p->get_tag() && (
// Either this is a closing tag...
$p->is_tag_closer() ||
// ...or it is an open tag, and has the directive attribute set.
( ! $p->is_tag_closer() && 'abc' === $p->get_attribute( 'foo-test' ) )
);
}
)
);
->method( 'process_foo_test' )
->with(
$this->callback(
function ( $p ) {
return 'DIV' === $p->get_tag() && (
// Either this is a closing tag...
$p->is_tag_closer() ||
// ...or it is an open tag, and has the directive attribute set.
( ! $p->is_tag_closer() && 'abc' === $p->get_attribute( 'foo-test' ) )
);
}
)
);

$directives = array(
'foo-test' => array( $test_helper, 'process_foo_test' ),
);

$markup = '<div>Example: <div foo-test="abc"><img><span>This is a test></span><div>Here is a nested div</div></div></div>';
$tags = new WP_HTML_Tag_Processor( $markup );
gutenberg_interactivity_process_directives( $tags, 'foo-', $directives );
gutenberg_interactivity_process_rendered_html( $tags, 'foo-', $directives );
}

public function test_directives_with_double_hyphen_processed_correctly() {
$test_helper = $this->createMock( Helper_Class::class );
$test_helper->expects( $this->atLeastOnce() )
->method( 'process_foo_test' );
->method( 'process_foo_test' );

$directives = array(
'foo-test' => array( $test_helper, 'process_foo_test' ),
);

$markup = '<div foo-test--value="abc"></div>';
$tags = new WP_HTML_Tag_Processor( $markup );
gutenberg_interactivity_process_directives( $tags, 'foo-', $directives );
gutenberg_interactivity_process_rendered_html( $tags, 'foo-', $directives );
}

public function test_interactivity_process_directives_in_root_blocks() {
$pattern_content =
'<!-- wp:paragraph -->' .
'<p>Pattern Content Block 1</p>' .
'<!-- /wp:paragraph -->' .
'<!-- wp:paragraph -->' .
'<p>Pattern Content Block 2</p>' .
'<!-- /wp:paragraph -->';
register_block_pattern(
'core/interactivity-pattern',
array(
'title' => 'Interactivity Pattern',
'content' => $pattern_content,
)
);

$providers = $this->data_only_root_blocks_are_processed();
foreach ( $providers as $provider ) {
do_blocks( $provider['page_content'] );
$this->assertSame( $provider['root_blocks'], count( WP_Directive_Processor::$root_blocks ) );

}
}

/**
* Data provider .
*
* @return array
**/
public function data_only_root_blocks_are_processed() {

return array(
array(
'root_blocks' => 2,
'page_content' =>
'<!-- wp:quote -->' .
'<blockquote class="wp-block-quote">
<!-- wp:paragraph -->' .
'<p>The XYZ Doohickey Company was founded in 1971, and has been providing' .
'quality doohickeys to the public ever since. Located in Gotham City, XYZ employs' .
'over 2,000 people and does all kinds of awesome things for the Gotham community.</p>' .
'<!-- /wp:paragraph -->
</blockquote>' .
'<!-- /wp:quote -->' .
'<!-- wp:quote -->' .
'<blockquote class="wp-block-quote">
<!-- wp:paragraph -->' .
'<p>The XYZ Doohickey Company was founded in 1971, and has been providing' .
'quality doohickeys to the public ever since. Located in Gotham City, XYZ employs' .
'over 2,000 people and does all kinds of awesome things for the Gotham community.</p>' .
'<!-- /wp:paragraph -->
</blockquote>' .
'<!-- /wp:quote -->',
),
array(
'root_blocks' => 2,
'page_content' =>
'<!-- wp:paragraph -->' .
'<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!</p>' .
'<!-- /wp:paragraph -->' .
'<!-- wp:pattern {"slug":"core/interactivity-pattern"} /-->',
),
);
}
}


/**
* Tests for the gutenberg_interactivity_evaluate_reference function.
*
Expand Down
Loading