Merge pull request #7504 from tchak/dev-axe-core
chore(axe-core): add a11y reports in dev environement
This commit is contained in:
commit
183dc4eb2b
4 changed files with 164 additions and 0 deletions
156
app/javascript/entrypoints/axe-core.ts
Normal file
156
app/javascript/entrypoints/axe-core.ts
Normal file
|
@ -0,0 +1,156 @@
|
|||
import type { AxeResults, NodeResult, RelatedNode } from 'axe-core';
|
||||
import axe from 'axe-core';
|
||||
|
||||
domReady().then(() => {
|
||||
axe.run(document.body, { reporter: 'v2' }).then((results) => {
|
||||
logToConsole(results);
|
||||
});
|
||||
});
|
||||
|
||||
// contrasted against Chrome default color of #ffffff
|
||||
const lightTheme = {
|
||||
serious: '#d93251',
|
||||
minor: '#d24700',
|
||||
text: 'black'
|
||||
};
|
||||
|
||||
// contrasted against Safari dark mode color of #535353
|
||||
const darkTheme = {
|
||||
serious: '#ffb3b3',
|
||||
minor: '#ffd500',
|
||||
text: 'white'
|
||||
};
|
||||
|
||||
const theme =
|
||||
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? darkTheme
|
||||
: lightTheme;
|
||||
|
||||
const boldCourier = 'font-weight:bold;font-family:Courier;';
|
||||
const critical = `color:${theme.serious};font-weight:bold;`;
|
||||
const serious = `color:${theme.serious};font-weight:normal;`;
|
||||
const moderate = `color:${theme.minor};font-weight:bold;`;
|
||||
const minor = `color:${theme.minor};font-weight:normal;`;
|
||||
const defaultReset = `font-color:${theme.text};font-weight:normal;`;
|
||||
|
||||
function logToConsole(results: AxeResults): void {
|
||||
console.group('%cNew axe issues', serious);
|
||||
results.violations.forEach((result) => {
|
||||
let fmt: string;
|
||||
switch (result.impact) {
|
||||
case 'critical':
|
||||
fmt = critical;
|
||||
break;
|
||||
case 'serious':
|
||||
fmt = serious;
|
||||
break;
|
||||
case 'moderate':
|
||||
fmt = moderate;
|
||||
break;
|
||||
case 'minor':
|
||||
fmt = minor;
|
||||
break;
|
||||
default:
|
||||
fmt = minor;
|
||||
break;
|
||||
}
|
||||
console.groupCollapsed(
|
||||
'%c%s: %c%s %s',
|
||||
fmt,
|
||||
result.impact,
|
||||
defaultReset,
|
||||
result.help,
|
||||
result.helpUrl
|
||||
);
|
||||
result.nodes.forEach((node) => {
|
||||
failureSummary(node, 'any');
|
||||
failureSummary(node, 'none');
|
||||
});
|
||||
console.groupEnd();
|
||||
});
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
function failureSummary(node: NodeResult, key: AxeCoreNodeResultKey): void {
|
||||
if (node[key].length > 0) {
|
||||
logElement(node, console.groupCollapsed);
|
||||
logHtml(node);
|
||||
logFailureMessage(node, key);
|
||||
|
||||
let relatedNodes: RelatedNode[] = [];
|
||||
node[key].forEach((check) => {
|
||||
relatedNodes = relatedNodes.concat(check.relatedNodes ?? []);
|
||||
});
|
||||
|
||||
if (relatedNodes.length > 0) {
|
||||
console.groupCollapsed('Related nodes');
|
||||
relatedNodes.forEach((relatedNode) => {
|
||||
logElement(relatedNode, console.log);
|
||||
logHtml(relatedNode);
|
||||
});
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
console.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
function logFailureMessage(node: NodeResult, key: AxeCoreNodeResultKey): void {
|
||||
// this exists on axe but we don't export it as part of the typescript
|
||||
// namespace, so just let me use it as I need
|
||||
const message: string = (
|
||||
axe as unknown as AxeWithAudit
|
||||
)._audit.data.failureSummaries[key].failureMessage(
|
||||
node[key].map((check) => check.message || '')
|
||||
);
|
||||
|
||||
console.error(message);
|
||||
}
|
||||
|
||||
function logElement(
|
||||
node: NodeResult | RelatedNode,
|
||||
logFn: (...args: unknown[]) => void
|
||||
): void {
|
||||
const el = document.querySelector(node.target.toString());
|
||||
if (!el) {
|
||||
logFn('Selector: %c%s', boldCourier, node.target.toString());
|
||||
} else {
|
||||
logFn('Element: %o', el);
|
||||
}
|
||||
}
|
||||
|
||||
function logHtml(node: NodeResult | RelatedNode): void {
|
||||
console.log('HTML: %c%s', boldCourier, node.html);
|
||||
}
|
||||
|
||||
type AxeCoreNodeResultKey = 'any' | 'all' | 'none';
|
||||
|
||||
interface AxeWithAudit {
|
||||
_audit: {
|
||||
data: {
|
||||
failureSummaries: {
|
||||
any: {
|
||||
failureMessage: (args: string[]) => string;
|
||||
};
|
||||
all: {
|
||||
failureMessage: (args: string[]) => string;
|
||||
};
|
||||
none: {
|
||||
failureMessage: (args: string[]) => string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function domReady() {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (document.readyState == 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => resolve(), {
|
||||
once: true
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
|
@ -48,6 +48,8 @@
|
|||
- if content_for?(:footer)
|
||||
= content_for(:footer)
|
||||
|
||||
- if Rails.env.development?
|
||||
= vite_typescript_tag 'axe-core'
|
||||
= yield :charts_js
|
||||
|
||||
// Container for custom turbo-stream actions
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
"@vitejs/plugin-legacy": "^1.2.3",
|
||||
"@vitejs/plugin-react": "^1.1.4",
|
||||
"@vitejs/plugin-react-refresh": "^1.3.0",
|
||||
"axe-core": "^4.4.2",
|
||||
"del-cli": "^4.0.1",
|
||||
"eslint": "^8.17.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
|
|
|
@ -3107,6 +3107,11 @@ aws4@^1.8.0:
|
|||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
|
||||
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
|
||||
|
||||
axe-core@^4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.2.tgz#dcf7fb6dea866166c3eab33d68208afe4d5f670c"
|
||||
integrity sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA==
|
||||
|
||||
babel-plugin-dynamic-import-node@^2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3"
|
||||
|
|
Loading…
Reference in a new issue