From add3b866ee806dea3e383591a640e8c77459fd81 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 18 May 2026 16:02:46 +0000 Subject: [PATCH] =?UTF-8?q?fix(footer):=20Close=20button=20now=20actually?= =?UTF-8?q?=20fires=20=E2=80=94=20wrong=20testid=20+=20bad=20fallback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs combined to make the footer Close a no-op: 1. The helper page_link's anchor carries ``data-testid="stPageLink-NavLink"`` — the bare ``stPageLink`` testid is on the OUTER WRAPPER div, not the anchor. The old selector ``a[data-testid="stPageLink"]`` matched nothing, so ``helper`` was always ``null``. 2. The fallback ``window.location.href = './close'`` ran inside the component iframe, so it only navigated the (invisible) srcdoc iframe. The main app stayed put. End result: click → nothing visible → shutdown_app never runs → farewell-script's ``window.close()`` attempt never happens → user sees the Close button as broken. Fixes: - Selector → ``a[data-testid="stPageLink-NavLink"][href*="close"]``. ``href*="close"`` covers both root (/close) and base-path (/myapp/close) deployments. - Fallback → resolve the parent window via ``doc.defaultView`` (the parent doc's window) with a ``window.top`` fallback, so the hard-nav navigates the whole app instead of just the iframe. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/gui/components/_legacy.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/gui/components/_legacy.py b/src/gui/components/_legacy.py index a23c2f5..88b81e1 100644 --- a/src/gui/components/_legacy.py +++ b/src/gui/components/_legacy.py @@ -815,17 +815,29 @@ a[data-testid="stPageLink"][href$="/close/"] { // reload, no websocket churn. Fall back to a hard nav if the // helper link hasn't rendered yet (first paint race) so the // button is never a no-op. + // + // The page_link's anchor uses ``data-testid="stPageLink-NavLink"`` + // (the outer wrapper div carries the bare ``stPageLink`` testid; + // dispatching click on the wrapper doesn't fire Streamlit's + // React onClick handler). ``href*="close"`` covers both root + // (/close) and base-path (e.g. /myapp/close) deployments. closeBtn.addEventListener('click', function (e) {{ e.preventDefault(); var helper = doc.querySelector( - 'a[data-testid="stPageLink"][href$="/close"], ' + - 'a[data-testid="stPageLink"][href$="/close/"]' + 'a[data-testid="stPageLink-NavLink"][href*="close"]' ); if (helper) {{ helper.click(); - }} else {{ - window.location.href = './close'; + return; }} + // Hard-nav fallback. ``window`` inside this script is the + // component iframe's window — changing ITS location only + // navigates the iframe (which lives in srcdoc and is + // invisible). Use the parent doc's location so the whole + // app navigates. + var topWin = (doc.defaultView) || window.parent || window.top || window; + try {{ topWin.location.href = './close'; }} + catch (err) {{ window.top.location.href = './close'; }} }}); div.appendChild(helpBtn);