From 61c4972a8892e0bbdc5e5e7189d66582c6de0c42 Mon Sep 17 00:00:00 2001 From: Simone Date: Sat, 25 Jul 2026 15:43:17 +0200 Subject: [PATCH] style(web): move the row-count select into the card header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was sitting in a strip below the heading with a "Show" label and a "newest first" hint — three lines of chrome for one control. The select now lives at the right end of the card's own header bar (`margin-left:auto` inside the flex h2), compact and case-normal so it reads as a control and does not grow the bar. Label and hint are gone; the header text names the card, the values speak for themselves, and screen readers keep the `aria-label`. --- docs/WEB-UI.md | 6 ++--- src/ci_orchestrator/web/static/app.css | 22 ++++++++++--------- .../web/templates/partials/executions.html | 20 +++++++++-------- tests/python/test_web_executions.py | 5 ++++- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/docs/WEB-UI.md b/docs/WEB-UI.md index 6856a8b..a6a3431 100644 --- a/docs/WEB-UI.md +++ b/docs/WEB-UI.md @@ -295,9 +295,9 @@ Gitea at most one call per 10s window; the VM columns are re-read on every refresh. Rows are ordered **newest first** (a queued run, which has no start time yet, -sorts above everything; ties break on Gitea's run id). The **Show** selector in -the card's header picks how many to list — 10 / 25 / 50 / 100, default from -`[web].executions_limit` (10). The choice rides on the card's own polling URL +sorts above everything; ties break on Gitea's run id). The row-count dropdown at +the right end of the card's header bar picks how many to list — 10 / 25 / 50 / +100, default from `[web].executions_limit` (10). The choice rides on the card's own polling URL (`/executions?limit=N`), so it survives every 5s refresh with no client-side state; a value set in the config outside those four is offered as an extra option rather than dropped. diff --git a/src/ci_orchestrator/web/static/app.css b/src/ci_orchestrator/web/static/app.css index 6d3ae4b..f1b2236 100644 --- a/src/ci_orchestrator/web/static/app.css +++ b/src/ci_orchestrator/web/static/app.css @@ -281,17 +281,19 @@ dd code, dd .mono { overflow-wrap: anywhere; } /* A row of buttons (e.g. Purge / Empty all) that wraps on narrow cards. */ .btn-row { display: flex; flex-wrap: wrap; gap: 0.5rem; } -/* Small control strip under a card's heading (Executions row count): right - * aligned so it reads as a view option, not as part of the data below. */ -.card-toolbar { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: flex-end; - gap: 0.5rem; - margin: -0.35rem 0 0.7rem; +/* A control living inside a card's header bar (Executions row count), pushed to + * the bar's right end. Compact and case-normal so it reads as a control rather + * than part of the uppercase heading text, and short enough not to grow the + * bar's height. */ +.head-select { + margin-left: auto; + padding: 0.1rem 0.35rem; + font-size: 0.8rem; + line-height: 1.2; + text-transform: none; + letter-spacing: normal; + color: var(--text); } -.card-toolbar select { padding: 0.2rem 0.4rem; } /* Per-template expandable backup list (Templates panel). */ .backups-details > summary { diff --git a/src/ci_orchestrator/web/templates/partials/executions.html b/src/ci_orchestrator/web/templates/partials/executions.html index e6a9027..9043688 100644 --- a/src/ci_orchestrator/web/templates/partials/executions.html +++ b/src/ci_orchestrator/web/templates/partials/executions.html @@ -11,15 +11,18 @@ scrolled out of the window (or that no run ever claimed) keeps its row and its Remove button. - The row-count selector re-fetches the card with ``?limit=N``, and the card it - renders carries that same limit on its own polling URL -- that is what makes - the choice survive the 5s refresh without any client-side state. + The row-count selector sits in the card's own header bar (right end) and + re-fetches the card with ``?limit=N``; the card it renders carries that same + limit on its polling URL, which is what makes the choice survive the 5s + refresh without any client-side state. It carries no visible label -- the + header text names the card and the values are self-evident -- so screen + readers get ``aria-label`` instead. #}
-

Executions

-
- - {% for choice in limit_choices %} @@ -28,8 +31,7 @@ {% endif %} - newest first -
+ {% if not backend_available %} {% endif %} diff --git a/tests/python/test_web_executions.py b/tests/python/test_web_executions.py index 1d5db77..85b4dc2 100644 --- a/tests/python/test_web_executions.py +++ b/tests/python/test_web_executions.py @@ -385,7 +385,10 @@ def test_default_limit_is_ten(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) - assert limits == [10] assert '' in resp.text - assert "newest first" in resp.text + # The selector lives inside the card's header bar, not in a strip below it. + head = resp.text.split("", 1)[0] + assert "head-select" in head + assert "