Problem/Motivation
Right-click a component that sits at the root and has a slot, then choose Duplicate: the copy lands inside the component's own first slot, not next to it.
The contextual menu works out where a paste or a duplicate lands with ContextualMenu.getSlotData(). Its third case, "the target is a container with its own slot", exists for Paste: it offers that slot as the destination. Duplicate reuses the same result. A root node has no parent, so data-slot-node-id falls back to the node itself, and the request names the node as its own parent.
The keyboard shortcut is right. For mod+d, keyboard.js takes the closest slot wrapper around the node, and the root when there is none. The two paths disagree.
ApiActionsController::paste() already carries a @todo about it. The "Contextual duplicate and remove" Playwright test does not see it: it counts components at any depth, and a copy nested in the slot still makes two.
With a Button component the copy is a button inside a button, which breaks the builder page: #3627001: Invalid HTML nesting in the Canvas takes the contextual menu and the messages out of the builder.
Steps to reproduce
- Open a builder and drop a component with a slot at the root.
- Right-click it and choose Duplicate.
- Open the Navigator: the copy is a child of the original.
Seen on a pattern preset builder whose root is a UI Suite Bootstrap Button.
Proposed resolution
- Duplicate resolves its target as
mod+ddoes: the slot the node lives in, or the root when it has none. The third case ofgetSlotData()stays for Paste. - The menu and the shortcut share one resolution, so they cannot disagree again.
- The Playwright test asserts the copy is a sibling: two components at the root, none nested.
In a pattern preset builder the root takes one node. Duplicating its root then answers "There is no room left here", from #3626659: Paste, duplicate and preset insert end in a server error when the target is full or gone.
Remaining tasks
At the root, paste() attaches at position 0 whatever position it is sent, so a root copy lands first, not after the original. Decide whether that is fixed here or in a follow-up.
User interface changes
The copy appears next to the original.
API changes
None.
Data model changes
None.
Issue fork display_builder-3627000
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
mogtofu33 commentedComment #5
mogtofu33 commented