Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions scanner/dashboard/console.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
input{font:inherit;padding:8px 10px;border:1px solid var(--border);border-radius:8px;min-width:280px}
button{font:inherit;font-weight:600;padding:8px 14px;border:0;border-radius:8px;background:var(--primary);color:var(--on-primary);cursor:pointer}
button.ghost{background:var(--surface);color:var(--primary);border:1px solid var(--border)}
button:disabled, button[aria-busy="true"], tr.scan[aria-disabled="true"] { opacity: 0.7; pointer-events: none; }
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:16px 18px;margin-bottom:16px}
.stats{display:flex;gap:12px;flex-wrap:wrap}
.stat{flex:1;min-width:130px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:12px 14px}
Expand Down Expand Up @@ -88,6 +89,7 @@ <h1>AppGuardrail Console</h1>
detail.innerHTML="";
if(lastDetailFocus instanceof HTMLElement && lastDetailFocus.isConnected){
lastDetailFocus.removeAttribute("aria-busy");
lastDetailFocus.removeAttribute("aria-disabled");
delete lastDetailFocus.dataset.detailRequest;
lastDetailFocus.focus();
}
Expand Down Expand Up @@ -135,8 +137,8 @@ <h1>AppGuardrail Console</h1>
<td>${esc(s.created_at)}</td><td>${esc(s.repo||"—")}</td><td><code>${esc((s.commit||"—").slice(0,10))}</code></td>
<td>${s.total}</td><td>${pill(s.deploy_blocking,"var(--crit)")}</td><td>${pill(s.new_blocking,"var(--high)")}</td></tr>`).join("")||'<tr><td colspan="6" class="muted">No scans. POST to /api/v1/scans from CI.</td></tr>';
document.querySelectorAll("tr.scan").forEach(tr=>{
tr.onclick=()=>detail(tr.dataset.id,tr);
tr.addEventListener('keydown', e => { if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); detail(tr.dataset.id,tr); } });
tr.onclick=()=>{ if(tr.getAttribute("aria-disabled")==="true") return; detail(tr.dataset.id,tr); };
tr.addEventListener('keydown', e => { if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if(tr.getAttribute("aria-disabled")==="true") return; detail(tr.dataset.id,tr); } });
});
}catch(e){ $("#msg").classList.remove("hidden");$("#app").classList.add("hidden");
$("#msg").innerHTML=`<span class="err">${esc(e.message)}</span>`; }
Expand All @@ -149,6 +151,7 @@ <h1>AppGuardrail Console</h1>
if(tr){
tr.dataset.detailRequest=String(requestId);
tr.setAttribute("aria-busy","true");
tr.setAttribute("aria-disabled","true");
}
d.classList.remove("hidden");
d.innerHTML='<div aria-live="polite" class="muted">Loading scan details...</div>';
Expand All @@ -173,6 +176,7 @@ <h1>AppGuardrail Console</h1>
}finally{
if(tr&&tr.dataset.detailRequest===String(requestId)){
tr.removeAttribute("aria-busy");
tr.removeAttribute("aria-disabled");
delete tr.dataset.detailRequest;
}
}
Expand Down
10 changes: 10 additions & 0 deletions tests/test_console_detail_loading_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ def test_console_exposes_loading_busy_and_error_states():
assert 'tr.removeAttribute("aria-busy");' in html


def test_console_busy_scan_row_exposes_temporary_action_unavailability():
"""A busy focusable scan row must expose and enforce its unavailable action state."""
html = _console_html()

assert 'tr.setAttribute("aria-busy","true");' in html
assert 'tr.setAttribute("aria-disabled","true");' in html
Comment thread
seonghobae marked this conversation as resolved.
assert html.count('if(tr.getAttribute("aria-disabled")==="true") return;') == 2
assert 'tr.removeAttribute("aria-disabled");' in html


def test_console_detail_scrolling_respects_reduced_motion():
"""Successful and failed detail requests must honor reduced-motion preferences."""
html = _console_html()
Expand Down
Loading