Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ certs/

# CodeGraph local index
.codegraph/
node_modules/
3 changes: 3 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
## 2026-07-13 - Async Table Actions UX
**Learning:** Adding explicit loading and disabled states to table action buttons that invoke asynchronous processes helps prevent redundant API calls and visually assures the user that their request is being handled.
**Action:** Consistently apply `disabled` state and `Loading...` text changes to inline table action buttons linked to async workflows, and carefully preserve underlying DOM structures with `Array.from(btn.childNodes)` during the loading cycle to avoid rendering regressions.
## 2026-07-19 - Adding context to generic data table actions
**Learning:** Repetitive table action buttons or links (e.g., 'Details', 'Open') in UI data tables create a frustrating experience for screen reader users because the label lacks context about what row is being modified.
**Action:** Always provide dynamically generated, context-specific `aria-label` attributes that include row-specific identifiers (like the filename or document name) to aid screen reader users.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@

### Fixed
- λ·°μ–΄ UI의 μž¬μ‹œλ„ λ²„νŠΌ λ‘œλ”© μƒνƒœκ°€ λ‚΄λΆ€ DOM을 μ†μƒμ‹œν‚€μ§€ μ•Šκ³  μ•ˆμ „ν•˜κ²Œ λ³΅μ›λ˜λ„λ‘ μˆ˜μ •
### ν–₯μƒλœ 점 (Enhancements)
* μ ‘κ·Όμ„±(UX/UI): 데이터 ν…Œμ΄λΈ” λ‚΄ 반볡적인 'Details', 'Status JSON', 'Open viewer' λ²„νŠΌ 및 링크에 λ™μ μœΌλ‘œ μ»¨ν…μŠ€νŠΈλ₯Ό ν¬ν•¨ν•˜λŠ” `aria-label`을 λΆ€μ—¬ν•˜μ—¬ 슀크린 리더 μ‚¬μš©μžμ˜ 접근성을 κ°œμ„ ν–ˆμŠ΅λ‹ˆλ‹€.
16 changes: 11 additions & 5 deletions src/main/resources/static/assets/viewer/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,16 @@ function updateJob(jobId, patch, { refreshKpisAfterUpdate = true } = {}) {
}
}

function createLink(href, label) {
function createLink(href, label, ariaLabel) {
const link = document.createElement("a");
link.href = href;
link.textContent = label;
link.className = "table-link";
link.target = "_blank";
link.rel = "noopener noreferrer";
if (ariaLabel) {
link.setAttribute("aria-label", ariaLabel);
}
return link;
}

Expand All @@ -110,11 +113,14 @@ async function openJsonDocument(url, title) {
: "Unable to load JSON evidence with the current tenant claim.";
}

function createActionButton(label, onClick) {
function createActionButton(label, onClick, ariaLabel) {
const button = document.createElement("button");
button.type = "button";
button.textContent = label;
button.className = "btn btn-secondary btn-compact";
if (ariaLabel) {
button.setAttribute("aria-label", ariaLabel);
}
button.addEventListener("click", onClick);
return button;
}
Expand Down Expand Up @@ -153,13 +159,13 @@ function renderHistory(history = loadHistory()) {
btn.replaceChildren(...initialChildren);
btn.disabled = false;
});
}));
}, `Details for ${job.fileName || "Document"}`));
actionsCell.appendChild(createActionButton("Status JSON", () => {
void openJsonDocument(job.statusUrl, "Clearfolio status JSON");
}));
}, `Status JSON for ${job.fileName || "Document"}`));
}
if (job.jobId) {
actionsCell.appendChild(createLink(`/viewer/${encodeURIComponent(job.jobId)}`, "Open viewer"));
actionsCell.appendChild(createLink(`/viewer/${encodeURIComponent(job.jobId)}`, "Open viewer", `Open viewer for ${job.fileName || "Document"}`));
}

row.append(fileCell, statusCell, submittedCell, actionsCell);
Expand Down
Loading