@tanstack/devtools-a11y 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/esm/core/components/IssueCard.d.ts +10 -0
- package/dist/esm/core/components/IssueCard.js +83 -0
- package/dist/esm/core/components/IssueCard.js.map +1 -0
- package/dist/esm/core/components/IssueList.d.ts +6 -0
- package/dist/esm/core/components/IssueList.js +134 -0
- package/dist/esm/core/components/IssueList.js.map +1 -0
- package/dist/esm/core/components/Settings.d.ts +6 -0
- package/dist/esm/core/components/Settings.js +251 -0
- package/dist/esm/core/components/Settings.js.map +1 -0
- package/dist/esm/core/components/Shell.d.ts +2 -0
- package/dist/esm/core/components/Shell.js +214 -0
- package/dist/esm/core/components/Shell.js.map +1 -0
- package/dist/esm/core/components/index.d.ts +2 -0
- package/dist/esm/core/components/index.js +14 -0
- package/dist/esm/core/components/index.js.map +1 -0
- package/dist/esm/core/contexts/allyContext.d.ts +17 -0
- package/dist/esm/core/contexts/allyContext.js +66 -0
- package/dist/esm/core/contexts/allyContext.js.map +1 -0
- package/dist/esm/core/core.d.ts +19 -0
- package/dist/esm/core/core.js +8 -0
- package/dist/esm/core/core.js.map +1 -0
- package/dist/esm/core/index.d.ts +9 -0
- package/dist/esm/core/index.js +9 -0
- package/dist/esm/core/index.js.map +1 -0
- package/dist/esm/core/production.d.ts +2 -0
- package/dist/esm/core/production.js +4 -0
- package/dist/esm/core/styles/styles.d.ts +85 -0
- package/dist/esm/core/styles/styles.js +547 -0
- package/dist/esm/core/styles/styles.js.map +1 -0
- package/dist/esm/core/types/types.d.ts +141 -0
- package/dist/esm/core/utils/ally-audit.utils.d.ts +19 -0
- package/dist/esm/core/utils/ally-audit.utils.js +226 -0
- package/dist/esm/core/utils/ally-audit.utils.js.map +1 -0
- package/dist/esm/core/utils/config.utils.d.ts +17 -0
- package/dist/esm/core/utils/config.utils.js +63 -0
- package/dist/esm/core/utils/config.utils.js.map +1 -0
- package/dist/esm/core/utils/custom-audit.utils.d.ts +13 -0
- package/dist/esm/core/utils/custom-audit.utils.js +426 -0
- package/dist/esm/core/utils/custom-audit.utils.js.map +1 -0
- package/dist/esm/core/utils/export-audit.uitls.d.ts +17 -0
- package/dist/esm/core/utils/export-audit.uitls.js +83 -0
- package/dist/esm/core/utils/export-audit.uitls.js.map +1 -0
- package/dist/esm/core/utils/ui.utils.d.ts +24 -0
- package/dist/esm/core/utils/ui.utils.js +330 -0
- package/dist/esm/core/utils/ui.utils.js.map +1 -0
- package/dist/esm/react/A11yDevtools.d.ts +5 -0
- package/dist/esm/react/A11yDevtools.js +8 -0
- package/dist/esm/react/A11yDevtools.js.map +1 -0
- package/dist/esm/react/index.d.ts +8 -0
- package/dist/esm/react/index.js +11 -0
- package/dist/esm/react/index.js.map +1 -0
- package/dist/esm/react/plugin.d.ts +12 -0
- package/dist/esm/react/plugin.js +11 -0
- package/dist/esm/react/plugin.js.map +1 -0
- package/dist/esm/react/production/A11yDevtools.d.ts +5 -0
- package/dist/esm/react/production/A11yDevtools.js +8 -0
- package/dist/esm/react/production/A11yDevtools.js.map +1 -0
- package/dist/esm/react/production/plugin.d.ts +7 -0
- package/dist/esm/react/production/plugin.js +11 -0
- package/dist/esm/react/production/plugin.js.map +1 -0
- package/dist/esm/react/production.d.ts +3 -0
- package/dist/esm/react/production.js +5 -0
- package/dist/esm/solid/A11yDevtools.d.ts +5 -0
- package/dist/esm/solid/A11yDevtools.js +8 -0
- package/dist/esm/solid/A11yDevtools.js.map +1 -0
- package/dist/esm/solid/index.d.ts +8 -0
- package/dist/esm/solid/index.js +9 -0
- package/dist/esm/solid/index.js.map +1 -0
- package/dist/esm/solid/plugin.d.ts +12 -0
- package/dist/esm/solid/plugin.js +11 -0
- package/dist/esm/solid/plugin.js.map +1 -0
- package/dist/esm/solid/production/A11yDevtools.d.ts +5 -0
- package/dist/esm/solid/production/A11yDevtools.js +8 -0
- package/dist/esm/solid/production/A11yDevtools.js.map +1 -0
- package/dist/esm/solid/production/plugin.d.ts +7 -0
- package/dist/esm/solid/production/plugin.js +11 -0
- package/dist/esm/solid/production/plugin.js.map +1 -0
- package/dist/esm/solid/production.d.ts +3 -0
- package/dist/esm/solid/production.js +3 -0
- package/package.json +110 -7
- package/src/core/components/IssueCard.tsx +75 -0
- package/src/core/components/IssueList.tsx +155 -0
- package/src/core/components/Settings.tsx +221 -0
- package/src/core/components/Shell.tsx +154 -0
- package/src/core/components/index.tsx +12 -0
- package/src/core/contexts/allyContext.tsx +118 -0
- package/src/core/core.tsx +11 -0
- package/src/core/index.ts +10 -0
- package/src/core/production.ts +5 -0
- package/src/core/styles/styles.ts +556 -0
- package/src/core/types/types.ts +177 -0
- package/src/core/utils/ally-audit.utils.ts +345 -0
- package/src/core/utils/config.utils.ts +68 -0
- package/src/core/utils/custom-audit.utils.ts +643 -0
- package/src/core/utils/export-audit.uitls.ts +180 -0
- package/src/core/utils/ui.utils.ts +483 -0
- package/src/react/A11yDevtools.ts +12 -0
- package/src/react/index.ts +16 -0
- package/src/react/plugin.ts +9 -0
- package/src/react/production/A11yDevtools.ts +11 -0
- package/src/react/production/plugin.ts +9 -0
- package/src/react/production.ts +7 -0
- package/src/solid/A11yDevtools.ts +11 -0
- package/src/solid/index.ts +14 -0
- package/src/solid/plugin.ts +9 -0
- package/src/solid/production/A11yDevtools.ts +10 -0
- package/src/solid/production/plugin.ts +9 -0
- package/src/solid/production.ts +5 -0
- package/README.md +0 -45
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Severity threshold for filtering issues
|
|
3
|
+
*/
|
|
4
|
+
export type SeverityThreshold = 'critical' | 'serious' | 'moderate' | 'minor';
|
|
5
|
+
/**
|
|
6
|
+
* Rule set presets
|
|
7
|
+
*/
|
|
8
|
+
export type RuleSetPreset = 'wcag2a' | 'wcag2aa' | 'wcag21aa' | 'wcag22aa' | 'section508' | 'best-practice' | 'all';
|
|
9
|
+
/**
|
|
10
|
+
* Rule categories (axe-core tags)
|
|
11
|
+
*/
|
|
12
|
+
export type RuleCategory = 'all' | 'cat.aria' | 'cat.color' | 'cat.forms' | 'cat.keyboard' | 'cat.language' | 'cat.name-role-value' | 'cat.parsing' | 'cat.semantics' | 'cat.sensory-and-visual-cues' | 'cat.structure' | 'cat.tables' | 'cat.text-alternatives' | 'cat.time-and-media';
|
|
13
|
+
/**
|
|
14
|
+
* Represents a single node affected by an accessibility issue
|
|
15
|
+
*/
|
|
16
|
+
export interface A11yNode {
|
|
17
|
+
/** CSS selector for the element */
|
|
18
|
+
selector: string;
|
|
19
|
+
/** HTML snippet of the element */
|
|
20
|
+
html: string;
|
|
21
|
+
/** XPath to the element (optional) */
|
|
22
|
+
xpath?: string;
|
|
23
|
+
/** Failure summary for this specific node */
|
|
24
|
+
failureSummary?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Represents a single accessibility issue
|
|
28
|
+
*/
|
|
29
|
+
export interface A11yIssue {
|
|
30
|
+
/** Unique identifier for this issue instance */
|
|
31
|
+
id: string;
|
|
32
|
+
/** The axe-core rule ID */
|
|
33
|
+
ruleId: string;
|
|
34
|
+
/** Impact severity level */
|
|
35
|
+
impact: SeverityThreshold;
|
|
36
|
+
/** Human-readable description of the issue */
|
|
37
|
+
message: string;
|
|
38
|
+
/** Detailed help text */
|
|
39
|
+
help: string;
|
|
40
|
+
/** URL to learn more about this issue */
|
|
41
|
+
helpUrl: string;
|
|
42
|
+
/** WCAG tags associated with this rule */
|
|
43
|
+
wcagTags: Array<string>;
|
|
44
|
+
/** DOM nodes affected by this issue */
|
|
45
|
+
nodes: Array<A11yNode>;
|
|
46
|
+
/** Whether this issue meets the current severity threshold */
|
|
47
|
+
meetsThreshold: boolean;
|
|
48
|
+
/** Timestamp when this issue was detected */
|
|
49
|
+
timestamp: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Summary statistics for an audit
|
|
53
|
+
*/
|
|
54
|
+
export interface A11ySummary {
|
|
55
|
+
total: number;
|
|
56
|
+
critical: number;
|
|
57
|
+
serious: number;
|
|
58
|
+
moderate: number;
|
|
59
|
+
minor: number;
|
|
60
|
+
passes: number;
|
|
61
|
+
incomplete: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Result of an accessibility audit
|
|
65
|
+
*/
|
|
66
|
+
export interface A11yAuditResult {
|
|
67
|
+
/** All issues found */
|
|
68
|
+
issues: Array<A11yIssue>;
|
|
69
|
+
/** Summary statistics */
|
|
70
|
+
summary: A11ySummary;
|
|
71
|
+
/** Timestamp when the audit was run */
|
|
72
|
+
timestamp: number;
|
|
73
|
+
/** URL of the page audited */
|
|
74
|
+
url: string;
|
|
75
|
+
/** Description of the context (document, selector, or element) */
|
|
76
|
+
context: string;
|
|
77
|
+
/** Time taken to run the audit in ms */
|
|
78
|
+
duration: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Configuration for custom rules
|
|
82
|
+
*/
|
|
83
|
+
export interface CustomRulesConfig {
|
|
84
|
+
/** Enable click-handler-on-non-interactive rule (default: true) */
|
|
85
|
+
clickHandlerOnNonInteractive?: boolean;
|
|
86
|
+
/** Enable mouse-only-event-handlers rule (default: true) */
|
|
87
|
+
mouseOnlyEventHandlers?: boolean;
|
|
88
|
+
/** Enable static-element-interaction rule (default: true) */
|
|
89
|
+
staticElementInteraction?: boolean;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Options for running an audit
|
|
93
|
+
*/
|
|
94
|
+
export interface A11yAuditOptions {
|
|
95
|
+
/** Minimum severity to report (default: 'serious') */
|
|
96
|
+
threshold?: SeverityThreshold;
|
|
97
|
+
/** DOM context to audit (default: document) */
|
|
98
|
+
context?: Document | Element | string;
|
|
99
|
+
/** Rule set preset to use (default: 'wcag21aa') */
|
|
100
|
+
ruleSet?: RuleSetPreset;
|
|
101
|
+
/** Specific rules to enable (overrides ruleSet) */
|
|
102
|
+
enabledRules?: Array<string>;
|
|
103
|
+
/** Specific rules to disable */
|
|
104
|
+
disabledRules?: Array<string>;
|
|
105
|
+
/** Selectors to exclude from auditing */
|
|
106
|
+
exclude?: Array<string>;
|
|
107
|
+
/** Configuration for custom rules (default: all enabled) */
|
|
108
|
+
customRules?: CustomRulesConfig;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Options for the A11y plugin
|
|
112
|
+
*/
|
|
113
|
+
export interface A11yPluginOptions {
|
|
114
|
+
/** Minimum severity threshold (default: 'serious') */
|
|
115
|
+
threshold?: SeverityThreshold;
|
|
116
|
+
/** Rule set preset (default: 'wcag21aa') */
|
|
117
|
+
ruleSet?: RuleSetPreset;
|
|
118
|
+
/** Show visual overlays on page (default: true) */
|
|
119
|
+
showOverlays?: boolean;
|
|
120
|
+
/** Persist settings to localStorage (default: true) */
|
|
121
|
+
persistSettings?: boolean;
|
|
122
|
+
/** Rules to disable (by rule ID) */
|
|
123
|
+
disabledRules?: Array<string>;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Export format options
|
|
127
|
+
*/
|
|
128
|
+
export type ExportFormat = 'json' | 'csv';
|
|
129
|
+
/**
|
|
130
|
+
* Export options
|
|
131
|
+
*/
|
|
132
|
+
export interface ExportOptions {
|
|
133
|
+
/** Export format */
|
|
134
|
+
format: ExportFormat;
|
|
135
|
+
/** Include passing rules in export */
|
|
136
|
+
includePasses?: boolean;
|
|
137
|
+
/** Include incomplete rules in export */
|
|
138
|
+
includeIncomplete?: boolean;
|
|
139
|
+
/** Custom filename (without extension) */
|
|
140
|
+
filename?: string;
|
|
141
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { A11yAuditOptions, A11yAuditResult, A11yIssue, SeverityThreshold } from '../types/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Check if an impact level meets or exceeds the threshold
|
|
4
|
+
*/
|
|
5
|
+
export declare function meetsThreshold(impact: SeverityThreshold | null | undefined, threshold: SeverityThreshold): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Run an accessibility audit using axe-core
|
|
8
|
+
*/
|
|
9
|
+
export declare function runAudit(options?: A11yAuditOptions): Promise<A11yAuditResult>;
|
|
10
|
+
/**
|
|
11
|
+
* Get a list of all available axe-core rules plus custom rules
|
|
12
|
+
*/
|
|
13
|
+
export declare function getAvailableRules(): Array<{
|
|
14
|
+
id: string;
|
|
15
|
+
description: string;
|
|
16
|
+
tags: Array<string>;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const IMPACTS: readonly ["critical", "serious", "moderate", "minor"];
|
|
19
|
+
export declare const filterIssuesAboveThreshold: (issues: A11yAuditResult["issues"], threshold: SeverityThreshold) => A11yIssue[];
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { getCustomRules, runCustomRules } from "./custom-audit.utils.js";
|
|
2
|
+
import { SEVERITY_ORDER } from "./ui.utils.js";
|
|
3
|
+
import axe from "axe-core";
|
|
4
|
+
//#region src/core/utils/ally-audit.utils.ts
|
|
5
|
+
/**
|
|
6
|
+
* Severity levels mapped to numeric values for comparison
|
|
7
|
+
*/
|
|
8
|
+
var IMPACT_SEVERITY = {
|
|
9
|
+
critical: 4,
|
|
10
|
+
serious: 3,
|
|
11
|
+
moderate: 2,
|
|
12
|
+
minor: 1
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Rule set configurations for different presets
|
|
16
|
+
*/
|
|
17
|
+
var RULE_SET_CONFIGS = {
|
|
18
|
+
wcag2a: { runOnly: {
|
|
19
|
+
type: "tag",
|
|
20
|
+
values: ["wcag2a"]
|
|
21
|
+
} },
|
|
22
|
+
wcag2aa: { runOnly: {
|
|
23
|
+
type: "tag",
|
|
24
|
+
values: ["wcag2a", "wcag2aa"]
|
|
25
|
+
} },
|
|
26
|
+
wcag21aa: { runOnly: {
|
|
27
|
+
type: "tag",
|
|
28
|
+
values: [
|
|
29
|
+
"wcag2a",
|
|
30
|
+
"wcag2aa",
|
|
31
|
+
"wcag21a",
|
|
32
|
+
"wcag21aa"
|
|
33
|
+
]
|
|
34
|
+
} },
|
|
35
|
+
wcag22aa: { runOnly: {
|
|
36
|
+
type: "tag",
|
|
37
|
+
values: [
|
|
38
|
+
"wcag2a",
|
|
39
|
+
"wcag2aa",
|
|
40
|
+
"wcag21a",
|
|
41
|
+
"wcag21aa",
|
|
42
|
+
"wcag22aa"
|
|
43
|
+
]
|
|
44
|
+
} },
|
|
45
|
+
section508: { runOnly: {
|
|
46
|
+
type: "tag",
|
|
47
|
+
values: ["section508"]
|
|
48
|
+
} },
|
|
49
|
+
"best-practice": { runOnly: {
|
|
50
|
+
type: "tag",
|
|
51
|
+
values: ["best-practice"]
|
|
52
|
+
} },
|
|
53
|
+
all: {}
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Check if an impact level meets or exceeds the threshold
|
|
57
|
+
*/
|
|
58
|
+
function meetsThreshold(impact, threshold) {
|
|
59
|
+
if (!impact) return false;
|
|
60
|
+
return IMPACT_SEVERITY[impact] >= IMPACT_SEVERITY[threshold];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Convert axe-core results to our issue format
|
|
64
|
+
*/
|
|
65
|
+
function convertToIssues(results, threshold) {
|
|
66
|
+
const issues = [];
|
|
67
|
+
for (const violation of results.violations) {
|
|
68
|
+
const impact = violation.impact;
|
|
69
|
+
for (let i = 0; i < violation.nodes.length; i++) {
|
|
70
|
+
const node = violation.nodes[i];
|
|
71
|
+
const a11yNode = {
|
|
72
|
+
selector: node.target.join(", "),
|
|
73
|
+
html: node.html,
|
|
74
|
+
xpath: node.xpath?.join(" > "),
|
|
75
|
+
failureSummary: node.failureSummary
|
|
76
|
+
};
|
|
77
|
+
issues.push({
|
|
78
|
+
id: `${violation.id}-${i}-${Date.now()}`,
|
|
79
|
+
ruleId: violation.id,
|
|
80
|
+
impact: impact || "minor",
|
|
81
|
+
message: node.failureSummary || violation.description,
|
|
82
|
+
help: violation.help,
|
|
83
|
+
helpUrl: violation.helpUrl,
|
|
84
|
+
wcagTags: violation.tags.filter((tag) => tag.startsWith("wcag") || tag.startsWith("section508")),
|
|
85
|
+
nodes: [a11yNode],
|
|
86
|
+
meetsThreshold: meetsThreshold(impact, threshold),
|
|
87
|
+
timestamp: Date.now()
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return issues;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Create summary statistics from issues array
|
|
95
|
+
* Used when combining axe-core results with custom rule results
|
|
96
|
+
*/
|
|
97
|
+
function createSummary(axeResults, issues) {
|
|
98
|
+
const summary = {
|
|
99
|
+
total: issues.length,
|
|
100
|
+
critical: 0,
|
|
101
|
+
serious: 0,
|
|
102
|
+
moderate: 0,
|
|
103
|
+
minor: 0,
|
|
104
|
+
passes: axeResults.passes.length,
|
|
105
|
+
incomplete: axeResults.incomplete.length
|
|
106
|
+
};
|
|
107
|
+
for (const issue of issues) {
|
|
108
|
+
const impact = issue.impact;
|
|
109
|
+
if (impact === "critical") summary.critical++;
|
|
110
|
+
else if (impact === "serious") summary.serious++;
|
|
111
|
+
else if (impact === "moderate") summary.moderate++;
|
|
112
|
+
else summary.minor++;
|
|
113
|
+
}
|
|
114
|
+
return summary;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get the context description for logging
|
|
118
|
+
*/
|
|
119
|
+
function getContextDescription(context) {
|
|
120
|
+
if (typeof context === "string") return context;
|
|
121
|
+
if (context instanceof Document) return "document";
|
|
122
|
+
if (context instanceof Element) return context.tagName.toLowerCase() + (context.id ? `#${context.id}` : "");
|
|
123
|
+
return "unknown";
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Default selectors to exclude from auditing (devtools panels, overlays, etc.)
|
|
127
|
+
*/
|
|
128
|
+
var DEFAULT_EXCLUDE_SELECTORS = [
|
|
129
|
+
"[data-testid=\"tanstack_devtools\"]",
|
|
130
|
+
"[data-a11y-overlay]",
|
|
131
|
+
"[data-devtools]",
|
|
132
|
+
"[data-devtools-panel]"
|
|
133
|
+
];
|
|
134
|
+
/**
|
|
135
|
+
* Run an accessibility audit using axe-core
|
|
136
|
+
*/
|
|
137
|
+
async function runAudit(options = {}) {
|
|
138
|
+
const { threshold = "serious", context = document, ruleSet = "wcag21aa", enabledRules, disabledRules, exclude = [], customRules = {} } = options;
|
|
139
|
+
const allExclusions = [...DEFAULT_EXCLUDE_SELECTORS, ...exclude];
|
|
140
|
+
const startTime = performance.now();
|
|
141
|
+
const contextDescription = getContextDescription(context);
|
|
142
|
+
try {
|
|
143
|
+
const axeOptions = {
|
|
144
|
+
resultTypes: [
|
|
145
|
+
"violations",
|
|
146
|
+
"passes",
|
|
147
|
+
"incomplete"
|
|
148
|
+
],
|
|
149
|
+
...RULE_SET_CONFIGS[ruleSet]
|
|
150
|
+
};
|
|
151
|
+
if (enabledRules && enabledRules.length > 0) axeOptions.runOnly = {
|
|
152
|
+
type: "rule",
|
|
153
|
+
values: enabledRules
|
|
154
|
+
};
|
|
155
|
+
if (disabledRules && disabledRules.length > 0) {
|
|
156
|
+
const rules = {};
|
|
157
|
+
for (const ruleId of disabledRules) rules[ruleId] = { enabled: false };
|
|
158
|
+
axeOptions.rules = rules;
|
|
159
|
+
}
|
|
160
|
+
let auditContext = context;
|
|
161
|
+
if (allExclusions.length > 0) auditContext = {
|
|
162
|
+
include: [auditContext],
|
|
163
|
+
exclude: allExclusions.map((sel) => [sel])
|
|
164
|
+
};
|
|
165
|
+
const results = await axe.run(auditContext, axeOptions);
|
|
166
|
+
const axeIssues = convertToIssues(results, threshold);
|
|
167
|
+
const customRulesConfig = {
|
|
168
|
+
clickHandlerOnNonInteractive: customRules.clickHandlerOnNonInteractive !== false && !disabledRules?.includes("click-handler-on-non-interactive"),
|
|
169
|
+
mouseOnlyEventHandlers: customRules.mouseOnlyEventHandlers !== false && !disabledRules?.includes("mouse-only-event-handlers"),
|
|
170
|
+
staticElementInteraction: customRules.staticElementInteraction !== false && !disabledRules?.includes("static-element-interaction")
|
|
171
|
+
};
|
|
172
|
+
const customIssues = runCustomRules(typeof context === "string" ? document.querySelector(context) || document : context, customRulesConfig, threshold);
|
|
173
|
+
const allIssues = [...axeIssues, ...customIssues];
|
|
174
|
+
const duration = performance.now() - startTime;
|
|
175
|
+
return {
|
|
176
|
+
issues: allIssues,
|
|
177
|
+
summary: createSummary(results, allIssues),
|
|
178
|
+
timestamp: Date.now(),
|
|
179
|
+
url: typeof window !== "undefined" ? window.location.href : "",
|
|
180
|
+
context: contextDescription,
|
|
181
|
+
duration
|
|
182
|
+
};
|
|
183
|
+
} catch (error) {
|
|
184
|
+
const duration = performance.now() - startTime;
|
|
185
|
+
console.error("[A11y Audit] Error running axe-core:", error);
|
|
186
|
+
return {
|
|
187
|
+
issues: [],
|
|
188
|
+
summary: {
|
|
189
|
+
total: 0,
|
|
190
|
+
critical: 0,
|
|
191
|
+
serious: 0,
|
|
192
|
+
moderate: 0,
|
|
193
|
+
minor: 0,
|
|
194
|
+
passes: 0,
|
|
195
|
+
incomplete: 0
|
|
196
|
+
},
|
|
197
|
+
timestamp: Date.now(),
|
|
198
|
+
url: typeof window !== "undefined" ? window.location.href : "",
|
|
199
|
+
context: contextDescription,
|
|
200
|
+
duration
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get a list of all available axe-core rules plus custom rules
|
|
206
|
+
*/
|
|
207
|
+
function getAvailableRules() {
|
|
208
|
+
const axeRules = axe.getRules().map((rule) => ({
|
|
209
|
+
id: rule.ruleId,
|
|
210
|
+
description: rule.description,
|
|
211
|
+
tags: rule.tags
|
|
212
|
+
}));
|
|
213
|
+
const customRules = getCustomRules();
|
|
214
|
+
return [...axeRules, ...customRules];
|
|
215
|
+
}
|
|
216
|
+
var IMPACTS = [
|
|
217
|
+
"critical",
|
|
218
|
+
"serious",
|
|
219
|
+
"moderate",
|
|
220
|
+
"minor"
|
|
221
|
+
];
|
|
222
|
+
var filterIssuesAboveThreshold = (issues, threshold) => issues.filter((issue) => SEVERITY_ORDER[issue.impact] >= SEVERITY_ORDER[threshold]);
|
|
223
|
+
//#endregion
|
|
224
|
+
export { IMPACTS, filterIssuesAboveThreshold, getAvailableRules, meetsThreshold, runAudit };
|
|
225
|
+
|
|
226
|
+
//# sourceMappingURL=ally-audit.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ally-audit.utils.js","names":[],"sources":["../../../../src/core/utils/ally-audit.utils.ts"],"sourcesContent":["import axe from 'axe-core'\nimport {\n getCustomRules as getCustomRulesInternal,\n runCustomRules,\n} from './custom-audit.utils.js'\nimport { SEVERITY_ORDER } from './ui.utils.js'\n\n// types\nimport type { AxeResults, RuleObject, RunOptions } from 'axe-core'\nimport type {\n A11yAuditOptions,\n A11yAuditResult,\n A11yIssue,\n A11yNode,\n A11ySummary,\n CustomRulesConfig,\n RuleSetPreset,\n SeverityThreshold,\n} from '../types/types.js'\n\n/**\n * Severity levels mapped to numeric values for comparison\n */\nconst IMPACT_SEVERITY: Record<SeverityThreshold, number> = {\n critical: 4,\n serious: 3,\n moderate: 2,\n minor: 1,\n}\n\n/**\n * Rule set configurations for different presets\n */\nconst RULE_SET_CONFIGS: Record<RuleSetPreset, Partial<RunOptions>> = {\n wcag2a: {\n runOnly: {\n type: 'tag',\n values: ['wcag2a'],\n },\n },\n wcag2aa: {\n runOnly: {\n type: 'tag',\n values: ['wcag2a', 'wcag2aa'],\n },\n },\n wcag21aa: {\n runOnly: {\n type: 'tag',\n values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'],\n },\n },\n wcag22aa: {\n runOnly: {\n type: 'tag',\n values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'],\n },\n },\n section508: {\n runOnly: {\n type: 'tag',\n values: ['section508'],\n },\n },\n 'best-practice': {\n runOnly: {\n type: 'tag',\n values: ['best-practice'],\n },\n },\n all: {\n // Run all rules\n },\n}\n\n/**\n * Check if an impact level meets or exceeds the threshold\n */\nexport function meetsThreshold(\n impact: SeverityThreshold | null | undefined,\n threshold: SeverityThreshold,\n): boolean {\n if (!impact) return false\n return IMPACT_SEVERITY[impact] >= IMPACT_SEVERITY[threshold]\n}\n\n/**\n * Convert axe-core results to our issue format\n */\nfunction convertToIssues(\n results: AxeResults,\n threshold: SeverityThreshold,\n): Array<A11yIssue> {\n const issues: Array<A11yIssue> = []\n\n for (const violation of results.violations) {\n const impact = violation.impact as SeverityThreshold | undefined\n\n for (let i = 0; i < violation.nodes.length; i++) {\n const node = violation.nodes[i]!\n const selector = node.target.join(', ')\n\n const a11yNode: A11yNode = {\n selector,\n html: node.html,\n xpath: node.xpath?.join(' > '),\n failureSummary: node.failureSummary,\n }\n\n issues.push({\n id: `${violation.id}-${i}-${Date.now()}`,\n ruleId: violation.id,\n impact: impact || 'minor',\n message: node.failureSummary || violation.description,\n help: violation.help,\n helpUrl: violation.helpUrl,\n wcagTags: violation.tags.filter(\n (tag) => tag.startsWith('wcag') || tag.startsWith('section508'),\n ),\n nodes: [a11yNode],\n meetsThreshold: meetsThreshold(impact, threshold),\n timestamp: Date.now(),\n })\n }\n }\n\n return issues\n}\n\n/**\n * Create summary statistics from issues array\n * Used when combining axe-core results with custom rule results\n */\nfunction createSummary(\n axeResults: AxeResults,\n issues: Array<A11yIssue>,\n): A11ySummary {\n const summary: A11ySummary = {\n total: issues.length,\n critical: 0,\n serious: 0,\n moderate: 0,\n minor: 0,\n passes: axeResults.passes.length,\n incomplete: axeResults.incomplete.length,\n }\n\n for (const issue of issues) {\n const impact = issue.impact\n if (impact === 'critical') summary.critical++\n else if (impact === 'serious') summary.serious++\n else if (impact === 'moderate') summary.moderate++\n else {\n summary.minor++\n }\n }\n\n return summary\n}\n\n/**\n * Get the context description for logging\n */\nfunction getContextDescription(context: Document | Element | string): string {\n if (typeof context === 'string') {\n return context\n }\n if (context instanceof Document) {\n return 'document'\n }\n if (context instanceof Element) {\n return context.tagName.toLowerCase() + (context.id ? `#${context.id}` : '')\n }\n return 'unknown'\n}\n\n/**\n * Default selectors to exclude from auditing (devtools panels, overlays, etc.)\n */\nconst DEFAULT_EXCLUDE_SELECTORS = [\n // TanStack Devtools root container\n '[data-testid=\"tanstack_devtools\"]',\n // A11y overlay elements\n '[data-a11y-overlay]',\n // Common devtools patterns\n '[data-devtools]',\n '[data-devtools-panel]',\n]\n\n/**\n * Run an accessibility audit using axe-core\n */\nexport async function runAudit(\n options: A11yAuditOptions = {},\n): Promise<A11yAuditResult> {\n const {\n threshold = 'serious',\n context = document,\n ruleSet = 'wcag21aa',\n enabledRules,\n disabledRules,\n exclude = [],\n customRules = {},\n } = options\n\n // Merge user exclusions with default devtools exclusions\n const allExclusions = [...DEFAULT_EXCLUDE_SELECTORS, ...exclude]\n\n const startTime = performance.now()\n const contextDescription = getContextDescription(context)\n\n try {\n // Build axe-core options\n const axeOptions: RunOptions = {\n resultTypes: ['violations', 'passes', 'incomplete'],\n ...RULE_SET_CONFIGS[ruleSet],\n }\n\n // Handle specific rule configurations\n if (enabledRules && enabledRules.length > 0) {\n axeOptions.runOnly = {\n type: 'rule',\n values: enabledRules,\n }\n }\n\n // Build rules configuration for disabled rules\n if (disabledRules && disabledRules.length > 0) {\n const rules: RuleObject = {}\n for (const ruleId of disabledRules) {\n rules[ruleId] = { enabled: false }\n }\n axeOptions.rules = rules\n }\n\n // Determine the context to audit\n let auditContext: axe.ElementContext = context as axe.ElementContext\n\n // Add exclusions if specified (always include devtools exclusions)\n if (allExclusions.length > 0) {\n auditContext = {\n include: [auditContext as Element],\n exclude: allExclusions.map((sel) => [sel]),\n } as axe.ElementContext\n }\n\n // Run the axe-core audit\n const results = await axe.run(auditContext, axeOptions)\n\n // Convert axe-core results to our format\n const axeIssues = convertToIssues(results, threshold)\n\n // Run custom rules (if not all disabled)\n const customRulesConfig: CustomRulesConfig = {\n clickHandlerOnNonInteractive:\n customRules.clickHandlerOnNonInteractive !== false &&\n !disabledRules?.includes('click-handler-on-non-interactive'),\n mouseOnlyEventHandlers:\n customRules.mouseOnlyEventHandlers !== false &&\n !disabledRules?.includes('mouse-only-event-handlers'),\n staticElementInteraction:\n customRules.staticElementInteraction !== false &&\n !disabledRules?.includes('static-element-interaction'),\n }\n\n const contextElement =\n typeof context === 'string'\n ? document.querySelector(context) || document\n : context\n\n const customIssues = runCustomRules(\n contextElement,\n customRulesConfig,\n threshold,\n )\n\n // Merge all issues\n const allIssues = [...axeIssues, ...customIssues]\n\n const duration = performance.now() - startTime\n\n // Create summary from combined issues\n const summary = createSummary(results, allIssues)\n\n return {\n issues: allIssues,\n summary,\n timestamp: Date.now(),\n url: typeof window !== 'undefined' ? window.location.href : '',\n context: contextDescription,\n duration,\n }\n } catch (error) {\n const duration = performance.now() - startTime\n console.error('[A11y Audit] Error running axe-core:', error)\n\n return {\n issues: [],\n summary: {\n total: 0,\n critical: 0,\n serious: 0,\n moderate: 0,\n minor: 0,\n passes: 0,\n incomplete: 0,\n },\n timestamp: Date.now(),\n url: typeof window !== 'undefined' ? window.location.href : '',\n context: contextDescription,\n duration,\n }\n }\n}\n\n/**\n * Get a list of all available axe-core rules plus custom rules\n */\nexport function getAvailableRules(): Array<{\n id: string\n description: string\n tags: Array<string>\n}> {\n // Get axe-core rules\n const axeRules = axe.getRules().map((rule) => ({\n id: rule.ruleId,\n description: rule.description,\n tags: rule.tags,\n }))\n\n // Get custom rules\n const customRules = getCustomRulesInternal()\n\n return [...axeRules, ...customRules]\n}\n\nexport const IMPACTS = ['critical', 'serious', 'moderate', 'minor'] as const\n\nexport const filterIssuesAboveThreshold = (\n issues: A11yAuditResult['issues'],\n threshold: SeverityThreshold,\n) =>\n issues.filter(\n (issue) => SEVERITY_ORDER[issue.impact] >= SEVERITY_ORDER[threshold],\n )\n"],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { A11yPluginOptions } from '../types/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Default plugin configuration
|
|
4
|
+
*/
|
|
5
|
+
export declare const DEFAULT_CONFIG: Required<A11yPluginOptions>;
|
|
6
|
+
/**
|
|
7
|
+
* Load configuration from localStorage
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadConfig(): Required<A11yPluginOptions>;
|
|
10
|
+
/**
|
|
11
|
+
* Save configuration to localStorage
|
|
12
|
+
*/
|
|
13
|
+
export declare function saveConfig(config: Partial<A11yPluginOptions>): void;
|
|
14
|
+
/**
|
|
15
|
+
* Merge user options with defaults
|
|
16
|
+
*/
|
|
17
|
+
export declare function mergeConfig(options?: A11yPluginOptions): Required<A11yPluginOptions>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//#region src/core/utils/config.utils.ts
|
|
2
|
+
var STORAGE_KEY = "tanstack-devtools-a11y-config";
|
|
3
|
+
/**
|
|
4
|
+
* Default plugin configuration
|
|
5
|
+
*/
|
|
6
|
+
var DEFAULT_CONFIG = {
|
|
7
|
+
threshold: "serious",
|
|
8
|
+
ruleSet: "wcag21aa",
|
|
9
|
+
showOverlays: true,
|
|
10
|
+
persistSettings: true,
|
|
11
|
+
disabledRules: []
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Load configuration from localStorage
|
|
15
|
+
*/
|
|
16
|
+
function loadConfig() {
|
|
17
|
+
if (typeof localStorage === "undefined") return DEFAULT_CONFIG;
|
|
18
|
+
try {
|
|
19
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
20
|
+
if (stored) {
|
|
21
|
+
const parsed = JSON.parse(stored);
|
|
22
|
+
return {
|
|
23
|
+
...DEFAULT_CONFIG,
|
|
24
|
+
...parsed
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.warn("[A11y Config] Failed to load config from localStorage:", error);
|
|
29
|
+
}
|
|
30
|
+
return DEFAULT_CONFIG;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Save configuration to localStorage
|
|
34
|
+
*/
|
|
35
|
+
function saveConfig(config) {
|
|
36
|
+
if (typeof localStorage === "undefined") return;
|
|
37
|
+
try {
|
|
38
|
+
const updated = {
|
|
39
|
+
...loadConfig(),
|
|
40
|
+
...config
|
|
41
|
+
};
|
|
42
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.warn("[A11y Config] Failed to save config to localStorage:", error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Merge user options with defaults
|
|
49
|
+
*/
|
|
50
|
+
function mergeConfig(options = {}) {
|
|
51
|
+
if (options.persistSettings !== false) return {
|
|
52
|
+
...loadConfig(),
|
|
53
|
+
...options
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
...DEFAULT_CONFIG,
|
|
57
|
+
...options
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { mergeConfig, saveConfig };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=config.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.utils.js","names":[],"sources":["../../../../src/core/utils/config.utils.ts"],"sourcesContent":["import type { A11yPluginOptions } from '../types/types'\n\nconst STORAGE_KEY = 'tanstack-devtools-a11y-config'\n\n/**\n * Default plugin configuration\n */\nexport const DEFAULT_CONFIG: Required<A11yPluginOptions> = {\n threshold: 'serious',\n ruleSet: 'wcag21aa',\n showOverlays: true,\n persistSettings: true,\n disabledRules: [],\n}\n\n/**\n * Load configuration from localStorage\n */\nexport function loadConfig(): Required<A11yPluginOptions> {\n if (typeof localStorage === 'undefined') {\n return DEFAULT_CONFIG\n }\n\n try {\n const stored = localStorage.getItem(STORAGE_KEY)\n if (stored) {\n const parsed = JSON.parse(stored) as Partial<A11yPluginOptions>\n return { ...DEFAULT_CONFIG, ...parsed }\n }\n } catch (error) {\n console.warn(\n '[A11y Config] Failed to load config from localStorage:',\n error,\n )\n }\n\n return DEFAULT_CONFIG\n}\n\n/**\n * Save configuration to localStorage\n */\nexport function saveConfig(config: Partial<A11yPluginOptions>): void {\n if (typeof localStorage === 'undefined') {\n return\n }\n\n try {\n const current = loadConfig()\n const updated = { ...current, ...config }\n localStorage.setItem(STORAGE_KEY, JSON.stringify(updated))\n } catch (error) {\n console.warn('[A11y Config] Failed to save config to localStorage:', error)\n }\n}\n\n/**\n * Merge user options with defaults\n */\nexport function mergeConfig(\n options: A11yPluginOptions = {},\n): Required<A11yPluginOptions> {\n if (options.persistSettings !== false) {\n const saved = loadConfig()\n return { ...saved, ...options }\n }\n return { ...DEFAULT_CONFIG, ...options }\n}\n"],"mappings":";AAEA,IAAM,cAAc;;;;AAKpB,IAAa,iBAA8C;CACzD,WAAW;CACX,SAAS;CACT,cAAc;CACd,iBAAiB;CACjB,eAAe,EAAE;CAClB;;;;AAKD,SAAgB,aAA0C;AACxD,KAAI,OAAO,iBAAiB,YAC1B,QAAO;AAGT,KAAI;EACF,MAAM,SAAS,aAAa,QAAQ,YAAY;AAChD,MAAI,QAAQ;GACV,MAAM,SAAS,KAAK,MAAM,OAAO;AACjC,UAAO;IAAE,GAAG;IAAgB,GAAG;IAAQ;;UAElC,OAAO;AACd,UAAQ,KACN,0DACA,MACD;;AAGH,QAAO;;;;;AAMT,SAAgB,WAAW,QAA0C;AACnE,KAAI,OAAO,iBAAiB,YAC1B;AAGF,KAAI;EAEF,MAAM,UAAU;GAAE,GADF,YAAY;GACE,GAAG;GAAQ;AACzC,eAAa,QAAQ,aAAa,KAAK,UAAU,QAAQ,CAAC;UACnD,OAAO;AACd,UAAQ,KAAK,wDAAwD,MAAM;;;;;;AAO/E,SAAgB,YACd,UAA6B,EAAE,EACF;AAC7B,KAAI,QAAQ,oBAAoB,MAE9B,QAAO;EAAE,GADK,YAAY;EACP,GAAG;EAAS;AAEjC,QAAO;EAAE,GAAG;EAAgB,GAAG;EAAS"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { A11yIssue, CustomRulesConfig, SeverityThreshold } from '../types/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Run all enabled custom rules
|
|
4
|
+
*/
|
|
5
|
+
export declare function runCustomRules(context?: Document | Element, config?: CustomRulesConfig, threshold?: SeverityThreshold): Array<A11yIssue>;
|
|
6
|
+
/**
|
|
7
|
+
* Get list of custom rule metadata (for UI display)
|
|
8
|
+
*/
|
|
9
|
+
export declare function getCustomRules(): Array<{
|
|
10
|
+
id: string;
|
|
11
|
+
description: string;
|
|
12
|
+
tags: Array<string>;
|
|
13
|
+
}>;
|