Problem/Motivation
The Canvas renders real component markup. When a slot holds markup its parent element cannot contain, the browser rewrites the page while parsing it, and the builder stops working.
Seen with a UI Suite Bootstrap Button placed in the label slot of another Button. A button cannot hold a button, so the parser closes the outer one early. The closing tags after it no longer match, and elements rendered after the panels fall out of the builder root, the contextual menu and the message stack among them.
Measured on a pattern preset builder:
- The server HTML nests two buttons in the Canvas. The live DOM nests none: the inner button is a sibling of the outer one.
.display-builder__menuand.db-messagesare children of.dialog-off-canvas-main-canvas, not ofsection.display-builder. On a builder without nested buttons both are inside it.
Consequences:
- A contextual menu action leaves without
node_id,parent_idandslot_id.menuAlterHtmxEvents()listens forhtmx:configRequeston the builder root, which the menu is no longer in. The action answers "This change cannot be applied" since #3626659: Paste, duplicate and preset insert end in a server error when the target is full or gone, and a server error before it. - The Canvas no longer shows the tree: the inner button looks like a sibling of the outer one.
- Right after the drop, before any reload, the swapped markup is parsed the same way and the Canvas looks wrong.
The way in is one click today: Duplicate on a root Button, #3627000: Duplicate from the contextual menu puts a root component inside its own first slot. Dropping a Button into a Button's slot by hand does the same.
Steps to reproduce
- With UI Suite Bootstrap, drop a Button at the root of a builder.
- Drop a second Button into its Label slot.
- Reload the page.
- Right-click a node and choose Duplicate: it is refused, and nothing on screen says why.
Proposed resolution
To decide. The options, which are not exclusive:
- Keep the builder working whatever the Canvas holds. Render the menu, the message stack and the summary before the panels in
display_builder.twig, so a misplaced closing tag cannot take them out of the root. Cheap, and it does not fix the Canvas. - Or make the menu listener independent of where the menu sits: listen on the document and match the builder by its ID.
- Tell the user. After a render, compare the Canvas with the tree and flag a node whose DOM parent is not its tree parent, pointing to Scaffold, which draws cards instead of real markup.
Refusing the drop is not an option in general: the builder does not know which element a component renders its slot in.
Remaining tasks
- Pick among the options above.
- Check the other pairs the HTML parser rewrites: a link in a link, a form in a form, a block element in a paragraph.
- A test that keeps the contextual menu working with a Button in a Button.
User interface changes
Depends on the option.
API changes
None.
Data model changes
None.
Issue fork display_builder-3627001
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 #4
mogtofu33 commented