Skip to content

Commit d55d5fa

Browse files
ockhamgzioloMamaduka
authored
Block Bindings: Communicate supported block attributes from server side (#71820)
* Block Bindings: Introduce gutenberg_get_block_bindings_supported_attributes * Block Bindings: Add blockBindingsSupportedAttributes to editor settings * Add backport changelog * Replace getBindableAttributes * Actually expose blockBindingsSupportedAttributes on the client side * Replace canBindAttribute * Replace canBindBlock * Change replacePatternOverridesDefaultBinding so it no longer relies on BLOCK_BINDINGS_ALLOWED_BLOCKS * Change param order * Prefix var with __experimental * 🔤 * Add EMPTY_ARRAY const and use to have a stable reference Co-authored-by: ockham <[email protected]> Co-authored-by: gziolo <[email protected]> Co-authored-by: Mamaduka <[email protected]>
1 parent 5beedbf commit d55d5fa

File tree

8 files changed

+164
-138
lines changed

8 files changed

+164
-138
lines changed

backport-changelog/6.9/9992.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
https://github.com/WordPress/wordpress-develop/pull/9992
2+
3+
* https://github.com/WordPress/gutenberg/pull/71820

lib/compat/wordpress-6.9/block-bindings.php

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,21 @@ function ( $attributes, $block_type ) {
2424
2
2525
);
2626

27+
// The following filter can be removed once the minimum required WordPress version is 6.9 or newer.
28+
add_filter(
29+
'block_editor_settings_all',
30+
function ( $editor_settings ) {
31+
$editor_settings['__experimentalBlockBindingsSupportedAttributes'] = array();
32+
foreach ( array_keys( WP_Block_Type_Registry::get_instance()->get_all_registered() ) as $block_type ) {
33+
$supported_block_attributes = gutenberg_get_block_bindings_supported_attributes( $block_type );
34+
if ( ! empty( $supported_block_attributes ) ) {
35+
$editor_settings['__experimentalBlockBindingsSupportedAttributes'][ $block_type ] = $supported_block_attributes;
36+
}
37+
}
38+
return $editor_settings;
39+
}
40+
);
41+
2742
/**
2843
* Callback function for the render_block filter.
2944
*
@@ -86,6 +101,59 @@ function gutenberg_block_bindings_render_block( $block_content, $block, $instanc
86101
}
87102
add_filter( 'render_block', 'gutenberg_block_bindings_render_block', 10, 3 );
88103

104+
/**
105+
* Retrieves the list of block attributes supported by block bindings.
106+
*
107+
* @since 6.9.0
108+
*
109+
* @param string $block_type The block type whose supported attributes are being retrieved.
110+
* @return array The list of block attributes that are supported by block bindings.
111+
*/
112+
function gutenberg_get_block_bindings_supported_attributes( $block_type ) {
113+
// List of block attributes supported by Block Bindings in WP 6.8.
114+
$block_bindings_supported_attributes_6_8 = array(
115+
'core/paragraph' => array( 'content' ),
116+
'core/heading' => array( 'content' ),
117+
'core/image' => array( 'id', 'url', 'title', 'alt' ),
118+
'core/button' => array( 'url', 'text', 'linkTarget', 'rel' ),
119+
);
120+
121+
$supported_block_attributes =
122+
$block_bindings_supported_attributes_6_8[ $block_type ] ??
123+
array();
124+
125+
/**
126+
* Filters the supported block attributes for block bindings.
127+
*
128+
* @since 6.9.0
129+
*
130+
* @param string[] $supported_block_attributes The block's attributes that are supported by block bindings.
131+
* @param string $block_type The block type whose attributes are being filtered.
132+
*/
133+
$supported_block_attributes = apply_filters(
134+
'block_bindings_supported_attributes',
135+
$supported_block_attributes,
136+
$block_type
137+
);
138+
139+
/**
140+
* Filters the supported block attributes for block bindings.
141+
*
142+
* The dynamic portion of the hook name, `$block_type`, refers to the block type
143+
* whose attributes are being filtered.
144+
*
145+
* @since 6.9.0
146+
*
147+
* @param string[] $supported_block_attributes The block's attributes that are supported by block bindings.
148+
*/
149+
$supported_block_attributes = apply_filters(
150+
"block_bindings_supported_attributes_{$block_type}",
151+
$supported_block_attributes
152+
);
153+
154+
return $supported_block_attributes;
155+
}
156+
89157
/**
90158
* Processes the block bindings and updates the block attributes with the values from the sources.
91159
*
@@ -138,38 +206,8 @@ function gutenberg_process_block_bindings( $instance ) {
138206
'core/image' => array( 'id', 'url', 'title', 'alt' ),
139207
'core/button' => array( 'url', 'text', 'linkTarget', 'rel' ),
140208
);
141-
$supported_block_attributes =
142-
$block_bindings_supported_attributes_6_8[ $block_type ] ??
143-
array();
144-
145-
/**
146-
* Filters the supported block attributes for block bindings.
147-
*
148-
* @since 6.9.0
149-
*
150-
* @param string[] $supported_block_attributes The block's attributes that are supported by block bindings.
151-
* @param string $block_type The block type whose attributes are being filtered.
152-
*/
153-
$supported_block_attributes = apply_filters(
154-
'block_bindings_supported_attributes',
155-
$supported_block_attributes,
156-
$block_type
157-
);
158209

159-
/**
160-
* Filters the supported block attributes for block bindings.
161-
*
162-
* The dynamic portion of the hook name, `$block_type`, refers to the block type
163-
* whose attributes are being filtered.
164-
*
165-
* @since 6.9.0
166-
*
167-
* @param string[] $supported_block_attributes The block's attributes that are supported by block bindings.
168-
*/
169-
$supported_block_attributes = apply_filters(
170-
"block_bindings_supported_attributes_{$block_type}",
171-
$supported_block_attributes
172-
);
210+
$supported_block_attributes = gutenberg_get_block_bindings_supported_attributes( $block_type );
173211

174212
/*
175213
* Remove attributes that we know are processed by WP 6.8 from the list,

packages/block-editor/src/components/block-edit/edit.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ import { useCallback, useContext, useMemo } from '@wordpress/element';
2222
import BlockContext from '../block-context';
2323
import isURLLike from '../link-control/is-url-like';
2424
import {
25-
canBindAttribute,
2625
hasPatternOverridesDefaultBinding,
2726
replacePatternOverridesDefaultBinding,
2827
} from '../../utils/block-bindings';
28+
import { store as blockEditorStore } from '../../store';
2929
import { unlock } from '../../lock-unlock';
3030

3131
/**
@@ -56,6 +56,8 @@ const Edit = ( props ) => {
5656

5757
const EditWithFilters = withFilters( 'editor.BlockEdit' )( Edit );
5858

59+
const EMPTY_ARRAY = [];
60+
5961
const EditWithGeneratedProps = ( props ) => {
6062
const { name, clientId, attributes, setAttributes } = props;
6163
const registry = useRegistry();
@@ -66,6 +68,17 @@ const EditWithGeneratedProps = ( props ) => {
6668
unlock( select( blocksStore ) ).getAllBlockBindingsSources(),
6769
[]
6870
);
71+
const bindableAttributes = useSelect(
72+
( select ) => {
73+
const { __experimentalBlockBindingsSupportedAttributes } =
74+
select( blockEditorStore ).getSettings();
75+
return (
76+
__experimentalBlockBindingsSupportedAttributes?.[ name ] ||
77+
EMPTY_ARRAY
78+
);
79+
},
80+
[ name ]
81+
);
6982

7083
const { blockBindings, context, hasPatternOverrides } = useMemo( () => {
7184
// Assign context values using the block type's declared context needs.
@@ -90,8 +103,8 @@ const EditWithGeneratedProps = ( props ) => {
90103
}
91104
return {
92105
blockBindings: replacePatternOverridesDefaultBinding(
93-
name,
94-
attributes?.metadata?.bindings
106+
attributes?.metadata?.bindings,
107+
bindableAttributes
95108
),
96109
context: computedContext,
97110
hasPatternOverrides: hasPatternOverridesDefaultBinding(
@@ -120,7 +133,10 @@ const EditWithGeneratedProps = ( props ) => {
120133
) ) {
121134
const { source: sourceName, args: sourceArgs } = binding;
122135
const source = registeredSources[ sourceName ];
123-
if ( ! source || ! canBindAttribute( name, attributeName ) ) {
136+
if (
137+
! source ||
138+
! bindableAttributes.includes( attributeName )
139+
) {
124140
continue;
125141
}
126142

@@ -172,6 +188,7 @@ const EditWithGeneratedProps = ( props ) => {
172188
},
173189
[
174190
attributes,
191+
bindableAttributes,
175192
blockBindings,
176193
clientId,
177194
context,
@@ -197,7 +214,7 @@ const EditWithGeneratedProps = ( props ) => {
197214
) ) {
198215
if (
199216
! blockBindings[ attributeName ] ||
200-
! canBindAttribute( name, attributeName )
217+
! bindableAttributes.includes( attributeName )
201218
) {
202219
continue;
203220
}
@@ -250,6 +267,7 @@ const EditWithGeneratedProps = ( props ) => {
250267
} );
251268
},
252269
[
270+
bindableAttributes,
253271
blockBindings,
254272
clientId,
255273
context,

packages/block-editor/src/components/block-list/use-block-props/index.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import { useBlockRefProvider } from './use-block-refs';
2929
import { useIntersectionObserver } from './use-intersection-observer';
3030
import { useScrollIntoView } from './use-scroll-into-view';
3131
import { useFlashEditableBlocks } from '../../use-flash-editable-blocks';
32-
import { canBindBlock } from '../../../utils/block-bindings';
3332
import { useFirefoxDraggableCompatibility } from './use-firefox-draggable-compatibility';
3433

3534
/**
@@ -128,14 +127,13 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
128127

129128
const blockEditContext = useBlockEditContext();
130129
const hasBlockBindings = !! blockEditContext[ blockBindingsKey ];
131-
const bindingsStyle =
132-
hasBlockBindings && canBindBlock( name )
133-
? {
134-
'--wp-admin-theme-color': 'var(--wp-block-synced-color)',
135-
'--wp-admin-theme-color--rgb':
136-
'var(--wp-block-synced-color--rgb)',
137-
}
138-
: {};
130+
const bindingsStyle = hasBlockBindings
131+
? {
132+
'--wp-admin-theme-color': 'var(--wp-block-synced-color)',
133+
'--wp-admin-theme-color--rgb':
134+
'var(--wp-block-synced-color--rgb)',
135+
}
136+
: {};
139137

140138
// Ensures it warns only inside the `edit` implementation for the block.
141139
if ( blockApiVersion < 2 && clientId === blockEditContext.clientId ) {

packages/block-editor/src/components/rich-text/index.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import FormatEdit from './format-edit';
3939
import { getAllowedFormats } from './utils';
4040
import { Content, valueToHTMLString } from './content';
4141
import { withDeprecations } from './with-deprecations';
42-
import { canBindBlock } from '../../utils/block-bindings';
4342
import BlockContext from '../block-context';
4443

4544
export const keyboardShortcutContext = createContext();
@@ -177,9 +176,14 @@ export function RichTextWrapper(
177176

178177
const { disableBoundBlock, bindingsPlaceholder, bindingsLabel } = useSelect(
179178
( select ) => {
179+
const { __experimentalBlockBindingsSupportedAttributes } =
180+
select( blockEditorStore ).getSettings();
181+
180182
if (
181183
! blockBindings?.[ identifier ] ||
182-
! canBindBlock( blockName )
184+
! (
185+
blockName in __experimentalBlockBindingsSupportedAttributes
186+
)
183187
) {
184188
return {};
185189
}

packages/block-editor/src/hooks/block-bindings.js

Lines changed: 49 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@ import { useViewportMatch } from '@wordpress/compose';
2323
/**
2424
* Internal dependencies
2525
*/
26-
import {
27-
canBindAttribute,
28-
getBindableAttributes,
29-
useBlockBindingsUtils,
30-
} from '../utils/block-bindings';
26+
import { useBlockBindingsUtils } from '../utils/block-bindings';
3127
import { unlock } from '../lock-unlock';
3228
import InspectorControls from '../components/inspector-controls';
3329
import BlockContext from '../components/block-context';
@@ -205,52 +201,62 @@ function EditableBlockBindingsPanelItems( {
205201
export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
206202
const blockContext = useContext( BlockContext );
207203
const { removeAllBlockBindings } = useBlockBindingsUtils();
208-
const bindableAttributes = getBindableAttributes( blockName );
209204
const dropdownMenuProps = useToolsPanelDropdownMenuProps();
210205

211206
// `useSelect` is used purposely here to ensure `getFieldsList`
212207
// is updated whenever there are updates in block context.
213208
// `source.getFieldsList` may also call a selector via `select`.
214209
const _fieldsList = {};
215-
const { fieldsList, canUpdateBlockBindings } = useSelect(
216-
( select ) => {
217-
if ( ! bindableAttributes || bindableAttributes.length === 0 ) {
218-
return EMPTY_OBJECT;
219-
}
220-
const registeredSources = getBlockBindingsSources();
221-
Object.entries( registeredSources ).forEach(
222-
( [ sourceName, { getFieldsList, usesContext } ] ) => {
223-
if ( getFieldsList ) {
224-
// Populate context.
225-
const context = {};
226-
if ( usesContext?.length ) {
227-
for ( const key of usesContext ) {
228-
context[ key ] = blockContext[ key ];
210+
const { bindableAttributes, fieldsList, canUpdateBlockBindings } =
211+
useSelect(
212+
( select ) => {
213+
const { __experimentalBlockBindingsSupportedAttributes } =
214+
select( blockEditorStore ).getSettings();
215+
const _bindableAttributes =
216+
__experimentalBlockBindingsSupportedAttributes?.[
217+
blockName
218+
];
219+
if (
220+
! _bindableAttributes ||
221+
_bindableAttributes.length === 0
222+
) {
223+
return EMPTY_OBJECT;
224+
}
225+
const registeredSources = getBlockBindingsSources();
226+
Object.entries( registeredSources ).forEach(
227+
( [ sourceName, { getFieldsList, usesContext } ] ) => {
228+
if ( getFieldsList ) {
229+
// Populate context.
230+
const context = {};
231+
if ( usesContext?.length ) {
232+
for ( const key of usesContext ) {
233+
context[ key ] = blockContext[ key ];
234+
}
235+
}
236+
const sourceList = getFieldsList( {
237+
select,
238+
context,
239+
} );
240+
// Only add source if the list is not empty.
241+
if ( Object.keys( sourceList || {} ).length ) {
242+
_fieldsList[ sourceName ] = { ...sourceList };
229243
}
230-
}
231-
const sourceList = getFieldsList( {
232-
select,
233-
context,
234-
} );
235-
// Only add source if the list is not empty.
236-
if ( Object.keys( sourceList || {} ).length ) {
237-
_fieldsList[ sourceName ] = { ...sourceList };
238244
}
239245
}
240-
}
241-
);
242-
return {
243-
fieldsList:
244-
Object.values( _fieldsList ).length > 0
245-
? _fieldsList
246-
: EMPTY_OBJECT,
247-
canUpdateBlockBindings:
248-
select( blockEditorStore ).getSettings()
249-
.canUpdateBlockBindings,
250-
};
251-
},
252-
[ blockContext, bindableAttributes ]
253-
);
246+
);
247+
return {
248+
bindableAttributes: _bindableAttributes,
249+
fieldsList:
250+
Object.values( _fieldsList ).length > 0
251+
? _fieldsList
252+
: EMPTY_OBJECT,
253+
canUpdateBlockBindings:
254+
select( blockEditorStore ).getSettings()
255+
.canUpdateBlockBindings,
256+
};
257+
},
258+
[ blockContext ]
259+
);
254260
// Return early if there are no bindable attributes.
255261
if ( ! bindableAttributes || bindableAttributes.length === 0 ) {
256262
return null;
@@ -260,7 +266,7 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
260266
const filteredBindings = { ...bindings };
261267
Object.keys( filteredBindings ).forEach( ( key ) => {
262268
if (
263-
! canBindAttribute( blockName, key ) ||
269+
! bindableAttributes.includes( key ) &&
264270
filteredBindings[ key ].source === 'core/pattern-overrides'
265271
) {
266272
delete filteredBindings[ key ];

0 commit comments

Comments
 (0)