Skip to content

Commit

Permalink
Block Bindings: Bootstrap server sources earlier (#66058) (#66167)
Browse files Browse the repository at this point in the history
* Remove bindings bootstrap functions

* Remove bootstrap calls

* Remove old filter

* Adapt unit tests

* Add compat filter in Gutenberg

* Only run script if not already registered

* Remove extra label in test

* Remove unit test

* Add backport changelog

* Return warning if any client-only prop is defined

Co-authored-by: cbravobernal <[email protected]>
Co-authored-by: gziolo <[email protected]>
Co-authored-by: ryanwelcher <[email protected]>
Co-authored-by: SantosGuillamot <[email protected]>
  • Loading branch information
5 people authored Oct 17, 2024
1 parent 3457f6b commit d0dae48
Show file tree
Hide file tree
Showing 11 changed files with 39 additions and 159 deletions.
3 changes: 3 additions & 0 deletions backport-changelog/6.7/7552.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
https://github.com/WordPress/wordpress-develop/pull/7552

* https://github.com/WordPress/gutenberg/pull/66058
42 changes: 16 additions & 26 deletions lib/compat/wordpress-6.7/block-bindings.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,28 @@
*/

/**
* Adds the block bindings sources registered in the server to the editor settings.
*
* This allows them to be bootstrapped in the editor.
*
* @param array $settings The block editor settings from the `block_editor_settings_all` filter.
* @return array The editor settings including the block bindings sources.
* Bootstrap the block bindings sources registered in the server.
*/
function gutenberg_add_server_block_bindings_sources_to_editor_settings( $editor_settings ) {
// Check if the sources are already exposed in the editor settings.
if ( isset( $editor_settings['blockBindingsSources'] ) ) {
return $editor_settings;
}

$registered_block_bindings_sources = get_all_registered_block_bindings_sources();
if ( ! empty( $registered_block_bindings_sources ) ) {
// Initialize array.
$editor_settings['blockBindingsSources'] = array();
foreach ( $registered_block_bindings_sources as $source_name => $source_properties ) {
// Add source with the label to editor settings.
$editor_settings['blockBindingsSources'][ $source_name ] = array(
'label' => $source_properties->label,
function gutenberg_bootstrap_server_block_bindings_sources() {
$registered_sources = get_all_registered_block_bindings_sources();
if ( ! empty( $registered_sources ) ) {
$filtered_sources = array();
foreach ( $registered_sources as $source ) {
$filtered_sources[] = array(
'name' => $source->name,
'label' => $source->label,
'usesContext' => $source->uses_context,
);
// Add `usesContext` property if exists.
if ( ! empty( $source_properties->uses_context ) ) {
$editor_settings['blockBindingsSources'][ $source_name ]['usesContext'] = $source_properties->uses_context;
}
}
$script = sprintf( 'for ( const source of %s ) { ! wp.blocks.getBlockBindingsSource( source.name ) && wp.blocks.registerBlockBindingsSource( source ); }', wp_json_encode( $filtered_sources ) );
wp_add_inline_script(
'wp-blocks',
$script
);
}
return $editor_settings;
}

add_filter( 'block_editor_settings_all', 'gutenberg_add_server_block_bindings_sources_to_editor_settings', 10 );
add_action( 'enqueue_block_editor_assets', 'gutenberg_bootstrap_server_block_bindings_sources', 5 );

/**
* Initialize `canUpdateBlockBindings` editor setting if it doesn't exist. By default, it is `true` only for admin users.
Expand Down
15 changes: 9 additions & 6 deletions packages/blocks/src/api/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -807,13 +807,16 @@ export const registerBlockBindingsSource = ( source ) => {

/*
* Check if the source has been already registered on the client.
* If the `getValues` property is defined, it could be assumed the source is already registered.
* If any property expected to be "client-only" is defined, return a warning.
*/
if ( existingSource?.getValues ) {
warning(
'Block bindings source "' + name + '" is already registered.'
);
return;
const serverProps = [ 'label', 'usesContext' ];
for ( const prop in existingSource ) {
if ( ! serverProps.includes( prop ) && existingSource[ prop ] ) {
warning(
'Block bindings source "' + name + '" is already registered.'
);
return;
}
}

// Check the `name` property is correct.
Expand Down
60 changes: 8 additions & 52 deletions packages/blocks/src/api/test/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -1513,10 +1513,8 @@ describe( 'blocks', () => {
} );

it( 'should not override label from the server', () => {
// Bootstrap source from the server.
unlock(
dispatch( blocksStore )
).addBootstrappedBlockBindingsSource( {
// Simulate bootstrap source from the server.
registerBlockBindingsSource( {
name: 'core/server',
label: 'Server label',
} );
Expand All @@ -1543,10 +1541,8 @@ describe( 'blocks', () => {
} );

it( 'should add usesContext when only defined in the server', () => {
// Bootstrap source from the server.
unlock(
dispatch( blocksStore )
).addBootstrappedBlockBindingsSource( {
// Simulate bootstrap source from the server.
registerBlockBindingsSource( {
name: 'core/testing',
label: 'testing',
usesContext: [ 'postId', 'postType' ],
Expand All @@ -1562,10 +1558,8 @@ describe( 'blocks', () => {
} );

it( 'should add usesContext when only defined in the client', () => {
// Bootstrap source from the server.
unlock(
dispatch( blocksStore )
).addBootstrappedBlockBindingsSource( {
// Simulate bootstrap source from the server.
registerBlockBindingsSource( {
name: 'core/testing',
label: 'testing',
} );
Expand All @@ -1581,10 +1575,8 @@ describe( 'blocks', () => {
} );

it( 'should merge usesContext from server and client without duplicates', () => {
// Bootstrap source from the server.
unlock(
dispatch( blocksStore )
).addBootstrappedBlockBindingsSource( {
// Simulate bootstrap source from the server.
registerBlockBindingsSource( {
name: 'core/testing',
label: 'testing',
usesContext: [ 'postId', 'postType' ],
Expand Down Expand Up @@ -1705,42 +1697,6 @@ describe( 'blocks', () => {
'Block bindings source "core/test-source" is already registered.'
);
} );

it( 'should correctly merge properties when bootstrap happens after registration', () => {
// Register source in the client.
const clientOnlyProperties = {
getValues: () => 'values',
setValues: () => 'new values',
canUserEditValue: () => true,
};
registerBlockBindingsSource( {
name: 'core/custom-source',
label: 'Client Label',
usesContext: [ 'postId', 'postType' ],
...clientOnlyProperties,
} );

// Bootstrap source from the server.
unlock(
dispatch( blocksStore )
).addBootstrappedBlockBindingsSource( {
name: 'core/custom-source',
label: 'Server Label',
usesContext: [ 'postId', 'serverContext' ],
} );

// Check that the bootstrap values prevail and the client properties are still there.
expect( getBlockBindingsSource( 'core/custom-source' ) ).toEqual( {
// Should use the server label.
label: 'Server Label',
// Should merge usesContext from server and client.
usesContext: [ 'postId', 'postType', 'serverContext' ],
// Should keep client properties.
...clientOnlyProperties,
} );

unregisterBlockBindingsSource( 'core/custom-source' );
} );
} );

describe( 'unregisterBlockBindingsSource', () => {
Expand Down
14 changes: 0 additions & 14 deletions packages/blocks/src/store/private-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,3 @@ export function removeBlockBindingsSource( name ) {
name,
};
}

/**
* Add bootstrapped block bindings sources, usually initialized from the server.
*
* @param {string} source Name of the source to bootstrap.
*/
export function addBootstrappedBlockBindingsSource( source ) {
return {
type: 'ADD_BOOTSTRAPPED_BLOCK_BINDINGS_SOURCE',
name: source.name,
label: source.label,
usesContext: source.usesContext,
};
}
16 changes: 0 additions & 16 deletions packages/blocks/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,22 +417,6 @@ export function blockBindingsSources( state = {}, action ) {
getFieldsList,
},
};
case 'ADD_BOOTSTRAPPED_BLOCK_BINDINGS_SOURCE':
return {
...state,
[ action.name ]: {
/*
* Keep the exisitng properties in case the source has been registered
* in the client before bootstrapping.
*/
...state[ action.name ],
label: action.label,
usesContext: getMergedUsesContext(
state[ action.name ]?.usesContext,
action.usesContext
),
},
};
case 'REMOVE_BLOCK_BINDINGS_SOURCE':
return omit( state, action.name );
}
Expand Down
1 change: 0 additions & 1 deletion packages/e2e-tests/plugins/block-bindings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ const setValues = ( { dispatch, bindings } ) => {

registerBlockBindingsSource( {
name: 'testing/complete-source',
label: 'Complete Source',
getValues,
setValues,
canUserEditValue: () => true,
Expand Down
2 changes: 0 additions & 2 deletions packages/edit-post/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { unlock } from './lock-unlock';
const {
BackButton: __experimentalMainDashboardButton,
registerCoreBlockBindingsSources,
bootstrapBlockBindingsSourcesFromServer,
} = unlock( editorPrivateApis );

/**
Expand Down Expand Up @@ -95,7 +94,6 @@ export function initializeEditor(
}

registerCoreBlocks();
bootstrapBlockBindingsSourcesFromServer( settings?.blockBindingsSources );
registerCoreBlockBindingsSources();
registerLegacyWidgetBlock( { inserter: false } );
registerWidgetGroupBlock( { inserter: false } );
Expand Down
6 changes: 1 addition & 5 deletions packages/edit-site/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ import { store as editSiteStore } from './store';
import { unlock } from './lock-unlock';
import App from './components/app';

const {
registerCoreBlockBindingsSources,
bootstrapBlockBindingsSourcesFromServer,
} = unlock( editorPrivateApis );
const { registerCoreBlockBindingsSources } = unlock( editorPrivateApis );

/**
* Initializes the site editor screen.
Expand All @@ -48,7 +45,6 @@ export function initializeEditor( id, settings ) {
( { name } ) => name !== 'core/freeform'
);
registerCoreBlocks( coreBlocks );
bootstrapBlockBindingsSourcesFromServer( settings?.blockBindingsSources );
registerCoreBlockBindingsSources();
dispatch( blocksStore ).setFreeformFallbackBlockName( 'core/html' );
registerLegacyWidgetBlock( { inserter: false } );
Expand Down
33 changes: 1 addition & 32 deletions packages/editor/src/bindings/api.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
/**
* WordPress dependencies
*/
import {
store as blocksStore,
registerBlockBindingsSource,
} from '@wordpress/blocks';
import { dispatch } from '@wordpress/data';
import { registerBlockBindingsSource } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import patternOverrides from './pattern-overrides';
import postMeta from './post-meta';
import { unlock } from '../lock-unlock';

/**
* Function to register core block bindings sources provided by the editor.
Expand All @@ -28,29 +23,3 @@ export function registerCoreBlockBindingsSources() {
registerBlockBindingsSource( patternOverrides );
registerBlockBindingsSource( postMeta );
}

/**
* Function to bootstrap core block bindings sources defined in the server.
*
* @param {Object} sources Object containing the sources to bootstrap.
*
* @example
* ```js
* import { bootstrapBlockBindingsSourcesFromServer } from '@wordpress/editor';
*
* bootstrapBlockBindingsSourcesFromServer( sources );
* ```
*/
export function bootstrapBlockBindingsSourcesFromServer( sources ) {
if ( sources ) {
const { addBootstrappedBlockBindingsSource } = unlock(
dispatch( blocksStore )
);
for ( const [ name, args ] of Object.entries( sources ) ) {
addBootstrappedBlockBindingsSource( {
name,
...args,
} );
}
}
}
6 changes: 1 addition & 5 deletions packages/editor/src/private-apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ import {
mergeBaseAndUserConfigs,
GlobalStylesProvider,
} from './components/global-styles-provider';
import {
registerCoreBlockBindingsSources,
bootstrapBlockBindingsSourcesFromServer,
} from './bindings/api';
import { registerCoreBlockBindingsSources } from './bindings/api';

const { store: interfaceStore, ...remainingInterfaceApis } = interfaceApis;

Expand All @@ -47,7 +44,6 @@ lock( privateApis, {
ViewMoreMenuGroup,
ResizableEditor,
registerCoreBlockBindingsSources,
bootstrapBlockBindingsSourcesFromServer,

// This is a temporary private API while we're updating the site editor to use EditorProvider.
interfaceStore,
Expand Down

0 comments on commit d0dae48

Please sign in to comment.