test(ui): pin the collapse/drawer/popup-menu behavior + breakpoint pair

Review follow-ups: the new rail collapse and mobile drawer had no
committed guards (the repo pattern is per-step string assertions in
test_shell_js.py) and openPopupMenu — now load-bearing for both the
tab dropdown and the footer user menu — was unpinned.

- test_rail_collapse_glyph_strip: persistence key, toggle +
  aria-controls, class-flip seam, cpill-label/manage-glyph companions,
  52px desktop-scoped CSS.
- test_mobile_drawer_off_canvas: burger, scrim, rail-open flip,
  pane-activation auto-close, off-canvas translateX + visibility:hidden.
- test_popup_menu_shared_helper: the export + both consumers (the user
  menu's prefer-up path included).
- shell.css: the 769/768 media blocks are a matched pair CSS cannot
  express as a shared token — both now carry a cross-referencing
  change-both comment.
This commit is contained in:
Patrick Buckley
2026-06-09 13:10:31 -07:00
parent 1d2046b5bb
commit 539ed3ccbf
2 changed files with 88 additions and 0 deletions
+81
View File
@@ -778,3 +778,84 @@ def test_coordinator_pane_reconnects_on_reopen() -> None:
"reconnect must only act on a missing/CLOSED stream"
)
assert "reconnect: reconnect," in coord, "the factory must return reconnect"
# ---------------------------------------------------------------------------
# Polish round: rail collapse + mobile drawer + the shared popup-menu helper.
# ---------------------------------------------------------------------------
def test_rail_collapse_glyph_strip() -> None:
"""Desktop rail collapse: a persisted preference (localStorage
``turnstone_interface.rail``) shrinks the rail to a 52px glyph-only strip —
live state glyphs stay the navigation, the cluster pills keep glyph+count
(the label rides a hideable span), Manage gets a single gear stand-in that
opens the Admin pane, and the toggle mirrors its state through
aria-expanded/aria-controls."""
shell = _SHELL_JS.read_text(encoding="utf-8")
assert 'const RAIL_COLLAPSE_KEY = "turnstone_interface.rail"' in shell, (
"the collapse preference must persist under the turnstone_interface key"
)
assert 'make("button", "rail-collapse")' in shell, "the collapse toggle must exist"
assert 'collapseBtn.setAttribute("aria-controls", "shell-rail")' in shell, (
"the toggle must reference the rail it controls"
)
assert 'classList.toggle("rail-collapsed", collapsed)' in shell, (
"collapse must be a class flip on .app (CSS owns the layout change)"
)
rail = _RAIL_JS.read_text(encoding="utf-8")
assert '"cpill-label"' in rail, (
"cluster pill labels must ride a span so the collapsed strip can hide "
"them while keeping glyph + count"
)
assert "manage-glyph" in rail, (
"Manage needs its collapsed-strip gear stand-in (rail.js mountManage)"
)
css = _SHELL_CSS.read_text(encoding="utf-8")
assert "grid-template-columns: 52px 1fr" in css, "the collapsed rail is 52px"
assert ".app.rail-collapsed .manage-glyph" in css, (
"the gear stand-in must flip on while collapsed"
)
assert "@media (min-width: 769px)" in css, (
"the collapse block must be desktop-scoped (the drawer owns mobile)"
)
def test_mobile_drawer_off_canvas() -> None:
"""Mobile drawer: below the breakpoint the rail overlays off-canvas at full
width. Burger in the tab bar opens it (focus moves into the rail);
Escape / scrim tap / any pane activation close it; the closed drawer is
visibility:hidden so its buttons leave the Tab order."""
shell = _SHELL_JS.read_text(encoding="utf-8")
assert 'make("button", "rail-burger"' in shell, "the drawer toggle must exist"
assert 'make("div", "rail-scrim")' in shell, "the backdrop scrim must exist"
assert 'classList.toggle("rail-open", open)' in shell, (
"drawer state must be a class flip on .app"
)
assert "pm.onActiveChange(() => setDrawer(false))" in shell, (
"opening/focusing a pane must close the drawer that did it"
)
css = _SHELL_CSS.read_text(encoding="utf-8")
assert "@media (max-width: 768px)" in css, "the drawer is mobile-scoped"
assert "translateX(-100%)" in css, "the closed drawer parks off-canvas"
assert "visibility: hidden" in css, (
"the closed drawer must leave the Tab order / a11y tree, not merely "
"translate off-screen"
)
def test_popup_menu_shared_helper() -> None:
"""One popup-menu chrome: pane.js exports openPopupMenu (items,
positioning with flip+clamp, dismissal, aria-expanded mirroring, arrow
roving) and BOTH consumers ride it — the tab-action dropdown and the
shell's footer user menu (which pops up from the viewport-bottom chip)."""
pane = _PANE_JS.read_text(encoding="utf-8")
assert "export function openPopupMenu(" in pane, "the shared helper must exist"
assert pane.count("openPopupMenu(") >= 2, (
"the tab-action dropdown must route through the helper"
)
shell = _SHELL_JS.read_text(encoding="utf-8")
assert "openPopupMenu(" in shell, "the user menu must ride the shared helper"
assert 'prefer: "up"' in shell, (
"the footer chip sits at the viewport bottom — the menu pops upward"
)
+7
View File
@@ -960,6 +960,10 @@
stacked cluster glyph+count pills, one ⚙ for Manage. Hover names come from
the title attrs rail.js always sets. Desktop-only: below the drawer
breakpoint the rail overlays at full width, so the preference lies dormant.
769 = the drawer's 768 breakpoint + 1 — a matched pair (CSS @media cannot
read a shared token). Change BOTH together or a 1px band gets neither
(or both) layouts.
========================================================================== */
@media (min-width: 769px) {
.app.rail-collapsed {
@@ -1078,6 +1082,9 @@
☰ in the tab bar opens it; scrim tap / Escape / opening a pane close it.
The closed drawer is visibility:hidden so its buttons drop out of the Tab
order and the a11y tree — not merely translated off-screen.
768 pairs with the collapse block's min-width: 769px above — change BOTH
together (see the note there).
========================================================================== */
@media (max-width: 768px) {
.app,