@techsio/storybook-better-a11y 0.0.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/AGENTS.MD +12 -0
- package/README.md +40 -0
- package/dist/100.js +122 -0
- package/dist/212.js +1965 -0
- package/dist/212.js.LICENSE.txt +19 -0
- package/dist/699.js +529 -0
- package/dist/AccessibilityRuleMaps.d.ts +9 -0
- package/dist/AccessibilityRuleMaps.d.ts.map +1 -0
- package/dist/a11yRunner.d.ts +4 -0
- package/dist/a11yRunner.d.ts.map +1 -0
- package/dist/a11yRunnerUtils.d.ts +4 -0
- package/dist/a11yRunnerUtils.d.ts.map +1 -0
- package/dist/apcaChecker.d.ts +13 -0
- package/dist/apcaChecker.d.ts.map +1 -0
- package/dist/axeRuleMappingHelper.d.ts +4 -0
- package/dist/axeRuleMappingHelper.d.ts.map +1 -0
- package/dist/components/A11YPanel.d.ts +3 -0
- package/dist/components/A11YPanel.d.ts.map +1 -0
- package/dist/components/A11yContext.d.ts +31 -0
- package/dist/components/A11yContext.d.ts.map +1 -0
- package/dist/components/Report/Details.d.ts +11 -0
- package/dist/components/Report/Details.d.ts.map +1 -0
- package/dist/components/Report/Report.d.ts +13 -0
- package/dist/components/Report/Report.d.ts.map +1 -0
- package/dist/components/Tabs.d.ts +14 -0
- package/dist/components/Tabs.d.ts.map +1 -0
- package/dist/components/TestDiscrepancyMessage.d.ts +7 -0
- package/dist/components/TestDiscrepancyMessage.d.ts.map +1 -0
- package/dist/components/VisionSimulator.d.ts +2 -0
- package/dist/components/VisionSimulator.d.ts.map +1 -0
- package/dist/constants.d.ts +19 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/manager.d.ts +2 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +1550 -0
- package/dist/params.d.ts +48 -0
- package/dist/params.d.ts.map +1 -0
- package/dist/postinstall.d.ts +9 -0
- package/dist/postinstall.d.ts.map +1 -0
- package/dist/postinstall.js +21 -0
- package/dist/preview.d.ts +14 -0
- package/dist/preview.d.ts.map +1 -0
- package/dist/preview.js +1 -0
- package/dist/results.mock.d.ts +2 -0
- package/dist/results.mock.d.ts.map +1 -0
- package/dist/rslib-runtime.js +37 -0
- package/dist/types.d.ts +52 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/visionSimulatorFilters.d.ts +7 -0
- package/dist/visionSimulatorFilters.d.ts.map +1 -0
- package/dist/withVisionSimulator.d.ts +3 -0
- package/dist/withVisionSimulator.d.ts.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license React
|
|
3
|
+
* react-is.development.js
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @license React
|
|
13
|
+
* react-is.production.min.js
|
|
14
|
+
*
|
|
15
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
16
|
+
*
|
|
17
|
+
* This source code is licensed under the MIT license found in the
|
|
18
|
+
* LICENSE file in the root directory of this source tree.
|
|
19
|
+
*/
|
package/dist/699.js
ADDED
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import { __webpack_require__ } from "./rslib-runtime.js";
|
|
2
|
+
import { expect } from "storybook/test";
|
|
3
|
+
import { ElementA11yParameterError } from "storybook/internal/preview-errors";
|
|
4
|
+
import { global } from "@storybook/global";
|
|
5
|
+
import { addons, useCallback, useEffect, waitForAnimations } from "storybook/preview-api";
|
|
6
|
+
import { EVENTS, filters, filterDefs, PANEL_ID } from "./100.js";
|
|
7
|
+
var preview_namespaceObject = {};
|
|
8
|
+
__webpack_require__.r(preview_namespaceObject);
|
|
9
|
+
__webpack_require__.d(preview_namespaceObject, {
|
|
10
|
+
afterEach: ()=>afterEach,
|
|
11
|
+
decorators: ()=>decorators,
|
|
12
|
+
initialGlobals: ()=>initialGlobals,
|
|
13
|
+
parameters: ()=>preview_parameters
|
|
14
|
+
});
|
|
15
|
+
const { document: a11yRunnerUtils_document } = global;
|
|
16
|
+
const withLinkPaths = (results, storyId)=>{
|
|
17
|
+
const pathname = a11yRunnerUtils_document.location.pathname.replace(/iframe\.html$/, '');
|
|
18
|
+
const enhancedResults = {
|
|
19
|
+
...results
|
|
20
|
+
};
|
|
21
|
+
const propertiesToAugment = [
|
|
22
|
+
'incomplete',
|
|
23
|
+
'passes',
|
|
24
|
+
'violations'
|
|
25
|
+
];
|
|
26
|
+
propertiesToAugment.forEach((key)=>{
|
|
27
|
+
if (Array.isArray(results[key])) enhancedResults[key] = results[key].map((result)=>({
|
|
28
|
+
...result,
|
|
29
|
+
nodes: result.nodes.map((node, index)=>{
|
|
30
|
+
const id = `${key}.${result.id}.${index + 1}`;
|
|
31
|
+
const linkPath = `${pathname}?path=/story/${storyId}&addonPanel=${PANEL_ID}&a11ySelection=${id}`;
|
|
32
|
+
return {
|
|
33
|
+
id,
|
|
34
|
+
...node,
|
|
35
|
+
linkPath
|
|
36
|
+
};
|
|
37
|
+
})
|
|
38
|
+
}));
|
|
39
|
+
});
|
|
40
|
+
return enhancedResults;
|
|
41
|
+
};
|
|
42
|
+
const { document: apcaChecker_document } = global;
|
|
43
|
+
const DEFAULT_APCA_OPTIONS = {
|
|
44
|
+
level: 'bronze',
|
|
45
|
+
useCase: 'body'
|
|
46
|
+
};
|
|
47
|
+
const APCA_LC_STEPS = [
|
|
48
|
+
15,
|
|
49
|
+
20,
|
|
50
|
+
25,
|
|
51
|
+
30,
|
|
52
|
+
35,
|
|
53
|
+
40,
|
|
54
|
+
45,
|
|
55
|
+
50,
|
|
56
|
+
55,
|
|
57
|
+
60,
|
|
58
|
+
65,
|
|
59
|
+
70,
|
|
60
|
+
75,
|
|
61
|
+
80,
|
|
62
|
+
85,
|
|
63
|
+
90,
|
|
64
|
+
95,
|
|
65
|
+
100,
|
|
66
|
+
105,
|
|
67
|
+
110,
|
|
68
|
+
115,
|
|
69
|
+
120,
|
|
70
|
+
125
|
|
71
|
+
];
|
|
72
|
+
const APCA_MAX_CONTRAST_LC = 90;
|
|
73
|
+
function getComputedColor(element, property) {
|
|
74
|
+
const computed = global.getComputedStyle(element);
|
|
75
|
+
return computed[property] || '';
|
|
76
|
+
}
|
|
77
|
+
function getEffectiveBackgroundColor(element) {
|
|
78
|
+
let current = element;
|
|
79
|
+
while(current && current !== apcaChecker_document.body){
|
|
80
|
+
const bgColor = getComputedColor(current, 'backgroundColor');
|
|
81
|
+
if (bgColor && 'rgba(0, 0, 0, 0)' !== bgColor && 'transparent' !== bgColor) return bgColor;
|
|
82
|
+
current = current.parentElement;
|
|
83
|
+
}
|
|
84
|
+
return 'rgb(255, 255, 255)';
|
|
85
|
+
}
|
|
86
|
+
function parseColor(color) {
|
|
87
|
+
const match = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
88
|
+
if (!match) return null;
|
|
89
|
+
return [
|
|
90
|
+
parseInt(match[1], 10),
|
|
91
|
+
parseInt(match[2], 10),
|
|
92
|
+
parseInt(match[3], 10)
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
function normalizeUseCase(value, fallback) {
|
|
96
|
+
if (!value) return fallback;
|
|
97
|
+
const normalized = value.toLowerCase().replace(/\s+/g, '-');
|
|
98
|
+
if (normalized.includes('body')) return 'body';
|
|
99
|
+
if (normalized.includes('sub') || normalized.includes('logo')) return 'sub-fluent';
|
|
100
|
+
if (normalized.includes('non') || normalized.includes('incidental') || normalized.includes('spot')) return 'non-fluent';
|
|
101
|
+
if (normalized.includes('fluent')) return 'fluent';
|
|
102
|
+
return fallback;
|
|
103
|
+
}
|
|
104
|
+
function getUseCaseForElement(element, fallback) {
|
|
105
|
+
const attr = element.getAttribute('data-apca-usecase') ?? element.getAttribute('data-apca-use-case') ?? element.getAttribute('data-apca-usage');
|
|
106
|
+
return normalizeUseCase(attr, fallback);
|
|
107
|
+
}
|
|
108
|
+
function normalizeWeightBucket(fontWeight) {
|
|
109
|
+
const weight = Number.isFinite(fontWeight) ? fontWeight : 400;
|
|
110
|
+
const clamped = Math.max(100, Math.min(900, weight));
|
|
111
|
+
return 100 * Math.round(clamped / 100);
|
|
112
|
+
}
|
|
113
|
+
function getBronzeThreshold(useCase, fontSize) {
|
|
114
|
+
if ('body' === useCase) return {
|
|
115
|
+
threshold: 75,
|
|
116
|
+
preferred: 90
|
|
117
|
+
};
|
|
118
|
+
if ('fluent' === useCase) {
|
|
119
|
+
if (fontSize > 32) return {
|
|
120
|
+
threshold: 45
|
|
121
|
+
};
|
|
122
|
+
if (fontSize >= 16) return {
|
|
123
|
+
threshold: 60
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
threshold: 75
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
function getMinFontSize(level, useCase) {
|
|
132
|
+
if ('bronze' === level) return;
|
|
133
|
+
if ('sub-fluent' === useCase) return 'gold' === level ? 12 : 10;
|
|
134
|
+
if ('fluent' === useCase || 'body' === useCase) return 'gold' === level ? 16 : 14;
|
|
135
|
+
}
|
|
136
|
+
function getMaxContrast(level, useCase, fontSize, fontWeight) {
|
|
137
|
+
if ('bronze' === level) {
|
|
138
|
+
if ('fluent' === useCase && fontSize > 32 && fontWeight >= 700) return APCA_MAX_CONTRAST_LC;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (('body' === useCase || 'fluent' === useCase) && fontSize > 36) return APCA_MAX_CONTRAST_LC;
|
|
142
|
+
}
|
|
143
|
+
function getBaseThresholdFromLookup(fontSize, fontWeight, allowNonContent, fontLookupAPCA) {
|
|
144
|
+
const weightBucket = normalizeWeightBucket(fontWeight);
|
|
145
|
+
const weightIndex = Math.round(weightBucket / 100);
|
|
146
|
+
for (const lc of APCA_LC_STEPS){
|
|
147
|
+
const row = fontLookupAPCA(lc, 2);
|
|
148
|
+
const requiredSize = Number(row[weightIndex]);
|
|
149
|
+
if (!Number.isFinite(requiredSize)) continue;
|
|
150
|
+
if (999 === requiredSize) continue;
|
|
151
|
+
if (777 === requiredSize && !allowNonContent) continue;
|
|
152
|
+
const minSize = 777 === requiredSize ? 0 : requiredSize;
|
|
153
|
+
if (fontSize >= minSize) return lc;
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
function getApcaThreshold(level, useCase, fontSize, fontWeight, fontLookupAPCA) {
|
|
158
|
+
if ('bronze' === level) {
|
|
159
|
+
const bronzeThreshold = getBronzeThreshold(useCase, fontSize);
|
|
160
|
+
if (!bronzeThreshold) return {
|
|
161
|
+
threshold: null,
|
|
162
|
+
skip: true
|
|
163
|
+
};
|
|
164
|
+
return {
|
|
165
|
+
threshold: bronzeThreshold.threshold,
|
|
166
|
+
maxContrast: getMaxContrast(level, useCase, fontSize, fontWeight)
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const minFontSize = getMinFontSize(level, useCase);
|
|
170
|
+
const baseThreshold = getBaseThresholdFromLookup(fontSize, fontWeight, 'non-fluent' === useCase, fontLookupAPCA);
|
|
171
|
+
if (null === baseThreshold) return {
|
|
172
|
+
threshold: null,
|
|
173
|
+
minFontSize,
|
|
174
|
+
note: 'Font size/weight is below the minimums in the APCA lookup table for this use case.'
|
|
175
|
+
};
|
|
176
|
+
let threshold = baseThreshold;
|
|
177
|
+
if ('sub-fluent' === useCase) threshold = Math.max(threshold - 15, 'silver' === level ? 40 : 45);
|
|
178
|
+
else if ('non-fluent' === useCase) threshold = Math.max(threshold - ('silver' === level ? 30 : 20), 30);
|
|
179
|
+
if ('body' === useCase && 'gold' === level && threshold < 75) threshold += 15;
|
|
180
|
+
return {
|
|
181
|
+
threshold,
|
|
182
|
+
minFontSize,
|
|
183
|
+
maxContrast: getMaxContrast(level, useCase, fontSize, fontWeight)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function hasReadableText(element) {
|
|
187
|
+
const text = element.textContent?.trim() || '';
|
|
188
|
+
return text.length > 0 && !element.hasAttribute('aria-hidden');
|
|
189
|
+
}
|
|
190
|
+
function isVisible(element) {
|
|
191
|
+
const computed = global.getComputedStyle(element);
|
|
192
|
+
return 'none' !== computed.display && 'hidden' !== computed.visibility && '0' !== computed.opacity;
|
|
193
|
+
}
|
|
194
|
+
async function runAPCACheck(context = apcaChecker_document, options = DEFAULT_APCA_OPTIONS) {
|
|
195
|
+
const { APCAcontrast, sRGBtoY, fontLookupAPCA } = await import("apca-w3");
|
|
196
|
+
const apcaOptions = {
|
|
197
|
+
...DEFAULT_APCA_OPTIONS,
|
|
198
|
+
...options
|
|
199
|
+
};
|
|
200
|
+
const violations = [];
|
|
201
|
+
const root = context instanceof Document ? context.body : context;
|
|
202
|
+
const textElements = root.querySelectorAll('p, span, div, h1, h2, h3, h4, h5, h6, a, button, label, td, th, li, input, textarea');
|
|
203
|
+
textElements.forEach((element)=>{
|
|
204
|
+
if (!isVisible(element) || !hasReadableText(element)) return;
|
|
205
|
+
const foreground = getComputedColor(element, 'color');
|
|
206
|
+
const background = getEffectiveBackgroundColor(element);
|
|
207
|
+
const fgColor = parseColor(foreground);
|
|
208
|
+
const bgColor = parseColor(background);
|
|
209
|
+
if (!fgColor || !bgColor) return;
|
|
210
|
+
const computed = global.getComputedStyle(element);
|
|
211
|
+
const fontSize = parseFloat(computed.fontSize);
|
|
212
|
+
const fontWeight = parseInt(computed.fontWeight, 10);
|
|
213
|
+
try {
|
|
214
|
+
const fgLuminance = sRGBtoY(fgColor);
|
|
215
|
+
const bgLuminance = sRGBtoY(bgColor);
|
|
216
|
+
const contrastValue = Math.abs(APCAcontrast(fgLuminance, bgLuminance));
|
|
217
|
+
const useCase = getUseCaseForElement(element, apcaOptions.useCase);
|
|
218
|
+
const level = apcaOptions.level;
|
|
219
|
+
const { threshold, minFontSize, maxContrast, note, skip } = getApcaThreshold(level, useCase, fontSize, fontWeight, fontLookupAPCA);
|
|
220
|
+
if (skip) return;
|
|
221
|
+
const messages = [];
|
|
222
|
+
if (null === threshold) {
|
|
223
|
+
if (note) messages.push(note);
|
|
224
|
+
} else {
|
|
225
|
+
if (contrastValue < threshold) messages.push(`APCA contrast of ${contrastValue.toFixed(1)} Lc is below the minimum of ${threshold} Lc for ${level} ${useCase} text.`);
|
|
226
|
+
if (void 0 !== maxContrast && contrastValue > maxContrast) messages.push(`APCA contrast of ${contrastValue.toFixed(1)} Lc exceeds the maximum of ${maxContrast} Lc for ${level} ${useCase} text at ${fontSize.toFixed(1)}px.`);
|
|
227
|
+
}
|
|
228
|
+
if (minFontSize && fontSize < minFontSize) messages.push(`Font size ${fontSize.toFixed(1)}px is below the minimum ${minFontSize}px for ${level} ${useCase} text.`);
|
|
229
|
+
if (messages.length > 0) violations.push({
|
|
230
|
+
element,
|
|
231
|
+
foreground,
|
|
232
|
+
background,
|
|
233
|
+
contrastValue,
|
|
234
|
+
fontSize,
|
|
235
|
+
fontWeight,
|
|
236
|
+
threshold,
|
|
237
|
+
maxContrast,
|
|
238
|
+
useCase,
|
|
239
|
+
level,
|
|
240
|
+
minFontSize,
|
|
241
|
+
note
|
|
242
|
+
});
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.warn('APCA calculation error:', error);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
const nodes = violations.map((violation)=>{
|
|
248
|
+
const impact = getImpact(violation.contrastValue, violation.threshold, violation.maxContrast);
|
|
249
|
+
const messages = [];
|
|
250
|
+
if (violation.note) messages.push(violation.note);
|
|
251
|
+
else if (null !== violation.threshold && violation.contrastValue < violation.threshold) messages.push(`APCA contrast of ${violation.contrastValue.toFixed(1)} Lc is below the minimum of ${violation.threshold} Lc for ${violation.level} ${violation.useCase} text.`);
|
|
252
|
+
if (void 0 !== violation.maxContrast && violation.contrastValue > violation.maxContrast) messages.push(`APCA contrast of ${violation.contrastValue.toFixed(1)} Lc exceeds the maximum of ${violation.maxContrast} Lc for ${violation.level} ${violation.useCase} text at ${violation.fontSize.toFixed(1)}px.`);
|
|
253
|
+
if (violation.minFontSize && violation.fontSize < violation.minFontSize) messages.push(`Font size ${violation.fontSize.toFixed(1)}px is below the minimum ${violation.minFontSize}px for ${violation.level} ${violation.useCase} text.`);
|
|
254
|
+
const rules = messages.map((message)=>({
|
|
255
|
+
id: 'apca-contrast',
|
|
256
|
+
impact,
|
|
257
|
+
message,
|
|
258
|
+
data: null,
|
|
259
|
+
relatedNodes: []
|
|
260
|
+
}));
|
|
261
|
+
const failureSummary = `Fix any of the following:\n ${messages.join('\n ')}`;
|
|
262
|
+
return {
|
|
263
|
+
html: violation.element.outerHTML,
|
|
264
|
+
target: [
|
|
265
|
+
getSelector(violation.element)
|
|
266
|
+
],
|
|
267
|
+
any: rules,
|
|
268
|
+
all: [],
|
|
269
|
+
none: [],
|
|
270
|
+
impact,
|
|
271
|
+
failureSummary
|
|
272
|
+
};
|
|
273
|
+
});
|
|
274
|
+
return {
|
|
275
|
+
id: 'apca-contrast',
|
|
276
|
+
impact: nodes.length > 0 ? 'serious' : null,
|
|
277
|
+
tags: [
|
|
278
|
+
'wcag3',
|
|
279
|
+
'wcag30',
|
|
280
|
+
'apca',
|
|
281
|
+
'contrast'
|
|
282
|
+
],
|
|
283
|
+
description: 'Ensures text has sufficient contrast using APCA (WCAG 3.0 method)',
|
|
284
|
+
help: 'Elements must have sufficient color contrast using APCA',
|
|
285
|
+
helpUrl: 'https://git.apcacontrast.com/',
|
|
286
|
+
nodes
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function getSelector(element) {
|
|
290
|
+
if (element.id) return `#${element.id}`;
|
|
291
|
+
const path = [];
|
|
292
|
+
let current = element;
|
|
293
|
+
while(current && current !== apcaChecker_document.body){
|
|
294
|
+
let selector = current.tagName.toLowerCase();
|
|
295
|
+
if (current.className) {
|
|
296
|
+
const classes = current.className.split(' ').filter(Boolean);
|
|
297
|
+
if (classes.length > 0) selector += `.${classes[0]}`;
|
|
298
|
+
}
|
|
299
|
+
const parent = current.parentElement;
|
|
300
|
+
if (parent) {
|
|
301
|
+
const siblings = Array.from(parent.children);
|
|
302
|
+
const index = siblings.indexOf(current) + 1;
|
|
303
|
+
selector += `:nth-child(${index})`;
|
|
304
|
+
}
|
|
305
|
+
path.unshift(selector);
|
|
306
|
+
current = parent;
|
|
307
|
+
}
|
|
308
|
+
return path.join(' > ');
|
|
309
|
+
}
|
|
310
|
+
function getImpact(contrastValue, threshold, maxContrast) {
|
|
311
|
+
if (null === threshold && void 0 === maxContrast) return 'serious';
|
|
312
|
+
const difference = void 0 !== maxContrast && contrastValue > maxContrast ? contrastValue - maxContrast : (threshold ?? 0) - contrastValue;
|
|
313
|
+
if (difference > 30) return 'critical';
|
|
314
|
+
if (difference > 20) return 'serious';
|
|
315
|
+
if (difference > 10) return 'moderate';
|
|
316
|
+
return 'minor';
|
|
317
|
+
}
|
|
318
|
+
const { document: a11yRunner_document } = global;
|
|
319
|
+
const channel = addons.getChannel();
|
|
320
|
+
const DEFAULT_PARAMETERS = {
|
|
321
|
+
config: {},
|
|
322
|
+
options: {}
|
|
323
|
+
};
|
|
324
|
+
const DISABLED_RULES = [
|
|
325
|
+
'region'
|
|
326
|
+
];
|
|
327
|
+
const queue = [];
|
|
328
|
+
let isRunning = false;
|
|
329
|
+
const runNext = async ()=>{
|
|
330
|
+
if (0 === queue.length) {
|
|
331
|
+
isRunning = false;
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
isRunning = true;
|
|
335
|
+
const next = queue.shift();
|
|
336
|
+
if (next) await next();
|
|
337
|
+
runNext();
|
|
338
|
+
};
|
|
339
|
+
const run = async (input = DEFAULT_PARAMETERS, storyId)=>{
|
|
340
|
+
const axeCore = await import("axe-core");
|
|
341
|
+
const axe = axeCore?.default || globalThis.axe;
|
|
342
|
+
const { config = {}, options = {} } = input;
|
|
343
|
+
if (input.element) throw new ElementA11yParameterError();
|
|
344
|
+
const context = {
|
|
345
|
+
include: a11yRunner_document?.body,
|
|
346
|
+
exclude: [
|
|
347
|
+
'.sb-wrapper',
|
|
348
|
+
'#storybook-docs',
|
|
349
|
+
'#storybook-highlights-root'
|
|
350
|
+
]
|
|
351
|
+
};
|
|
352
|
+
if (input.context) {
|
|
353
|
+
const hasInclude = 'object' == typeof input.context && 'include' in input.context && void 0 !== input.context.include;
|
|
354
|
+
const hasExclude = 'object' == typeof input.context && 'exclude' in input.context && void 0 !== input.context.exclude;
|
|
355
|
+
if (hasInclude) context.include = input.context.include;
|
|
356
|
+
else if (!hasInclude && !hasExclude) context.include = input.context;
|
|
357
|
+
if (hasExclude) context.exclude = context.exclude.concat(input.context.exclude);
|
|
358
|
+
}
|
|
359
|
+
axe.reset();
|
|
360
|
+
const configWithDefault = {
|
|
361
|
+
...config,
|
|
362
|
+
rules: [
|
|
363
|
+
...DISABLED_RULES.map((id)=>({
|
|
364
|
+
id,
|
|
365
|
+
enabled: false
|
|
366
|
+
})),
|
|
367
|
+
...config?.rules ?? []
|
|
368
|
+
]
|
|
369
|
+
};
|
|
370
|
+
axe.configure(configWithDefault);
|
|
371
|
+
return new Promise((resolve, reject)=>{
|
|
372
|
+
const highlightsRoot = a11yRunner_document?.getElementById('storybook-highlights-root');
|
|
373
|
+
if (highlightsRoot) highlightsRoot.style.display = 'none';
|
|
374
|
+
const task = async ()=>{
|
|
375
|
+
try {
|
|
376
|
+
const result = await axe.run(context, options);
|
|
377
|
+
let contextElement = a11yRunner_document;
|
|
378
|
+
if (context.include instanceof Element) contextElement = context.include;
|
|
379
|
+
else if (Array.isArray(context.include)) {
|
|
380
|
+
const first = context.include[0];
|
|
381
|
+
if (first instanceof Element) contextElement = first;
|
|
382
|
+
else if ('string' == typeof first) contextElement = a11yRunner_document.querySelector(first) || a11yRunner_document;
|
|
383
|
+
} else if ('string' == typeof context.include) contextElement = a11yRunner_document.querySelector(context.include) || a11yRunner_document;
|
|
384
|
+
const apcaResult = await runAPCACheck(contextElement, input.apca);
|
|
385
|
+
if (apcaResult.nodes.length > 0) result.violations.push(apcaResult);
|
|
386
|
+
else result.passes.push(apcaResult);
|
|
387
|
+
const resultWithLinks = withLinkPaths(result, storyId);
|
|
388
|
+
globalThis.__TECHSIO_A11Y_RESULTS__ = {
|
|
389
|
+
storyId,
|
|
390
|
+
results: resultWithLinks,
|
|
391
|
+
timestamp: Date.now()
|
|
392
|
+
};
|
|
393
|
+
resolve(resultWithLinks);
|
|
394
|
+
} catch (error) {
|
|
395
|
+
reject(error);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
queue.push(task);
|
|
399
|
+
if (!isRunning) runNext();
|
|
400
|
+
if (highlightsRoot) highlightsRoot.style.display = '';
|
|
401
|
+
});
|
|
402
|
+
};
|
|
403
|
+
channel.on(EVENTS.MANUAL, async (storyId, input = DEFAULT_PARAMETERS)=>{
|
|
404
|
+
try {
|
|
405
|
+
await waitForAnimations();
|
|
406
|
+
const result = await run(input, storyId);
|
|
407
|
+
const resultJson = JSON.parse(JSON.stringify(result));
|
|
408
|
+
channel.emit(EVENTS.RESULT, resultJson, storyId);
|
|
409
|
+
} catch (error) {
|
|
410
|
+
channel.emit(EVENTS.ERROR, error);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
function getIsVitestStandaloneRun() {
|
|
414
|
+
try {
|
|
415
|
+
return 'false' === ({
|
|
416
|
+
MODE: "production",
|
|
417
|
+
DEV: false,
|
|
418
|
+
PROD: true,
|
|
419
|
+
BASE_URL: "/",
|
|
420
|
+
ASSET_PREFIX: "auto"
|
|
421
|
+
}).VITEST_STORYBOOK;
|
|
422
|
+
} catch (e) {
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const knownFilters = Object.values(filters).map((f)=>f.filter);
|
|
427
|
+
const knownFiltersRegExp = new RegExp(`\\b(${knownFilters.join('|')})\\b`, 'g');
|
|
428
|
+
const withVisionSimulator = (StoryFn, { globals })=>{
|
|
429
|
+
const { vision } = globals;
|
|
430
|
+
const applyVisionFilter = useCallback(()=>{
|
|
431
|
+
const existingFilters = document.body.style.filter.replaceAll(knownFiltersRegExp, '').trim();
|
|
432
|
+
const visionFilter = filters[vision]?.filter;
|
|
433
|
+
if (visionFilter && document.body.classList.contains('sb-show-main')) if (existingFilters && 'none' !== existingFilters) document.body.style.filter = `${existingFilters} ${visionFilter}`;
|
|
434
|
+
else document.body.style.filter = visionFilter;
|
|
435
|
+
else document.body.style.filter = existingFilters || 'none';
|
|
436
|
+
return ()=>document.body.style.filter = existingFilters || 'none';
|
|
437
|
+
}, [
|
|
438
|
+
vision
|
|
439
|
+
]);
|
|
440
|
+
useEffect(()=>{
|
|
441
|
+
const cleanup = applyVisionFilter();
|
|
442
|
+
const observer = new MutationObserver(()=>applyVisionFilter());
|
|
443
|
+
observer.observe(document.body, {
|
|
444
|
+
attributeFilter: [
|
|
445
|
+
'class'
|
|
446
|
+
]
|
|
447
|
+
});
|
|
448
|
+
return ()=>{
|
|
449
|
+
cleanup();
|
|
450
|
+
observer.disconnect();
|
|
451
|
+
};
|
|
452
|
+
}, [
|
|
453
|
+
applyVisionFilter
|
|
454
|
+
]);
|
|
455
|
+
useEffect(()=>{
|
|
456
|
+
document.body.insertAdjacentHTML('beforeend', filterDefs);
|
|
457
|
+
return ()=>{
|
|
458
|
+
const filterDefsElement = document.getElementById('storybook-a11y-vision-filters');
|
|
459
|
+
filterDefsElement?.parentElement?.removeChild(filterDefsElement);
|
|
460
|
+
};
|
|
461
|
+
}, []);
|
|
462
|
+
return StoryFn();
|
|
463
|
+
};
|
|
464
|
+
let vitestMatchersExtended = false;
|
|
465
|
+
const decorators = [
|
|
466
|
+
withVisionSimulator
|
|
467
|
+
];
|
|
468
|
+
const afterEach = async ({ id: storyId, reporting, parameters, globals, viewMode })=>{
|
|
469
|
+
const a11yParameter = parameters.a11y;
|
|
470
|
+
const a11yGlobals = globals.a11y;
|
|
471
|
+
const shouldRunEnvironmentIndependent = a11yParameter?.disable !== true && a11yParameter?.test !== 'off' && a11yGlobals?.manual !== true;
|
|
472
|
+
const getMode = ()=>{
|
|
473
|
+
switch(a11yParameter?.test){
|
|
474
|
+
case 'todo':
|
|
475
|
+
return 'warning';
|
|
476
|
+
case 'error':
|
|
477
|
+
default:
|
|
478
|
+
return 'failed';
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
if (shouldRunEnvironmentIndependent && 'story' === viewMode) try {
|
|
482
|
+
const result = await run(a11yParameter, storyId);
|
|
483
|
+
if (result) {
|
|
484
|
+
const hasViolations = (result?.violations.length ?? 0) > 0;
|
|
485
|
+
reporting.addReport({
|
|
486
|
+
type: 'a11y',
|
|
487
|
+
version: 1,
|
|
488
|
+
result,
|
|
489
|
+
status: hasViolations ? getMode() : 'passed'
|
|
490
|
+
});
|
|
491
|
+
if (getIsVitestStandaloneRun()) {
|
|
492
|
+
if (hasViolations && 'failed' === getMode()) {
|
|
493
|
+
if (!vitestMatchersExtended) {
|
|
494
|
+
const { toHaveNoViolations } = await import("./212.js").then((mod)=>({
|
|
495
|
+
toHaveNoViolations: mod.toHaveNoViolations
|
|
496
|
+
}));
|
|
497
|
+
expect.extend({
|
|
498
|
+
toHaveNoViolations
|
|
499
|
+
});
|
|
500
|
+
vitestMatchersExtended = true;
|
|
501
|
+
}
|
|
502
|
+
expect(result).toHaveNoViolations();
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
} catch (e) {
|
|
507
|
+
reporting.addReport({
|
|
508
|
+
type: 'a11y',
|
|
509
|
+
version: 1,
|
|
510
|
+
result: {
|
|
511
|
+
error: e
|
|
512
|
+
},
|
|
513
|
+
status: 'failed'
|
|
514
|
+
});
|
|
515
|
+
if (getIsVitestStandaloneRun()) throw e;
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
const initialGlobals = {
|
|
519
|
+
a11y: {
|
|
520
|
+
manual: false
|
|
521
|
+
},
|
|
522
|
+
vision: void 0
|
|
523
|
+
};
|
|
524
|
+
const preview_parameters = {
|
|
525
|
+
a11y: {
|
|
526
|
+
test: 'todo'
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
export { afterEach, decorators, initialGlobals, preview_namespaceObject, preview_parameters as parameters };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccessibilityRuleMaps.d.ts","sourceRoot":"","sources":["../src/AccessibilityRuleMaps.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IAEvB,CAAC,KAAK,EAAE,MAAM,GAAG;QAEf,KAAK,EAAE,MAAM,CAAC;QAEd,UAAU,EAAE,MAAM,CAAC;QAEnB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;CACH,CAAC;AA2kBF,eAAO,MAAM,gBAAgB,EAAE,UAS9B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a11yRunner.d.ts","sourceRoot":"","sources":["../src/a11yRunner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAA4B,MAAM,UAAU,CAAC;AAMrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAiC/C,eAAO,MAAM,GAAG,GAAU,OAAO,cAAc,YAAqB,EAAE,SAAS,MAAM,wBA4GpF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a11yRunnerUtils.d.ts","sourceRoot":"","sources":["../src/a11yRunnerUtils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAU,MAAM,UAAU,CAAC;AAGnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAK/C,eAAO,MAAM,aAAa,GAAI,SAAS,UAAU,EAAE,SAAS,MAAM,KAsBtC,eAC3B,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Result } from 'axe-core';
|
|
2
|
+
type ApcaConformanceLevel = 'bronze' | 'silver' | 'gold';
|
|
3
|
+
type ApcaUseCase = 'body' | 'fluent' | 'sub-fluent' | 'non-fluent';
|
|
4
|
+
interface ApcaOptions {
|
|
5
|
+
level?: ApcaConformanceLevel;
|
|
6
|
+
useCase?: ApcaUseCase;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Run APCA contrast checks on the document
|
|
10
|
+
*/
|
|
11
|
+
export declare function runAPCACheck(context?: Element | Document, options?: ApcaOptions): Promise<Result>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=apcaChecker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apcaChecker.d.ts","sourceRoot":"","sources":["../src/apcaChecker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,UAAU,CAAC;AAYnD,KAAK,oBAAoB,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AACzD,KAAK,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,GAAG,YAAY,CAAC;AAEnE,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AA8OD;;GAEG;AACH,wBAAsB,YAAY,CAChC,OAAO,GAAE,OAAO,GAAG,QAAmB,EACtC,OAAO,GAAE,WAAkC,GAC1C,OAAO,CAAC,MAAM,CAAC,CAoKjB"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { EnhancedResult } from './types';
|
|
2
|
+
export declare const getTitleForAxeResult: (axeResult: EnhancedResult) => string;
|
|
3
|
+
export declare const getFriendlySummaryForAxeResult: (axeResult: EnhancedResult) => string | undefined;
|
|
4
|
+
//# sourceMappingURL=axeRuleMappingHelper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"axeRuleMappingHelper.d.ts","sourceRoot":"","sources":["../src/axeRuleMappingHelper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,eAAO,MAAM,oBAAoB,GAAI,WAAW,cAAc,KAAG,MACV,CAAC;AAExD,eAAO,MAAM,8BAA8B,GAAI,WAAW,cAAc,KAAG,MAAM,GAAG,SACV,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"A11YPanel.d.ts","sourceRoot":"","sources":["../../src/components/A11YPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAuDvC,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAkL7B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { FC, PropsWithChildren } from 'react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { A11yParameters } from '../params';
|
|
4
|
+
import type { EnhancedResult, EnhancedResults, Status } from '../types';
|
|
5
|
+
import { RuleType } from '../types';
|
|
6
|
+
import type { TestDiscrepancy } from './TestDiscrepancyMessage';
|
|
7
|
+
export interface A11yContextStore {
|
|
8
|
+
parameters: A11yParameters;
|
|
9
|
+
results: EnhancedResults | undefined;
|
|
10
|
+
highlighted: boolean;
|
|
11
|
+
toggleHighlight: () => void;
|
|
12
|
+
tab: RuleType;
|
|
13
|
+
handleCopyLink: (key: string) => void;
|
|
14
|
+
setTab: (type: RuleType) => void;
|
|
15
|
+
status: Status;
|
|
16
|
+
setStatus: (status: Status) => void;
|
|
17
|
+
error: unknown;
|
|
18
|
+
handleManual: () => void;
|
|
19
|
+
discrepancy: TestDiscrepancy;
|
|
20
|
+
selectedItems: Map<string, string>;
|
|
21
|
+
toggleOpen: (event: React.SyntheticEvent<Element>, type: RuleType, item: EnhancedResult) => void;
|
|
22
|
+
allExpanded: boolean;
|
|
23
|
+
handleCollapseAll: () => void;
|
|
24
|
+
handleExpandAll: () => void;
|
|
25
|
+
handleJumpToElement: (target: string) => void;
|
|
26
|
+
handleSelectionChange: (key: string) => void;
|
|
27
|
+
}
|
|
28
|
+
export declare const A11yContext: React.Context<A11yContextStore>;
|
|
29
|
+
export declare const A11yContextProvider: FC<PropsWithChildren>;
|
|
30
|
+
export declare const useA11yContext: () => A11yContextStore;
|
|
31
|
+
//# sourceMappingURL=A11yContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"A11yContext.d.ts","sourceRoot":"","sources":["../../src/components/A11yContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,KAA+E,MAAM,OAAO,CAAC;AA2BpG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAc,cAAc,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACpF,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAMhE,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,EAAE,eAAe,GAAG,SAAS,CAAC;IACrC,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,GAAG,EAAE,QAAQ,CAAC;IACd,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,MAAM,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,EAAE,eAAe,CAAC;IAC7B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACjG,WAAW,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,mBAAmB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,qBAAqB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AASD,eAAO,MAAM,WAAW,iCAoBtB,CAAC;AAEH,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,iBAAiB,CA0YrD,CAAC;AAEF,eAAO,MAAM,cAAc,wBAAgC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EnhancedResult, RuleType } from '../../types';
|
|
2
|
+
interface DetailsProps {
|
|
3
|
+
id: string;
|
|
4
|
+
item: EnhancedResult;
|
|
5
|
+
type: RuleType;
|
|
6
|
+
selection: string | undefined;
|
|
7
|
+
handleSelectionChange: (key: string) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare const Details: ({ id, item, type, selection, handleSelectionChange }: DetailsProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=Details.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Details.d.ts","sourceRoot":"","sources":["../../../src/components/Report/Details.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAsB,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAyHhF,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,qBAAqB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED,eAAO,MAAM,OAAO,GAAI,sDAAsD,YAAY,4CAiDzF,CAAC"}
|