Skip to content

Commit

Permalink
Block API: Stabilize Block Hooks feature (#54293)
Browse files Browse the repository at this point in the history
* Block API: Stabilize Block Hooks feature

* Improve the API handling for block patterns REST API endpoing

* Add the default value for `blockHooks` during block registration

* Explain when Block Hooks get wired with the site

* Update docs/reference-guides/block-api/block-metadata.md

Co-authored-by: Bernie Reiter <96308+ockham@users.noreply.github.com>

* Update the explanation which parts of the site are impacted by the Block Hooks feature
Props to @fabiankaegy, @ndiego and @colorful-tones for helping with that part.

* Rephrase the description of Block Hooks in the docs
Props to @ockham for proposing edits.

* Add tests for polyfilling blockHooks when not exposed on the server

* Extend block.json schema to include block hooks

---------

Co-authored-by: Bernie Reiter <96308+ockham@users.noreply.github.com>
  • Loading branch information
gziolo and ockham authored Sep 13, 2023
1 parent a6a2809 commit 658deef
Show file tree
Hide file tree
Showing 17 changed files with 157 additions and 66 deletions.
21 changes: 20 additions & 1 deletion docs/reference-guides/block-api/block-metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ Plugins and Themes can also register [custom block style](/docs/reference-guides

It provides structured example data for the block. This data is used to construct a preview for the block to be shown in the Inspector Help Panel when the user mouses over the block.

See the [the example documentation](/docs/reference-guides/block-api/block-registration.md#example-optional) for more details.
See the [Example documentation](/docs/reference-guides/block-api/block-registration.md#example-optional) for more details.

### Variations

Expand Down Expand Up @@ -497,6 +497,25 @@ _Note: In JavaScript you can provide a function for the `isActive` property, and

See the [the variations documentation](/docs/reference-guides/block-api/block-variations.md) for more details.

### Block Hooks

- Type: `object`
- Optional
- Property: `blockHooks`
- Since: `WordPress 6.4.0`

```json
{
"blockHooks": {
"my-plugin/banner": "after"
}
}
```

Block Hooks is an API that allows a block to automatically insert itself next to all instances of a given block type, in a relative position also specified by the "hooked" block. That is, a block can opt to be inserted before or after a given block type, or as its first or last child (i.e. to be prepended or appended to the list of its child blocks, respectively). Hooked blocks will appear both on the frontend and in the editor (to allow for customization by the user).

The key is the name of the block (`string`) to hook into, and the value is the position to hook into (`string`). Take a look at the [Block Hooks documentation](/docs/reference-guides/block-api/block-registration.md#block-hooks-optional) for more info about available configurations.

### Editor Script

- Type: `WPDefinedAsset`|`WPDefinedAsset[]` ([learn more](#wpdefinedasset))
Expand Down
31 changes: 31 additions & 0 deletions docs/reference-guides/block-api/block-registration.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ example: {
#### variations (optional)

- **Type:** `Object[]`
- **Since**: `WordPress 5.9.0`

Similarly to how the block's styles can be declared, a block type can define block variations that the user can pick from. The difference is that, rather than changing only the visual appearance, this field provides a way to apply initial custom attributes and inner blocks at the time when a block is inserted. See the [Block Variations API](/docs/reference-guides/block-api/block-variations.md) for more details.

Expand Down Expand Up @@ -265,6 +266,7 @@ parent: [ 'core/columns' ],
#### ancestor (optional)

- **Type:** `Array`
- **Since**: `WordPress 6.0.0`

The `ancestor` property makes a block available inside the specified block types at any position of the ancestor block subtree. That allows, for example, to place a 'Comment Content' block inside a 'Column' block, as long as 'Column' is somewhere within a 'Comment Template' block. In comparison to the `parent` property blocks that specify their `ancestor` can be placed anywhere in the subtree whilst blocks with a specified `parent` need to be direct children.

Expand All @@ -273,6 +275,35 @@ The `ancestor` property makes a block available inside the specified block types
ancestor: [ 'core/columns' ],
```

#### Block Hooks (optional)

- **Type:** `Object`
- **Since**: `WordPress 6.4.0`

Block Hooks is an API that allows a block to automatically insert itself next to all instances of a given block type, in a relative position also specified by the "hooked" block. That is, a block can opt to be inserted before or after a given block type, or as its first or last child (i.e. to be prepended or appended to the list of its child blocks, respectively). Hooked blocks will appear both on the frontend and in the editor (to allow for customization by the user).

The key is the name of the block (`string`) to hook into, and the value is the position to hook into (`string`). Allowed target values are:

- `before` – inject before the target block.
- `after` - inject after the target block.
- `firstChild` - inject before the first inner block of the target container block.
- `lastChild` - inject after the last inner block of the target container block.

```js
{
blockHooks: {
'core/verse': 'before'
'core/spacer': 'after',
'core/column': 'firstChild',
'core/group': 'lastChild',
}
}
```

It’s crucial to emphasize that the Block Hooks feature is only designed to work with _static_ block-based templates, template parts, and patterns. For patterns, this includes those provided by the theme, from [Block Pattern Directory](https://wordpress.org/patterns/), or from calls to [`register_block_pattern`](https://developer.wordpress.org/reference/functions/register_block_pattern/).

Block Hooks will not work with post content or patterns crafted by the user, such as synced patterns, or theme templates and template parts that have been modified by the user.

## Block Collections

## `registerBlockCollection`
Expand Down
13 changes: 0 additions & 13 deletions lib/compat/wordpress-6.3/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,6 @@ function add_modified_wp_template_schema() {
}
add_filter( 'rest_api_init', 'add_modified_wp_template_schema' );

// If the Block Hooks experiment is enabled, we load the block patterns
// controller in lib/experimental/rest-api.php instead.
if ( ! gutenberg_is_experiment_enabled( 'gutenberg-block-hooks' ) ) {
/**
* Registers the block patterns REST API routes.
*/
function gutenberg_register_rest_block_patterns() {
$block_patterns = new Gutenberg_REST_Block_Patterns_Controller_6_3();
$block_patterns->register_routes();
}
add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' );
}

/**
* Registers the Navigation Fallbacks REST API routes.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
* @return array Updated settings array.
*/
function gutenberg_add_hooked_blocks( $settings, $metadata ) {
if ( ! isset( $metadata['__experimentalBlockHooks'] ) ) {
if ( ! isset( $metadata['blockHooks'] ) ) {
return $settings;
}
$block_hooks = $metadata['__experimentalBlockHooks'];
$block_hooks = $metadata['blockHooks'];

/**
* Map the camelCased position string from block.json to the snake_cased block type position
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ class Gutenberg_REST_Block_Patterns_Controller extends Gutenberg_REST_Block_Patt
*/
public function prepare_item_for_response( $item, $request ) {
$response = parent::prepare_item_for_response( $item, $request );
if ( ! gutenberg_is_experiment_enabled( 'gutenberg-block-hooks' ) ) {
return $response;
}

$data = $response->get_data();

if ( empty( $data['content'] ) ) {
return $response;
}

$blocks = parse_blocks( $data['content'] );
$data['content'] = gutenberg_serialize_blocks( $blocks ); // Serialize or render?
Expand Down
9 changes: 9 additions & 0 deletions lib/compat/wordpress-6.4/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@
die( 'Silence is golden.' );
}

/**
* Registers the block patterns REST API routes.
*/
function gutenberg_register_rest_block_patterns_routes() {
$block_patterns = new Gutenberg_REST_Block_Patterns_Controller();
$block_patterns->register_routes();
}
add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns_routes' );

/**
* Registers the Global Styles Revisions REST API routes.
*/
Expand Down
4 changes: 0 additions & 4 deletions lib/experimental/editor-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ function gutenberg_enable_experiments() {
if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) {
wp_add_inline_script( 'wp-block-library', 'window.__experimentalDisableTinymce = true', 'before' );
}

if ( $gutenberg_experiments && array_key_exists( 'gutenberg-block-hooks', $gutenberg_experiments ) ) {
wp_add_inline_script( 'wp-block-editor', 'window.__experimentalBlockHooks = true', 'before' );
}
}

add_action( 'admin_init', 'gutenberg_enable_experiments' );
11 changes: 0 additions & 11 deletions lib/experimental/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,6 @@
die( 'Silence is golden.' );
}

if ( gutenberg_is_experiment_enabled( 'gutenberg-block-hooks' ) ) {
/**
* Registers the block patterns REST API routes.
*/
function gutenberg_register_rest_block_patterns() {
$block_patterns = new Gutenberg_REST_Block_Patterns_Controller();
$block_patterns->register_routes();
}
add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' );
}

/**
* Registers the customizer nonces REST API routes.
*/
Expand Down
12 changes: 0 additions & 12 deletions lib/experiments-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,6 @@ function gutenberg_initialize_experiments_settings() {
)
);

add_settings_field(
'gutenberg-block-hooks',
__( 'Block hooks', 'gutenberg' ),
'gutenberg_display_experiment_field',
'gutenberg-experiments',
'gutenberg_experiments_section',
array(
'label' => __( 'Block hooks allow automatically inserting a block in a position relative to another.', 'gutenberg' ),
'id' => 'gutenberg-block-hooks',
)
);

add_settings_field(
'gutenberg-custom-fields',
__( 'Connections', 'gutenberg' ),
Expand Down
8 changes: 2 additions & 6 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ function gutenberg_is_experiment_enabled( $name ) {

// WordPress 6.4 compat.
require_once __DIR__ . '/compat/wordpress-6.4/class-gutenberg-rest-global-styles-revisions-controller-6-4.php';
require_once __DIR__ . '/compat/wordpress-6.4/class-gutenberg-rest-block-patterns-controller.php';
require_once __DIR__ . '/compat/wordpress-6.4/rest-api.php';
require_once __DIR__ . '/compat/wordpress-6.4/theme-previews.php';

Expand All @@ -64,9 +65,6 @@ function gutenberg_is_experiment_enabled( $name ) {
require_once __DIR__ . '/experimental/class-wp-rest-customizer-nonces.php';
}
require_once __DIR__ . '/experimental/class-gutenberg-rest-template-revision-count.php';
if ( gutenberg_is_experiment_enabled( 'gutenberg-block-hooks' ) ) {
require_once __DIR__ . '/experimental/class-gutenberg-rest-block-patterns-controller.php';
}
require_once __DIR__ . '/experimental/rest-api.php';
}

Expand Down Expand Up @@ -96,6 +94,7 @@ function gutenberg_is_experiment_enabled( $name ) {

// WordPress 6.4 compat.
require __DIR__ . '/compat/wordpress-6.4/blocks.php';
require __DIR__ . '/compat/wordpress-6.4/block-hooks.php';
require __DIR__ . '/compat/wordpress-6.4/block-patterns.php';
require __DIR__ . '/compat/wordpress-6.4/script-loader.php';

Expand All @@ -111,9 +110,6 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/experimental/disable-tinymce.php';
}

if ( gutenberg_is_experiment_enabled( 'gutenberg-block-hooks' ) ) {
require __DIR__ . '/experimental/block-hooks.php';
}
require __DIR__ . '/experimental/interactivity-api/class-wp-interactivity-store.php';
require __DIR__ . '/experimental/interactivity-api/store.php';
require __DIR__ . '/experimental/interactivity-api/scripts.php';
Expand Down
12 changes: 5 additions & 7 deletions packages/block-editor/src/hooks/block-hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,8 @@ export const withBlockHooks = createHigherOrderComponent( ( BlockEdit ) => {
};
}, 'withBlockHooks' );

if ( window?.__experimentalBlockHooks ) {
addFilter(
'editor.BlockEdit',
'core/block-hooks/with-inspector-control',
withBlockHooks
);
}
addFilter(
'editor.BlockEdit',
'core/block-hooks/with-inspector-control',
withBlockHooks
);
4 changes: 2 additions & 2 deletions packages/block-library/src/pattern/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ function render_block_core_pattern( $attributes ) {
$pattern = $registry->get_registered( $slug );
$content = _inject_theme_attribute_in_block_template_content( $pattern['content'] );

$gutenberg_experiments = get_option( 'gutenberg-experiments' );
if ( $gutenberg_experiments && ! empty( $gutenberg_experiments['gutenberg-block-hooks'] ) ) {
// This can be removed when the minimum supported WordPress is >= 6.4.
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
// TODO: In the long run, we'd likely want to have a filter in the `WP_Block_Patterns_Registry` class
// instead to allow us plugging in code like this.
$blocks = parse_blocks( $content );
Expand Down
2 changes: 1 addition & 1 deletion packages/blocks/src/api/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ function getBlockSettingsFromMetadata( { textdomain, ...metadata } ) {
'styles',
'example',
'variations',
'__experimentalBlockHooks',
'blockHooks',
];

const settings = Object.fromEntries(
Expand Down
Loading

1 comment on commit 658deef

@github-actions
Copy link

Choose a reason for hiding this comment

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

Flaky tests detected in 658deef.
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/6172623778
📝 Reported issues:

Please sign in to comment.