Skip to content
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

Fix block edit toolbar appearing twice #12580

Closed
wants to merge 4 commits into from

Conversation

johngodley
Copy link
Contributor

If you duplicate a block with the keyboard shortcut shift+cmd+d it duplicates the block toolbar:

edit_post_ _wordpress_latest_ _wordpress

This PR stops this happening, but doesn’t fix the underlying problem. I'm putting it here as an open question (I'm not really sure what is supposed to happen), along with some fixes.

As far as I can tell the problem is caused by the Slot/Fill system, where it’s possible for two same-named slots to be added, with the second overwriting the first. When either slot is removed it then removes both. This is only made apparent when using the keyboard shortcut as it changes the order in which slots are created and removed.

To make it easier to understand I'll use the duplicate block toolbar as an example. Note in this context, BlockActions is the slot used to show the block edit toolbar. I've added some debug to Gutenberg so it outputs the slot/fill actions to make it clearer what is happening.

I may have misunderstood some/all parts of this!

Duplicating a block using the block menu

  1. Block is selected and BlockActions slot appears

edit_post_ _wordpress_latest_ _wordpress

SLOT: register
FILL: register

  1. Duplicate is chosen from block menu
  2. Block menu disappears, and slot is removed

SLOT: unregister
FILL: unregister

  1. Block is duplicated
  2. New block is auto-selected and a new BlockActions slot is created for it:

SLOT: register
FILL: register

  1. Duplicated block appears with edit toolbar, as expected:

edit_post_ _wordpress_latest_ _wordpress

Duplicating a block using the keyboard shortcut shift+cmd+d

  1. Block is selected and BlockActions slot appears

SLOT: register
FILL: register

  1. Shortcut is triggered
  2. Block is duplicated, existing BlockActions slot still exists
  3. New block is auto-selected and another BlockControls slot is created, overwriting the existing BlockControls slot. The old one is still visible as it's not been updated yet (I am stepping through the code here to show each part)

SLOT: register
FILL: register

edit_post_ _wordpress_latest_ _wordpress

5. The new slot is updated and we see the original slot with its fill, and the new slot with the original fill and the new block fill:

edit_post_ _wordpress_latest_ _wordpress

6. The original block is no longer selected so it tries to remove itself. However, it actually removes the new `BlockControls` slot

SLOT: unregister
FILL: unregister

We are left in this state:
edit_post_ _wordpress_latest_ _wordpress
7. At some point something happens (a heartbeat maybe?) that triggers an update, and the slot returns to normal:
edit_post_ _wordpress_latest_ _wordpress

What is happening?

The above example is trying to show that when using the menu, the slot is created, removed, and then created again, and everything works as expected. When using the keyboard shortcut, the slot is created for the first block, created for the second block, and then the first removed. Because of the way the slot/fill system works the second slot overwrites the first, and when the first is removed, it's actually removing the second.

The fix

Back to this PR!

I'm not really sure how the slot/fill system is supposed to behave when two slots of the same name are added. This is the real problem, which I haven't attempted to fix. I note in https://github.com/WordPress/gutenberg/blob/master/packages/components/src/slot-fill/slot.js#L37 that an instance is being passed, even though it's not used, and I wonder if this indicates it could work like the fills (where multiple same named fills can exist)?

I have included two fixes here. Both of them fix the problem in different ways, and I wanted to get some feedback about the preferred route (which may be neither):

  • BlockActions has been modified to clear the block selection when the duplicate action is triggered. This forces it to behave the same regardless of if from the menu or keyboard. This only fixes the block edit toolbar and shouldnt affect anything else
  • SlotFillProvider has been modified to forceUpdateSlot before deleting a slot. This ensures the slot is cleared first, and stops the duplicate. This fixes the display, but not what happens underneath. I don't know if it could affect other things.

There's also a bunch of unit tests, of which one tests the duplicate slot problem and will always fail. I've included it here to make it easier to test, but it would need to be fixed properly or removed.

Fixes #12355

If you duplicate a block using the keyboard shortcut you end up creating two BlockControls slots, and the fills are repeated
This ensures the slot is cleared before it’s deleted
These test the force update slot when a slot is removed
@johngodley johngodley added the [Status] In Progress Tracking issues with work in progress label Dec 4, 2018
@johngodley
Copy link
Contributor Author

@aduth I think you may know about this - appreciate any insight

@gziolo gziolo added the [Feature] Blocks Overall functionality of blocks label Dec 14, 2018
@gziolo gziolo requested a review from aduth December 14, 2018 14:56
@gziolo gziolo added the [Package] Components /packages/components label Dec 14, 2018
@aduth
Copy link
Member

aduth commented Dec 14, 2018

an instance is being passed, even though it's not used

It's indirect, but it is used by the Fill, in creating its portal to the slot instance's node:

const slot = getSlot( name );

return createPortal( children, slot.node );

@aduth
Copy link
Member

aduth commented Dec 14, 2018

I'm not really sure how the slot/fill system is supposed to behave when two slots of the same name are added.

Two opposing thoughts come to mind:

  • Fully support multiple slots, where the same fills are shown / replicated across each instance.
    • This is made challenging by the implementation of the Fill to render into its own slot via createPortal, assuming a single slot node.
  • Check whether another instance already exists and force it to destroy itself.

@aduth
Copy link
Member

aduth commented Dec 14, 2018

  1. The original block is no longer selected so it tries to remove itself. However, it actually removes the new BlockControls slot

I think there's actually a quick fix for this. We pass the instance to unregisterSlot, so before it deletes the slot, check that the instance for the slot matches its current tracked value.

This actually seems to solve the overall problem for me, but needs more testing. I'll try to put together some proposal.

@@ -79,6 +80,9 @@ export default compose( [
}

const clonedBlocks = blocks.map( ( block ) => cloneBlock( block ) );

clearSelectedBlock();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we shouldn't need to explicitly clear here. What's causing the new blocks to become selected? Is it insertBlocks ? Thinking the editor's reducer for tracking selection should be responding to the insertion, rather than needing to be explicit with clearing the block here.

@aduth
Copy link
Member

aduth commented Dec 14, 2018

I'll try to put together some proposal.

See #12882

@johngodley johngodley closed this Feb 5, 2019
@johngodley johngodley deleted the fix/duplicate-block-slot branch February 5, 2019 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Blocks Overall functionality of blocks [Package] Components /packages/components [Status] In Progress Tracking issues with work in progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Duplicating a custom HTML block can show two block toolbars
3 participants