-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Block Bindings: Communicate supported block attributes from server side #71820
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
Block Bindings: Communicate supported block attributes from server side #71820
Conversation
|
Size Change: -5 B (0%) Total Size: 1.95 MB
ℹ️ View Unchanged
|
|
Flaky tests detected in 1659414. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/18009387538
|
…n BLOCK_BINDINGS_ALLOWED_BLOCKS
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Per this discussion with @gziolo, I'm prefixing |
|
Not a blocker: I thought we were avoiding more (block) editor settings like these, in favor of configs via REST API. Since these attributes are connected to the block types, can they be part of the block schema? |
There's a whole discussion on this: WordPress/wordpress-develop#9992 (comment) 😅 The tl;dr is: In the long term, we hope to use a block attributes's |
|
Thanks for linking the discussion, @ockham! |
| const { __experimentalBlockBindingsSupportedAttributes } = | ||
| select( blockEditorStore ).getSettings(); | ||
| return ( | ||
| __experimentalBlockBindingsSupportedAttributes?.[ name ] || [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to include a stable reference to an empty array to avoid unnecessary re-renders when there are no bindable attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 'var(--wp-block-synced-color--rgb)', | ||
| } | ||
| : {}; | ||
| const bindingsStyle = hasBlockBindings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I recall correctly, the check was in place to cover for the case when someone adds bindings in metadata, but the block doesn't support bindings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that makes sense. This is the only part where I had to change the logic.
useBlockProps doesn't use useSelect (nor does it read from any stores at all AFAICS); this makes sense to me, since it's a fairly low-level tool. I didn't want to introduce a dependency on a store, even if that means that the block could be shown with a purple border in the editor because it has bindings in the metadata even though the block doesn't support bindings.
TBH I'm not even sure is useBlockProps (and a modification of the block style, rather than e.g. adding a class name) is that right place for this. I looked around a bit, and other code that highlights reusable blocks (e.g. patterns or template parts) tends to do that quite a bit differently.
I'm willing to look into tightening the logic so it'll really only highlight the block if it does support bindings, but it looked like it needed further research how to best do this, and it seemed like a small enough edge case that I didn't want to block this PR by it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't consider it a blocker. If you find a better place to inject this special styling, that would be the best solution for me. It can be handled seperately.
|
Code looks good. I left only some minor notes. I will test it next. |
gziolo
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test well, too 🚢
| Object.keys( filteredBindings ).forEach( ( key ) => { | ||
| if ( | ||
| ! canBindAttribute( blockName, key ) || | ||
| ! bindableAttributes.includes( key ) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@talldan, all e2e tests pass, but I would still appreciate some confirmation that this change doesn't introduce regressions. I noticed it when performing a post-merge check on trunk.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gziolo Do you mean the change from || to &&? That does look kinda wrong now 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it wasn't immediately apparent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The || makes more sense to me. || would delete anything that's either a non-bindable attribute or has pattern overrides, which is what the comment above the code says.
With this change you only delete the attribute if it has a pattern overrides binding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll file a fix for this tomorrow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, looks like the second part of the criterion was already removed altogether: 2a9598c#diff-bff98e322c4269c50aa13e4b251beb9d53d2d0c3d4066406f735d2d49bed83a1L269-L270
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure the new version is quite correct TBH:
gutenberg/packages/block-editor/src/hooks/block-bindings.js
Lines 439 to 445 in 1c5976e
| // Filter bindings to only show bindable attributes. | |
| const { bindings } = metadata || {}; | |
| const filteredBindings = { ...bindings }; | |
| Object.keys( filteredBindings ).forEach( ( key ) => { | |
| if ( ! bindableAttributes.includes( key ) ) { | |
| delete filteredBindings[ key ]; | |
| } |
Maybe we did this accidentally, since the && was giving the wrong behavior? We might want to revisit this and bring back the previous logic.
diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js
index 87ab4e3632..a8cc0d369f 100644
--- a/packages/block-editor/src/hooks/block-bindings.js
+++ b/packages/block-editor/src/hooks/block-bindings.js
@@ -436,11 +436,14 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
if ( ! bindableAttributes || bindableAttributes.length === 0 ) {
return null;
}
- // Filter bindings to only show bindable attributes.
+ // Filter bindings to only show bindable attributes and remove pattern overrides.
const { bindings } = metadata || {};
const filteredBindings = { ...bindings };
Object.keys( filteredBindings ).forEach( ( key ) => {
- if ( ! bindableAttributes.includes( key ) ) {
+ if (
+ ! bindableAttributes.includes( key ) ||
+ filteredBindings[ key ].source === 'core/pattern-overrides'
+ ) {
delete filteredBindings[ key ];
}
} );I'll be AFK on Thu and Fri; @gziolo @cbravobernal Maybe y'all could look into this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit again:
The pattern overrides should never appear. As it doesn't contain an editorUI attribute. And when a block contains a pattern override, the __default attribute won't appear on the UI, as is not a "supported" attribute.
That check is not needed. I found another bug if there are no sources. We show the panel without a readonly attributes panel, which is not ideal.
<!-- wp:image {"metadata":{"bindings":{"url":{"source":"core/undefined"},"alt":{"source":"core/undefined"},"title":{"source":"core/post-data", "args":{"key":"date"}}}}} -->
<figure class="wp-block-image"><img alt=""/></figure>
<!-- /wp:image -->
Drafted a PR: #72253
|
Thank you for landing this, @gziolo! |
|
It's possible this PR caused some performance issues with typing looking at the codevitals metrics - https://www.codevitals.run/project/gutenberg/site-editor-type The performance CI job for that last commit seems to show it too. It might be the extra edit: it's also more noticeable for the inserter hover metric - https://www.codevitals.run/project/gutenberg/inserterHover |
| unlock( select( blocksStore ) ).getAllBlockBindingsSources(), | ||
| [] | ||
| ); | ||
| const bindableAttributes = useSelect( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at code vitals, this PR made the "hover" metric slower. My guess is it's because of this addition here. I think it's also responsible of a small impact on the "type" metrics (even though it's hard to see there)
I think the reason (a guess, not 100% certain) is that we prefer to avoid adding new useSelect calls as much as possible to these components that run for every block. Now, it's possible to solve this as we have a "private edit context" (or something like that) to solve this. Basically we group all the useSelect calls that we need for a block at a single place.
Can we try moving this there to see?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Just saw the previous related discussion)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cbravobernal, is that on your radar for 6.9?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added it to the "Bugfixes" section in #67520.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@youknowriad Can you help me figure out where to put this specifically? The selector checks what attributes are supported by block bindings, which is information passed by the server (via getSettings()). Potentially, any block can have such attributes, which is why we're running this code for all blocks...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe in this useSelect
| const selectedProps = useSelect( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cbravobernal has filed a PR to address this: #72351
| if ( ! source || ! canBindAttribute( name, attributeName ) ) { | ||
| if ( | ||
| ! source || | ||
| ! bindableAttributes.includes( attributeName ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uncaught TypeError: Cannot read properties of undefined (reading 'includes')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.

What?
Instead of keeping a separate list of block attributes that are supported by Block Bindings, communicate that list from the server -- including block attributes that were added there via the
block_bindings_supported_attributesfilter.See https://github.com/WordPress/gutenberg/pull/70975/files#r2367242822.
Why?
To have a single source of truth.
How?
We use the
block_editor_settings_allfilter to expose the list of block attributes supported by block bindings as JS variable on the client side. This makes it available to client-side code viaselect( blockEditorStore ).getSettings().blockBindingsSupportedAttributes. We then replace all functions that relied on theBLOCK_BINDINGS_ALLOWED_BLOCKSconstant by inline code that reads from that new variable.Testing Instructions
Verify that block bindings and pattern overrides still work as before.
In particular, test the following:
captionattribute to a source.Note that support for block bindings is added to those blocks on the server side via the
block_bindings_supported_attributesfilter, so if those bindings work, it means that the client picks up the information from the server side correctly.Follow-up
BLOCK_BINDINGS_SUPPORTED_BLOCKSingetTransformedMetadata().PARTIAL_SYNCING_SUPPORTED_BLOCKSinpackages/patterns/src/constants.js(see).