fix(desk): a row's keep, release and wipe take no room of their own
Operator, on the live Desk: "release and x take up space whether or not they're visible." They sat in a side column at opacity 0, which hides a control and still reserves its box, and hover-only never worked on touch. Each control now sits on the facts line beside the state it changes: release after "kept", keep after a countdown or hold, wipe last. They are always visible and quiet, and wipe turns danger only under the pointer or focus. The side column renders only when the row carries a badge. The row is flex, so an absent column costs no gap. Forms, POST targets and data-confirm wording are unchanged. The flex row exposed a latent sizing bug: the stacked Desk column was a bare 1fr, whose minimum is its content's, so a long nowrap provenance line scrolled the page sideways at phone width (1029px at 390). It is now minmax(0,1fr). Both behaviours have browser tests, mutation-proved (r2_flow.toml: 21/21). Contract C4 amended.
This commit is contained in:
+28
-21
@@ -162,7 +162,10 @@
|
|||||||
"Needs you" rows carry an amber inner edge — the one thing on the page
|
"Needs you" rows carry an amber inner edge — the one thing on the page
|
||||||
that asks to be looked at. */
|
that asks to be looked at. */
|
||||||
.desk{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:var(--space-6);align-items:start}
|
.desk{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:var(--space-6);align-items:start}
|
||||||
@media (max-width:1000px){.desk{grid-template-columns:1fr}}
|
/* minmax(0,…), never a bare 1fr: 1fr's minimum is the content's, and a
|
||||||
|
Desk row's nowrap provenance line would then set the column's width —
|
||||||
|
the page scrolled sideways on a phone. */
|
||||||
|
@media (max-width:1000px){.desk{grid-template-columns:minmax(0,1fr)}}
|
||||||
.desk-sec{margin:0 0 var(--space-6)}
|
.desk-sec{margin:0 0 var(--space-6)}
|
||||||
.desk-head{display:flex;align-items:center;gap:10px;margin:0 0 10px;font-family:var(--font-mono);
|
.desk-head{display:flex;align-items:center;gap:10px;margin:0 0 10px;font-family:var(--font-mono);
|
||||||
font-size:var(--size-micro);font-weight:500;letter-spacing:var(--tracking-caps);text-transform:uppercase;
|
font-size:var(--size-micro);font-weight:500;letter-spacing:var(--tracking-caps);text-transform:uppercase;
|
||||||
@@ -171,19 +174,19 @@
|
|||||||
.desk-head-needs{color:var(--warning-text)}
|
.desk-head-needs{color:var(--warning-text)}
|
||||||
.desk-head-new{color:var(--intel-text)}
|
.desk-head-new{color:var(--intel-text)}
|
||||||
.desk-rule{font-weight:400;letter-spacing:0;text-transform:none;color:var(--text-muted)}
|
.desk-rule{font-weight:400;letter-spacing:0;text-transform:none;color:var(--text-muted)}
|
||||||
.desk-row{display:grid;grid-template-columns:210px minmax(0,1fr) auto;gap:var(--space-4);align-items:center;
|
.desk-row{display:flex;gap:var(--space-4);align-items:center;
|
||||||
padding:12px;margin-bottom:8px;border:1px solid var(--border-default);border-radius:var(--radius-xl);
|
padding:12px;margin-bottom:8px;border:1px solid var(--border-default);border-radius:var(--radius-xl);
|
||||||
background:var(--surface-card);box-shadow:var(--shadow-sm);
|
background:var(--surface-card);box-shadow:var(--shadow-sm);
|
||||||
transition:border-color var(--dur-2) var(--ease-out)}
|
transition:border-color var(--dur-2) var(--ease-out)}
|
||||||
.desk-row:hover,.desk-row:focus-within{border-color:var(--border-strong)}
|
.desk-row:hover,.desk-row:focus-within{border-color:var(--border-strong)}
|
||||||
.desk-row.is-needs{box-shadow:inset 3px 0 0 var(--warning),var(--shadow-sm)}
|
.desk-row.is-needs{box-shadow:inset 3px 0 0 var(--warning),var(--shadow-sm)}
|
||||||
.desk-strip{display:grid;grid-template-columns:repeat(4,1fr);gap:3px;height:58px;border-radius:var(--radius-md);
|
.desk-strip{flex:0 0 210px;display:grid;grid-template-columns:repeat(4,1fr);gap:3px;height:58px;border-radius:var(--radius-md);
|
||||||
overflow:hidden;background:var(--surface-sunken)}
|
overflow:hidden;background:var(--surface-sunken)}
|
||||||
.desk-strip img{width:100%;height:100%;object-fit:cover;display:block}
|
.desk-strip img{width:100%;height:100%;object-fit:cover;display:block}
|
||||||
.desk-strip .ph{grid-column:1/-1;display:flex;align-items:center;justify-content:center;font-family:var(--font-mono);
|
.desk-strip .ph{grid-column:1/-1;display:flex;align-items:center;justify-content:center;font-family:var(--font-mono);
|
||||||
font-size:var(--size-caption);letter-spacing:var(--tracking-caps);text-transform:uppercase;color:var(--text-muted)}
|
font-size:var(--size-caption);letter-spacing:var(--tracking-caps);text-transform:uppercase;color:var(--text-muted)}
|
||||||
.desk-strip:hover{text-decoration:none}
|
.desk-strip:hover{text-decoration:none}
|
||||||
.desk-main{min-width:0}
|
.desk-main{flex:1 1 auto;min-width:0}
|
||||||
.desk-title{display:block;font-weight:600;font-size:var(--size-h3);line-height:1.3;color:var(--text-heading);
|
.desk-title{display:block;font-weight:600;font-size:var(--size-h3);line-height:1.3;color:var(--text-heading);
|
||||||
overflow-wrap:anywhere}
|
overflow-wrap:anywhere}
|
||||||
.desk-title:hover{color:var(--text-link);text-decoration:none}
|
.desk-title:hover{color:var(--text-link);text-decoration:none}
|
||||||
@@ -191,25 +194,28 @@
|
|||||||
.desk-main .prov{margin-top:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
.desk-main .prov{margin-top:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||||
.desk-facts{margin-top:5px;font-family:var(--font-mono);font-size:var(--size-caption);color:var(--text-muted)}
|
.desk-facts{margin-top:5px;font-family:var(--font-mono);font-size:var(--size-caption);color:var(--text-muted)}
|
||||||
.desk-flags{color:var(--success-text)}
|
.desk-flags{color:var(--success-text)}
|
||||||
.desk-side{display:flex;flex-direction:column;align-items:flex-end;gap:8px}
|
.desk-side{flex:none;display:flex;flex-direction:column;align-items:flex-end;gap:8px}
|
||||||
.badge-new{color:var(--intel-text);background:var(--intel-soft)}
|
.badge-new{color:var(--intel-text);background:var(--intel-soft)}
|
||||||
.badge-broken{color:var(--danger-text);background:var(--danger-soft);border-color:color-mix(in oklab,var(--danger) 40%,transparent)}
|
.badge-broken{color:var(--danger-text);background:var(--danger-soft);border-color:color-mix(in oklab,var(--danger) 40%,transparent)}
|
||||||
/* The row's keep / release / × — quiet until the row is hovered or focused,
|
/* The row's keep / release / wipe, inline on the facts line beside the
|
||||||
because a destructive control must not compete with the thing you came to
|
state each one changes. Quiet — muted mono like the line they sit in, so
|
||||||
read. Static here: on the old cards they floated over a thumbnail. */
|
a destructive control does not compete with the thing you came to read —
|
||||||
.desk-acts{display:flex;gap:6px;opacity:0;transition:opacity var(--dur-1)}
|
but always there and always tappable; wipe turns danger only under the
|
||||||
.desk-row:hover .desk-acts,.desk-acts:focus-within{opacity:1}
|
pointer or focus. */
|
||||||
.desk-acts form{margin:0}
|
.desk-facts form{display:inline;margin:0}
|
||||||
.desk-acts button{height:28px;min-width:28px;padding:0 8px;font-size:var(--size-sm);border-radius:var(--radius-md);
|
.desk-facts form button{display:inline-block;margin-left:4px;padding:1px 6px;height:auto;min-width:0;
|
||||||
border:1px solid var(--border-strong);background:var(--surface-raised);color:var(--text-body);
|
font:inherit;line-height:1.4;color:var(--text-muted);background:none;
|
||||||
|
border:1px solid var(--border-default);border-radius:var(--radius-sm);
|
||||||
-webkit-backdrop-filter:none;backdrop-filter:none;cursor:pointer}
|
-webkit-backdrop-filter:none;backdrop-filter:none;cursor:pointer}
|
||||||
.desk-acts .release button{font-family:var(--font-mono);font-size:var(--size-caption)}
|
.desk-facts .wipe button{margin-left:0}
|
||||||
.desk-acts .wipe button:hover{background:var(--danger);border-color:var(--danger);color:var(--danger-contrast)}
|
.desk-facts form button:hover,.desk-facts form button:focus-visible{color:var(--text-body);
|
||||||
.desk-acts .keepit button:hover,.desk-acts .release button:hover{background:var(--surface-overlay)}
|
border-color:var(--border-strong);background:var(--surface-overlay)}
|
||||||
|
.desk-facts .wipe button:hover,.desk-facts .wipe button:focus-visible{background:var(--danger);
|
||||||
|
border-color:var(--danger);color:var(--danger-contrast)}
|
||||||
@media (max-width:700px){
|
@media (max-width:700px){
|
||||||
.desk-row{grid-template-columns:1fr}
|
.desk-row{flex-wrap:wrap}
|
||||||
.desk-side{flex-direction:row;align-items:center;justify-content:space-between}
|
.desk-strip{flex-basis:100%}
|
||||||
.desk-acts{opacity:1}
|
.desk-side{flex-basis:100%;flex-direction:row;align-items:center}
|
||||||
}
|
}
|
||||||
/* the side column */
|
/* the side column */
|
||||||
.desk-panel{border:1px solid var(--border-default);border-radius:var(--radius-xl);background:var(--surface-card);
|
.desk-panel{border:1px solid var(--border-default);border-radius:var(--radius-xl);background:var(--surface-card);
|
||||||
@@ -252,8 +258,9 @@
|
|||||||
.prov-broken{color:var(--warning-text);font-style:italic;cursor:help}
|
.prov-broken{color:var(--warning-text);font-style:italic;cursor:help}
|
||||||
|
|
||||||
/* keep / release / wipe — the controls themselves. Where they sit is the
|
/* keep / release / wipe — the controls themselves. Where they sit is the
|
||||||
surface's business: a Desk row (.desk-acts) or the booth header (.wipe-lg,
|
surface's business: a Desk row's facts line (.desk-facts) or the booth
|
||||||
.keep-lg). The kept/ephemeral CARDS they used to float over are gone. */
|
header (.wipe-lg, .keep-lg). The kept/ephemeral CARDS they used to float
|
||||||
|
over are gone. */
|
||||||
.wipe,.keepit,.release{margin:0}
|
.wipe,.keepit,.release{margin:0}
|
||||||
.wipe button,.keepit button,.release button{cursor:pointer;font:inherit;line-height:1;
|
.wipe button,.keepit button,.release button{cursor:pointer;font:inherit;line-height:1;
|
||||||
transition:background var(--dur-1) var(--ease-out),border-color var(--dur-1) var(--ease-out),color var(--dur-1) var(--ease-out)}
|
transition:background var(--dur-1) var(--ease-out),border-color var(--dur-1) var(--ease-out),color var(--dur-1) var(--ease-out)}
|
||||||
|
|||||||
+25
-24
@@ -36,43 +36,44 @@
|
|||||||
{%- else -%}{{ b.name }}{%- endif -%}
|
{%- else -%}{{ b.name }}{%- endif -%}
|
||||||
</a>
|
</a>
|
||||||
{{ provenance(b.manifest) }}
|
{{ provenance(b.manifest) }}
|
||||||
|
{# The row's controls sit ON the facts line, each beside the state it
|
||||||
|
changes: release after "kept", keep after the countdown, wipe last.
|
||||||
|
They were a hover-revealed column that reserved its room while
|
||||||
|
invisible (operator, on the live Desk: "release and x take up space
|
||||||
|
whether or not they're visible") — and hover never existed on touch.
|
||||||
|
The confirmation text is DATA-DRIVEN: the booth name travels as a data
|
||||||
|
attribute and never reaches a JS string (see the script below).
|
||||||
|
Release is reversible, so it has no prompt beyond the wording. #}
|
||||||
<div class="desk-facts">
|
<div class="desk-facts">
|
||||||
{{ b.count }} item{{ '' if b.count == 1 else 's' }}
|
{{ b.count }} item{{ '' if b.count == 1 else 's' }}
|
||||||
{% if b.flags %} · <span class="desk-flags">{{ b.flags }} flagged</span>{% endif %}
|
{% if b.flags %} · <span class="desk-flags">{{ b.flags }} flagged</span>{% endif %}
|
||||||
· {{ lifetime(b.kept, b.hold, b.expires_in) }}
|
· {{ lifetime(b.kept, b.hold, b.expires_in) }}
|
||||||
|
{%- if b.kept %}
|
||||||
|
<form class="release" method="post" action="/b/{{ b.name_url }}/unkeep"
|
||||||
|
data-booth="{{ b.name }}" data-confirm="release"><button title="release this booth so it can be wiped">release</button></form>
|
||||||
|
{%- else %}
|
||||||
|
<form class="keepit" method="post" action="/b/{{ b.name_url }}/keep"><button title="keep — exempt from the {{ ttl_hours }}h sweep" aria-label="keep booth">★ keep</button></form>
|
||||||
|
{%- endif %}
|
||||||
· <a class="dl-link" href="/b/{{ b.name_url }}/?download=1" title="download this booth as a zip">⬇ zip</a>
|
· <a class="dl-link" href="/b/{{ b.name_url }}/?download=1" title="download this booth as a zip">⬇ zip</a>
|
||||||
|
{%- if b.kept %}
|
||||||
|
· <form class="wipe wipe-kept" method="post" action="/b/{{ b.name_url }}/delete"
|
||||||
|
data-booth="{{ b.name }}" data-confirm="wipe-kept"><button title="wipe this KEPT booth now" aria-label="wipe kept booth">× wipe</button></form>
|
||||||
|
{%- else %}
|
||||||
|
· <form class="wipe" method="post" action="/b/{{ b.name_url }}/delete"
|
||||||
|
data-booth="{{ b.name }}" data-confirm="wipe"><button title="wipe now" aria-label="wipe booth">× wipe</button></form>
|
||||||
|
{%- endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{# Badges only: a row with none renders no side column, so it reserves
|
||||||
|
no room (the row is flex — an absent item costs no gap). #}
|
||||||
|
{% if b.marks_open or b.hold == "unreadable" or section == 'new' or b.uploaded %}
|
||||||
<div class="desk-side">
|
<div class="desk-side">
|
||||||
{% if b.marks_open %}<span class="badge badge-mark">? {{ b.marks_open }} open</span>
|
{% if b.marks_open %}<span class="badge badge-mark">? {{ b.marks_open }} open</span>
|
||||||
{% elif b.hold == "unreadable" %}<span class="badge badge-broken">marks unreadable</span>
|
{% elif b.hold == "unreadable" %}<span class="badge badge-broken">marks unreadable</span>
|
||||||
{% elif section == 'new' %}<span class="badge badge-new">new</span>{% endif %}
|
{% elif section == 'new' %}<span class="badge badge-new">new</span>{% endif %}
|
||||||
{% if b.uploaded %}<span class="badge">⬆ pickup</span>{% endif %}
|
{% if b.uploaded %}<span class="badge">⬆ pickup</span>{% endif %}
|
||||||
<div class="desk-acts">
|
|
||||||
{# Keep / release and the ×. The confirmation text is DATA-DRIVEN: the
|
|
||||||
booth name travels as a data attribute and never reaches a JS string
|
|
||||||
(see the script below). Release is reversible, so it has no prompt of
|
|
||||||
its own beyond the wording. #}
|
|
||||||
{% if b.kept %}
|
|
||||||
<form class="release" method="post" action="/b/{{ b.name_url }}/unkeep"
|
|
||||||
data-booth="{{ b.name }}" data-confirm="release">
|
|
||||||
<button title="release this booth so it can be wiped">release</button>
|
|
||||||
</form>
|
|
||||||
<form class="wipe wipe-kept" method="post" action="/b/{{ b.name_url }}/delete"
|
|
||||||
data-booth="{{ b.name }}" data-confirm="wipe-kept">
|
|
||||||
<button title="wipe this KEPT booth now" aria-label="wipe kept booth">×</button>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<form class="keepit" method="post" action="/b/{{ b.name_url }}/keep">
|
|
||||||
<button title="keep — exempt from the {{ ttl_hours }}h sweep" aria-label="keep booth">★</button>
|
|
||||||
</form>
|
|
||||||
<form class="wipe" method="post" action="/b/{{ b.name_url }}/delete"
|
|
||||||
data-booth="{{ b.name }}" data-confirm="wipe">
|
|
||||||
<button title="wipe now" aria-label="wipe booth">×</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</article>
|
</article>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
|||||||
@@ -328,6 +328,15 @@ The kept/ephemeral lanes are **removed**: 23 of 24 live booths are kept, so the
|
|||||||
lanes sort nothing. Kept status and the lifetime line (`_lifetime.html`,
|
lanes sort nothing. Kept status and the lifetime line (`_lifetime.html`,
|
||||||
unchanged) remain on every row.
|
unchanged) remain on every row.
|
||||||
|
|
||||||
|
**A row's controls sit on its facts line**, each beside the state it changes:
|
||||||
|
`release` after "kept", `★ keep` after a countdown or a hold, `× wipe` last.
|
||||||
|
They are always visible, with no hover-only reveal: that was a column that
|
||||||
|
reserved its room while invisible (operator, 2026-09-23, on the live Desk:
|
||||||
|
"release and x take up space whether or not they're visible"), and touch has
|
||||||
|
no hover. The side column renders only when the row has a badge, so a row
|
||||||
|
without one reserves nothing. The forms, POST targets and `data-confirm`
|
||||||
|
wording are unchanged.
|
||||||
|
|
||||||
### C5 — the lightbox (booth.html, booth_view)
|
### C5 — the lightbox (booth.html, booth_view)
|
||||||
|
|
||||||
- **Layout.** Two panes on a gallery booth: the set on the left, the
|
- **Layout.** Two panes on a gallery booth: the set on the left, the
|
||||||
|
|||||||
@@ -158,3 +158,32 @@ new = '''
|
|||||||
continue
|
continue
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
continue'''
|
continue'''
|
||||||
|
|
||||||
|
[[mutation]]
|
||||||
|
label = "Desk row controls hidden until hover again (opacity 0 keeps their box)"
|
||||||
|
file = "booth/templates/base.html"
|
||||||
|
test = "tests/test_flow_browser.py::test_a_rows_keep_release_and_wipe_take_no_room_of_their_own"
|
||||||
|
old = '''
|
||||||
|
.desk-facts form{display:inline;margin:0}'''
|
||||||
|
new = '''
|
||||||
|
.desk-facts form{display:inline;margin:0;opacity:0}'''
|
||||||
|
|
||||||
|
[[mutation]]
|
||||||
|
label = "Desk row renders its side column with no badge in it (a reserved gap)"
|
||||||
|
file = "booth/templates/index.html"
|
||||||
|
test = "tests/test_flow_browser.py::test_a_rows_keep_release_and_wipe_take_no_room_of_their_own"
|
||||||
|
old = '''
|
||||||
|
{% if b.marks_open or b.hold == "unreadable" or section == 'new' or b.uploaded %}
|
||||||
|
<div class="desk-side">'''
|
||||||
|
new = '''
|
||||||
|
{% if true %}
|
||||||
|
<div class="desk-side">'''
|
||||||
|
|
||||||
|
[[mutation]]
|
||||||
|
label = "the stacked Desk column is a bare 1fr (content sets its minimum)"
|
||||||
|
file = "booth/templates/base.html"
|
||||||
|
test = "tests/test_flow_browser.py::test_the_desk_never_scrolls_sideways_on_a_phone"
|
||||||
|
old = '''
|
||||||
|
@media (max-width:1000px){.desk{grid-template-columns:minmax(0,1fr)}}'''
|
||||||
|
new = '''
|
||||||
|
@media (max-width:1000px){.desk{grid-template-columns:1fr}}'''
|
||||||
|
|||||||
@@ -374,3 +374,61 @@ def test_the_next_arrow_clears_the_rail_only_beside_it(browser, live):
|
|||||||
"getComputedStyle(document.querySelector('.vnav.vnext')).right")
|
"getComputedStyle(document.querySelector('.vnav.vnext')).right")
|
||||||
page.close()
|
page.close()
|
||||||
assert rights == {1400: "360px", 390: "0px"}
|
assert rights == {1400: "360px", 390: "0px"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_rows_keep_release_and_wipe_take_no_room_of_their_own(browser, live):
|
||||||
|
"""Operator, on the live Desk: 'release and x take up space whether or not
|
||||||
|
they're visible.' They were `opacity:0` in their own side column, which
|
||||||
|
hides a control and still reserves its box. Each now sits on the facts
|
||||||
|
line beside the state it changes, visible without hover (a touch screen
|
||||||
|
never had hover), so a row with no badge has no side column at all."""
|
||||||
|
import os
|
||||||
|
base, root = live
|
||||||
|
past = time.time() - 10_000
|
||||||
|
for name, kept in (("kept1", True), ("loose", False)):
|
||||||
|
d = root / name
|
||||||
|
d.mkdir()
|
||||||
|
(d / "a.png").write_bytes(PNG)
|
||||||
|
os.utime(d / "a.png", (past, past))
|
||||||
|
(d / ".viewed").write_bytes(b"") # looked at since: no 'new' badge
|
||||||
|
if kept:
|
||||||
|
(d / ".forever").write_bytes(b"")
|
||||||
|
page = browser.new_page(viewport={"width": 1400, "height": 900})
|
||||||
|
page.goto(f"{base}/", wait_until="networkidle")
|
||||||
|
page.mouse.move(0, 0) # nothing hovered
|
||||||
|
out = {}
|
||||||
|
for name, form in (("kept1", "form.release"), ("kept1", "form.wipe-kept"),
|
||||||
|
("loose", "form.keepit"), ("loose", "form.wipe")):
|
||||||
|
btn = page.locator(f'.desk-row[data-booth="{name}"] {form} button')
|
||||||
|
out[(name, form)] = btn.is_visible() and btn.evaluate(
|
||||||
|
"b => { for (let e = b; e; e = e.parentElement)"
|
||||||
|
" if (getComputedStyle(e).opacity === '0') return false;"
|
||||||
|
" return true; }")
|
||||||
|
gaps = page.evaluate("""() => [...document.querySelectorAll('.desk-row')].map(r => {
|
||||||
|
const row = r.getBoundingClientRect(), main = r.querySelector('.desk-main').getBoundingClientRect();
|
||||||
|
return Math.round(row.right - main.right); })""")
|
||||||
|
page.close()
|
||||||
|
assert all(out.values()), out
|
||||||
|
# row padding (12) + border (1) + nothing else: no side column is reserved
|
||||||
|
assert gaps and max(gaps) <= 14, gaps
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_desk_never_scrolls_sideways_on_a_phone(browser, live):
|
||||||
|
"""The row-controls change made a Desk row a flex container, and a long
|
||||||
|
provenance line (nowrap, ellipsised) then set the Desk column's MINIMUM
|
||||||
|
width: at 390px the page scrolled sideways to 1029px. The column is capped
|
||||||
|
at the space it has; the ellipsis does the rest."""
|
||||||
|
from booth.manifest import write_manifest
|
||||||
|
base, root = live
|
||||||
|
d = root / "longwhy"
|
||||||
|
d.mkdir()
|
||||||
|
(d / "a.png").write_bytes(PNG)
|
||||||
|
write_manifest(d, "design-dev", title="A set with a long reason",
|
||||||
|
why="a reason long enough to overflow any phone " * 6)
|
||||||
|
widths = {}
|
||||||
|
for w in (390, 1400):
|
||||||
|
page = browser.new_page(viewport={"width": w, "height": 844})
|
||||||
|
page.goto(f"{base}/", wait_until="networkidle")
|
||||||
|
widths[w] = page.evaluate("document.documentElement.scrollWidth")
|
||||||
|
page.close()
|
||||||
|
assert widths == {390: 390, 1400: 1400}, widths
|
||||||
|
|||||||
Reference in New Issue
Block a user