-
Notifications
You must be signed in to change notification settings - Fork 11
⚛️ Use a wp-inner-block
attribute for each inner block
#77
Changes from all commits
1c7646e
fe4994b
aed8605
c37fa79
ac29856
ff129a8
07d04e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,6 +53,15 @@ function block_hydration_experiments_init() | |
|
||
function bhe_block_wrapper($block_content, $block, $instance) | ||
{ | ||
// Append the `wp-inner-block` attribute for inner blocks of interactive blocks. | ||
if (isset($instance->parsed_block['isInnerBlock'])) { | ||
$block_content = bhe_append_attributes( | ||
$instance->name, | ||
$block_content, | ||
'wp-inner-block' | ||
); | ||
} | ||
|
||
$block_type = $instance->block_type; | ||
|
||
if (!block_has_support($block_type, ['view'])) { | ||
|
@@ -93,50 +102,70 @@ function bhe_block_wrapper($block_content, $block, $instance) | |
WP_Block_Supports::$block_to_render = $previous_block_to_render; | ||
|
||
// Generate all required wrapper attributes. | ||
$block_wrapper_attributes = | ||
sprintf( | ||
'data-wp-block-type="%1$s" ' . | ||
$block_wrapper_attributes = sprintf( | ||
'data-wp-block-type="%1$s" ' . | ||
'data-wp-block-uses-block-context="%2$s" ' . | ||
'data-wp-block-provides-block-context="%3$s" ' . | ||
'data-wp-block-attributes="%4$s" ' . | ||
'data-wp-block-sourced-attributes="%5$s" ' . | ||
'data-wp-block-props="%6$s" ' . | ||
'data-wp-block-hydration="%7$s"', | ||
esc_attr($block['blockName']), | ||
esc_attr(json_encode($block_type->uses_context)), | ||
esc_attr(json_encode($block_type->provides_context)), | ||
esc_attr(json_encode($attributes)), | ||
esc_attr(json_encode($sourced_attributes)), | ||
esc_attr(json_encode($block_props)), | ||
esc_attr($hydration_technique) | ||
); | ||
esc_attr($block['blockName']), | ||
esc_attr(json_encode($block_type->uses_context)), | ||
esc_attr(json_encode($block_type->provides_context)), | ||
esc_attr(json_encode($attributes)), | ||
esc_attr(json_encode($sourced_attributes)), | ||
esc_attr(json_encode($block_props)), | ||
esc_attr($hydration_technique) | ||
); | ||
|
||
// Append block wrapper attributes. | ||
$block_content = bhe_append_attributes( | ||
$instance->name, | ||
$block_content, | ||
$block_wrapper_attributes | ||
); | ||
|
||
// The block content comes between two line breaks that seem to be included during block | ||
// The block content comes between line breaks that seem to be included during block | ||
// serialization, corresponding to those between the block markup and the block content. | ||
// | ||
// They need to be removed here; otherwise, the preact hydration fails. | ||
$block_content = substr($block_content, 1, -1); | ||
$block_content = trim($block_content); | ||
|
||
// Append all wp block attributes after the class attribute containing the block class name. | ||
// Elements with that class name are supposed to be those with the wrapper attributes. | ||
// | ||
// We could use `WP_HTML_Walker` (see https://github.com/WordPress/gutenberg/pull/42485) in the | ||
// future if that PR is finally merged. | ||
|
||
// This replace is similar to the one used in Gutenberg (see | ||
// https://github.com/WordPress/gutenberg/blob/1582c723f31ce0f728cd4dcc1af37821342eeaaa/packages/blocks/src/api/serializer.js#L41-L44). | ||
$block_classname = 'wp-block-' . preg_replace( | ||
['/\//', '/^core-/'], | ||
['-', ''], | ||
$instance->name | ||
); | ||
return $block_content; | ||
} | ||
|
||
add_filter('render_block', 'bhe_block_wrapper', 10, 3); | ||
|
||
/** | ||
* Add a flag to mark inner blocks of interactive blocks. | ||
*/ | ||
function bhe_inner_blocks($parsed_block, $source_block, $parent_block) | ||
{ | ||
if ( | ||
isset($parent_block) && | ||
block_has_support($parent_block->block_type, ['view']) | ||
) { | ||
$parsed_block['isInnerBlock'] = true; | ||
} | ||
|
||
return $parsed_block; | ||
} | ||
|
||
add_filter('render_block_data', 'bhe_inner_blocks', 10, 3); | ||
|
||
/** | ||
* Append attributes to the block wrapper element, which is assumed to be the first one. | ||
* | ||
* TODO: use `WP_HTML_Tag_Processor` (see https://github.com/WordPress/gutenberg/pull/42485) once | ||
* the API is released. | ||
*/ | ||
function bhe_append_attributes($block_name, $block_content, $attributes) | ||
{ | ||
// Be aware that this pattern could not cover some edge cases. | ||
$class_pattern = '/class="\s*(?:[\w\s-]\s+)*' . $block_classname . '(?:\s+[\w-]+)*\s*"/'; | ||
$class_replacement = '$0 ' . $block_wrapper_attributes; | ||
$block_content = preg_replace( $class_pattern, $class_replacement, $block_content, 1 ); | ||
$pattern = '/^\s*<[^>]+/'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, so this is now looking for the first tag opener, whatever it is, and it could be replaced with this, right? $w = new WP_HTML_Tag_Processor($block_content);
$w->next_tag();
foreach ($attributes as $key => $value) {
$w->set_attribute($key, $value);
} With a bit more logic for the |
||
$replacement = '$0 ' . $attributes; | ||
$block_content = preg_replace($pattern, $replacement, $block_content, 1); | ||
|
||
return $block_content; | ||
} | ||
|
||
add_filter('render_block', 'bhe_block_wrapper', 10, 3); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,15 @@ export default function toVdom(n) { | |
// Walk child nodes and return vDOM children. | ||
const children = [].map.call(n.childNodes, toVdom).filter(exists); | ||
|
||
// Create an array of inner blocks if they are found in children. All nodes in between (e.g., | ||
// line breaks, white spaces, etc.) are preserved in order to prevent hydration failure. | ||
const isInnerBlock = ({ props }) => props && 'wp-inner-block' in props; | ||
if (children.some(isInnerBlock)) { | ||
Comment on lines
+51
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note for the future: this is probably not the best approach because, although If we continue with this approach, we could create a nice set of benchmarks for the |
||
const first = children.findIndex(isInnerBlock); | ||
const last = children.findLastIndex(isInnerBlock); | ||
innerBlocksFound = children.slice(first, last + 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note for the future: As we are not doing out-of-order hydration for the moment, we could bypass the |
||
} | ||
|
||
// Add inner blocks. | ||
if (wpBlock.type && innerBlocksFound) { | ||
wpBlock.innerBlocks = innerBlocksFound; | ||
|
@@ -58,11 +67,6 @@ export default function toVdom(n) { | |
// Create vNode. Note that all `wpBlock` props should exist now to make directives work. | ||
const vNode = h(type, props, children); | ||
|
||
// Save a renference to this vNode if it's an <wp-inner-blocks>` wrapper. | ||
if (type === 'wp-inner-blocks') { | ||
innerBlocksFound = vNode; | ||
} | ||
|
||
return vNode; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$block_name
doesn't seem to be used anymore.