Repository navigation
fix(uve): scope stopPropagation to clicks that resolve to a link - #37972
gortiz-dotcms wants to merge 1 commit into
Conversation
QA verification on #37961 failed this PR: the navigation fix worked, but stopping propagation for every unrecognized click — not just link clicks — broke page-owned, non-navigating UI inside the editor. Confirmed with a real Usercentrics cookie-consent dialog: its Accept/Reject buttons stopped responding, blocking the contentlets beneath it from being edited at all. Root cause of the original bug (per QA's repro): a page's own unrelated bubble-phase code (e.g. a sidebar's `onclick="event.stopPropagation()"`) could stop a click from ever reaching the old bubble-phase listener on `window`, so preventDefault() never ran and the anchor's native navigation went through unimpeded. The capture-phase listener added earlier in this PR already fixes that on its own — preventDefault() called during capture blocks the native action regardless of what any later node does with the event. The *unconditional* stopPropagation() was never required for that case; it was added for a narrower one (a page script delegating click-to-navigate on a wrapper around a `pointer-events: none` anchor, with no real `<a>` at the click target), which resolveClickedAnchor's descendant fallback already classifies as `hasLink: true`. Scope stopPropagation() to exactly that: clicks that resolve to a link. Everything else — cookie-consent dialogs, accordions, tabs, carousels — now reaches the page's own handlers exactly as it did before this PR existed. Verified live against the real running editor (not just unit tests): - QA's exact root-cause repro (ancestor stopPropagation + real link) still navigates via the SPA, native navigation still prevented. - The reported regression (a simulated consent dialog's Accept button) now correctly dismisses. - The pointer-events:none delegate-wrapper pattern is still protected (stopPropagation still fires, the delegate router never sees it). - Contentlet selection still works on a fresh page load. Updated the five capture-phase stopPropagation tests: the four exemption tests now use a link embedded in each target type (block editor, WYSIWYG, TinyMCE toolbar, contentlet selection), since hasLink is now a prerequisite for stopping at all — without an embedded link they'd pass vacuously and stop catching a regression if the exemption logic were ever removed. Added a positive case (plain link click DOES stop) alongside the QA regression case (non-link click does NOT stop).
|
Claude finished @gortiz-dotcms's task in 1m 23s —— View job Code Review — scope
|
Summary
Follow-up to #37962 (merged as
baeaf675c268). That PR's final commit — scopingstopPropagation()to clicks that resolve to a link, to fix the QA-reported regression where a cookie-consent dialog's own buttons stopped responding inside the editor — was pushed to the branch after #37962 had already been squash-merged, so it never made it intomain/trunk. The QA-reported regression is still live right now. This PR carries exactly that one fix forward.What this fixes
QA verification on #37961 (comment on the issue, 2026-10-09) returned a failed verdict against the version of #37962 that had just merged: the navigation fix worked, but stopping propagation for every click the editor didn't recognize — not just navigation clicks — broke page-owned, non-navigating UI. Repro'd with a real Usercentrics cookie-consent dialog: its Accept/Reject buttons stopped responding, blocking the contentlets beneath it from being edited at all.
Root cause of the original bug, per QA's own repro: a page's own unrelated bubble-phase code (their fixture: a sidebar's
onclick="event.stopPropagation()") could stop a click from ever reaching the old bubble-phase listener onwindow, sopreventDefault()never ran and the anchor's native navigation went through unimpeded. The capture-phase listener #37962 already added fixes that on its own —preventDefault()called during capture blocks the native action regardless of what any later node does with the event afterward. The unconditionalstopPropagation()was never actually required for that case; it was added for a narrower one (a page script delegating click-to-navigate on a wrapper around apointer-events: noneanchor, with no real<a>at the click target), whichresolveClickedAnchor's descendant fallback already classifies ashasLink: true.The fix: scope
stopPropagation()to exactly that — clicks that resolve to a link. Everything else (cookie-consent dialogs, accordions, tabs, carousels) now reaches the page's own handlers exactly as it did before #37962 existed.Testing
Unit tests: updated the five capture-phase
stopPropagationtests indot-uve-iframe.component.spec.ts. The four exemption tests (block editor, WYSIWYG, TinyMCE toolbar, contentlet selection) now embed a link in each target, sincehasLinkis now a prerequisite for stopping at all — without an embedded link they'd pass vacuously and stop catching a regression if the exemption logic were ever removed. Added a positive case (plain link click still stops) alongside the regression case (non-link click no longer stops). 451 tests passing, 3 skipped, across the three affected spec files.Live, against a real running editor:
onclick="event.stopPropagation()"wrapping a real link) — still navigates via the SPA, native navigation still correctly prevented.pointer-events: nonedelegate-wrapper patternresolveClickedAnchorwas built for — still protected; the page's own delegate router never sees that click.Fixes #37961
🤖 Generated with Claude Code