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

feat: accessibility tests #194

Merged
merged 7 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 4 additions & 3 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = {
},

addons: [
'@storybook/addon-a11y',
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
Expand All @@ -32,12 +33,12 @@ module.exports = {

framework: {
name: '@storybook/react-webpack5',
options: {}
options: {},
},

staticDirs: ['../static', '../public'],

docs: {
autodocs: true
}
autodocs: true,
},
};
2 changes: 1 addition & 1 deletion .storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ export const parameters = {
mswInitialize();

// Provide the MSW addon decorator globally
export const decorators = [mswDecorator];
export const decorators = [mswDecorator];
14 changes: 14 additions & 0 deletions .storybook/test-runner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const { injectAxe, checkA11y } = require('axe-playwright');
module.exports = {
preVisit: async (page) => {
// before the story has been rendered inject AXE
await injectAxe(page);
},
postVisit: async (page) => {
// after the story has been rendered run AXE checks only on #storybook-root, not the whole page
await checkA11y(page, '#storybook-root', {
detailedReport: true,
detailedReportOptions: { html: true },
});
},
};
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:
target: ${DOCKER_TARGET:-development}
ports:
- '3000:3000'
- '6006:6006' # Storybook
- '6006:6006' # Storybook
volumes:
- .:/app/
- /app/node_modules # Exclude node_modules
- /app/node_modules # Exclude node_modules
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"lint": "yarn eslint \"src/**/*.{ts,tsx}\" && yarn prettier ./src -c",
"lint-fix": "yarn eslint \"src/**/*.{ts,tsx}\" --fix && yarn prettier ./src -w",
"test": "jest",
"test-storybook": "test-storybook",
"build": "rollup -c --bundleConfigAsCjs",
"docker:dev": "cross-env DOCKER_TARGET=development docker compose up",
"docker:prod": "cross-env DOCKER_TARGET=production docker compose up",
Expand Down Expand Up @@ -71,6 +72,7 @@
"@rollup/plugin-commonjs": "^25.0.4",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.2.0",
"@storybook/addon-a11y": "^7.3.1",
"@storybook/addon-actions": "^7.3.1",
"@storybook/addon-essentials": "^7.3.1",
"@storybook/addon-interactions": "^7.3.1",
Expand All @@ -79,6 +81,7 @@
"@storybook/preset-scss": "^1.0.3",
"@storybook/react": "^7.3.1",
"@storybook/react-webpack5": "^7.3.1",
"@storybook/test-runner": "^0.16.0",
"@storybook/testing-library": "^0.2.0",
"@testing-library/jest-dom": "^6.1.6",
"@testing-library/react": "^14.1.0",
Expand All @@ -91,6 +94,7 @@
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"axe-playwright": "^1.1.11",
"babel-loader": "^9.1.3",
"classnames": "^2.3.1",
"cross-env": "^7.0.3",
Expand Down
2 changes: 1 addition & 1 deletion public/mockServiceWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,4 @@ function sleep(timeMs) {
async function respondWithMock(response) {
await sleep(response.delay)
return new Response(response.body, response)
}
}
3 changes: 2 additions & 1 deletion src/apollo/pageContent/PageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { PageContent as PageContentWithoutData } from '../../core/pageContent/Pa
import type { PageContentProps as PageContentPropsWithoutData } from '../../core/pageContent/types';
import styles from './pageContent.module.scss';
import useApolloPageContext from '../page/useApolloPageContext';
import { MAIN_CONTENT_ID } from '../../common/constants';

export type PageProps = Omit<PageContentPropsWithoutData, 'page'> & {
notFoundPageContent?: JSX.Element;
Expand All @@ -32,7 +33,7 @@ export function PageContent({

if (pageQuery.loading) {
return (
<div className={styles.loadingSpinnerContainer}>
<div id={MAIN_CONTENT_ID} className={styles.loadingSpinnerContainer}>
<LoadingSpinner multicolor />
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/common/components/grid/Grid.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Template: StoryFn<typeof Grid> = (args) => {
const getCellContent = (i: number) => (
<div
style={{
backgroundColor: 'salmon',
backgroundColor: '#333',
padding: 4,
color: 'white',
textAlign: 'center',
Expand Down
8 changes: 1 addition & 7 deletions src/core/card/Card.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,7 @@ const Template: StoryFn<typeof Card> = (args) => (
}}
>
<StoryContainer maxWidth={640}>
<Card
withBorder
hasLink
openLinkInNewTab
imageLabel="Article"
{...args}
/>
<Card withBorder hasLink openLinkInNewTab {...args} />
</StoryContainer>
</ConfigProvider>
);
Expand Down
1 change: 1 addition & 0 deletions src/core/card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ export function Card({
openInNewTab={openLinkInNewTab}
iconLeft={<IconArrowRight aria-hidden="true" />}
showExternalIcon={false}
aria-label={ariaLabel}
>
{linkArrowLabel && (
<span className={styles.linkArrowLabel}>{linkArrowLabel}</span>
Expand Down
98 changes: 55 additions & 43 deletions src/core/card/LargeCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,52 +46,64 @@ export function LargeCard({
const [isHovered, setIsHovered] = useState(false);
const handleToggleActive = () => setIsHovered((val) => !val);

return (
<LinkBox
id={id}
href={url}
className={classNames(styles.cardLink, className)}
aria-label={ariaLabel || ''}
openInNewTab={openInNewTab}
onMouseEnter={handleToggleActive}
onMouseLeave={handleToggleActive}
const content = (
<div
className={classNames(
styles[`${imagePosition}`],
styles.cardWrapper,
withBorder && styles.withBorder,
isHovered && styles.isHovered,
)}
>
<div
className={classNames(
styles[`${imagePosition}`],
styles.cardWrapper,
withBorder && styles.withBorder,
isHovered && styles.isHovered,
<BackgroundImage
id={id}
url={imageUrl}
labelTag={imageLabel}
className={styles.imageWrapper}
/>
<div className={styles.textWrapper}>
{title && <div className={styles.title}>{title}</div>}
{subTitle && <div className={styles.subTitle}>{subTitle}</div>}
<div className={classNames(styles.text, clampText && styles.clamp)}>
{text}
</div>
{customContent && (
<div className={styles.customContent}>{customContent}</div>
)}
>
<BackgroundImage
id={id}
url={imageUrl}
labelTag={imageLabel}
className={styles.imageWrapper}
/>
<div className={styles.textWrapper}>
{title && <div className={styles.title}>{title}</div>}
{subTitle && <div className={styles.subTitle}>{subTitle}</div>}
<div className={classNames(styles.text, clampText && styles.clamp)}>
{text}
{url && hasLink && (
<div className={styles.buttonWrapper}>
<Link
tabIndex={-1}
href={url}
openInNewTab={openInNewTab}
iconLeft={<IconArrowRight aria-hidden="true" />}
showExternalIcon={false}
/>
</div>
{customContent && (
<div className={styles.customContent}>{customContent}</div>
)}
{url && hasLink && (
<div className={styles.buttonWrapper}>
<Link
tabIndex={-1}
href={url}
openInNewTab={openInNewTab}
iconLeft={<IconArrowRight aria-hidden="true" />}
showExternalIcon={false}
/>
</div>
)}
</div>
)}
</div>
</LinkBox>
</div>
);

return (
// eslint-disable-next-line react/jsx-no-useless-fragment
<>
{hasLink ? (
<LinkBox
id={id}
href={url}
className={classNames(styles.cardLink, className)}
aria-label={ariaLabel || ''}
openInNewTab={openInNewTab}
onMouseEnter={handleToggleActive}
onMouseLeave={handleToggleActive}
>
{content}
</LinkBox>
) : (
// eslint-disable-next-line react/jsx-no-useless-fragment
<>{content}</>
)}
</>
);
}
1 change: 1 addition & 0 deletions src/core/hero/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export default function Hero({
<Link
href={backUrl}
openInNewTab={false}
aria-label="Back"
iconLeft={<IconArrowLeft aria-hidden="true" />}
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/core/pageContent/__mocks__/page.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ const mockPage: PageQuery['page'] = {
<p><strong>Kulttuurikasvatussuunnitelma </strong></p>
<p>Kulttuurikasvatussuunnitelma&nbsp;Helsingissä toimii Kulttuuri- ja vapaa-aika -toimialan, Kasvatus ja koulutus -toimialan&nbsp;(sis. yksityisen koulut)&nbsp;sekä taide-&nbsp;ja&nbsp;kulttuuritoimijoiden välisenä&nbsp;ohjaavana suunnitelmana.&nbsp;Yhteistyön tavoitteena on&nbsp;säännöllisten ja helposti&nbsp;lähestyttävien toimintatapojen, kuten kulttuuripolkujen,&nbsp;viestintäpalveluiden (esim. Kultus.fi) ja alan&nbsp;verkostojen&nbsp;kehittäminen ja ylläpitäminen. Suunnitelma koskee varhaiskasvatusta ja kouluja. Taide- ja taitoaineiden tavoitteet määritellään opetussuunnitelmissa ja varhaiskasvatussuunnitelmissa. Kulttuurikasvatusta voi sisältyä myös näiden kokonaisuuksien sisään.&nbsp;</p>
<p>Helsingin kulttuurikasvatussuunnitelma koskee kulttuuriperinnön, kulttuuriperintökasvatuksen, taiteen ja taidekasvatuksen sisältöjä, menetelmiä ja paikkoja. Se ei kuitenkaan ohjaa lapsen, nuoren tai perheen vapaa-ajan kulttuuri- ja taidekasvatuksen yhteistyön muotoja.</p>
<ul><li>List item 1</li><li>List item 1</li><li>List item 1</li><ul><li>Nested list item 1</li></ul></ul>
<ul><li>List item 1</li><li>List item 1</li><li>List item 1</li><li><ul><li>Nested list item 1</li></ul></li></ul>
`,
title: 'Kulttuurikasvatus',
featuredImage: {
Expand Down
6 changes: 3 additions & 3 deletions src/core/pageContent/__mocks__/pageWithDiverseContent.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const pageWithDiverseContent: PageQuery['page'] = {
</thead>
<tbody>
<tr>
<td>Row 1 Cell 1 with <a href="https://hel.fi">Link</a></td>
<td>Row 1 Cell 1 with <a aria-label="link" href="https://hel.fi">Link</a></td>
<td>Row 1 Cell 2</td>
</tr>
<tr>
Expand All @@ -62,7 +62,7 @@ const pageWithDiverseContent: PageQuery['page'] = {
</thead>
<tbody>
<tr>
<td>Row 1 Cell 1 with <a href="https://hel.fi">Link</a></td>
<td>Row 1 Cell 1 with <a aria-label="link" href="https://hel.fi">Link</a></td>
<td>Row 1 Cell 2</td>
</tr>
<tr>
Expand All @@ -72,7 +72,7 @@ const pageWithDiverseContent: PageQuery['page'] = {
</tbody>
</table>
</figure>
<p><a href="https://hel.fi">Link</a></p>
<p><a aria-label="link" href="https://hel.fi">Link</a></p>
<p><abbr title="City of Helsinki">CoH</abbr></p>
<p><b>Bold</b></p>
<p><bdi>إيان</bdi>: 90 points</p>
Expand Down
12 changes: 7 additions & 5 deletions src/core/pageContent/__tests__/__snapshots__/Page.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,13 @@ exports[`PageContent renders page with expected content 1`] = `
<li>
List item 1
</li>
<ul>
<li>
Nested list item 1
</li>
</ul>
<li>
<ul>
<li>
Nested list item 1
</li>
</ul>
</li>
</ul>


Expand Down
8 changes: 8 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
"alwaysStrict": true
},
"include": ["src", "jest-setup.ts"],
"exclude": [
"dist",
"**/__mocks__/*",
"**/node_modules/*",
"build",
"src/**/*.test.ts",
"src/**/*.test.tsx"
],
"compileOnSave": false,
"buildOnSave": false
}
Loading
Loading