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

[WIP] Block Supports: Try stabilizing default controls to top-level property #67729

Closed
wants to merge 6 commits into from
Closed
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
59 changes: 54 additions & 5 deletions lib/compat/wordpress-6.8/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ function gutenberg_stabilize_experimental_block_supports( $args ) {
}

$experimental_supports_map = array( '__experimentalBorder' => 'border' );
$common_experimental_properties = array(
'__experimentalDefaultControls' => 'defaultControls',
'__experimentalSkipSerialization' => 'skipSerialization',
);
$common_experimental_properties = array( '__experimentalSkipSerialization' => 'skipSerialization' );
$experimental_support_properties = array(
'typography' => array(
'__experimentalFontFamily' => 'fontFamily',
Expand All @@ -35,9 +32,11 @@ function gutenberg_stabilize_experimental_block_supports( $args ) {
'__experimentalTextTransform' => 'textTransform',
),
);
$done = array();

$done = array();
$default_controls = array();
$updated_supports = array();

foreach ( $args['supports'] as $support => $config ) {
/*
* If this support config has already been stabilized, skip it.
Expand All @@ -50,6 +49,12 @@ function gutenberg_stabilize_experimental_block_supports( $args ) {

$stable_support_key = $experimental_supports_map[ $support ] ?? $support;

// Extract default controls config as it is being moved to top-level property.
if ( is_array( $config ) && ! empty( $config['__experimentalDefaultControls'] ) ) {
$default_controls[ $stable_support_key ] = $config['__experimentalDefaultControls'];
unset( $config['__experimentalDefaultControls'] );
}

/*
* Use the support's config as is when it's not in need of stabilization.
*
Expand Down Expand Up @@ -77,6 +82,11 @@ function gutenberg_stabilize_experimental_block_supports( $args ) {

$stable_config = array();
foreach ( $unstable_config as $key => $value ) {
// Skip `__experimentalDefaultControls` as they are handled separately.
if ( '__experimentalDefaultControls' === $key ) {
continue;
}

// Get stable key from support-specific map, common properties map, or keep original.
$stable_key = $experimental_support_properties[ $stable_support_key ][ $key ] ??
$common_experimental_properties[ $key ] ??
Expand Down Expand Up @@ -120,6 +130,14 @@ function gutenberg_stabilize_experimental_block_supports( $args ) {
( $key_positions[ $support ] ?? PHP_INT_MAX ) <
( $key_positions[ $stable_support_key ] ?? PHP_INT_MAX );

// Extract default controls as they are relocated to top-level property.
if ( is_array( $args['supports'][ $stable_support_key ] ) && ! empty( $args['supports'][ $stable_support_key ]['__experimentalDefaultControls'] ) ) {
$controls = $args['supports'][ $stable_support_key ]['__experimentalDefaultControls'];
$default_controls[ $stable_support_key ] = $experimental_first ?
array_merge( $default_controls[ $stable_support_key ] ?? array(), $controls ) :
array_merge( $controls, $default_controls[ $stable_support_key ] ?? array() );
}

/*
* To merge the alternative support config effectively, it also needs to be
* stabilized before merging to keep stabilized and experimental flags in
Expand All @@ -143,9 +161,40 @@ function gutenberg_stabilize_experimental_block_supports( $args ) {
$updated_supports[ $stable_support_key ] = $stable_config;
}

$final_default_controls = $args['default_controls'] ?? array();
foreach ( $default_controls as $support_key => $stabilized_default_controls ) {
// Only add experimental controls if no top-level controls exist for this support key.
if ( ! isset( $final_default_controls[ $support_key ] ) && ! empty( $stabilized_default_controls ) ) {
$final_default_controls[ $support_key ] = $stabilized_default_controls;
}
}

$args['supports'] = $updated_supports;
if ( ! empty( $final_default_controls ) ) {
$args['default_controls'] = $final_default_controls;
}

return $args;
}

add_filter( 'register_block_type_args', 'gutenberg_stabilize_experimental_block_supports', PHP_INT_MAX, 1 );

/**
* Ensure the `defaultControls` property, set via block.json metadata, or
* stabilized block supports filter, is included within the block type's settings.
*
* Note: This should be removed when the minimum required WP version is >= 6.8.
*
* @param array $settings Current block type settings.
* @param array $metadata Block metadata as read in via block.json.
*
* @return array Filtered block type settings.
*/
function gutenberg_add_default_controls_property_to_block_type_settings( $settings, $metadata ) {
if ( ! isset( $settings['default_controls'] ) && isset( $metadata['defaultControls'] ) ) {
$settings['default_controls'] = $metadata['defaultControls'];
}
return $settings;
}

add_filter( 'block_type_metadata_settings', 'gutenberg_add_default_controls_property_to_block_type_settings', PHP_INT_MAX, 2 );
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/**
* REST API: Gutenberg_REST_Block_Types_Controller_6_8 class
*
* @package gutenberg
*/

/**
* Gutenberg_REST_Block_Types_Controller_6_8 class
*
* Add default controls to the block type response and schema to support
* stabilization of `__expermentalDefaultControls` from within `supports`
* to it's own top-level block type property.
*/
class Gutenberg_REST_Block_Types_Controller_6_8 extends WP_REST_Block_Types_Controller {
/**
* Add default_controls to the item schema.
*
* @return array Item schema data.
*/
public function get_item_schema() {
$schema = parent::get_item_schema();
$schema['properties']['default_controls'] = array(
'description' => __( 'UI controls to display by default.' ),
'type' => 'object',
'default' => array(),
'properties' => array(),
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
);
return $schema;
}

/**
* Add default_controls to the response.
*
* @param WP_Block_Type $block_type Block type object.
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response
*/
public function prepare_item_for_response( $block_type, $request ) {
$response = parent::prepare_item_for_response( $block_type, $request );
$default_controls = $block_type->default_controls ?? $this->default_controls_schema['default'];
$data = $response->get_data();
$data['default_controls'] = rest_sanitize_value_from_schema(
$default_controls,
array(
'description' => __( 'UI controls to display by default.' ),
'type' => 'object',
'default' => array(),
'properties' => array(),
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
)
);
$response->set_data( $data );
return $response;
}
}


add_action(
'rest_api_init',
function () {
$controller = new Gutenberg_REST_Block_Types_Controller_6_8();
$controller->register_routes();
}
);
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ function gutenberg_is_experiment_enabled( $name ) {

// WordPress 6.8 compat.
require __DIR__ . '/compat/wordpress-6.8/block-comments.php';
require __DIR__ . '/compat/wordpress-6.8/class-gutenberg-rest-block-types-controller-6-8.php';
require __DIR__ . '/compat/wordpress-6.8/class-gutenberg-rest-comment-controller-6-8.php';
require __DIR__ . '/compat/wordpress-6.8/class-gutenberg-rest-post-types-controller-6-8.php';
require __DIR__ . '/compat/wordpress-6.8/rest-api.php';
Expand Down
1 change: 1 addition & 0 deletions packages/block-directory/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export const installBlockType =
'styles',
'example',
'variations',
'default_controls',
];
await apiFetch( {
path: addQueryArgs( `/wp/v2/block-types/${ name }`, {
Expand Down
10 changes: 7 additions & 3 deletions packages/block-editor/src/hooks/border.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import clsx from 'clsx';
/**
* WordPress dependencies
*/
import { hasBlockSupport, getBlockSupport } from '@wordpress/blocks';
import {
hasBlockSupport,
getBlockSupport,
getBlockDefaultControls,
} from '@wordpress/blocks';
import { __experimentalHasSplitBorders as hasSplitBorders } from '@wordpress/components';
import { Platform, useCallback, useMemo } from '@wordpress/element';
import { addFilter } from '@wordpress/hooks';
Expand Down Expand Up @@ -161,8 +165,8 @@ export function BorderPanel( { clientId, name, setAttributes, settings } ) {
}

const defaultControls = {
...getBlockSupport( name, [ BORDER_SUPPORT_KEY, 'defaultControls' ] ),
...getBlockSupport( name, [ SHADOW_SUPPORT_KEY, 'defaultControls' ] ),
...getBlockDefaultControls( name, [ BORDER_SUPPORT_KEY ] ),
...getBlockDefaultControls( name, [ SHADOW_SUPPORT_KEY ] ),
};

return (
Expand Down
5 changes: 2 additions & 3 deletions packages/block-editor/src/hooks/color.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import clsx from 'clsx';
* WordPress dependencies
*/
import { addFilter } from '@wordpress/hooks';
import { getBlockSupport } from '@wordpress/blocks';
import { getBlockSupport, getBlockDefaultControls } from '@wordpress/blocks';
import { useMemo, Platform, useCallback } from '@wordpress/element';
import { useSelect } from '@wordpress/data';

Expand Down Expand Up @@ -288,9 +288,8 @@ export function ColorEdit( { clientId, name, setAttributes, settings } ) {
return null;
}

const defaultControls = getBlockSupport( name, [
const defaultControls = getBlockDefaultControls( name, [
COLOR_SUPPORT_KEY,
'defaultControls',
] );

const enableContrastChecking =
Expand Down
8 changes: 3 additions & 5 deletions packages/block-editor/src/hooks/dimensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import clsx from 'clsx';
*/
import { Platform, useState, useEffect, useCallback } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { getBlockSupport } from '@wordpress/blocks';
import { getBlockSupport, getBlockDefaultControls } from '@wordpress/blocks';
import deprecated from '@wordpress/deprecated';

/**
Expand Down Expand Up @@ -86,13 +86,11 @@ export function DimensionsPanel( { clientId, name, setAttributes, settings } ) {
return null;
}

const defaultDimensionsControls = getBlockSupport( name, [
const defaultDimensionsControls = getBlockDefaultControls( name, [
DIMENSIONS_SUPPORT_KEY,
'defaultControls',
] );
const defaultSpacingControls = getBlockSupport( name, [
const defaultSpacingControls = getBlockDefaultControls( name, [
SPACING_SUPPORT_KEY,
'defaultControls',
] );
const defaultControls = {
...defaultDimensionsControls,
Expand Down
5 changes: 2 additions & 3 deletions packages/block-editor/src/hooks/typography.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
import { getBlockSupport, hasBlockSupport } from '@wordpress/blocks';
import { hasBlockSupport, getBlockDefaultControls } from '@wordpress/blocks';
import { useMemo, useCallback } from '@wordpress/element';
import { useSelect } from '@wordpress/data';

Expand Down Expand Up @@ -131,9 +131,8 @@ export function TypographyPanel( { clientId, name, setAttributes, settings } ) {
return null;
}

const defaultControls = getBlockSupport( name, [
const defaultControls = getBlockDefaultControls( name, [
TYPOGRAPHY_SUPPORT_KEY,
'defaultControls',
] );

return (
Expand Down
10 changes: 10 additions & 0 deletions packages/blocks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,16 @@ _Returns_

- `string`: The block's default class.

### getBlockDefaultControls

Returns a block type's default controls config for a feature, if defined.

_Parameters_

- _name_ `(string|Object)`: Block name or type object
- _feature_ `string`: Feature to retrieve
- _defaultControls_ `*`: Default config to use if not explicitly defined.

### getBlockFromExample

Create a block object from the example API.
Expand Down
1 change: 1 addition & 0 deletions packages/blocks/src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export {
getBlockTypes,
getBlockSupport,
hasBlockSupport,
getBlockDefaultControls,
getBlockVariations,
isReusableBlock,
isTemplatePart,
Expand Down
17 changes: 17 additions & 0 deletions packages/blocks/src/api/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import warning from '@wordpress/warning';
*/
import i18nBlockSchema from './i18n-block.json';
import { store as blocksStore } from '../store';
import { getValueFromObjectPath } from '../store/utils';
import { unlock } from '../lock-unlock';

/**
Expand Down Expand Up @@ -172,6 +173,7 @@ function getBlockSettingsFromMetadata( { textdomain, ...metadata } ) {
'variations',
'blockHooks',
'allowedBlocks',
'defaultControls',
];

const settings = Object.fromEntries(
Expand Down Expand Up @@ -553,6 +555,21 @@ export function hasBlockSupport( nameOrType, feature, defaultSupports ) {
);
}

/**
* Returns a block type's default controls config for a feature, if defined.
*
* @param {(string|Object)} name Block name or type object
* @param {string} feature Feature to retrieve
* @param {*} defaultControls Default config to use if not explicitly defined.
*/
export function getBlockDefaultControls( name, feature, defaultControls ) {
return getValueFromObjectPath(
select( blocksStore )?.getBlockType( name )?.defaultControls,
feature,
defaultControls
);
}

/**
* Determines whether or not the given block is a reusable block. This is a
* special block type that is used to point to a global block stored via the
Expand Down
Loading
Loading