Files
bench/tests/test_column_flex.py
istosandClaude Fable 5 e1b9a8f662 Let kanban columns flex down to a token floor on small laptops
Five fixed 296px columns overflowed 13" MacBooks (~1556px footprint vs
~1440 viewport), clipping the Done column. Columns now grow/shrink from
a zero basis, capped at --col (pixel-identical at >=1556px) and floored
at a new --col-min token (240px: five columns + gaps + board padding =
1276px, fitting 1280px viewports). Below the floor the existing
overflow-x:auto fallback is unchanged.

Sessions, Focus and the header are already fluid at 1280px — checked,
nothing to fix there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 07:31:41 +02:00

83 lines
3.6 KiB
Python

"""The kanban's columns yield on small laptops (task 09).
board.html is a single file with inline JS and no frontend test runner, so
these are source-level invariants: the ones that, if broken, would bring
back the rigid 296px columns that clipped the Done column on a 13" MacBook,
or let the floor drift above what a 1280px viewport can hold.
python3 -m unittest discover -s tests -v
"""
from __future__ import annotations
import re
import unittest
from pathlib import Path
BOARD = Path(__file__).resolve().parents[1] / "manager" / "core" / "board.html"
COLUMNS = 5 # backlog → to-do → in-progress → review → done
class ColumnFlexTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.html = BOARD.read_text(encoding="utf-8")
m = re.search(r"\.kcol\{([^}]*)\}", cls.html)
assert m, "board.html lost its .kcol rule"
cls.kcol = m.group(1)
def token(self, name: str) -> int:
m = re.search(rf"{name}:\s*(\d+)px", self.html)
self.assertIsNotNone(m, f"{name} must be a px token at the top of board.html")
return int(m.group(1))
def test_floor_is_a_design_token(self):
"""The floor lives beside --col with the other layout tokens, not as
a magic number buried in the rule."""
self.token("--col-min")
self.assertIn("min-width:var(--col-min)", self.kcol.replace(" ", ""),
".kcol must take its floor from the --col-min token")
def test_columns_yield_between_floor_and_cap(self):
"""flex:1 1 0 with max-width:var(--col): wide screens cap at today's
296px (pixel-identical), narrower viewports shrink all five evenly."""
flat = self.kcol.replace(" ", "")
self.assertIn("flex:110", flat,
".kcol must grow and shrink from a zero basis")
self.assertIn("max-width:var(--col)", flat,
"wide screens must still cap columns at --col")
self.assertNotIn("width:var(--col);", flat.replace("max-width", ""),
"a fixed width would undo the flex")
self.assertEqual(self.token("--col"), 296,
"the cap is today's column width; changing it is a "
"redesign, not this fix")
def test_five_columns_fit_a_1280px_viewport(self):
"""At the floor, 5 columns + 4 gaps + the board's own padding must
not exceed 1280 CSS px — the smallest laptop this board honours."""
floor = self.token("--col-min")
gap = self.token("--gap")
board = re.search(r"#board\{([^}]*)\}", self.html).group(1)
pad = re.search(r"padding:\s*\d+px\s+(\d+)px", board)
self.assertIsNotNone(pad, "#board lost its horizontal padding")
footprint = COLUMNS * floor + (COLUMNS - 1) * gap + 2 * int(pad.group(1))
self.assertLessEqual(footprint, 1280,
f"five columns at the floor need {footprint}px; "
"a 1280px viewport would scroll")
def test_cards_stay_legible_at_the_floor(self):
"""Shrinking must stop somewhere: a floor under ~240px would start
crushing card internals instead of ellipsising them."""
self.assertGreaterEqual(self.token("--col-min"), 240)
def test_overflow_fallback_survives(self):
"""Below the floor the old behaviour is the fallback: #board still
scrolls horizontally rather than clipping."""
board = re.search(r"#board\{([^}]*)\}", self.html).group(1)
self.assertIn("overflow-x:auto", board.replace(" ", ""))
if __name__ == "__main__":
unittest.main()