UNPKG

@storybook/addon-a11y

Version:

Storybook Addon A11y: Test UI component compliance with WCAG web accessibility standards

187 lines (178 loc) • 7.07 kB
import { EVENTS, PANEL_ID, filterDefs, filters } from "./chunk-7OPMK7TU.js"; import { __export } from "./chunk-4BE7D4DS.js"; // src/preview.tsx var preview_exports = {}; __export(preview_exports, { afterEach: () => afterEach, decorators: () => decorators, initialGlobals: () => initialGlobals, parameters: () => parameters }); import { expect } from "storybook/test"; // src/a11yRunner.ts import { ElementA11yParameterError } from "storybook/internal/preview-errors"; import { global as global2 } from "@storybook/global"; import { addons, waitForAnimations } from "storybook/preview-api"; // src/a11yRunnerUtils.ts import { global } from "@storybook/global"; var { document: document2 } = global, withLinkPaths = (results, storyId) => { let pathname = document2.location.pathname.replace(/iframe\.html$/, ""), enhancedResults = { ...results }; return ["incomplete", "passes", "violations"].forEach((key) => { Array.isArray(results[key]) && (enhancedResults[key] = results[key].map((result) => ({ ...result, nodes: result.nodes.map((node, index) => { let id = `${key}.${result.id}.${index + 1}`, linkPath = `${pathname}?path=/story/${storyId}&addonPanel=${PANEL_ID}&a11ySelection=${id}`; return { id, ...node, linkPath }; }) }))); }), enhancedResults; }; // src/a11yRunner.ts var { document: document3 } = global2, channel = addons.getChannel(), DEFAULT_PARAMETERS = { config: {}, options: {} }, DISABLED_RULES = [ // In component testing, landmarks are not always present // and the rule check can cause false positives "region" ], queue = [], isRunning = !1, runNext = async () => { if (queue.length === 0) { isRunning = !1; return; } isRunning = !0; let next = queue.shift(); next && await next(), runNext(); }, run = async (input = DEFAULT_PARAMETERS, storyId) => { let axe = (await import("axe-core"))?.default || globalThis.axe, { config = {}, options = {} } = input; if (input.element) throw new ElementA11yParameterError(); let context = { include: document3?.body, exclude: [".sb-wrapper", "#storybook-docs", "#storybook-highlights-root"] // Internal Storybook elements that are always in the document }; if (input.context) { let hasInclude = typeof input.context == "object" && "include" in input.context && input.context.include !== void 0, hasExclude = typeof input.context == "object" && "exclude" in input.context && input.context.exclude !== void 0; hasInclude ? context.include = input.context.include : !hasInclude && !hasExclude && (context.include = input.context), hasExclude && (context.exclude = context.exclude.concat(input.context.exclude)); } axe.reset(); let configWithDefault = { ...config, rules: [...DISABLED_RULES.map((id) => ({ id, enabled: !1 })), ...config?.rules ?? []] }; return axe.configure(configWithDefault), new Promise((resolve, reject) => { let highlightsRoot = document3?.getElementById("storybook-highlights-root"); highlightsRoot && (highlightsRoot.style.display = "none"); let task = async () => { try { let result = await axe.run(context, options), resultWithLinks = withLinkPaths(result, storyId); resolve(resultWithLinks); } catch (error) { reject(error); } }; queue.push(task), isRunning || runNext(), highlightsRoot && (highlightsRoot.style.display = ""); }); }; channel.on(EVENTS.MANUAL, async (storyId, input = DEFAULT_PARAMETERS) => { try { await waitForAnimations(); let result = await run(input, storyId), resultJson = JSON.parse(JSON.stringify(result)); channel.emit(EVENTS.RESULT, resultJson, storyId); } catch (error) { channel.emit(EVENTS.ERROR, error); } }); // src/utils.ts function getIsVitestStandaloneRun() { try { return import.meta.env.VITEST_STORYBOOK === "false"; } catch { return !1; } } // src/withVisionSimulator.ts import { useCallback, useEffect } from "storybook/preview-api"; var knownFilters = Object.values(filters).map((f) => f.filter), knownFiltersRegExp = new RegExp(`\\b(${knownFilters.join("|")})\\b`, "g"), withVisionSimulator = (StoryFn, { globals }) => { let { vision } = globals, applyVisionFilter = useCallback(() => { let existingFilters = document.body.style.filter.replaceAll(knownFiltersRegExp, "").trim(), visionFilter = filters[vision]?.filter; return visionFilter && document.body.classList.contains("sb-show-main") ? !existingFilters || existingFilters === "none" ? document.body.style.filter = visionFilter : document.body.style.filter = `${existingFilters} ${visionFilter}` : document.body.style.filter = existingFilters || "none", () => document.body.style.filter = existingFilters || "none"; }, [vision]); return useEffect(() => { let cleanup = applyVisionFilter(), observer = new MutationObserver(() => applyVisionFilter()); return observer.observe(document.body, { attributeFilter: ["class"] }), () => { cleanup(), observer.disconnect(); }; }, [applyVisionFilter]), useEffect(() => (document.body.insertAdjacentHTML("beforeend", filterDefs), () => { let filterDefsElement = document.getElementById("storybook-a11y-vision-filters"); filterDefsElement?.parentElement?.removeChild(filterDefsElement); }), []), StoryFn(); }; // src/preview.tsx var vitestMatchersExtended = !1, decorators = [withVisionSimulator], afterEach = async ({ id: storyId, reporting, parameters: parameters2, globals, viewMode }) => { let a11yParameter = parameters2.a11y, a11yGlobals = globals.a11y, shouldRunEnvironmentIndependent = !!!globals.ghostStories && a11yParameter?.disable !== !0 && a11yParameter?.test !== "off" && a11yGlobals?.manual !== !0, getMode = () => { switch (a11yParameter?.test) { case "todo": return "warning"; case "error": default: return "failed"; } }; if (shouldRunEnvironmentIndependent && viewMode === "story") try { let result = await run(a11yParameter, storyId); if (result) { let hasViolations = (result?.violations.length ?? 0) > 0; if (reporting.addReport({ type: "a11y", version: 1, result, status: hasViolations ? getMode() : "passed" }), getIsVitestStandaloneRun() && hasViolations && getMode() === "failed") { if (!vitestMatchersExtended) { let { toHaveNoViolations } = await import("./matchers-5TDFFDYO.js"); expect.extend({ toHaveNoViolations }), vitestMatchersExtended = !0; } expect(result).toHaveNoViolations(); } } } catch (e) { if (reporting.addReport({ type: "a11y", version: 1, result: { error: e }, status: "failed" }), getIsVitestStandaloneRun()) throw e; } }, initialGlobals = { a11y: { manual: !1 }, vision: void 0 }, parameters = { a11y: { test: "todo" } }; export { decorators, afterEach, initialGlobals, parameters, preview_exports };