You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Image Block is replaced as a Youtube block after dragging multiple blocks outside their parent #61778
I have created blank theme with the Create Block Theme Plugin.
I have created a post and I added the following blocks structure:
Group
Group
Heading
Paragraph
Image
Youtube (I have created this block by pasting the video URL in the post editor)
Then I have selected the Heading, Paragraph, Image and Youtube blocks and dragged them outside their parent.
The image block got replaced by another instance of a youtube block, with the same video. Doesn't matter if I drag it one level upper or at root level.
Seems to happen only when an Image block is sibling of the youtube block. I can't replicate this behavior with the paragraph or heading blocks.
After dragging the blocks, and trying to press Undo I need to spam it multiple times till I get to the state before dragging the blocks. Doing single clicks to it doesn't work.
I also tried with the TT4 theme. And the exact same happens.
Step-by-step reproduction instructions
Create a blank theme using Create Block Theme (or use the TT4 theme)
Create a new post
Create a Group Block within another Group Block that contains at least one image block and a Youtube block (siblings).
Create a multiple selection that comprehends at least the image and youtube blocks.
Drag those blocks outside of it's parent.
Check if the image block got converted to a Youtube Block
Check if single clicks to the undo button doesn't work
Check if you have to spam click the undo button for it to work again
Screenshots, screen recording, code snippet
gg.mp4
Environment info
WP 5.3.0
Gutenberg 18.3.0
Blank theme created with the Create Block Theme Plugin
Chrome v124.0.6367.209
Desktop Windows 11 23H2
Please confirm that you have searched existing issues in the repo.
Yes
Please confirm that you have tested with all plugins deactivated except Gutenberg.
I can reproduce this. When both an Image and Embed block are selected and dragged to a new location, the React UI for both will be unmounted and re-mounted at the new location.
The Embed block has this effect with a setAttributes call that updates the Embed block's attributes with data from the embed preview:
Because both the Embed and Image blocks are selected, the Image block's url attribute is set to a YouTube URL. This URL fails to load as an image, and that triggers an automatic transform of the Image to YouTube Embed:
@youknowriad@ellatrix This behavior where setAttributes updates all selected block has been there for years, and yet it seems horribly wrong? The documentation for the block Edit function says quite clearly that I can do this:
This proves again that "on-mount effects" should be avoided in blocks or used very carefully.
I think that the Embed block doesn't do anything wrong. It simply wants to update its own attributes, that's legit. The problem is that the setAttributes function updates all selected blocks. There's certainly also some good reason why that was added. We need to discuss that and figure out what is the right fix.
Should we add some early returns in that setAttributes action? It should only update a multi-selection if the blocks are of the same type. I believe there are checks on render, but not in that action.
Should we add some early returns in that setAttributes action? It should only update a multi-selection if the blocks are of the same type. I believe there are checks on render, but not in that action.
That will only partially solve the issue. If you drag multiple embed blocks with different URLs, they'll have the same URL after the dragging.
Edit: The original logic was introduced in #22470.
It should only update a multi-selection if the blocks are of the same type.
This is still wrong, because if I select and drag two adjacent YouTube Embed blocks, they will update each other's URLs.
The question is more like: why is setAttributes looking at the multi-selection at all? Why isn't it updating just the one clientId that it is bound to? What's the use case?
I see it now: the use case is that you select two paragraph blocks, and then in the sidebar edit the text color:
This will update the attributes of both blocks.
But it looks like a bad idea to use setAttributes for that. The controls should be able to explicitly choose if they want to update just one block, or multiple ones.
@ellatrix, @jsnajdr, what do you think about creating a separate issue to continue discussions regarding solving potential bugs with current setAttributes behavior?
The Embed block's "on-mount effect" bug can be solved separately.
Oh I'm very happy to see everyone's response in my bug report. Unfortunately I yet lack the knowledge to contribute beyond filing a report. I would be completely lost creating pull requests, committing changes etc. I guess no further actions are required from me, is that correct ?
But it looks like a bad idea to use setAttributes for that. The controls should be able to explicitly choose if they want to update just one block, or multiple ones.
@cbirdsong Yes, this bug has the same underlying cause. The Layout control is not really aware that it's editing layout for multiple blocks. It receives the attributes of one block, the first selected one. It reads .layout, which is:
Here setAttributes will update the attributes of all selected blocks, and that's how the justifyContent: 'right' of the first selected block is applied to all others.
I don't think we can just remove current behavior, as it would be considered a breaking change.
@Mamaduka This will be tough, because the current API behavior is seriously broken and there are many cases that are handled incorrectly. We can't keep it that way forever.
If setAttributes is fixed to always modify just one block, and there is a new API introduced for block controls to modify multiple blocks at once, how will that break existing blocks? Instead of modifying all selected blocks, they will modify only the first. They will need to be patched if they want to modify multiple blocks. That's not awful. When balanced against the value of having a well-behaved stable API, it might turn out to be acceptable.
@jsnajdr, that's a good point. I doubt many are aware of the current setAttributes behavior anyway. A dev note with a migration path should help mitigate any breakage.
What do you think of my proposal to create a separate issue for the general setAttributes fix?
P.S. The updateBlockAttributes has an uniqueByBlock flag/argument, which I believe was an attempt to fix this behavior. Currently used only by the Gallery block. See: #29099.
Sure, we can create a new API to modify multiple blocks. This was a tradeoff we made when adding the capability to modify multiple blocks. Unfortunately all controls are nested within block edit functions. If we do something like Block API: try separate controls registration, it will also fix this problem, because we can just get all the controls for a block type.
what do you think about creating a separate issue to continue discussions regarding solving potential bugs with current setAttributes behavior?
Maybe #41260 could be the "canonical" bug for this? It is more directly related to the setAttributes behavior.
The effects in the Embed block can be tidied up: currently setAttributes is called even when no attributes are going to change -- it should be conditional. That alone will fix this bug.
The updateBlockAttributes has an uniqueByBlock flag/argument, which I believe was an attempt to fix this behavior.
It seems to me only weakly related. It's used by the Gallery block to loop through the child blocks, Images, and set attributes on them. It's a shortcut for registry.batch + loop + updateBlockAttributes (single).
While it might be known already, sharing that this affects all embed blocks (Spotify, YouTube, etc.) and images wherever you drag them. Also, dragging them into a group or block with group-like behavior will have the same bug.
I've just created a PR to address the embed block's side of the problem, as it shouldn't be calling setAttributes() when remounted if no attributes have actually been changed.
Just to add another perspective on how to improve setAttributes(), I’m not sure introducing a new API is strictly necessary as the intention from a block calling it is already clear. Adding another layer of abstraction might complicate things unnecessarily, without any real benefits. Could we not explore tweaking setAttributes() itself to handle all cases more efficiently? It's an opportunity to further improve the UX too.
Here's a mini breakdown of the main (only?) use cases for setAttributes():
Single block selected:
[No bugs] User wants to update one of its attributes - Working as expected, although entire properties are getting replaced, even if just one single nested property (e.g. layout.flexWrap) changed.
Multiple blocks selected of same block type e.g. Multiple Row blocks
[No bugs] User updates a root level string attribute (e.g. fontSize) - works as expected.
[Bug] User updates a single nested property (e.g. layout.flexWrap) - not working as expected, as the entire root property (layout) is taken from one block and applied to all selected blocks.
[Bug] If one block inadvertently calls setAttributes() (e.g. as seen in this embed issue), all the attributes from one of those blocks is applied to all other selected blocks, causing unintended changes.
Multiple blocks selected of different block types e.g. Paragraph block and a Heading block
[Bugs] If any block calls setAttributes(), an attempt is made to set its attributes to all other selected blocks, even if they’re of different types. E.g. as seen in this embed issue again.
[Not currently possible] Users cannot change shared attributes (e.g. textColor) across different block types.
In an ideal world setAttributes() would be tweaked to handle:
Check if any attributes have actually changed before doing updates to prevent redundant changes.
When multiple blocks of different types are selected, show any shared controls, e.g. typography/color settings, in the block settings panel. This would improve the UX and allow users to easily update things across multiple block types simultaneously, similar to how it works for blocks of the same type.
I may be missing more use cases, but if I'm not, that would fix all the bugs and not impact any other existing code, or require any blocks to change their behaviour.
Just some thoughts 👍
Reacted by Vegard Stronen, Cory Birdsong and George Mamadashvili
Activity
Mamaduka commentedon May 20, 2024
I can also reproduce this on the Gutenberg trunk. I can reproduce the issue with WP 6.5 and no Gutenberg plugin.
The order in which Image and Embed blocks are placed also doesn't matter.
What's happening
urlattributes. I'm not sure yet why.onImageErrorand converts it into an embed block.jsnajdr commentedon May 20, 2024
I can reproduce this. When both an Image and Embed block are selected and dragged to a new location, the React UI for both will be unmounted and re-mounted at the new location.
The Embed block has this effect with a
setAttributescall that updates the Embed block's attributes with data from the embed preview:gutenberg/packages/block-library/src/embed/edit.js
Lines 169 to 187 in 608b890
But this
setAttributesfunction doesn't just update the Embed block, it updates all selected blocks in a multi-selection:gutenberg/packages/block-editor/src/components/block-list/block.js
Lines 270 to 280 in 608b890
Because both the Embed and Image blocks are selected, the Image block's
urlattribute is set to a YouTube URL. This URL fails to load as an image, and that triggers an automatic transform of the Image to YouTube Embed:gutenberg/packages/block-library/src/image/image.js
Lines 248 to 256 in 608b890
@youknowriad @ellatrix This behavior where
setAttributesupdates all selected block has been there for years, and yet it seems horribly wrong? The documentation for the blockEditfunction says quite clearly that I can do this:and the intent is quite obviously to set the attributes of exactly one block. And yet it's setting
footobaron all multi-selected blocks.Mamaduka commentedon May 20, 2024
Thanks for finding the attribute update cause, @jsnajdr! I should've guessed it was a multi-selected block attribute update.
Jarda, do you plan to work on a fix? I have an idea for an Embed block "hotfix" and happy to push it.
P.S. This proves again that "on-mount effects" should be avoided in blocks or used very carefully.
jsnajdr commentedon May 20, 2024
I think that the Embed block doesn't do anything wrong. It simply wants to update its own attributes, that's legit. The problem is that the
setAttributesfunction updates all selected blocks. There's certainly also some good reason why that was added. We need to discuss that and figure out what is the right fix.ellatrix commentedon May 20, 2024
Should we add some early returns in that setAttributes action? It should only update a multi-selection if the blocks are of the same type. I believe there are checks on render, but not in that action.
Mamaduka commentedon May 20, 2024
That will only partially solve the issue. If you drag multiple embed blocks with different URLs, they'll have the same URL after the dragging.
Edit: The original logic was introduced in #22470.
jsnajdr commentedon May 20, 2024
This is still wrong, because if I select and drag two adjacent YouTube Embed blocks, they will update each other's URLs.
The question is more like: why is
setAttributeslooking at the multi-selection at all? Why isn't it updating just the oneclientIdthat it is bound to? What's the use case?jsnajdr commentedon May 20, 2024
I see it now: the use case is that you select two paragraph blocks, and then in the sidebar edit the text color:
This will update the attributes of both blocks.
But it looks like a bad idea to use
setAttributesfor that. The controls should be able to explicitly choose if they want to update just one block, or multiple ones.Mamaduka commentedon May 20, 2024
I don't think we can just remove current behavior, as it would be considered a breaking change.
FYI, updating values for multiple blocks of the same type also has issue #51609.
Mamaduka commentedon May 20, 2024
@ellatrix, @jsnajdr, what do you think about creating a separate issue to continue discussions regarding solving potential bugs with current
setAttributesbehavior?The Embed block's "on-mount effect" bug can be solved separately.
emdashftw commentedon May 20, 2024
Oh I'm very happy to see everyone's response in my bug report. Unfortunately I yet lack the knowledge to contribute beyond filing a report. I would be completely lost creating pull requests, committing changes etc. I guess no further actions are required from me, is that correct ?
Mamaduka commentedon May 20, 2024
Thanks for reporting the bug, @jeflopodev!
That's right; you don't need to do anything else.
P.S. You might get a notification to link your GitHub account with WP.org when the pull request is merged so that you get contribution credit.
cbirdsong commentedon May 20, 2024
This may be the source of the bug in #41260?
jsnajdr commentedon May 21, 2024
@cbirdsong Yes, this bug has the same underlying cause. The Layout control is not really aware that it's editing layout for multiple blocks. It receives the
attributesof one block, the first selected one. It reads.layout, which is:When you disable the "Allow to wrap" toggle, it changes the
flexWrapfield and calls:Here
setAttributeswill update the attributes of all selected blocks, and that's how thejustifyContent: 'right'of the first selected block is applied to all others.@Mamaduka This will be tough, because the current API behavior is seriously broken and there are many cases that are handled incorrectly. We can't keep it that way forever.
If
setAttributesis fixed to always modify just one block, and there is a new API introduced for block controls to modify multiple blocks at once, how will that break existing blocks? Instead of modifying all selected blocks, they will modify only the first. They will need to be patched if they want to modify multiple blocks. That's not awful. When balanced against the value of having a well-behaved stable API, it might turn out to be acceptable.Mamaduka commentedon May 21, 2024
@jsnajdr, that's a good point. I doubt many are aware of the current
setAttributesbehavior anyway. A dev note with a migration path should help mitigate any breakage.What do you think of my proposal to create a separate issue for the general
setAttributesfix?P.S. The
updateBlockAttributeshas anuniqueByBlockflag/argument, which I believe was an attempt to fix this behavior. Currently used only by the Gallery block. See: #29099.ellatrix commentedon May 21, 2024
Sure, we can create a new API to modify multiple blocks. This was a tradeoff we made when adding the capability to modify multiple blocks. Unfortunately all controls are nested within block edit functions. If we do something like Block API: try separate controls registration, it will also fix this problem, because we can just get all the controls for a block type.
jsnajdr commentedon May 21, 2024
Maybe #41260 could be the "canonical" bug for this? It is more directly related to the
setAttributesbehavior.The effects in the Embed block can be tidied up: currently
setAttributesis called even when no attributes are going to change -- it should be conditional. That alone will fix this bug.It seems to me only weakly related. It's used by the Gallery block to loop through the child blocks, Images, and set attributes on them. It's a shortcut for
registry.batch+ loop +updateBlockAttributes(single).stronenv commentedon Oct 22, 2024
While it might be known already, sharing that this affects all embed blocks (Spotify, YouTube, etc.) and images wherever you drag them. Also, dragging them into a group or block with group-like behavior will have the same bug.
chrisbellboy commentedon Dec 19, 2024
Hey everyone 👋
I've just created a PR to address the embed block's side of the problem, as it shouldn't be calling
setAttributes()when remounted if no attributes have actually been changed.Just to add another perspective on how to improve
setAttributes(), I’m not sure introducing a new API is strictly necessary as the intention from a block calling it is already clear. Adding another layer of abstraction might complicate things unnecessarily, without any real benefits. Could we not explore tweakingsetAttributes()itself to handle all cases more efficiently? It's an opportunity to further improve the UX too.Here's a mini breakdown of the main (only?) use cases for
setAttributes():Single block selected:
layout.flexWrap) changed.Multiple blocks selected of same block type e.g. Multiple Row blocks
fontSize) - works as expected.layout.flexWrap) - not working as expected, as the entire root property (layout) is taken from one block and applied to all selected blocks.setAttributes()(e.g. as seen in this embed issue), all the attributes from one of those blocks is applied to all other selected blocks, causing unintended changes.Multiple blocks selected of different block types e.g. Paragraph block and a Heading block
setAttributes(), an attempt is made to set its attributes to all other selected blocks, even if they’re of different types. E.g. as seen in this embed issue again.textColor) across different block types.In an ideal world
setAttributes()would be tweaked to handle:I may be missing more use cases, but if I'm not, that would fix all the bugs and not impact any other existing code, or require any blocks to change their behaviour.
Just some thoughts 👍