-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Extensibility: Make Block Bindings work with editor.BlockEdit
hook (2nd try)
#67523
Conversation
Size Change: -104 B (-0.01%) Total Size: 1.83 MB
ℹ️ View Unchanged
|
I see this warning:
I think it is why the context was handled that way. |
The problem is that this hook observers the changes from: values = source.getValues( {
select,
context: contextFromSources,
clientId,
bindings,
} ); We need to figure out how to listen to these changes differently to get rid of the warning. |
I've explored a similar solution at #67524. Context is computed in the |
Flaky tests detected in bdf4397. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/12142449699
|
The performance metrics currently look as follows: @ellatrix, can you verify if everything is on track? I'm not entirely sure what the confidence level in these results is, as @SantosGuillamot and I observe a bit of randomness in both directions on every new commit. |
I carried over most of the refactoring from #67370 for Block Bindings utils. |
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. |
@SantosGuillamot, do you still see this warning? How can I test it? |
I think it was solved moving the context to the useMemo call. I was just testing it opening a page with a connected block and checking the console. |
withBlockBindingSupport
editor.BlockEdit
hook
editor.BlockEdit
hookeditor.BlockEdit
hook (2nd try)
Maybe enhancements should happen at the source in @ellatrix has a better understanding of performance intricacies for |
attributes?.metadata?.bindings | ||
), | ||
[ attributes?.metadata?.bindings, 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.
Can't this be moved within 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.
I was looking at it before. I need to check again.
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 think it's used also by useCallback, but it could be moved within both maybe.
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.
In f192c04, I merged it with another useMemo
instead.
Yeah, at some point we should make built it in instead of adding it in a hook. But if this doesn't add select calls unless a block is bound, it seems fine. I looked at the changes and didn't see any obvious concerns. |
The performance runs on each run can be a bit off... If you have concerns, what I do is do a few runs and then average them. Or we'll see it in the graph for sure after merge. |
const blockType = getBlockType( name ); | ||
const blockContext = useContext( BlockContext ); | ||
const registeredSources = useSelect( ( select ) => |
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 only slightly concerning thing is the extra blocks store sub. But this store usually almost never changes, so it's not as bad as a blockEditor store sub. I don't think it will affect the results.
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.
Right, it's a trade-off.
ec2d136
to
f192c04
Compare
values[ attr ] = source.label; | ||
} ); | ||
} else { | ||
values = source.getValues( { |
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.
@Mamaduka, as I'm looking at:
I'm wondering what the pattern should be for observing store changes when you expect that for the same set of dependencies, there are expected different results. In this particular case, source.getValues
might read the value for another store, code example:
gutenberg/packages/editor/src/bindings/post-meta.js
Lines 77 to 89 in 5e3d575
getValues( { select, context, bindings } ) { | |
const metaFields = getPostMetaFields( select, context ); | |
const newValues = {}; | |
for ( const [ attributeName, source ] of Object.entries( bindings ) ) { | |
// Use the value, the field label, or the field key. | |
const fieldKey = source.args.key; | |
const { value: fieldValue, label: fieldLabel } = | |
metaFields?.[ fieldKey ] || {}; | |
newValues[ attributeName ] = fieldValue ?? fieldLabel ?? fieldKey; | |
} | |
return newValues; | |
}, |
In effect, where coreDataStore
updates for Post Meta source, then it should compute a different result. I now realize it's a limitation of the current implementation. How can we improve it?
If performance looks ok and it works, this is fine with me :) |
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.
From my side, this looks good 🙂 Everything seems to keep working as expected and e2e tests pass. I've also compared the performance locally, and it seems it doesn't impact:
This branch | Trunk |
---|---|
I just left a small comment that I am not sure if it could cause issues.
Apart from that, I think it needs to be rebased after these changes to solve the CI problems.
@@ -48,27 +57,223 @@ const Edit = ( props ) => { | |||
const EditWithFilters = withFilters( 'editor.BlockEdit' )( Edit ); | |||
|
|||
const EditWithGeneratedProps = ( props ) => { | |||
const { attributes = {}, name } = props; | |||
const { name, clientId, attributes, setAttributes } = props; |
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.
In the previous declaration, attributes
had a default empty object. Could this cause any issues if it is undefined? Maybe some developers are relying on it being an object in BlockEdit
?
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 think there was ever guaranteed that attributes
is going to be an object due to historical reasons. computedAttributes
gets passed down instead, which is always an object when there are no bindings. Otherwise, it will be the same value as defined by the block. I'm not entirely sure why it would get mapped to an empty object in this place, but if we want to keep that, then we should use a stable reference instead of overriding it with a new empty object instance on every re-render.
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.
On the server, attributes can be set to null
:
https://github.com/WordPress/wordpress-develop/blob/32880ec4e910837cd267e1685733867b243e22fa/src/wp-includes/class-wp-block-type.php#L169-L175
The same applies to REST API response:
It was hardened in block.json
schema:
gutenberg/schemas/json/block.json
Lines 107 to 110 in af34d1e
"attributes": { | |
"description": "Attributes provide the structured data needs of a block. They can exist in different forms when they are serialized, but they are declared together under a common interface.\n\nSee the attributes documentation at https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/ for more details.", | |
"type": "object", | |
"patternProperties": { |
However, when not provided on the server, that would still mean it's null
.
On the client, it defaults to an empty object:
gutenberg/packages/blocks/src/store/process-block-type.js
Lines 235 to 239 in af34d1e
const blockType = { | |
name, | |
icon: BLOCK_ICON_DEFAULT, | |
keywords: [], | |
attributes: {}, |
However, I'm not entirely sure what happens if the server sets null
when bootstraping the value.
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 think for the block type processing, attributes
will never be null
because it defines some attributes that are common to all blocks: metadata
and lock
: https://github.com/WordPress/wordpress-develop/blob/32880ec4e910837cd267e1685733867b243e22fa/src/wp-includes/class-wp-block-type.php#L282-L285.
If null
is a valid value for the BlockEdit
, I'm fine keeping it this way.
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.
Good point, it's probably impossible to see attributes
set to null
on the client based on the fact it always contains global attributes on the server:
…(2nd try) (WordPress#67523) * Block Bindings: Refactor `withBlockBindingSupport` * Fix issues reported by CI * Move bindings handling to EditWithGeneratedProps * Move sources context to `useMemo` * Reuse `getBlockBindingsSources` * Add e2e test * Add missing dependencies to block bindings JS script in e2e test * Refactor block bindings utils * Final touches on EditWithGeneratedProps * Improve block bindings utils * Address feedback from code review --------- Co-authored-by: Mario Santos <[email protected]> Co-authored-by: gziolo <[email protected]> Co-authored-by: SantosGuillamot <[email protected]> Co-authored-by: ellatrix <[email protected]> Co-authored-by: Mamaduka <[email protected]>
What?
Second attempt for #67370.
It wasn't possible to use
editor.BlockEdit
filter to extend the block with additional controls and successfully usesetAttributes
orattributes
for connected attribute using Block Bindings.Why?
The way all the logic for enhancing
attributes
andsetAttributes
was connected too deep in the React components chain. It was below the point whereeditor.BlockEdit
allows overriding the Edit component. In effect the defaultattributes
andsetAttributes
were passed to the component as if it wasn't connected with Block Bindings mechanism.How?
Refactored the code to move the logic higher in the hierarchy so all required child blocks receive access to enhanced
attributes
andsetAttributes
props.Testing Instructions
I included a new e2e test that covers the extensibility point for
editor.BlockEdit
hooks that uses the enhanced props correctly by consuming them through input field injected into inspector controls:The simplest way to test it is using the Gutenberg Test Block Bindings plugin included with e2e tests:
text_field_value
.Content
.