Skip to content

Commit

Permalink
Block Bindings: Improve the way block bindings sources are registered (
Browse files Browse the repository at this point in the history
…#63117)

* Move bindings registration to editor provider

* Remove old bindings import

* Remove sources from editor provider

* Create registerBlockBindingsSource

* Export function as private

* Create `registerCoreBlockBindingsSources` function

* Register core sources in edit-post and edit-site

* Don't reuse existing source

* Add comment explaining function

* Change the object in registration

* Add a few checks more

* Add more registration functions

* Add private action to remove binding source

* Add unit tests

* Export all functions as private

Co-authored-by: SantosGuillamot <santosguillamot@git.wordpress.org>
Co-authored-by: youknowriad <youknowriad@git.wordpress.org>
Co-authored-by: artemiomorales <artemiosans@git.wordpress.org>
Co-authored-by: cbravobernal <cbravobernal@git.wordpress.org>
Co-authored-by: gziolo <gziolo@git.wordpress.org>
  • Loading branch information
6 people authored Jul 16, 2024
1 parent e64a666 commit 4730e12
Show file tree
Hide file tree
Showing 12 changed files with 475 additions and 34 deletions.
4 changes: 4 additions & 0 deletions packages/blocks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,10 @@ _Returns_

- `Array|string`: A list of blocks or a string, depending on `handlerMode`.

### privateApis

Undocumented declaration.

### rawHandler

Converts an HTML string to known blocks.
Expand Down
19 changes: 19 additions & 0 deletions packages/blocks/src/api/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
/**
* Internal dependencies
*/
import { lock } from '../lock-unlock';
import {
registerBlockBindingsSource,
unregisterBlockBindingsSource,
getBlockBindingsSource,
getBlockBindingsSources,
} from './registration';

// The blocktype is the most important concept within the block API. It defines
// all aspects of the block configuration and its interfaces, including `edit`
// and `save`. The transforms specification allows converting one blocktype to
Expand Down Expand Up @@ -164,3 +175,11 @@ export {
__EXPERIMENTAL_ELEMENTS,
__EXPERIMENTAL_PATHS_WITH_OVERRIDE,
} from './constants';

export const privateApis = {};
lock( privateApis, {
registerBlockBindingsSource,
unregisterBlockBindingsSource,
getBlockBindingsSource,
getBlockBindingsSources,
} );
175 changes: 175 additions & 0 deletions packages/blocks/src/api/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -758,3 +758,178 @@ export const registerBlockVariation = ( blockName, variation ) => {
export const unregisterBlockVariation = ( blockName, variationName ) => {
dispatch( blocksStore ).removeBlockVariations( blockName, variationName );
};

/**
* Registers a new block bindings source with an object defining its
* behavior. Once registered, the source is available to be connected
* to the supported block attributes.
*
* @param {Object} source Properties of the source to be registered.
* @param {string} source.name The unique and machine-readable name.
* @param {string} source.label Human-readable label.
* @param {Function} [source.getValue] Function to get the value of the source.
* @param {Function} [source.setValue] Function to update the value of the source.
* @param {Function} [source.setValues] Function to update multiple values connected to the source.
* @param {Function} [source.getPlaceholder] Function to get the placeholder when the value is undefined.
* @param {Function} [source.canUserEditValue] Function to determine if the user can edit the value.
*
* @example
* ```js
* import { _x } from '@wordpress/i18n';
* import { registerBlockBindingsSource } from '@wordpress/blocks'
*
* registerBlockBindingsSource( {
* name: 'plugin/my-custom-source',
* label: _x( 'My Custom Source', 'block bindings source' ),
* getValue: () => 'Value to place in the block attribute',
* setValue: () => updateMyCustomValue(),
* setValues: () => updateMyCustomValuesInBatch(),
* getPlaceholder: () => 'Placeholder text when the value is undefined',
* canUserEditValue: () => true,
* } );
* ```
*/
export const registerBlockBindingsSource = ( source ) => {
const {
name,
label,
getValue,
setValue,
setValues,
getPlaceholder,
canUserEditValue,
} = source;

// Check if the source is already registered.
const existingSource = unlock(
select( blocksStore )
).getBlockBindingsSource( name );
if ( existingSource ) {
console.error(
'Block bindings source "' + name + '" is already registered.'
);
return;
}

// Check the `name` property is correct.
if ( ! name ) {
console.error( 'Block bindings source must contain a name.' );
return;
}

if ( typeof name !== 'string' ) {
console.error( 'Block bindings source name must be a string.' );
return;
}

if ( /[A-Z]+/.test( name ) ) {
console.error(
'Block bindings source name must not contain uppercase characters.'
);
return;
}

if ( ! /^[a-z0-9/-]+$/.test( name ) ) {
console.error(
'Block bindings source name must contain only valid characters: lowercase characters, hyphens, or digits. Example: my-plugin/my-custom-source.'
);
return;
}

if ( ! /^[a-z0-9-]+\/[a-z0-9-]+$/.test( name ) ) {
console.error(
'Block bindings source name must contain a namespace and valid characters. Example: my-plugin/my-custom-source.'
);
return;
}

// Check the `label` property is correct.
if ( ! label ) {
console.error( 'Block bindings source must contain a label.' );
return;
}

if ( typeof label !== 'string' ) {
console.error( 'Block bindings source label must be a string.' );
return;
}

// Check the `getValue` property is correct.
if ( getValue && typeof getValue !== 'function' ) {
console.error( 'Block bindings source getValue must be a function.' );
return;
}

// Check the `setValue` property is correct.
if ( setValue && typeof setValue !== 'function' ) {
console.error( 'Block bindings source setValue must be a function.' );
return;
}

// Check the `setValues` property is correct.
if ( setValues && typeof setValues !== 'function' ) {
console.error( 'Block bindings source setValues must be a function.' );
return;
}

// Check the `getPlaceholder` property is correct.
if ( getPlaceholder && typeof getPlaceholder !== 'function' ) {
console.error(
'Block bindings source getPlaceholder must be a function.'
);
return;
}

// Check the `getPlaceholder` property is correct.
if ( canUserEditValue && typeof canUserEditValue !== 'function' ) {
console.error(
'Block bindings source canUserEditValue must be a function.'
);
return;
}

return unlock( dispatch( blocksStore ) ).addBlockBindingsSource( source );
};

/**
* Unregisters a block bindings source
*
* @param {string} name The name of the block bindings source to unregister.
*
* @example
* ```js
* import { unregisterBlockBindingsSource } from '@wordpress/blocks';
*
* unregisterBlockBindingsSource( 'plugin/my-custom-source' );
* ```
*/
export function unregisterBlockBindingsSource( name ) {
const oldSource = getBlockBindingsSource( name );
if ( ! oldSource ) {
console.error(
'Block bindings source "' + name + '" is not registered.'
);
return;
}
unlock( dispatch( blocksStore ) ).removeBlockBindingsSource( name );
}

/**
* Returns a registered block bindings source.
*
* @param {string} name Block bindings source name.
*
* @return {?Object} Block bindings source.
*/
export function getBlockBindingsSource( name ) {
return unlock( select( blocksStore ) ).getBlockBindingsSource( name );
}

/**
* Returns all registered block bindings sources.
*
* @return {Array} Block bindings sources.
*/
export function getBlockBindingsSources() {
return unlock( select( blocksStore ) ).getAllBlockBindingsSources();
}
Loading

0 comments on commit 4730e12

Please sign in to comment.