Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
30 changes: 30 additions & 0 deletions .storybook/test-runner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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',
{
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",
"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
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
1 change: 0 additions & 1 deletion scss/bitstyles/atoms/dropdown/dropdown.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ const menu = `
}).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({
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/organisms/page-header/PageHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ breadCrumbsMenu.innerHTML = `
</svg>
</li>`;

export const header = document.createElement('header');
export const header = document.createElement('div');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This component is already wrapped in a header landmark, so it cannot have another header landmark inside of it.

header.innerHTML = `
<div class="u-flex u-justify-between u-flex-wrap u-items-center u-margin-m-y">
<div class="u-flex u-flex-wrap u-items-center">
Expand Down
17 changes: 8 additions & 9 deletions scss/bitstyles/organisms/sidebar/Sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,9 @@ buttonList.classList.add(
'u-padding-s4-left'
);

const listItem = document.createElement('li');
listItem.classList.add('u-margin-s2-bottom', 'u-flex');

const buttomComponent = (label) =>
const buttonComponent = (label) =>
Button({
classname: ['u-justify-start'],
classname: ['u-justify-start', 'u-flex-grow-1'],
children: `<svg width="20" height="20" class="a-icon a-icon--m" aria-hidden="true" focusable="false"><use xlink:href="${icons}#icon-caret-right"></use></svg><span class="u-margin-s1-left">${label}</span>`,
colorVariant: ['transparent'],
});
Expand All @@ -47,9 +44,12 @@ const labels = [
'Sales',
];

labels.forEach((label) =>
buttonList.appendChild(listItem.appendChild(buttomComponent(label)))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This cause appendChild returns the child, not the parent, so this code was resulting in buttons being appended directly to the list, and the list item was unused.

);
labels.forEach((label) => {
const listItem = document.createElement('li');
listItem.classList.add('u-margin-s2-bottom', 'u-flex');
listItem.appendChild(buttonComponent(label));
buttonList.appendChild(listItem);
});

export const bottom = document.createElement('div');
bottom.innerHTML = `
Expand Down Expand Up @@ -91,7 +91,6 @@ bottom.innerHTML = `
<li>
<a href="/" class="a-button a-button--transparent"> Privacy </a>
</li>
<li role="separator"></li>
<li>
<a href="/" class="a-button a-button--transparent"> Sign out </a>
</li>
Expand Down
Loading