From 6ded56eb653781108e71d992d078c1651f27bf91 Mon Sep 17 00:00:00 2001 From: Peter Amiri Date: Tue, 4 Aug 2026 15:29:34 -0700 Subject: [PATCH 1/2] fix(events): render debug bar restore button outside the container it un-hides (#3345) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The minimized 'Debug' restore button (##wdb-minimized) was nested inside the ##wheels-debugbar container, which wdbMinimize() hides with display:none — a descendant of a display:none element never renders, so the button could not appear and the bar stayed hidden for the whole browser session (sessionStorage re-minimizes on every load). Move the ##wdb-minimized block after the container's closing div, still inside the cfsavecontent so the whitespace-collapse ReReplace applies. It is independently position:fixed, so as a sibling it stays visible when the container is hidden. The debugbar.js script include moves below both elements because its load-time wdbMinimize() re-invocation does getElementById('wdb-minimized') and must find it in the DOM. New structural spec asserts id="wdb-minimized" appears only after the div balance for ##wheels-debugbar returns to zero (sibling, not descendant). Signed-off-by: Peter Amiri --- changelog.d/debugbar-restore-button.fixed.md | 1 + vendor/wheels/events/onrequestend/debug.cfm | 4 +- .../events/DebugBarMinimizedButtonSpec.cfc | 81 +++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 changelog.d/debugbar-restore-button.fixed.md create mode 100644 vendor/wheels/tests/specs/events/DebugBarMinimizedButtonSpec.cfc diff --git a/changelog.d/debugbar-restore-button.fixed.md b/changelog.d/debugbar-restore-button.fixed.md new file mode 100644 index 000000000..c341590f2 --- /dev/null +++ b/changelog.d/debugbar-restore-button.fixed.md @@ -0,0 +1 @@ +- Debug bar: the minimized "Debug" restore button now renders after clicking the X. The `#wdb-minimized` button was nested inside the `#wheels-debugbar` container that `wdbMinimize()` hides with `display:none`, so it could never appear and the bar stayed gone for the whole browser session (manual `sessionStorage` cleanup was the only recovery). It is now a sibling of the container, so minimizing shows the restore button bottom-right and clicking it brings the bar back ([#3345](https://github.com/wheels-dev/wheels/issues/3345)). diff --git a/vendor/wheels/events/onrequestend/debug.cfm b/vendor/wheels/events/onrequestend/debug.cfm index ad7b2c944..4585901fc 100644 --- a/vendor/wheels/events/onrequestend/debug.cfm +++ b/vendor/wheels/events/onrequestend/debug.cfm @@ -489,7 +489,10 @@ OR (StructKeyExists(url, "format") AND ListFindNoCase("json,xml,csv,pdf", url.fo + + + - #ReReplace(local.wdbHtml, "(?m)>\s+<", "><", "all")# diff --git a/vendor/wheels/tests/specs/events/DebugBarMinimizedButtonSpec.cfc b/vendor/wheels/tests/specs/events/DebugBarMinimizedButtonSpec.cfc new file mode 100644 index 000000000..d2d0baa57 --- /dev/null +++ b/vendor/wheels/tests/specs/events/DebugBarMinimizedButtonSpec.cfc @@ -0,0 +1,81 @@ +component extends="wheels.WheelsTest" { + + function run() { + describe("debug bar minimized restore button placement", () => { + // wdbMinimize() (public/assets/js/debugbar.js) sets #wheels-debugbar to + // display:none and #wdb-minimized to display:block. A descendant of a + // display:none element is never rendered regardless of its own display + // value, so the "Debug" restore button must be a SIBLING of the + // container, not a child — otherwise clicking the X hides the bar for + // the whole browser session with no visible recovery (issue #3345). + // This spec renders debug.cfm and asserts structurally that + // id="wdb-minimized" appears only after the div balance for + // #wheels-debugbar has returned to zero (sibling, not descendant). + it("renders the wdb-minimized button as a sibling of the debug bar container", () => { + var priorReqWheels = StructKeyExists(request, "wheels") ? Duplicate(request.wheels) : {}; + + try { + if (!StructKeyExists(request, "wheels")) { + request.wheels = {}; + } + request.wheels.execution = {total = 0}; + request.wheels.params = {controller = "wheels", action = "tests", route = ""}; + + // debug.cfm bails out (cfexit) when url.format is one of + // json/xml/csv/pdf so it never breaks an API response. The + // test runner is hit with format=json — clear it for the + // duration of the include so the template renders. + var hadUrlFormat = StructKeyExists(url, "format"); + var priorUrlFormat = hadUrlFormat ? url.format : ""; + if (hadUrlFormat) { + StructDelete(url, "format"); + } + + var output = ""; + try { + output = application.wo.$includeAndReturnOutput($template = "/wheels/events/onrequestend/debug.cfm"); + } finally { + if (hadUrlFormat) { + url.format = priorUrlFormat; + } + } + + var containerPos = FindNoCase('id="wheels-debugbar"', output); + expect(containerPos).toBeGT(0, "the ##wheels-debugbar container should render"); + + var minimizedPos = FindNoCase('id="wdb-minimized"', output); + expect(minimizedPos).toBeGT(0, "the ##wdb-minimized restore button should render"); + + // Walk
0) { + var nextOpen = FindNoCase(" 0 && nextOpen < nextClose) { + depth += 1; + pos = nextOpen; + } else { + depth -= 1; + pos = nextClose; + containerClosePos = nextClose; + } + } + + expect(minimizedPos).toBeGT( + containerClosePos, + "##wdb-minimized must render AFTER ##wheels-debugbar closes (sibling, not descendant) — " & + "nested inside the container, wdbMinimize()'s display:none makes the restore button unreachable (##3345)" + ); + } finally { + request.wheels = priorReqWheels; + } + }); + }); + } + +} From 46013319c7707f9028f1884a708b7bcb1ff324ee Mon Sep 17 00:00:00 2001 From: Peter Amiri Date: Tue, 4 Aug 2026 15:51:43 -0700 Subject: [PATCH 2/2] fix(events): isolate the relocated restore button from host-page CSS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As a sibling of ##wheels-debugbar the ##wdb-minimized button no longer sits under the container's all:initial isolation or its font stack, so the host app's global CSS bled in (font-family:inherit resolved to the app's body font — verified live: the demo app's Lato replaced the system stack). Mirror the container's guard on the sibling: all:initial first, then its own declarations plus the same font-family. The JS display toggling still wins because style.display updates in place after the all shorthand. Live-verified on Lucee 7: minimize/restore cycle, sessionStorage persistence across reload, button at bottom-right 8px/8px in the system font. Full core suite 4759 passed / 0 failed / 0 errors. Signed-off-by: Peter Amiri --- vendor/wheels/events/onrequestend/debug.cfm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/wheels/events/onrequestend/debug.cfm b/vendor/wheels/events/onrequestend/debug.cfm index 4585901fc..145339698 100644 --- a/vendor/wheels/events/onrequestend/debug.cfm +++ b/vendor/wheels/events/onrequestend/debug.cfm @@ -493,7 +493,7 @@ OR (StructKeyExists(url, "format") AND ListFindNoCase("json,xml,csv,pdf", url.fo -