diff --git a/src/visualBuilder/utils/__test__/getCsDataOfElement.test.ts b/src/visualBuilder/utils/__test__/getCsDataOfElement.test.ts index 91b5c471..42c01485 100644 --- a/src/visualBuilder/utils/__test__/getCsDataOfElement.test.ts +++ b/src/visualBuilder/utils/__test__/getCsDataOfElement.test.ts @@ -196,6 +196,38 @@ describe("getCsDataOfElement", () => { }); }); + test("does not pierce the visual builder's own UI (e.g. the field toolbar) even when fallback is enabled", () => { + Config.set("overlayPropagation", { enable: true }); + + // clicks on the SDK's own toolbar land on elements inside the + // visual builder container and must never resolve to the canvas + // field underneath + const vbContainer = document.createElement("div"); + vbContainer.classList.add("visual-builder__container"); + const toolbarButton = document.createElement("button"); + vbContainer.appendChild(toolbarButton); + document.body.appendChild(vbContainer); + + const toolbarClick = new MouseEvent("click", { + bubbles: true, + cancelable: true, + clientX: 100, + clientY: 100, + }); + Object.defineProperty(toolbarClick, "target", { + value: toolbarButton, + writable: false, + }); + ( + document.elementsFromPoint as ReturnType + ).mockReturnValue([toolbarButton, vbContainer, cslpEl]); + + const result = getCsDataOfElement(toolbarClick); + + expect(result).toBeUndefined(); + expect(document.elementsFromPoint).not.toHaveBeenCalled(); + }); + test("returns undefined when fallback is enabled but no element under the cursor has data-cslp", () => { Config.set("overlayPropagation", { enable: true }); const otherBlocker = document.createElement("div"); diff --git a/src/visualBuilder/utils/getCsDataOfElement.ts b/src/visualBuilder/utils/getCsDataOfElement.ts index 50cc76ca..220fd937 100644 --- a/src/visualBuilder/utils/getCsDataOfElement.ts +++ b/src/visualBuilder/utils/getCsDataOfElement.ts @@ -28,7 +28,18 @@ export function getCsDataOfElement( let editableElement: Element | null = targetElement.closest("[data-cslp]"); - if (!editableElement && Config.get().overlayPropagation.enable) { + // Clicks on the visual builder's own UI (field toolbar, overlays, add + // buttons) must never resolve to the canvas field underneath them. The + // elementsFromPoint fallback exists to pierce the website's own empty + // overlapping elements, not the SDK's chrome: piercing the toolbar made + // the edit (pencil) button double as a click on the field it covered. + // The closest() check runs last so hover events only pay for it when + // the fallback is enabled and no field matched. + if ( + !editableElement && + Config.get().overlayPropagation.enable && + !targetElement.closest(".visual-builder__container") + ) { const stack = document.elementsFromPoint( event.clientX, event.clientY