fix(footer): restore soft-nav for Close (no page reload on shutdown)
Footer Close was using ``<a href="./close">`` which triggers a
browser hard-nav. That's a visible page-reload flash, websocket
churn, and slower shutdown than the previous sidebar Close —
which used ``st.navigation``'s soft nav.
Restore the soft-nav path:
- ``render_sticky_footer`` now renders a hidden ``st.page_link``
pointing at ``pages/99_Close.py``. Positioned off-screen via
CSS (``stElementContainer:has(a[data-testid=stPageLink]
[href$=/close])``) so it occupies no layout space but stays in
the DOM, reachable + clickable.
- Footer's Close <button> click handler now dispatches a
programmatic click on that hidden page_link. Streamlit's React
handler picks it up and runs the soft nav (same code path the
old sidebar entry used). Falls back to ``window.location.href``
if the helper link hasn't rendered yet so the button is never
a no-op.
- The page_link call is wrapped in try/except: ``AppTest`` doesn't
populate the page-nav session keys it needs and raises
``KeyError('url_pathname')``. Failure costs only the soft-nav
optimization — Close still works via the hard-nav fallback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -728,11 +728,59 @@ def render_sticky_footer() -> None:
|
|||||||
#datatools-help-popover .dt-help-dismiss:hover {
|
#datatools-help-popover .dt-help-dismiss:hover {
|
||||||
color: rgb(38, 39, 48) !important;
|
color: rgb(38, 39, 48) !important;
|
||||||
}
|
}
|
||||||
|
/* Hide the sticky-footer's helper st.page_link off-screen but
|
||||||
|
keep it in the DOM + clickable. The footer's Close button
|
||||||
|
dispatches a programmatic click on this link so navigation uses
|
||||||
|
Streamlit's soft nav (preserves the websocket, no visible page
|
||||||
|
reload) instead of the browser hard-nav an ``<a href="./close">``
|
||||||
|
would trigger. Off-screen (rather than ``display:none``) so
|
||||||
|
React event delegation works reliably across browsers. */
|
||||||
|
[data-testid="stElementContainer"]:has(a[data-testid="stPageLink"][href$="/close"]),
|
||||||
|
[data-testid="stElementContainer"]:has(a[data-testid="stPageLink"][href$="/close/"]) {
|
||||||
|
position: absolute !important;
|
||||||
|
left: -9999px !important;
|
||||||
|
top: -9999px !important;
|
||||||
|
width: 1px !important;
|
||||||
|
height: 1px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
opacity: 0 !important;
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
|
/* Defensive fallback for browsers without :has() — at least
|
||||||
|
shrink the inline page_link so it doesn't render a visible row. */
|
||||||
|
a[data-testid="stPageLink"][href$="/close"],
|
||||||
|
a[data-testid="stPageLink"][href$="/close/"] {
|
||||||
|
visibility: hidden !important;
|
||||||
|
height: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
""",
|
""",
|
||||||
unsafe_allow_html=True,
|
unsafe_allow_html=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Hidden Streamlit page_link to the close page. The footer's
|
||||||
|
# Close button programmatically clicks the anchor this renders,
|
||||||
|
# which triggers Streamlit's soft navigation (same code path
|
||||||
|
# the previous sidebar Close entry used). The link is positioned
|
||||||
|
# off-screen via the CSS above so it doesn't take page space
|
||||||
|
# but remains reachable to the JS click dispatch.
|
||||||
|
#
|
||||||
|
# Wrapped because ``st.page_link`` raises ``KeyError('url_pathname')``
|
||||||
|
# under ``AppTest`` (the test harness does not populate the page-nav
|
||||||
|
# session keys ``page_link`` needs to mark itself active/inactive).
|
||||||
|
# The JS click handler has a hard-nav fallback when this helper
|
||||||
|
# link isn't present, so a failure here only costs the soft-nav
|
||||||
|
# optimization — Close still works.
|
||||||
|
try:
|
||||||
|
st.page_link(
|
||||||
|
"pages/99_Close.py",
|
||||||
|
label=_t("footer.close"),
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
from streamlit.components.v1 import html as _components_html
|
from streamlit.components.v1 import html as _components_html
|
||||||
_components_html(
|
_components_html(
|
||||||
f"""
|
f"""
|
||||||
@@ -757,14 +805,32 @@ def render_sticky_footer() -> None:
|
|||||||
helpBtn.className = 'datatools-footer-btn help';
|
helpBtn.className = 'datatools-footer-btn help';
|
||||||
helpBtn.textContent = labels.help;
|
helpBtn.textContent = labels.help;
|
||||||
|
|
||||||
var closeLink = doc.createElement('a');
|
var closeBtn = doc.createElement('button');
|
||||||
closeLink.className = 'datatools-footer-btn close';
|
closeBtn.type = 'button';
|
||||||
closeLink.href = './close';
|
closeBtn.className = 'datatools-footer-btn close';
|
||||||
closeLink.target = '_self';
|
closeBtn.textContent = labels.close;
|
||||||
closeLink.textContent = labels.close;
|
// Soft-nav via the hidden ``st.page_link`` that
|
||||||
|
// ``render_sticky_footer`` injects. Streamlit owns its click
|
||||||
|
// handler and will route through ``st.switch_page`` (same
|
||||||
|
// code path the old sidebar Close entry used) — no full-page
|
||||||
|
// 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.
|
||||||
|
closeBtn.addEventListener('click', function (e) {{
|
||||||
|
e.preventDefault();
|
||||||
|
var helper = doc.querySelector(
|
||||||
|
'a[data-testid="stPageLink"][href$="/close"], ' +
|
||||||
|
'a[data-testid="stPageLink"][href$="/close/"]'
|
||||||
|
);
|
||||||
|
if (helper) {{
|
||||||
|
helper.click();
|
||||||
|
}} else {{
|
||||||
|
window.location.href = './close';
|
||||||
|
}}
|
||||||
|
}});
|
||||||
|
|
||||||
div.appendChild(helpBtn);
|
div.appendChild(helpBtn);
|
||||||
div.appendChild(closeLink);
|
div.appendChild(closeBtn);
|
||||||
|
|
||||||
var pop = doc.createElement('div');
|
var pop = doc.createElement('div');
|
||||||
pop.id = 'datatools-help-popover';
|
pop.id = 'datatools-help-popover';
|
||||||
|
|||||||
Reference in New Issue
Block a user