diff --git a/CHANGELOG.md b/CHANGELOG.md index f766f9cf..6439b950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ # Changelog +## Sprint 72 Mobile overflow hardening (2026-06-20) + +- Clamped page-level horizontal overflow for the workbench shell on mobile. +- Kept sidebar navigation and workspace shortcut chips as contained horizontal scroll areas. +- Added wrapping/containment for long QA identifiers, dataset links and inspector values. +- Made inspector tabs two-column on narrow screens to avoid header overflow. +- Added static regression coverage for mobile overflow and long-identifier wrapping contracts. +- No API contracts, migrations, backend behavior, provider fetching or AI model behavior changed. + ## Sprint 71 QA/QC metric card polish (2026-06-19) - Added core metric evidence cards for precision, recall, F1, mean IoU and false positive/negative counts. diff --git a/backend/tests/test_sprint72_mobile_overflow_hardening.py b/backend/tests/test_sprint72_mobile_overflow_hardening.py new file mode 100644 index 00000000..5e53877b --- /dev/null +++ b/backend/tests/test_sprint72_mobile_overflow_hardening.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] + + +def test_mobile_overflow_hardening_css_contracts() -> None: + css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") + + assert "overflow-x: hidden;" in css + assert ".workbench-shell" in css + assert "max-width: 100vw;" in css + assert ".workbench-sidebar nav" in css + assert ".workspace-nav-cluster" in css + assert ".inspector-tabs" in css + assert "repeat(2, minmax(0, 1fr))" in css + assert "overflow-x: clip;" in css + assert "overscroll-behavior-x: contain;" in css + + +def test_long_identifier_wrapping_contracts() -> None: + css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") + + assert ".quality-check-dataset-link" in css + assert ".quality-score-row strong" in css + assert ".inspector-field strong" in css + assert "overflow-wrap: anywhere;" in css + assert "word-break: break-word;" in css diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index 6fd5a79a..910f9a5d 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -2697,3 +2697,32 @@ Limitations: Next recommended pass: - Continue with QA/QC metric/result filtering or result-card density for long-lived demo projects. + +## Sprint 72 Mobile overflow hardening (2026-06-20) + +Changed: +- Clamped page-level horizontal overflow for `html`, `body` and the workbench shell. +- Kept mobile sidebar navigation and workspace shortcut chips as contained horizontal scroll regions with overscroll containment. +- Added min-width/max-width containment for the workbench layout, main area, inspector panel and card surfaces. +- Allowed long QA check IDs, dataset links and inspector values to wrap instead of widening result cards. +- Switched inspector tabs to a two-column layout on narrow screens. +- Added `backend/tests/test_sprint72_mobile_overflow_hardening.py`. +- Updated `frontend/README.md`, `docs/TODO.md` and `CHANGELOG.md`. + +Tested: +- Red step: `python -m pytest backend/tests/test_sprint72_mobile_overflow_hardening.py -q` failed on missing mobile overflow and identifier wrapping contracts. +- `python -m pytest backend/tests/test_sprint72_mobile_overflow_hardening.py backend/tests/test_sprint71_quality_metric_polish.py backend/tests/test_sprint62_frontend_visual_polish.py backend/tests/test_sprint30_workbench_components.py -q` (`11 passed`) +- `cd frontend && npm run typecheck` +- `cd frontend && npm run build` +- Local browser check against `http://127.0.0.1:5176` with live API proxy at a 390px viewport: QA/QC workspace rendered 2 quality cards and 12 metric cards; body, shell, inspector, inspector panel and inspector tabs stayed within viewport width; sidebar/workspace nav retained contained horizontal scrolling; no console warnings/errors. +- Screenshot captured under `artifacts/sprint72-mobile-overflow-hardening/`. +- `bash scripts/run_readiness_check.sh` (`255 passed`) + +Open: +- Redeploy Tower and verify live mobile overflow after deployment. + +Limitations: +- Frontend CSS hardening only; no UI flow, API, persistence, migration, provider or AI/model changes. + +Next recommended pass: +- Continue with QA/QC result filtering/density for long-lived demo projects, or a broader mobile visual pass across Data and Map once the overflow baseline is stable. diff --git a/docs/TODO.md b/docs/TODO.md index 55067570..c214ee7e 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -343,3 +343,4 @@ This file now starts with the current implementation status. Older preparation/b - [x] Add Data catalog action polish for map, metadata, export and QA affordances. - [x] Add QA/QC handoff polish for candidate/reference context and persisted results. - [x] Add QA/QC metric card polish for precision, recall, F1, IoU and error counts. +- [x] Add mobile overflow hardening for workbench navigation, inspector and long QA identifiers. diff --git a/frontend/README.md b/frontend/README.md index bedb98a6..60b5b4ef 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -285,6 +285,8 @@ The frontend API client uses same-origin requests by default. In Docker Compose, The QA/QC workspace promotes core metrics into evidence cards before the raw metric list, keeping precision, recall, F1, mean IoU and false positive/negative counts scan-friendly while preserving the persisted raw metrics. +The workbench shell clamps page-level horizontal overflow on mobile while keeping the sidebar and workspace shortcut rows intentionally scrollable. Long QA identifiers and inspector values wrap inside their cards instead of widening the viewport. + ## Useful repository scripts - `bash scripts/frontend_install.sh` diff --git a/frontend/src/styles/app.css b/frontend/src/styles/app.css index 01b6e61f..11b0ae0f 100644 --- a/frontend/src/styles/app.css +++ b/frontend/src/styles/app.css @@ -25,10 +25,12 @@ } html { + overflow-x: hidden; background: var(--bg); } body { + overflow-x: hidden; margin: 0; font-family: Inter, 'Avenir Next', 'Segoe UI', sans-serif; color: var(--text); @@ -39,6 +41,11 @@ body { text-rendering: optimizeLegibility; } +.workbench-shell { + max-width: 100vw; + overflow-x: clip; +} + button, input, select, @@ -514,7 +521,8 @@ section li strong + div { .workbench-shell { width: 100%; - max-width: none; + max-width: 100vw; + overflow-x: clip; padding: 0; background: #eef4f1; } @@ -579,10 +587,13 @@ section li strong + div { gap: 0; height: calc(100vh - 4.35rem); min-height: 42rem; + max-width: 100vw; + min-width: 0; } .workbench-sidebar, .workbench-inspector { + min-width: 0; min-height: 0; overflow: auto; border-color: var(--line); @@ -640,6 +651,7 @@ section li strong + div { .workbench-main { min-width: 0; min-height: 0; + max-width: 100%; overflow: auto; padding: 0.9rem 1.05rem 1.15rem; } @@ -1066,6 +1078,7 @@ button.entity-card { .export-card, .latest-export-card, .empty-state { + min-width: 0; padding: 0.85rem; } @@ -1270,6 +1283,7 @@ button.entity-card { .quality-dataset-name, .quality-check-dataset-link { overflow-wrap: anywhere; + word-break: break-word; } .quality-dataset-name { @@ -1301,7 +1315,13 @@ button.entity-card { font-size: 0.9rem; line-height: 1.25; text-overflow: ellipsis; - white-space: nowrap; + white-space: normal; +} + +.quality-score-row strong, +.inspector-field strong { + overflow-wrap: anywhere; + word-break: break-word; } .quality-metric-section { @@ -1710,11 +1730,17 @@ button.entity-card { .workbench-inspector-panel { display: grid; gap: 0.68rem; + max-width: 100%; + min-width: 0; + overflow-x: clip; } .inspector-header { display: grid; gap: 0.2rem; + max-width: 100%; + min-width: 0; + overflow-x: clip; } .inspector-header h2 { @@ -1734,12 +1760,14 @@ button.entity-card { } .inspector-tab { + min-width: 0; min-height: 2.2rem; padding: 0.38rem 0.42rem; background: #ffffff; box-shadow: none; font-size: 0.78rem; line-height: 1.2; + overflow-wrap: anywhere; } .inspector-tab-active { @@ -1882,6 +1910,7 @@ button.entity-card { position: sticky; top: 5.8rem; z-index: 10; + max-width: 100vw; border-right: 0; border-bottom: 1px solid var(--line); } @@ -1941,16 +1970,26 @@ button.entity-card { padding: 0.75rem; } + .workbench-inspector { + overflow-x: hidden; + } + .context-bar, .quick-action-grid, .workspace-nav-cluster { grid-template-columns: 1fr; } + .inspector-tabs { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .workbench-sidebar nav { display: flex; gap: 0.5rem; + max-width: 100%; overflow-x: auto; + overscroll-behavior-x: contain; padding-bottom: 0.2rem; } @@ -1961,7 +2000,9 @@ button.entity-card { .workspace-nav-cluster { display: flex; flex-wrap: nowrap; + max-width: 100%; overflow-x: auto; + overscroll-behavior-x: contain; padding-bottom: 0.1rem; }