Compare commits

..

4 commits

Author SHA1 Message Date
149cc9a0be feat(form): render admin orders as mobile cards 2026-07-30 20:25:38 -04:00
2638e6f589 test(form): cover a11y labels, status region, and desc toggle
Structural checks for dual status nodes and no alert(); Playwright for
init-time qty labels, aria-pressed chips, overflow More/Less, and
failed-submit text landing in role=alert.
2026-07-30 20:25:37 -04:00
e2afa70395 fix(form): wire a11y labels, status helper, and desc expand
Meal-scoped qty labels at card create time, aria-pressed filter chips,
dual-node showStatus replacing all alert() calls (confirm retained),
overflow-gated More/Less, and success-heading focus after submit.
2026-07-30 20:25:37 -04:00
d99d7cebfb fix(form): add status regions, live total, and a11y CSS
Dual alert/status slots for form and admin, aria-live on the sticky
total, success heading focus target, More/Less affordance styles, and
44px coarse-pointer chip padding.
2026-07-30 20:25:37 -04:00
3 changed files with 3 additions and 21 deletions

View file

@ -454,15 +454,13 @@ function downloadSummaryPdf() {
function adminStartEdit(email) {
window.FormUtils.clearStatus('admin');
const requestedWeek = currentAdminWeek;
setAdminActionDisabled('start-edit', true);
adminFetch(ADMIN_URL + '?week=' + encodeURIComponent(requestedWeek), { headers: adminHeaders() })
adminFetch(ADMIN_URL + '?week=' + encodeURIComponent(currentAdminWeek), { headers: adminHeaders() })
.then(r => {
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.json();
})
.then(data => {
if (currentAdminWeek !== requestedWeek) return;
lastAdminData = data;
const order = data.orders.find(o => o.employee_email === email);
if (!order) return;

View file

@ -99,7 +99,7 @@
<div id="form-status-info" class="form-status-msg form-status-info" role="status"></div>
</div>
<div class="inner">
<div aria-live="polite" aria-atomic="true">
<div aria-live="polite">
<span class="total" id="total-display">$0.00</span>
<span class="count-display" id="count-display">0 meals</span>
</div>

View file

@ -101,14 +101,6 @@ def _mock_form_routes(page):
),
),
)
page.route(
"**/api/admin/orders",
lambda route: route.fulfill(
status=403,
content_type="application/json",
body='{"error":"Forbidden"}',
),
)
class TestGenerateFormStructural:
@ -399,15 +391,10 @@ class TestGenerateFormPlaywright:
bottom: box.bottom,
};
};
const footer = document.querySelector('#sticky-footer');
return {
clientWidth: document.documentElement.clientWidth,
scrollWidth: document.documentElement.scrollWidth,
viewportHeight: window.innerHeight,
footer: {
...rect('#sticky-footer'),
position: getComputedStyle(footer).position,
},
footer: rect('#sticky-footer'),
total: rect('#total-display'),
count: rect('#count-display'),
submit: rect('#submit-btn'),
@ -420,9 +407,6 @@ class TestGenerateFormPlaywright:
for selector in ("footer", "total", "count", "submit"):
assert layout[selector]["left"] >= 0
assert layout[selector]["right"] <= layout["clientWidth"]
assert layout["footer"]["position"] == "fixed"
assert layout["footer"]["top"] >= 0
assert layout["footer"]["bottom"] <= layout["viewportHeight"]
assert layout["lastCard"]["bottom"] <= layout["footer"]["top"]
finally:
browser.close()