@storybook/addon-a11y
Version:
Test component compliance with web accessibility standards
96 lines (79 loc) • 2.2 kB
JavaScript
import global from 'global';
import { addons } from '@storybook/addons';
import { EVENTS } from './constants';
const {
document,
window: globalWindow
} = global;
if (module && module.hot && module.hot.decline) {
module.hot.decline();
}
const channel = addons.getChannel(); // Holds axe core running state
let active = false; // Holds latest story we requested a run
let activeStoryId;
const getElement = () => {
const storyRoot = document.getElementById('story-root');
return storyRoot ? storyRoot.childNodes : document.getElementById('root');
};
/**
* Handle A11yContext events.
* Because the event are sent without manual check, we split calls
*/
const handleRequest = async storyId => {
const {
manual
} = await getParams(storyId);
if (!manual) {
await run(storyId);
}
};
const run = async storyId => {
activeStoryId = storyId;
try {
const input = await getParams(storyId);
if (!active) {
active = true;
channel.emit(EVENTS.RUNNING);
const axe = await import('axe-core');
const {
element = getElement(),
config,
options = {}
} = input;
axe.reset();
if (config) {
axe.configure(config);
}
const result = await axe.run(element, options); // It's possible that we requested a new run on a different story.
// Unfortunately, axe doesn't support a cancel method to abort current run.
// We check if the story we run against is still the current one,
// if not, trigger a new run using the current story
if (activeStoryId === storyId) {
channel.emit(EVENTS.RESULT, result);
} else {
active = false;
run(activeStoryId);
}
}
} catch (error) {
channel.emit(EVENTS.ERROR, error);
} finally {
active = false;
}
};
/** Returns story parameters or default ones. */
const getParams = async storyId => {
const {
parameters
} = (await globalWindow.__STORYBOOK_STORY_STORE__.loadStory({
storyId
})) || {};
return parameters.a11y || {
config: {},
options: {
restoreScroll: true
}
};
};
channel.on(EVENTS.REQUEST, handleRequest);
channel.on(EVENTS.MANUAL, run);