diff --git a/packages/code-analyzer-core/output-templates/html-template-0.0.12.txt b/packages/code-analyzer-core/output-templates/html-template-0.0.13.txt
similarity index 86%
rename from packages/code-analyzer-core/output-templates/html-template-0.0.12.txt
rename to packages/code-analyzer-core/output-templates/html-template-0.0.13.txt
index c5e3e564..0f11ea30 100644
--- a/packages/code-analyzer-core/output-templates/html-template-0.0.12.txt
+++ b/packages/code-analyzer-core/output-templates/html-template-0.0.13.txt
@@ -47,11 +47,11 @@
fetch(link.href, fetchOpts);
}
})();
-
+
// ==== START OF VIOLATIONS ====
const data = {{###VIOLATIONS###}};
// ==== END OF VIOLATIONS ====
-
+
class Model {
constructor(data2) {
(this.violations = this.processViolations(data2)),
@@ -182,6 +182,7 @@
(this.defaultSort = { column: "file", direction: "asc" }),
(this.currentViolations = []),
(this.currentViolationIndex = -1),
+ (this.triggeringElement = null),
(this.summary = document.getElementById("summary")),
(this.violationsTable = document.getElementById("violationsTable")),
(this.tableBody = this.violationsTable.querySelector("tbody")),
@@ -265,11 +266,48 @@
}
updateBoxes(data2) {
const counts = data2.reduce(
- (acc, v) => ((acc[v.severity] = (acc[v.severity] || 0) + 1), acc),
- {},
- );
- for (let i = 1; i <= 5; i++)
- document.getElementById(`sev${i}`).textContent = counts[i] || 0;
+ (acc, v) => ((acc[v.severity] = (acc[v.severity] || 0) + 1), acc),
+ {},
+ ),
+ severityNames = {
+ 1: "Critical",
+ 2: "High",
+ 3: "Moderate",
+ 4: "Low",
+ 5: "Info",
+ };
+ for (let i = 1; i <= 5; i++) {
+ const count = counts[i] || 0;
+ document.getElementById(`sev${i}`).textContent = count;
+ const box = document.querySelector(`[data-severity="${i}"]`);
+ box &&
+ box.setAttribute(
+ "aria-label",
+ `Severity ${i}, ${severityNames[i]}: ${count} violations`,
+ );
+ }
+ }
+ updateBoxesAriaLabels(filteredData) {
+ const filteredCounts = filteredData.reduce(
+ (acc, v) => ((acc[v.severity] = (acc[v.severity] || 0) + 1), acc),
+ {},
+ ),
+ severityNames = {
+ 1: "Critical",
+ 2: "High",
+ 3: "Moderate",
+ 4: "Low",
+ 5: "Info",
+ };
+ for (let i = 1; i <= 5; i++) {
+ const count = filteredCounts[i] || 0,
+ box = document.querySelector(`[data-severity="${i}"]`);
+ box &&
+ box.setAttribute(
+ "aria-label",
+ `Severity ${i}, ${severityNames[i]}: ${count} violations`,
+ );
+ }
}
toggleBoxes(severity) {
document.querySelectorAll(".box").forEach((box) => {
@@ -308,6 +346,7 @@
populateRow(violation) {
const row = this.tableBody.insertRow();
(row.dataset.index = this.model.violations.indexOf(violation)),
+ (row.tabIndex = 0),
(row.innerHTML = `
${this.model.getFile(violation.file)} |
@@ -376,6 +415,7 @@
(this.currentViolations = sortedViolations),
this.populateTable(sortedViolations, groupBy),
this.updateSummary(sortedViolations),
+ this.updateBoxesAriaLabels(sortedViolations),
this.toggleBoxes(severityFilter),
this.updateReset();
}
@@ -390,14 +430,23 @@
updateArrow() {
document.querySelectorAll("#violationsTable th").forEach((th) => {
th.classList.remove("asc", "desc"),
- th.dataset.sort === this.defaultSort.column &&
- th.classList.add(this.defaultSort.direction);
+ th.dataset.sort === this.defaultSort.column
+ ? (th.classList.add(this.defaultSort.direction),
+ th.setAttribute(
+ "aria-sort",
+ "asc" === this.defaultSort.direction
+ ? "ascending"
+ : "descending",
+ ))
+ : th.setAttribute("aria-sort", "none");
});
}
updatePagination() {
(this.panelPrev.disabled = this.currentViolationIndex <= 0),
(this.panelNext.disabled =
this.currentViolationIndex >= this.currentViolations.length - 1);
+ const contextText = `Violation ${this.currentViolationIndex + 1} of ${this.currentViolations.length}`;
+ document.getElementById("panel-title").textContent = contextText;
}
populatePanel(violation) {
const panelFields = [
@@ -433,15 +482,35 @@
(this.panelContent.innerHTML = ""),
this.panelContent.appendChild(dl);
}
- showPanel(violation) {
+ getFocusableElements() {
+ return this.panel.querySelectorAll(
+ 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',
+ );
+ }
+ trapFocus(event) {
+ const focusableElements = this.getFocusableElements(),
+ firstElement = focusableElements[0],
+ lastElement = focusableElements[focusableElements.length - 1];
+ "Tab" === event.key &&
+ (event.shiftKey
+ ? document.activeElement === firstElement &&
+ (event.preventDefault(), lastElement.focus())
+ : document.activeElement === lastElement &&
+ (event.preventDefault(), firstElement.focus()));
+ }
+ showPanel(violation, triggeringElement = null) {
(this.currentViolationIndex = this.currentViolations.findIndex(
(v) => v === violation,
)),
+ (this.triggeringElement = triggeringElement),
this.populatePanel(violation),
this.overlay.classList.add("visible"),
this.panel.classList.add("visible"),
this.updatePagination(),
- (document.body.style.overflow = "hidden");
+ (document.body.style.overflow = "hidden"),
+ setTimeout(() => {
+ this.panel.focus();
+ }, 100);
}
paginatePanel(direction) {
const newIndex = this.currentViolationIndex + direction;
@@ -456,7 +525,10 @@
this.overlay.classList.remove("visible"),
this.panel.classList.remove("visible"),
setTimeout(() => {
- document.body.style.overflow = "";
+ (document.body.style.overflow = ""),
+ this.triggeringElement &&
+ this.triggeringElement.focus &&
+ this.triggeringElement.focus();
}, 300);
}
populateVersions(versions) {
@@ -512,7 +584,7 @@
if (row) {
const violationIndex = row.dataset.index,
violation = this.model.violations[violationIndex];
- this.view.showPanel(violation);
+ this.view.showPanel(violation, row);
}
}
handleGroupBy(value) {
@@ -520,7 +592,20 @@
this.view.updateTable();
}
handleShortcuts(event) {
+ if (this.view.overlay.classList.contains("visible"))
+ return (
+ this.view.trapFocus(event),
+ "Escape" === event.key
+ ? (event.preventDefault(), void this.handleEscapeKey())
+ : "ArrowLeft" === event.key || "ArrowUp" === event.key
+ ? (event.preventDefault(), void this.handleArrowKey(-1))
+ : "ArrowRight" === event.key || "ArrowDown" === event.key
+ ? (event.preventDefault(), void this.handleArrowKey(1))
+ : void 0
+ );
if ("INPUT" === document.activeElement.tagName) return;
+ if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey)
+ return;
const action = {
n: () => this.handleGroupBy("file"),
e: () => this.handleGroupBy("engine"),
@@ -680,7 +765,7 @@
font-size: 16px;
font-weight: normal;
margin: 0;
- padding: 20px 20px 20px 36px;
+ padding: 20px 0 20px 36px;
text-align: center;
}
@@ -692,14 +777,15 @@
font-size: 16px;
font-weight: normal;
margin: 0;
- padding: 20px 20px 20px 36px;
+ padding: 20px 0 20px 36px;
text-align: center;
}
- .header p.help {
+ .header a.help {
font-size: 16px;
margin: 0;
- padding: 20px 0 20px 20px;
+ padding: 20px 0 20px 0;
+ display: inline-block;
}
.boxes {
@@ -798,13 +884,13 @@
margin: 20px 20px;
}
- .toolbar h3 {
+ .toolbar h4 {
color: var(--color-text);
font-size: 14px;
}
@media screen and (max-width: 1200px) {
- .toolbar h3 {
+ .toolbar h4 {
display: none;
}
}
@@ -987,7 +1073,7 @@
opacity: 1;
}
- table th.asc::after {
+ table th.desc::after {
transform: rotate(180deg);
}
@@ -1073,6 +1159,12 @@
font-size: 12px;
}
+ .footer code {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ font-weight: normal;
+ }
+
.versions {
display: flex;
flex-direction: row;
@@ -1518,68 +1610,115 @@
|