diff --git a/.github/workflows/storybook-tests.yml b/.github/workflows/storybook-tests.yml
new file mode 100644
index 00000000..5332048d
--- /dev/null
+++ b/.github/workflows/storybook-tests.yml
@@ -0,0 +1,20 @@
+name: 'Storybook Tests'
+on: push
+jobs:
+ test:
+ timeout-minutes: 60
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
+ - name: Install dependencies
+ run: yarn
+ - name: Install Playwright
+ run: npx playwright install --with-deps
+ - name: Build Storybook
+ run: yarn build-storybook --quiet
+ - name: Serve Storybook and run tests
+ run: |
+ npx --yes concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
+ "npx --yes http-server storybook-static --port 6006 --silent" \
+ "npx --yes wait-on http://127.0.0.1:6006 && yarn test-storybook"
diff --git a/.gitignore b/.gitignore
index 595b2073..3e373d91 100644
--- a/.gitignore
+++ b/.gitignore
@@ -113,3 +113,5 @@ storybook-static/
# IDEA files
.idea/
+
+*__diff_output__*
\ No newline at end of file
diff --git a/.storybook/test-runner.ts b/.storybook/test-runner.ts
new file mode 100644
index 00000000..922f430e
--- /dev/null
+++ b/.storybook/test-runner.ts
@@ -0,0 +1,24 @@
+import { TestRunnerConfig } from '@storybook/test-runner';
+
+import { toMatchImageSnapshot } from 'jest-image-snapshot';
+
+const config: TestRunnerConfig = {
+ tags: {
+ /**
+ * Any Story can be skipped by adding the `no-test` tag to it.
+ * This is primaraily used for stories that generate dynamic id's as the snapshot will fail.
+ */
+ skip: ['no-test'],
+ },
+ setup() {
+ expect.extend({ toMatchImageSnapshot });
+ },
+ async postVisit(page) {
+ const elementHandler = await page.$('#storybook-root');
+ const innerHTML = await elementHandler?.innerHTML();
+ if (!innerHTML) throw new Error('No innerHTML found');
+ expect(innerHTML).toMatchSnapshot();
+ },
+};
+
+export default config;
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..55712c19
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "typescript.tsdk": "node_modules/typescript/lib"
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 8196b10c..0b30f51c 100644
--- a/package.json
+++ b/package.json
@@ -10,22 +10,31 @@
"devDependencies": {
"@babel/cli": "^7.13.16",
"@babel/core": "^7.19.6",
- "@babel/preset-react": "^7.18.6",
+ "@babel/plugin-proposal-class-properties": "^7.18.6",
+ "@babel/plugin-proposal-numeric-separator": "^7.18.6",
+ "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
+ "@babel/plugin-proposal-private-methods": "^7.18.6",
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
+ "@babel/preset-env": "^7.23.8",
+ "@babel/preset-react": "^7.23.3",
+ "@babel/preset-typescript": "^7.23.3",
"@rollup/plugin-commonjs": "^24.1.0",
"@rollup/plugin-node-resolve": "^15.0.2",
"@rollup/plugin-typescript": "^11.1.0",
- "@storybook/addon-actions": "^7.0.2",
- "@storybook/addon-essentials": "^7.0.2",
- "@storybook/addon-links": "^7.0.2",
- "@storybook/addons": "^7.0.2",
- "@storybook/blocks": "^7.0.2",
- "@storybook/preview-web": "^7.0.2",
- "@storybook/react": "^7.0.2",
- "@storybook/react-vite": "^7.0.2",
- "@storybook/theming": "^7.0.2",
+ "@storybook/addon-actions": "^7.6.7",
+ "@storybook/addon-essentials": "^7.6.7",
+ "@storybook/addon-links": "^7.6.7",
+ "@storybook/addons": "^7.6.7",
+ "@storybook/blocks": "^7.6.7",
+ "@storybook/preview-web": "^7.6.7",
+ "@storybook/react": "^7.6.7",
+ "@storybook/react-vite": "^7.6.7",
+ "@storybook/test-runner": "^0.16.0",
+ "@storybook/theming": "^7.6.7",
"@types/enzyme": "^3.10.8",
"@types/jest": "^26.0.23",
"@types/jest-axe": "^3.5.1",
+ "@types/jest-image-snapshot": "^6.4.0",
"@types/node": "^15.0.2",
"@types/react": "16.14.0",
"@types/react-dom": "^16.9.12",
@@ -47,6 +56,7 @@
"eslint-plugin-react-hooks": "^4.2.0",
"jest": "^26.6.3",
"jest-axe": "^4.1.0",
+ "jest-image-snapshot": "^6.4.0",
"nhsuk-frontend": "5.0.0",
"nhsuk-frontend-legacy": "npm:nhsuk-frontend@3.1.0",
"prettier": "^2.3.0",
@@ -57,7 +67,7 @@
"rollup-plugin-dts": "^5.3.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"sass": "^1.32.12",
- "storybook": "^7.0.2",
+ "storybook": "^7.6.7",
"ts-jest": "^26.5.6",
"ts-node": "^9.1.1",
"typescript": "4.2.4",
@@ -82,6 +92,7 @@
"build": "yarn cleanup && yarn build:dist && yarn build:lib",
"test": "jest",
"test:ci": "jest --coverage",
+ "test:storybook": "test-storybook",
"lint": "eslint 'src/**/*.{js,ts,tsx}' 'stories/**/*.{js,ts,tsx}' --fix",
"lint:ci": "eslint 'src/**/*.{js,ts,tsx}' 'stories/**/*.{js,ts,tsx}'",
"build-storybook": "storybook build",
diff --git a/stories/Components/Checkboxes.stories.tsx b/stories/Components/Checkboxes.stories.tsx
index 9953690f..4f584603 100644
--- a/stories/Components/Checkboxes.stories.tsx
+++ b/stories/Components/Checkboxes.stories.tsx
@@ -61,7 +61,7 @@ export const WithHintText: Story = {
render: (args) => (