diff --git a/accessibility-checker-extension/src/assets/img/3.1Checker123.png b/accessibility-checker-extension/src/assets/img/3.1Checker123.png new file mode 100644 index 000000000..20f942c27 Binary files /dev/null and b/accessibility-checker-extension/src/assets/img/3.1Checker123.png differ diff --git a/accessibility-checker-extension/src/ts/background/backgroundController.ts b/accessibility-checker-extension/src/ts/background/backgroundController.ts index 2bf7d39fb..8225804a6 100644 --- a/accessibility-checker-extension/src/ts/background/backgroundController.ts +++ b/accessibility-checker-extension/src/ts/background/backgroundController.ts @@ -31,10 +31,11 @@ export type TabChangeType = { type eLevel = "Violation" | "Needs review" | "Recommendation"; -export function issueBaselineMatch(baselineIssue: {ruleId: string, reasonId:string, path: { dom: string}}, issue: IIssue) { +export function issueBaselineMatch(baselineIssue: {ruleId: string, reasonId:string, path: { dom: string}, messageArgs: string[]}, issue: IIssue) { return baselineIssue.ruleId === issue.ruleId && baselineIssue.reasonId === issue.reasonId - && baselineIssue.path.dom === issue.path.dom; + && baselineIssue.path.dom === issue.path.dom + && JSON.stringify(baselineIssue.messageArgs) === JSON.stringify(issue.messageArgs); } class BackgroundController extends Controller { @@ -399,6 +400,17 @@ class BackgroundController extends Controller { } }); }, [settings]); + + // Remove issues that are essentially equivalent to the user (same path, rule, reason, and message) + for (let idx=0; idx extends React.Component{ignoreAction} diff --git a/accessibility-checker-extension/src/ts/devtools/devtoolsController.ts b/accessibility-checker-extension/src/ts/devtools/devtoolsController.ts index 6dc264f1f..2e05b19dd 100644 --- a/accessibility-checker-extension/src/ts/devtools/devtoolsController.ts +++ b/accessibility-checker-extension/src/ts/devtools/devtoolsController.ts @@ -83,6 +83,9 @@ export class DevtoolsController extends Controller { }); } + /** + * Set stored reports + */ public async setStoredReportsMeta(updateMetaArr: IStoredReportMeta[]) : Promise { return await this.hook("setStoredReportsMeta", updateMetaArr, async () => { if (updateMetaArr.length === 0) { @@ -177,6 +180,65 @@ export class DevtoolsController extends Controller { private scanCounter = 1; + valueMap: { [key: string]: { [key2: string]: string } } = { + "VIOLATION": { + "POTENTIAL": "Needs review", + "FAIL": "Violation", + "PASS": "Pass", + "MANUAL": "Needs review" + }, + "RECOMMENDATION": { + "POTENTIAL": "Recommendation", + "FAIL": "Recommendation", + "PASS": "Pass", + "MANUAL": "Recommendation" + }, + "INFORMATION": { + "POTENTIAL": "Needs review", + "FAIL": "Violation", + "PASS": "Pass", + "MANUAL": "Recommendation" + } + }; + + initCount() { + return { + "Violation": 0, + "Needs review": 0, + "Recommendation": 0, + "Hidden": 0, + "Pass": 0, + "total": 0, + } + } + + async getCountsWithHidden (reportCounts: IReport["counts"], ignored: IIssue[]) { + let counts = this.initCount(); // setup counts + // populate initial counts + counts.Violation = reportCounts.Violation; + counts["Needs review"] = reportCounts["Needs review"]; + counts.Recommendation = reportCounts.Recommendation; + counts.Hidden = ignored.length; + counts.Pass = reportCounts.Pass; + + // correct issue type counts to take into account the hidden issues + if (ignored.length > 0) { // if we have hidden + for (const ignoredIssue of ignored) { + if ("Violation" === this.valueMap[ignoredIssue.value[0]][ignoredIssue.value[1]]) { + counts.Violation--; + } + if ("Needs review" === this.valueMap[ignoredIssue.value[0]][ignoredIssue.value[1]]) { + counts["Needs review"]--; + } + if ("Recommendation" === this.valueMap[ignoredIssue.value[0]][ignoredIssue.value[1]]) { + counts.Recommendation--; + } + } + } + counts.total = counts.Violation + counts["Needs review"] + counts.Recommendation; + return counts; + } + /** * Set report */ @@ -187,6 +249,8 @@ export class DevtoolsController extends Controller { if (report) { let tabId = getTabId(); let tabInfo = await bgController.getTabInfo(tabId); + let ignored: IIssue[] = await bgController.getIgnore(tabInfo.url!); + let newCounts = await this.getCountsWithHidden(report.counts, ignored); const now = new Date().getTime(); devtoolsState!.lastReportMeta = { id: devtoolsState!.storedReports.length+"", @@ -200,13 +264,14 @@ export class DevtoolsController extends Controller { storedScanData: MultiScanData.issues_sheet_rows({ settings: settings, report: report, + ignored: ignored, pageTitle: tabInfo.title!, pageURL: tabInfo.url!, timestamp: now+"", rulesets: await bgController.getRulesets(tabId!) }), testedUniqueElements: report.testedUniqueElements, - counts: report.counts + counts: newCounts }; if (devtoolsState?.storeReports) { devtoolsState.storedReports.push(devtoolsState.lastReportMeta); @@ -534,6 +599,7 @@ export class DevtoolsController extends Controller { this.xlsxReportHandler("current"); } }); + } private async htmlReportHandler() { diff --git a/accessibility-checker-extension/src/ts/docs/QuickGuideACApp.tsx b/accessibility-checker-extension/src/ts/docs/QuickGuideACApp.tsx index 9aee5c578..a638230ed 100644 --- a/accessibility-checker-extension/src/ts/docs/QuickGuideACApp.tsx +++ b/accessibility-checker-extension/src/ts/docs/QuickGuideACApp.tsx @@ -308,10 +308,9 @@ export class QuickGuideACApp extends React.Component<{}, quickGuideACAppState> {

6. Show/Hide issues

-

- The Hide feature allows issues to be ignored or marked as resolved. - When this feature is used, issues are not only hidden from view, they are also subtracted from the respective issue counts. - Issues that are determined to be irrelevant or resolved can be hidden and removed from the counts towards achieving a goal of zero counts both in the issues list and in the Scan summary report. +

+ The Hide feature allows issues to be removed from view and subtracted from the respective issue counts and Scan summary report. + This can be useful for showing a set of issues or reflecting progress by hiding issues that have been resolved or identified to be fixed later.

{

DevTools with 'Elements tab', 'Accessibility Checker tab', and blue scan button highlighted and numbered 1 to 3

@@ -613,10 +613,8 @@ class UsingACApp extends React.Component<{}, UsingACAppState> {

3.8 Show/Hide issues

- The Hide feature allows issues to be ignored or marked as resolved. - When this feature is used, issues are not only hidden from view, they are also subtracted from the respective issue counts. - Issues that are determined to be irrelevant or resolved can be hidden and removed from the counts - towards achieving a goal of zero counts both in the issues list and in the Scan summary report. + The Hide feature allows issues to be removed from view and subtracted from the respective issue counts and Scan summary report. + This can be useful for showing a set of issues or reflecting progress by hiding issues that have been resolved or identified to be fixed later.

(a[1] < b[1]) ? 1 : -1); + // add array of rows @@ -612,9 +730,8 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; - // row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; - // row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, @@ -627,6 +744,12 @@ export default class MultiScanReport { bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; }); @@ -656,13 +779,30 @@ export default class MultiScanReport { right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + // hidden counts for level 1 needs review + let level1HiddenNRCounts = 0; + for (const property in level1NRrowValues) { + level1HiddenNRCounts += level1NRrowValues[property][1]; + } + + level1NeedsReviewRow.getCell(3).value = level1HiddenNRCounts; // hidden level 1 needs review + level1NeedsReviewRow.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level1NeedsReviewRow.getCell(3).font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; + level1NeedsReviewRow.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF4E08A' } }; + level1NeedsReviewRow.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 1 Needs review Rows // build rows rowArray = []; for (const property in level1NRrowValues) { - let row = [" " + `${property}`, parseInt(`${level1NRrowValues[property]}`) + let row = [" " + `${property}`, parseInt(`${level1NRrowValues[property][0]}`), parseInt(`${level1NRrowValues[property][1]}`) ]; rowArray.push(row); } @@ -680,9 +820,8 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; - //row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; - //row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, @@ -695,6 +834,12 @@ export default class MultiScanReport { bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; }); // Level 1 Recommendation title @@ -723,6 +868,23 @@ export default class MultiScanReport { right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + // hidden counts for level 1 recommendations + let level1HiddenRCounts = 0; + for (const property in level1RrowValues) { + level1HiddenRCounts += level1RrowValues[property][1]; + } + + level1RecommendationRow.getCell(3).value = level1HiddenRCounts; // hidden level 1 recommendations + level1RecommendationRow.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level1RecommendationRow.getCell(3).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 12 }; + level1RecommendationRow.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF96A9D7' } }; + level1RecommendationRow.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 1 Recommendation Rows @@ -730,7 +892,7 @@ export default class MultiScanReport { rowArray = []; for (const property in level1RrowValues) { - let row = [" " + `${property}`, parseInt(`${level1RrowValues[property]}`) + let row = [" " + `${property}`, parseInt(`${level1RrowValues[property][0]}`), parseInt(`${level1RrowValues[property][1]}`) ]; rowArray.push(row); } @@ -748,9 +910,8 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; - //row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; - //row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, @@ -763,6 +924,12 @@ export default class MultiScanReport { bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; }); @@ -777,11 +944,23 @@ export default class MultiScanReport { level2Row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; level2Row.getCell(1).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; level2Row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; - + level2Row.getCell(2).value = level2Counts[0]; // total Level 2 issues level2Row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; level2Row.getCell(2).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; level2Row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; + + level2Row.getCell(3).value = level2Counts[4]; // Level 2 hidden counts + level2Row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level2Row.getCell(3).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; + level2Row.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; + level2Row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 2 Violation title const level2ViolationRow = worksheet.addRow(["", 0]); @@ -809,13 +988,30 @@ export default class MultiScanReport { right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + // hidden counts for level 2 violations + let level2HiddenVCounts = 0; + for (const property in level2VrowValues) { + level2HiddenVCounts += level2VrowValues[property][1]; + } + + level2ViolationRow.getCell(3).value = level2HiddenVCounts; // hidden level 2 violations + level2ViolationRow.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level2ViolationRow.getCell(3).font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; + level2ViolationRow.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE4AAAF' } }; + level2ViolationRow.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 2 Violation Rows // build rows rowArray = []; for (const property in level2VrowValues) { - let row = [" " + `${property}`, parseInt(`${level2VrowValues[property]}`) + let row = [" " + `${property}`, parseInt(`${level2VrowValues[property][0]}`), parseInt(`${level2VrowValues[property][1]}`) ]; rowArray.push(row); } @@ -831,9 +1027,8 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; - //row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; - //row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, @@ -846,6 +1041,12 @@ export default class MultiScanReport { bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; }); @@ -875,13 +1076,30 @@ export default class MultiScanReport { right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + // hidden counts for level 2 needs review + let level2HiddenNRCounts = 0; + for (const property in level2NRrowValues) { + level2HiddenNRCounts += level2NRrowValues[property][1]; + } + + level2NeedsReviewRow.getCell(3).value = level2HiddenNRCounts; // hidden level 2 needs review + level2NeedsReviewRow.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level2NeedsReviewRow.getCell(3).font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; + level2NeedsReviewRow.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF4E08A' } }; + level2NeedsReviewRow.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 2 Needs review Rows // build rows rowArray = []; for (const property in level2NRrowValues) { - let row = [" " + `${property}`, parseInt(`${level2NRrowValues[property]}`) + let row = [" " + `${property}`, parseInt(`${level2NRrowValues[property][0]}`), parseInt(`${level2NRrowValues[property][1]}`) ]; rowArray.push(row); } @@ -899,9 +1117,8 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; - //row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; - //row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, @@ -914,6 +1131,12 @@ export default class MultiScanReport { bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; }); // Level 2 Recommendation title @@ -942,13 +1165,30 @@ export default class MultiScanReport { right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + // hidden counts for level 2 recommendations + let level2HiddenRCounts = 0; + for (const property in level2RrowValues) { + level2HiddenRCounts += level2RrowValues[property][1]; + } + + level2RecommendationRow.getCell(3).value = level2HiddenRCounts; // hidden level 2 recommendations + level2RecommendationRow.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level2RecommendationRow.getCell(3).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 12 }; + level2RecommendationRow.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF96A9D7' } }; + level2RecommendationRow.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 2 Recommendation Rows // build rows rowArray = []; for (const property in level2RrowValues) { - let row = [" " + `${property}`, parseInt(`${level2RrowValues[property]}`) + let row = [" " + `${property}`, parseInt(`${level2RrowValues[property][0]}`), parseInt(`${level2RrowValues[property][1]}`) ]; rowArray.push(row); } @@ -966,6 +1206,7 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; //row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; //row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; @@ -981,6 +1222,12 @@ export default class MultiScanReport { bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; }); ///////////////////////////// @@ -1000,6 +1247,17 @@ export default class MultiScanReport { level3Row.getCell(2).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; level3Row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; + level3Row.getCell(3).value = level3Counts[4]; // Level 1 hidden counts + level3Row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level3Row.getCell(3).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; + level3Row.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; + level3Row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 3 Violation title const level3ViolationRow = worksheet.addRow(["", 0]); level3ViolationRow.height = 18; // target is 21 @@ -1026,13 +1284,30 @@ export default class MultiScanReport { right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + // hidden counts for level 3 violations + let level3HiddenVCounts = 0; + for (const property in level3VrowValues) { + level3HiddenVCounts += level3VrowValues[property][1]; + } + + level3ViolationRow.getCell(3).value = level3HiddenVCounts; // hidden level 3 violations + level3ViolationRow.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level3ViolationRow.getCell(3).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 12 }; + level3ViolationRow.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE4AAAF' } }; + level3ViolationRow.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 3 Violation Rows // build rows rowArray = []; for (const property in level3VrowValues) { - let row = [" " + `${property}`, parseInt(`${level3VrowValues[property]}`) + let row = [" " + `${property}`, parseInt(`${level3VrowValues[property][0]}`), parseInt(`${level3VrowValues[property][1]}`) ]; rowArray.push(row); } @@ -1048,9 +1323,8 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; - //row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; - //row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, @@ -1063,6 +1337,12 @@ export default class MultiScanReport { bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; }); @@ -1092,13 +1372,30 @@ export default class MultiScanReport { right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + // hidden counts for level 3 needs review + let level3HiddenNRCounts = 0; + for (const property in level3NRrowValues) { + level3HiddenNRCounts += level3NRrowValues[property][1]; + } + + level3NeedsReviewRow.getCell(3).value = level3HiddenNRCounts; // hidden level 3 needs review + level3NeedsReviewRow.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level3NeedsReviewRow.getCell(3).font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; + level3NeedsReviewRow.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF4E08A' } }; + level3NeedsReviewRow.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 3 Needs review Rows // build rows rowArray = []; for (const property in level3NRrowValues) { - let row = [" " + `${property}`, parseInt(`${level3NRrowValues[property]}`) + let row = [" " + `${property}`, parseInt(`${level3NRrowValues[property][0]}`), parseInt(`${level3NRrowValues[property][1]}`) ]; rowArray.push(row); } @@ -1116,9 +1413,8 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; - //row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; - //row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, @@ -1131,6 +1427,12 @@ export default class MultiScanReport { bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; }); // Level 3 Recommendation title @@ -1159,13 +1461,30 @@ export default class MultiScanReport { right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + // hidden counts for level 3 recommendations + let level3HiddenRCounts = 0; + for (const property in level3RrowValues) { + level3HiddenRCounts += level3RrowValues[property][1]; + } + + level3RecommendationRow.getCell(3).value = level3HiddenRCounts; // hidden level 3 recommendations + level3RecommendationRow.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level3RecommendationRow.getCell(3).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 12 }; + level3RecommendationRow.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF96A9D7' } }; + level3RecommendationRow.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 3 Recommendation Rows // build rows rowArray = []; for (const property in level3RrowValues) { - let row = [" " + `${property}`, parseInt(`${level3RrowValues[property]}`) + let row = [" " + `${property}`, parseInt(`${level3RrowValues[property][0]}`), parseInt(`${level3RrowValues[property][1]}`) ]; rowArray.push(row); } @@ -1183,9 +1502,8 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; - //row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; - //row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, @@ -1198,6 +1516,12 @@ export default class MultiScanReport { bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; }); @@ -1218,6 +1542,17 @@ export default class MultiScanReport { level4Row.getCell(2).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; level4Row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; + level4Row.getCell(3).value = level4Counts[4]; // Level 1 hidden counts + level4Row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level4Row.getCell(3).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; + level4Row.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; + level4Row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 4 Violation title const level4ViolationRow = worksheet.addRow(["", 0]); level4ViolationRow.height = 18; // target is 21 @@ -1244,13 +1579,30 @@ export default class MultiScanReport { right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + // hidden counts for level 4 violations + let level4HiddenVCounts = 0; + for (const property in level4VrowValues) { + level4HiddenVCounts += level4VrowValues[property][1]; + } + + level4ViolationRow.getCell(3).value = level4HiddenVCounts; // total level 4 violations + level4ViolationRow.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level4ViolationRow.getCell(3).font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; + level4ViolationRow.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE4AAAF' } }; + level4ViolationRow.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 4 Violation Rows // build rows rowArray = []; for (const property in level4VrowValues) { - let row = [" " + `${property}`, parseInt(`${level4VrowValues[property]}`) + let row = [" " + `${property}`, parseInt(`${level4VrowValues[property][0]}`), parseInt(`${level4VrowValues[property][1]}`) ]; rowArray.push(row); } @@ -1266,9 +1618,8 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; - //row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; - //row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, @@ -1281,6 +1632,12 @@ export default class MultiScanReport { bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; }); @@ -1310,13 +1667,30 @@ export default class MultiScanReport { right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + // hidden counts for level 4 needs review + let level4HiddenNRCounts = 0; + for (const property in level4NRrowValues) { + level4HiddenNRCounts += level4NRrowValues[property][1]; + } + + level4NeedsReviewRow.getCell(3).value = level4HiddenNRCounts; // total level 4 needs review + level4NeedsReviewRow.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level4NeedsReviewRow.getCell(3).font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; + level4NeedsReviewRow.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF4E08A' } }; + level4NeedsReviewRow.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 4 Needs review Rows // build rows rowArray = []; for (const property in level4NRrowValues) { - let row = [" " + `${property}`, parseInt(`${level4NRrowValues[property]}`) + let row = [" " + `${property}`, parseInt(`${level4NRrowValues[property][0]}`), parseInt(`${level4NRrowValues[property][1]}`) ]; rowArray.push(row); } @@ -1334,6 +1708,7 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; //row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; //row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; @@ -1349,6 +1724,12 @@ export default class MultiScanReport { bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; }); // Level 4 Recommendation title @@ -1377,13 +1758,30 @@ export default class MultiScanReport { right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; + // hidden counts for level 4 recommendations + let level4HiddenRCounts = 0; + for (const property in level4RrowValues) { + level4HiddenRCounts += level4RrowValues[property][1]; + } + + level4RecommendationRow.getCell(3).value = level4HiddenRCounts; // total level 4 recommendations + level4RecommendationRow.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; + level4RecommendationRow.getCell(3).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 12 }; + level4RecommendationRow.getCell(3).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF96A9D7' } }; + level4RecommendationRow.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + // Level 4 Recommendation Rows // build rows rowArray = []; for (const property in level4RrowValues) { - let row = [" " + `${property}`, parseInt(`${level4RrowValues[property]}`) + let row = [" " + `${property}`, parseInt(`${level4RrowValues[property][0]}`), parseInt(`${level4RrowValues[property][1]}`) ]; rowArray.push(row); } @@ -1401,17 +1799,21 @@ export default class MultiScanReport { row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; + row.getCell(3).alignment = { vertical: "middle", horizontal: "center" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; - //row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; - //row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, - // right: {style:'thin', color: {argb: 'FFA6A6A6'}} }; row.getCell(2).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + + bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, + right: { style: 'thin', color: { argb: 'FFA6A6A6' } } + }; + row.getCell(3).border = { + top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } @@ -1429,7 +1831,7 @@ export default class MultiScanReport { const myStoredData = storedScan.storedScanData; for (let i = 0; i < myStoredData.length; i++) { let row = [myStoredData[i][0], myStoredData[i][1], storedScan.label, - myStoredData[i][3], myStoredData[i][4], Number.isNaN(myStoredData[i][5]) ? "n/a" : myStoredData[i][5], + myStoredData[i][3], myStoredData[i][14] ? "Hidden:"+myStoredData[i][4] : myStoredData[i][4], Number.isNaN(myStoredData[i][5]) ? "n/a" : myStoredData[i][5], myStoredData[i][6], Number.isNaN(myStoredData[i][5]) ? "n/a" : myStoredData[i][7], myStoredData[i][8], myStoredData[i][9], myStoredData[i][10], myStoredData[i][11], myStoredData[i][12], myStoredData[i][13] @@ -1448,7 +1850,7 @@ export default class MultiScanReport { { col: 'B', width: 20.5, alignment: { vertical: "middle", horizontal: "left" } }, { col: 'C', width: 21.0, alignment: { vertical: "middle", horizontal: "center" } }, { col: 'D', width: 18.5, alignment: { vertical: "middle", horizontal: "left" } }, - { col: 'E', width: 17.0, alignment: { vertical: "middle", horizontal: "center" } }, + { col: 'E', width: 23.0, alignment: { vertical: "middle", horizontal: "center" } }, { col: 'F', width: 17.17, alignment: { vertical: "middle", horizontal: "center" } }, { col: 'G', width: 17.17, alignment: { vertical: "middle", horizontal: "left" } }, { col: 'H', width: 17.17, alignment: { vertical: "middle", horizontal: "center" } }, @@ -1594,6 +1996,7 @@ export default class MultiScanReport { { key1: 'Violations', key2: 'Accessibility failures that need to be corrected.' }, { key1: 'Needs review', key2: 'Issues that may not be a violation. These need a manual review to identify whether there is an accessibility problem.' }, { key1: 'Recommendations', key2: 'Opportunities to apply best practices to further improve accessibility.' }, + { key1: 'Hidden', key2: 'Issues the user has selected to be hidden from view and subtracted from the issue counts.' }, { key1: '% elements without violations', key2: 'Percentage of elements on the page that had no violations found.' }, { key1: '% elements without violations or items to review', key2: 'Percentage of elements on the page that had no violations found and no items to review.' }, { key1: 'Level 1,2,3', key2: 'Priority level defined by the IBM Equal Access Toolkit. See https://www.ibm.com/able/toolkit/plan/overview#pace-of-completion for details.' } @@ -1671,13 +2074,26 @@ export default class MultiScanReport { } - private static countDuplicatesInArray(array: string[]) { - let count : { + private static countDuplicatesInArray(array: {issueDef: string; hidden: boolean;}[]) { // count issues with duplicate description string + let dupCount : { + [key: string]: number + } = {}; + let hidCount : { [key: string]: number } = {}; + let finalCount: { + [key: string]: [number,number] + } = {}; for (const item of array) { - count[item] = (count[item] || 0) + 1; + if (!item.hidden) { + dupCount[item.issueDef] = (dupCount[item.issueDef] || 0) + 1; + hidCount[item.issueDef] = (hidCount[item.issueDef] || 0) + 0; + } else { + dupCount[item.issueDef] = (dupCount[item.issueDef] || 0) + 0; + hidCount[item.issueDef] = (hidCount[item.issueDef] || 0) + 1; + } + finalCount[item.issueDef] = [dupCount[item.issueDef], hidCount[item.issueDef]] } - return count; - } + return finalCount; + } } \ No newline at end of file