Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate axe-playwright into storybook/test-runner #828

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
26 changes: 18 additions & 8 deletions .github/workflows/run-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,27 @@ jobs:
with:
node-version: '18.16.1'

- name: Install dependencies ⚙️
run: yarn

- name: Lint 👀
run: |
yarn
yarn lint
run: yarn lint

- name: Test 🚀
run: |
yarn
yarn test
run: yarn test

- name: Build Storybook 📖
run: yarn build-storybook

- name: Setup Playwright for Storybook tests
run: yarn playwright install

- name: Storybook Tests (axe-core) ♿️
run: |
yarn
yarn build-storybook
yarn storybook &
echo "wait for storybook to start"
until curl --output /dev/null --silent --head --fail http://localhost:6006; do
printf '.'
sleep 5
done
yarn test-storybook
1 change: 1 addition & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = {
'../scss/**/*.stories.mdx',
'../scss/**/*.stories.@(js|jsx|ts|tsx)',
],
features: { buildStoriesJson: true },
addons: [
'@storybook/addon-links',
'@storybook/addon-a11y',
Expand Down
38 changes: 38 additions & 0 deletions .storybook/test-runner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const { getStoryContext } = require('@storybook/test-runner');
const { injectAxe, checkA11y } = require('axe-playwright');
/*
* See https://storybook.js.org/docs/writing-tests/test-runner#test-hook-api
* to learn more about the test-runner hooks API.
*/
module.exports = {
async preVisit(page) {
await injectAxe(page);
},
async postVisit(page, context) {
// Get the entire context of a story, including parameters, args, argTypes, etc.
const storyContext = await getStoryContext(page, context);

// Do not run a11y tests on disabled stories.
if (storyContext.parameters?.a11y?.disable) {
return;
}
await checkA11y(
page,
'#root',
{
axeOptions: {
rules: {
// TODO: enable this rule again once all color contrast issues are fixed
'color-contrast': {
enabled: false,
},
Comment on lines +25 to +28
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be enbled when working on #814

},
},
detailedReport: true,
verbose: false,
},
false,
'v2'
);
},
};
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"format:docs": "prettier --ignore-path .gitignore --write './**/*.{md,mdx}'",
"format": "yarn format:js && yarn format:scss && yarn format:docs",
"test": "script/test",
"test-storybook": "test-storybook --stories-json",
"checks": "yarn lint && yarn css:stats && yarn test",
"watch": "yarn build && yarn build --watch",
"clean": "rimraf build/*",
Expand Down Expand Up @@ -58,10 +59,12 @@
"@storybook/builder-webpack5": "^6.5.9",
"@storybook/html": "^6.5.9",
"@storybook/manager-webpack5": "^6.5.9",
"@storybook/test-runner": "^0.16.0",
"a11y-dialog": "^7.1.0",
"alpinejs": "^2.8.2",
"analyze-css": "^2.1.50",
"autoprefixer": "^10.4.8",
"axe-playwright": "^2.0.1",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"cssnano": "^5.1.13",
Expand Down
6 changes: 3 additions & 3 deletions scss/bitstyles/atoms/avatar/avatar.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ A user’s visual representation — an image that will represent them in your U
<Story name="Avatar">
{`
<div class="a-avatar">
<img src="https://placekitten.com/100/100" width="46" height="46" alt="Username’s avatar" />
<img src="https://placehold.co/100x100" width="46" height="46" alt="Username’s avatar" />
</div>
`}
</Story>
Expand All @@ -23,7 +23,7 @@ Avatars are available at `m` and `l` sizes by default:
{`
<div class="u-flex u-items-center">
<div class="a-avatar a-avatar--m u-flex u-items-center">
<img src="https://placekitten.com/100/100" width="46" height="46" alt="Username’s avatar" />
<img src="https://placehold.co/100x100" width="46" height="46" alt="Username’s avatar" />
</div>
<span class="u-margin-s2-left">Username</span>
</div>
Expand All @@ -33,7 +33,7 @@ Avatars are available at `m` and `l` sizes by default:
{`
<div class="u-flex u-items-center">
<div class="a-avatar a-avatar--l u-flex u-items-center">
<img src="https://placekitten.com/100/100" width="46" height="46" alt="Username’s avatar" />
<img src="https://placehold.co/100x100" width="46" height="46" alt="Username’s avatar" />
</div>
<span class="u-margin-s2-left">Username</span>
</div>
Expand Down
4 changes: 4 additions & 0 deletions scss/bitstyles/atoms/button/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export default ({
children,
colorVariant = [],
shapeVariant = [],
role = undefined,
ariaLabel = undefined,
ariaPressed = false,
ariaDisabled = false,
ariaCurrent = false,
Expand Down Expand Up @@ -33,6 +35,8 @@ export default ({
button.classList.add(cls);
});
if (element === 'button') button.type = 'button';
if (role) button.setAttribute('role', role);
if (ariaLabel) button.setAttribute('aria-label', ariaLabel);
if (ariaPressed) button.setAttribute('aria-pressed', ariaPressed);
if (ariaDisabled) button.setAttribute('aria-disabled', ariaDisabled);
if (ariaCurrent) button.setAttribute('aria-current', ariaCurrent);
Expand Down
39 changes: 30 additions & 9 deletions scss/bitstyles/atoms/button/button.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,21 @@ DefaultOutline.parameters = {
],
};

const baseTabArgs = {
colorVariant: ['tab'],
role: 'tab',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aria-selected is not allowed on normal buttons, so a tab role must be added. And then the button needs to we wrapped in a tablist because tabs must be children of tablists only, they cannot stand alone.

};

const TabDecorator = (story) => {
const decorator = document.createElement('ul');
decorator.setAttribute('role', 'tablist');
decorator.appendChild(story());
return decorator;
};

export const DefaultTab = Template.bind({});
DefaultTab.args = { colorVariant: ['tab'] };
DefaultTab.decorators = [TabDecorator];
DefaultTab.args = { ...baseTabArgs };
DefaultTab.parameters = {
zeplinLink: [
{
Expand Down Expand Up @@ -771,7 +784,8 @@ OutlinePressed.parameters = {
};

export const TabPressed = Template.bind({});
TabPressed.args = { colorVariant: ['tab'], ariaCurrent: 'page' };
TabPressed.decorators = [TabDecorator];
TabPressed.args = { ...baseTabArgs, ariaCurrent: 'page' };
TabPressed.parameters = {
zeplinLink:
'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=640094b52890b84043f89ba4',
Expand Down Expand Up @@ -815,7 +829,8 @@ OutlineDisabled.parameters = {
};

export const TabDisabled = Template.bind({});
TabDisabled.args = { colorVariant: ['tab'], disabled: true };
TabDisabled.decorators = [TabDecorator];
TabDisabled.args = { ...baseTabArgs, disabled: true };
TabDisabled.parameters = {
zeplinLink:
'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=640094b30ee0b5408ad023fd',
Expand Down Expand Up @@ -930,7 +945,8 @@ OutlineAnchor.parameters = {
};

export const TabAnchor = Template.bind({});
TabAnchor.args = { colorVariant: ['tab'], element: 'anchor' };
TabAnchor.decorators = [TabDecorator];
TabAnchor.args = { ...baseTabArgs, element: 'anchor' };
TabAnchor.parameters = {
zeplinLink: [
{
Expand All @@ -949,8 +965,9 @@ TabAnchor.parameters = {
};

export const TabAnchorPressed = Template.bind({});
TabAnchorPressed.decorators = [TabDecorator];
TabAnchorPressed.args = {
colorVariant: ['tab'],
...baseTabArgs,
element: 'anchor',
ariaCurrent: 'page',
};
Expand Down Expand Up @@ -1087,8 +1104,9 @@ OutlineAnchorDisabled.parameters = {
};

export const TabAnchorDisabled = Template.bind({});
TabAnchorDisabled.decorators = [TabDecorator];
TabAnchorDisabled.args = {
colorVariant: ['tab'],
...baseTabArgs,
ariaDisabled: true,
element: 'a',
};
Expand Down Expand Up @@ -1144,8 +1162,9 @@ DangerOutlineAnchorDisabled.parameters = {
// ***** Icon-only tab buttons ****************** //

export const TabIconOnlyBase = Template.bind({});
TabIconOnlyBase.decorators = [TabDecorator];
TabIconOnlyBase.args = {
colorVariant: ['tab'],
...baseTabArgs,
shapeVariant: ['square'],
children: `<svg width="20" height="20" class="a-icon a-icon--m" aria-hidden="true" focusable="false"><use xlink:href="${icons}#icon-plus"></use></svg><span class="u-sr-only">Add</span>`,
};
Expand All @@ -1167,8 +1186,9 @@ TabIconOnlyBase.parameters = {
};

export const TabIconOnlySelected = Template.bind({});
TabIconOnlySelected.decorators = [TabDecorator];
TabIconOnlySelected.args = {
colorVariant: ['tab'],
...baseTabArgs,
shapeVariant: ['square'],
children: `<svg width="20" height="20" class="a-icon a-icon--m" aria-hidden="true" focusable="false"><use xlink:href="${icons}#icon-plus"></use></svg><span class="u-sr-only">Add</span>`,
ariaSelected: true,
Expand All @@ -1179,8 +1199,9 @@ TabIconOnlySelected.parameters = {
};

export const TabIconOnlyDisabled = Template.bind({});
TabIconOnlyDisabled.decorators = [TabDecorator];
TabIconOnlyDisabled.args = {
colorVariant: ['tab'],
...baseTabArgs,
shapeVariant: ['square'],
children: `<svg width="20" height="20" class="a-icon a-icon--m" aria-hidden="true" focusable="false"><use xlink:href="${icons}#icon-plus"></use></svg><span class="u-sr-only">Add</span>`,
disabled: true,
Expand Down
9 changes: 4 additions & 5 deletions scss/bitstyles/atoms/dropdown/dropdown.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const menu = `
colorVariant: ['transparent'],
shapeVariant: ['menu'],
children: 'Settings',
element: 'a',
classname: ['u-width-full'],
}).outerHTML
}
</li>
Expand All @@ -72,7 +72,7 @@ const menu = `
colorVariant: ['transparent'],
shapeVariant: ['menu'],
children: 'Help',
element: 'a',
classname: ['u-width-full'],
}).outerHTML
}
</li>
Expand All @@ -82,18 +82,17 @@ const menu = `
colorVariant: ['transparent'],
shapeVariant: ['menu'],
children: 'Privacy',
element: 'a',
classname: ['u-width-full'],
}).outerHTML
}
</li>
<li role="separator"></li>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #829

<li>
${
Button({
colorVariant: ['transparent'],
shapeVariant: ['menu'],
children: 'Sign out',
element: 'a',
classname: ['u-width-full'],
}).outerHTML
}
</li>
Expand Down
1 change: 1 addition & 0 deletions scss/bitstyles/atoms/flash/Flash.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default ({ children, theme = 'brand-1', icon, onClick = null }) => {
Button({
shapeVariant: ['x-small', 'square'],
children: Icon({ name: 'cross' }),
ariaLabel: 'Close',
onClick,
})
);
Expand Down
2 changes: 1 addition & 1 deletion scss/bitstyles/base/figure/figure.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Canvas, Meta, Story } from '@storybook/addon-docs';
<Story name="Figure and figcaption">
{`
<figure>
<img src="https://placekitten.com/200/300">
<img src="https://placehold.co/200x300" alt="">
<figcaption>A portrait of a kitten</figcaption>
</figure>
`}
Expand Down
2 changes: 1 addition & 1 deletion scss/bitstyles/base/forms/forms.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ This includes all standard `type="text"` inputs, plus all the text-based inputs
<Story name="Select and label (invalid)">
{`
<label for="input_d2">Invalid select</label>
<select required>
<select id="input_d2" required>
<option value="" disabled hidden selected>Please select</option>
<option value="value2">Value 2</option>
<option value="value3">Value 3</option>
Expand Down
Loading
Loading