diff --git a/.browserslistrc b/.browserslistrc
new file mode 100644
index 00000000..48a4efd1
--- /dev/null
+++ b/.browserslistrc
@@ -0,0 +1,5 @@
+[javascripts]
+supports es6-module
+
+[node]
+node 22
diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index c5c41edb..00000000
--- a/.eslintignore
+++ /dev/null
@@ -1,4 +0,0 @@
-jest.config.js
-.eslintrc.js
-rollup.config.mjs
-dist
\ No newline at end of file
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index 97688586..00000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,41 +0,0 @@
-module.exports = {
- parserOptions: {
- project: './tsconfig.json',
- tsconfigRootDir: __dirname,
- },
- env: {
- browser: true,
- jest: true,
- },
- settings: {
- 'import/resolver': {
- typescript: {},
- },
- react: {
- version: 'detect',
- },
- },
- extends: [
- 'plugin:react/recommended',
- 'plugin:@typescript-eslint/recommended',
- 'prettier',
- 'plugin:import/errors',
- 'plugin:import/warnings',
- 'plugin:import/typescript',
- 'plugin:jsx-a11y/recommended',
- 'plugin:react-hooks/recommended',
- ],
- rules: {
- 'react/prop-types': 0,
- 'jsx-a11y/anchor-has-content': 0,
- 'jsx-a11y/alt-text': 0,
- 'jsx-a11y/heading-has-content': 0,
- 'react-hooks/exhaustive-deps': 0,
- },
- overrides: [
- {
- files: ['*.stories.tsx'],
- rules: { '@typescript-eslint/no-unused-vars': 'off' },
- },
- ],
-};
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index aaf9601b..0079701c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,9 +22,9 @@ jobs:
- name: Yarn Install
run: yarn
- name: Lint
- run: yarn lint:ci
+ run: yarn lint
- name: Jest Tests
- run: yarn test:ci
+ run: yarn test --coverage
- name: Typescript build
run: yarn build
- name: Storybook build
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c3d4732f..da2ef318 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -28,10 +28,10 @@ jobs:
run: yarn
- name: Lint
- run: yarn lint:ci
+ run: yarn lint
- name: Jest Tests
- run: yarn test:ci
+ run: yarn test --coverage
- name: Typescript build
run: yarn build
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..3741fba3
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,12 @@
+# Node.js modules
+node_modules/
+
+# Test coverage
+coverage/
+
+# Build output
+dist/
+
+# Files to ignore
+.yarnrc.yml
+package-lock.json
diff --git a/.prettierrc b/.prettierrc
index ca8527e0..feb65279 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,7 +1,8 @@
{
+ "printWidth": 100,
+ "quoteProps": "consistent",
"semi": true,
- "trailingComma": "all",
"singleQuote": true,
- "printWidth": 100,
- "tabWidth": 2
+ "tabWidth": 2,
+ "trailingComma": "all"
}
diff --git a/.storybook/main.ts b/.storybook/main.ts
index f42bd8fe..41dbb818 100644
--- a/.storybook/main.ts
+++ b/.storybook/main.ts
@@ -4,21 +4,29 @@ import tsConfigPaths from 'vite-tsconfig-paths';
const config: StorybookConfig = {
stories: ['../stories/**/*.stories.@(ts|tsx)', '../stories/**/*.mdx'],
- addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
+ addons: ['@storybook/addon-links', '@storybook/addon-docs'],
framework: {
name: '@storybook/react-vite',
options: {},
},
- docs: {
- autodocs: true,
- },
+
typescript: {
reactDocgen: 'react-docgen-typescript',
},
+
viteFinal(config) {
return mergeConfig(config, {
plugins: [tsConfigPaths()],
+ css: {
+ preprocessorOptions: {
+ scss: {
+ quietDeps: true,
+ loadPaths: ['node_modules'],
+ },
+ },
+ },
});
},
};
+
export default config;
diff --git a/.storybook/manager.ts b/.storybook/manager.ts
index 0e2f6b66..2a4e8134 100644
--- a/.storybook/manager.ts
+++ b/.storybook/manager.ts
@@ -1,15 +1,18 @@
-import { addons } from '@storybook/manager-api';
+import { addons } from 'storybook/manager-api';
import nhsTheme from './theme';
import { startCase, upperFirst } from 'lodash';
-const sentenceCase = (string) => {
- if (typeof string !== 'string') return '';
- return upperFirst(startCase(string).toLowerCase());
+const sentenceCase = (name = '') => {
+ if (!name || typeof name !== 'string') {
+ return '';
+ }
+
+ return upperFirst(startCase(name).toLowerCase());
};
addons.setConfig({
sidebar: {
- renderLabel: ({ name, type }) => sentenceCase(name),
+ renderLabel: ({ name }) => sentenceCase(name),
},
theme: nhsTheme,
});
diff --git a/.storybook/preview.ts b/.storybook/preview.ts
index fce54dde..763bb406 100644
--- a/.storybook/preview.ts
+++ b/.storybook/preview.ts
@@ -1,5 +1,5 @@
import './storybook.scss';
-import { Preview } from '@storybook/react';
+import { type Preview } from '@storybook/react-vite';
const preview: Preview = {
parameters: {
@@ -18,5 +18,8 @@ const preview: Preview = {
},
},
},
+
+ tags: ['autodocs'],
};
+
export default preview;
diff --git a/.storybook/storybook.scss b/.storybook/storybook.scss
index b168b639..ea4b70a2 100644
--- a/.storybook/storybook.scss
+++ b/.storybook/storybook.scss
@@ -1,5 +1,4 @@
-// Allow current nhsuk styles to override legacy
-@import 'nhsuk-frontend/dist/nhsuk';
+@forward 'nhsuk-frontend/dist/nhsuk';
.tag-wrapper {
display: flex;
diff --git a/.storybook/theme.ts b/.storybook/theme.ts
index 5d3ea651..a7f0b8e9 100644
--- a/.storybook/theme.ts
+++ b/.storybook/theme.ts
@@ -1,5 +1,5 @@
-import { create } from '@storybook/theming/create';
-const version = require('../package.json').version;
+import { create } from 'storybook/theming/create';
+import packageJson from '../package.json' with { type: 'json' };
export default create({
base: 'light',
@@ -31,6 +31,6 @@ export default create({
inputTextColor: '#212b32',
inputBorderRadius: 4,
- brandTitle: `NHS.UK React Components (v${version})`,
+ brandTitle: `NHS.UK React Components (v${packageJson.version})`,
brandUrl: 'https://github.com/NHSDigital/nhsuk-react-components',
});
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 667ff68e..340494c7 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,11 +6,9 @@
"source.fixAll": "explicit",
"source.fixAll.eslint": "explicit"
},
- "eslint.validate": ["javascript", "typescript"],
+ "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
"eslint.codeAction.showDocumentation": {
"enable": true
},
- "eslint.alwaysShowStatus": true,
- "eslint.workingDirectories": ["src"],
"typescript.tsdk": "node_modules/typescript/lib"
}
diff --git a/.yarnrc.yml b/.yarnrc.yml
index 7af90284..52c22f2d 100644
--- a/.yarnrc.yml
+++ b/.yarnrc.yml
@@ -1,2 +1,17 @@
nodeLinker: node-modules
-npmRegistryServer: https://registry.yarnpkg.com
+
+npmRegistryServer: "https://registry.yarnpkg.com"
+
+packageExtensions:
+ "@storybook/addon-docs@*":
+ peerDependencies:
+ "@types/react": "*"
+ "@types/react-dom": "*"
+
+ "@storybook/react-vite@*":
+ peerDependencies:
+ typescript: "*"
+
+ "storybook@*":
+ peerDependencies:
+ "@testing-library/dom": "*"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5141e92b..9c08600f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# NHS.UK React components
+## 6.0.0-beta.1 - 8 October 2025
+
+This version adds the panel component from NHS.UK frontend v9.3.0 and supports React v19.
+
+For a full list of changes in this release please refer to the [migration doc](https://github.com/NHSDigital/nhsuk-react-components/blob/main/docs/upgrade-to-6.0.md).
+
## 6.0.0-beta.0 - 30 September 2025
This version provides support for nhsuk-frontend version 10.
diff --git a/README.md b/README.md
index f6708662..4b61a13a 100644
--- a/README.md
+++ b/README.md
@@ -21,13 +21,13 @@ yarn add nhsuk-react-components
## Usage
```jsx
-import React, { PureComponent } from 'react';
+import { PureComponent } from 'react';
// You can import components from the global module
import { Button } from 'nhsuk-react-components';
// Or you can import components directly
-import Button from 'nhsuk-react-components/lib/components/button';
+import { Button } from 'nhsuk-react-components/dist/esm/components/button';
class GetStartedButton extends PureComponent {
render() {
@@ -43,6 +43,7 @@ class GetStartedButton extends PureComponent {
- [Upgrading to 3.0](/docs/upgrade-to-3.0.md)
- [Upgrading to 4.0](/docs/upgrade-to-4.0.md)
- [Upgrading to 5.0](/docs/upgrade-to-5.0.md)
+- [Upgrading to 6.0](/docs/upgrade-to-6.0.md)
## Maintainers
diff --git a/babel.config.cjs b/babel.config.cjs
new file mode 100644
index 00000000..114f1daf
--- /dev/null
+++ b/babel.config.cjs
@@ -0,0 +1,69 @@
+const { NODE_ENV } = process.env;
+
+/**
+ * Babel config
+ *
+ * @type {TransformOptions}
+ */
+module.exports = {
+ browserslistEnv: 'javascripts',
+ presets: [
+ [
+ '@babel/preset-env',
+ {
+ // Apply bug fixes to avoid transforms
+ bugfixes: true,
+
+ // Apply smaller "loose" transforms for browsers
+ loose: true,
+
+ // Apply ES module transforms for Jest
+ // https://jestjs.io/docs/ecmascript-modules
+ modules: NODE_ENV === 'test' ? 'auto' : false,
+ },
+ ],
+ [
+ '@babel/preset-react',
+ {
+ development: NODE_ENV === 'development',
+ runtime: 'automatic',
+ useBuiltIns: true,
+ },
+ ],
+ '@babel/preset-typescript',
+ ],
+ env: {
+ test: {
+ browserslistEnv: 'node',
+ plugins: [
+ // Override package.json "imports" for Jest to use sources
+ // otherwise a build step to output `./dist` is necessary
+ [
+ 'module-resolver',
+ {
+ alias: {
+ '#components': './src/components',
+ '#patterns': './src/patterns',
+ '#util': './src/util',
+ 'nhsuk-react-components': './src/index.ts',
+ },
+ },
+ ],
+ // Remove mandatory ES module file extensions for Jest
+ // https://nodejs.org/api/esm.html#mandatory-file-extensions
+ [
+ 'replace-import-extension',
+ {
+ extMapping: {
+ '.js': '',
+ },
+ },
+ ],
+ ],
+ },
+ },
+};
+
+/**
+ * @import { TransformOptions } from '@babel/core'
+ */
diff --git a/bundle-base.tsconfig.json b/bundle-base.tsconfig.json
deleted file mode 100644
index f2c7791e..00000000
--- a/bundle-base.tsconfig.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "compilerOptions": {
- "jsx": "react",
- "target": "es6",
- "module": "esnext",
- "moduleResolution": "node",
- "baseUrl": "./",
- "types": ["jest", "node"],
- "sourceMap": true,
- "forceConsistentCasingInFileNames": true,
- "allowSyntheticDefaultImports": true,
- "esModuleInterop": true,
- "skipLibCheck": true,
- "strict": true,
- "strictNullChecks": true,
- "resolveJsonModule": true,
- "allowJs": true,
- "outDir": "./dist",
- "paths": {
- "@components/*": ["src/components/*"],
- "@content-presentation/*": ["src/components/content-presentation/*"],
- "@form-elements/*": ["src/components/form-elements/*"],
- "@navigation/*": ["src/components/navigation/*"],
- "@typography/*": ["src/components/typography/*"],
- "@util/*": ["src/util/*"],
- "@patterns/*": ["src/patterns/*"]
- }
- },
- "include": ["src"],
- "exclude": ["node_modules", "**/__tests__", "src/setupTests.ts"]
-}
diff --git a/docs/upgrade-to-6.0.md b/docs/upgrade-to-6.0.md
index b16402ff..8c09dc05 100644
--- a/docs/upgrade-to-6.0.md
+++ b/docs/upgrade-to-6.0.md
@@ -6,6 +6,31 @@ There are some breaking changes you'll need to be aware of when upgrading to v6.
You must read and apply these updates carefully to make sure your service does not break.
+## New features
+
+### New header component with account section
+
+The updated [header](https://service-manual.nhs.uk/design-system/components/header) component from NHS.UK frontend v10.x has been added, including support for account information and links. As part of this work we’ve also made some other improvements to the header:
+
+- show currently active section or page in the navigation
+- align navigation items to the left by default
+- update navigation label from ’Primary navigation’ to ‘Menu’, and remove superfluous `role` and `id` attributes
+- update NHS logo in the header to have higher contrast when focused
+- refactor CSS classes and BEM naming, use hidden attributes instead of modifier classes, use generic search element
+
+### Panel component
+
+The [panel](https://service-manual.nhs.uk/design-system/components/panel) component from NHS.UK frontend v9.3.0 has been added:
+
+```jsx
+
+ Booking complete
+ We have sent you a confirmation email
+
+```
+
+This replaces the [list panel component](#list-panel) which was removed in NHS.UK frontend v6.0.0.
+
## Breaking changes
### Update the JavaScript supported script snippet
@@ -210,7 +235,7 @@ To align with NHS.UK frontend, the date input component automatically renders it
The custom `autoSelectNext` prop is no longer supported.
-### New header component with account section
+### Header
The updated header component from NHS.UK frontend v10.x has been added. You will need to make the following changes:
@@ -464,3 +489,22 @@ To align with NHS.UK frontend, the warning callout `WarningCallout.Label` compon
```
+
+## Fixes
+
+- [#52: Expose header navigation open/close state (with setter)](https://github.com/NHSDigital/nhsuk-react-components/issues/52)
+- [#69: Unable to use ref attribute on some components](https://github.com/NHSDigital/nhsuk-react-components/issues/69)
+- [#71: Expose FormGroup component to consumers](https://github.com/NHSDigital/nhsuk-react-components/issues/71)
+- [#105: getHeadingsFromChildren forces use of string as table cell child](https://github.com/NHSDigital/nhsuk-react-components/issues/105)
+- [#166: SkipLink double jumps to first heading then #maincontent if disableDefaultBehaviour is not set](https://github.com/NHSDigital/nhsuk-react-components/issues/166)
+- [#174: Responsive tables and validation errors](https://github.com/NHSDigital/nhsuk-react-components/issues/174)
+- [#214: Hints and errors are not semantically associated with fieldsets](https://github.com/NHSDigital/nhsuk-react-components/issues/214)
+- [#215: Suggestion: remove all 'boolean' examples from storybook](https://github.com/NHSDigital/nhsuk-react-components/issues/215)
+- [#243: Use correct NHS.UK frontend JavaScript when rendered client-side](https://github.com/NHSDigital/nhsuk-react-components/issues/243)
+- [#244: Breaking change: remove default legend and label sizes or else change to l](https://github.com/NHSDigital/nhsuk-react-components/issues/244)
+- [#245: Fieldset incorrectly gets set in error when a child input is in error](https://github.com/NHSDigital/nhsuk-react-components/issues/245)
+- [#247: Date component uses label rather than fieldset with legend](https://github.com/NHSDigital/nhsuk-react-components/issues/247)
+- [#256: SkipLink does not work if intended target header is rerendered](https://github.com/NHSDigital/nhsuk-react-components/issues/256)
+- [#259: Remove pattern="[0-9]\*" from date inputs](https://github.com/NHSDigital/nhsuk-react-components/issues/259)
+- [#260: Allow custom component for button links](https://github.com/NHSDigital/nhsuk-react-components/issues/260)
+- [#265: Header logo is not labeled correctly when organisation info is provided](https://github.com/NHSDigital/nhsuk-react-components/issues/265)
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 00000000..2d8cf8cf
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,103 @@
+import { join } from 'node:path';
+import configPrettier from 'eslint-config-prettier/flat';
+import pluginReact from 'eslint-plugin-react';
+import pluginReactHooks from 'eslint-plugin-react-hooks';
+import eslint from '@eslint/js';
+import pluginJsxA11y from 'eslint-plugin-jsx-a11y';
+import { includeIgnoreFile } from '@eslint/compat';
+import { defineConfig, globalIgnores } from 'eslint/config';
+import globals from 'globals';
+import pluginImport from 'eslint-plugin-import';
+import pluginTypeScript from 'typescript-eslint';
+
+const rootPath = import.meta.dirname;
+const gitignorePath = join(rootPath, '.gitignore');
+
+export default defineConfig([
+ {
+ files: ['**/*.{js,mjs,ts,tsx}'],
+ extends: [
+ configPrettier,
+ eslint.configs.recommended,
+ pluginTypeScript.configs.recommended,
+ pluginImport.flatConfigs.recommended,
+ pluginImport.flatConfigs.typescript,
+ ],
+ languageOptions: {
+ parser: pluginTypeScript.parser,
+ parserOptions: {
+ ecmaVersion: 'latest',
+ projectService: true,
+ tsconfigRootDir: rootPath,
+ },
+ },
+ rules: {
+ // Turn off rules that are handled by TypeScript
+ // https://typescript-eslint.io/troubleshooting/typed-linting/performance/#eslint-plugin-import
+ 'import/default': 'off',
+ 'import/named': 'off',
+ 'import/namespace': 'off',
+ 'import/no-cycle': 'off',
+ 'import/no-deprecated': 'off',
+ 'import/no-named-as-default': 'off',
+ 'import/no-named-as-default-member': 'off',
+ 'import/no-unresolved': 'off',
+ 'import/no-unused-modules': 'off',
+ },
+ settings: {
+ 'import/resolver': {
+ node: true,
+ typescript: true,
+ },
+ },
+ },
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ pluginJsxA11y.flatConfigs.recommended,
+ pluginReact.configs.flat.recommended,
+ pluginReact.configs.flat['jsx-runtime'],
+ 'react-hooks/recommended-latest',
+ ],
+ languageOptions: {
+ globals: globals.browser,
+ parserOptions: {
+ ecmaFeatures: { jsx: true },
+ sourceType: 'module',
+ },
+ },
+ plugins: {
+ 'react-hooks': pluginReactHooks,
+ },
+ settings: {
+ react: {
+ version: 'detect',
+ },
+ },
+ },
+ {
+ files: ['**/*.{cjs,js,mjs}'],
+ languageOptions: {
+ globals: globals.node,
+ },
+ },
+ {
+ files: ['**/*.cjs'],
+ rules: {
+ '@typescript-eslint/no-require-imports': 'off',
+ '@typescript-eslint/no-var-requires': 'off',
+ },
+ },
+ {
+ files: ['**/*.test.{ts,tsx}'],
+ languageOptions: {
+ globals: globals.jest,
+ },
+ },
+ {
+ files: ['**/*.stories.tsx'],
+ rules: { '@typescript-eslint/no-unused-vars': 'off' },
+ },
+ globalIgnores(['**/coverage/', '**/dist/']),
+ includeIgnoreFile(gitignorePath, 'Imported .gitignore patterns'),
+]);
diff --git a/jest.config.js b/jest.config.js
index 4c0cc8c5..975ac97a 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,24 +1,18 @@
-const { pathsToModuleNameMapper } = require('ts-jest');
-const { compilerOptions } = require('./tsconfig.json');
-
-const jestConfig = {
- testEnvironment: 'jsdom',
- rootDir: './',
+/**
+ * Jest config
+ *
+ * @type {Config}
+ */
+export default {
+ collectCoverageFrom: ['/src/**/*.{js,mjs,ts,tsx}'],
+ extensionsToTreatAsEsm: ['.jsx', '.ts', '.tsx'],
setupFilesAfterEnv: ['/src/setupTests.ts'],
- collectCoverageFrom: ['/src/**/*.{ts,tsx}'],
- moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
- prefix: '',
- }),
+ testEnvironment: 'jsdom',
transform: {
- '^.+\\.(t|j)sx?$': [
- 'ts-jest',
- {
- babelConfig: {
- plugins: ['@babel/plugin-transform-modules-commonjs'],
- },
- },
- ],
+ '^.+\\.(js|mjs|ts|tsx)$': ['babel-jest', { rootMode: 'upward' }],
},
};
-module.exports = jestConfig;
+/**
+ * @import { Config } from 'jest'
+ */
diff --git a/package.json b/package.json
index b5330e40..d0cc1bd2 100644
--- a/package.json
+++ b/package.json
@@ -1,96 +1,132 @@
{
"name": "nhsuk-react-components",
- "version": "6.0.0-beta.0",
+ "version": "6.0.0-beta.1",
+ "license": "MIT",
"author": {
"name": "NHS England"
},
+ "sideEffects": false,
+ "type": "module",
+ "imports": {
+ "#components/*": {
+ "import": "./dist/esm/components/*",
+ "require": "./dist/cjs/components/*"
+ },
+ "#components": {
+ "import": "./dist/esm/components/index.js",
+ "require": "./dist/cjs/components/index.cjs"
+ },
+ "#patterns/*": {
+ "import": "./dist/esm/patterns/*",
+ "require": "./dist/cjs/patterns/*"
+ },
+ "#patterns": {
+ "import": "./dist/esm/patterns/index.js",
+ "require": "./dist/cjs/patterns/index.cjs"
+ },
+ "#util/*": {
+ "import": "./dist/esm/util/*",
+ "require": "./dist/cjs/util/*"
+ }
+ },
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/esm/index.d.ts",
+ "default": "./dist/esm/index.js"
+ },
+ "require": {
+ "types": "./dist/cjs/index.d.ts",
+ "default": "./dist/cjs/index.cjs"
+ }
+ },
+ "./*": "./*",
+ "./package.json": "./package.json"
+ },
+ "main": "dist/cjs/index.cjs",
+ "module": "dist/esm/index.js",
+ "types": "dist/esm/index.d.ts",
"files": [
- "dist",
- "lib"
+ "src",
+ "dist"
],
- "main": "dist/cjs/index.js",
- "module": "dist/esm",
- "types": "dist/index.d.ts",
"scripts": {
- "cleanup": "rm -rf dist/ > /dev/null && rm -rf lib/ > /dev/null",
- "storybook": "storybook dev -p 6006",
+ "cleanup": "rm -rf dist/ > /dev/null",
"build": "yarn cleanup && rollup -c",
+ "storybook": "NODE_ENV=development storybook dev -p 6006",
+ "build-storybook": "storybook build",
"test": "jest",
"test:watch": "jest --watch",
- "test:ci": "jest --coverage",
- "lint": "eslint 'src/**/*.{js,ts,tsx}' 'stories/**/*.{js,ts,tsx}'",
- "lint:fix": "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",
- "prepublishOnly": "yarn lint:ci && yarn test:ci && yarn storybook --smoke-test"
+ "lint": "yarn lint:types && yarn lint:js && yarn lint:prettier",
+ "lint:fix": "yarn lint:js:fix && yarn lint:prettier:fix",
+ "lint:prettier": "prettier --check .",
+ "lint:prettier:fix": "prettier --write .",
+ "lint:js": "eslint . --max-warnings 0",
+ "lint:js:fix": "yarn lint:js --fix",
+ "lint:types": "tsc --build tsconfig.json --pretty",
+ "prepublishOnly": "yarn lint && yarn test && yarn storybook --smoke-test"
},
- "license": "MIT",
"devDependencies": {
- "@babel/core": "^7.24.1",
- "@babel/plugin-transform-modules-commonjs": "^7.23.3",
- "@rollup/plugin-commonjs": "^25.0.7",
- "@rollup/plugin-node-resolve": "^15.2.3",
- "@rollup/plugin-terser": "^0.4.4",
- "@rollup/plugin-typescript": "^11.1.6",
- "@storybook/addon-actions": "^8.0.5",
- "@storybook/addon-essentials": "^8.0.5",
- "@storybook/addon-links": "^8.0.5",
- "@storybook/blocks": "^8.0.5",
- "@storybook/manager-api": "^8.0.5",
- "@storybook/preview-api": "^8.0.5",
- "@storybook/react": "^8.0.5",
- "@storybook/react-vite": "^8.0.5",
- "@storybook/theming": "^8.0.5",
- "@testing-library/jest-dom": "^6.4.2",
- "@testing-library/react": "^14.2.1",
- "@types/babel__core": "^7",
- "@types/jest": "^29.5.12",
+ "@babel/core": "^7.28.4",
+ "@babel/preset-env": "^7.28.3",
+ "@babel/preset-react": "^7.27.1",
+ "@babel/preset-typescript": "^7.27.1",
+ "@eslint/compat": "^1.4.0",
+ "@eslint/js": "^9.37.0",
+ "@rollup/plugin-babel": "^6.0.4",
+ "@rollup/plugin-commonjs": "^28.0.6",
+ "@rollup/plugin-node-resolve": "^16.0.1",
+ "@rollup/plugin-typescript": "^12.1.4",
+ "@storybook/addon-docs": "9.1.10",
+ "@storybook/addon-links": "^9.1.10",
+ "@storybook/react-vite": "^9.1.10",
+ "@testing-library/dom": "^10.4.1",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.0",
+ "@types/eslint": "^9.6.1",
+ "@types/jest": "^30.0.0",
"@types/jest-axe": "^3.5.9",
- "@types/node": "^15.0.2",
- "@types/react": "^18.2.60",
- "@types/react-dom": "^18.2.19",
- "@types/rollup-plugin-peer-deps-external": "^2.2.1",
- "@typescript-eslint/eslint-plugin": "^7.1.0",
- "@typescript-eslint/parser": "^7.1.0",
- "babel-jest": "^29.7.0",
- "chromatic": "^6.17.3",
- "eslint": "^8.57.0",
- "eslint-config-airbnb": "^19.0.4",
- "eslint-config-prettier": "^9.1.0",
- "eslint-import-resolver-typescript": "^3.6.1",
- "eslint-plugin-import": "^2.29.1",
- "eslint-plugin-jsx-a11y": "^6.8.0",
- "eslint-plugin-prettier": "^5.1.3",
- "eslint-plugin-react": "^7.33.2",
- "eslint-plugin-react-hooks": "^4.6.0",
- "jest": "^29.7.0",
- "jest-axe": "^8.0.0",
- "jest-environment-jsdom": "^29.7.0",
+ "@types/lodash": "^4.17.20",
+ "@types/node": "^24.6.2",
+ "@types/react": "^19.2.2",
+ "@types/react-dom": "^19.2.1",
+ "babel-jest": "^30.2.0",
+ "babel-plugin-module-resolver": "^5.0.2",
+ "babel-plugin-replace-import-extension": "^1.1.5",
+ "classnames": "^2.5.1",
+ "eslint": "^9.37.0",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-import-resolver-typescript": "^4.4.4",
+ "eslint-plugin-import": "^2.32.0",
+ "eslint-plugin-jsx-a11y": "^6.10.2",
+ "eslint-plugin-react": "^7.37.5",
+ "eslint-plugin-react-hooks": "^6.1.0",
+ "globals": "^16.4.0",
+ "jest": "^30.2.0",
+ "jest-axe": "^10.0.0",
+ "jest-environment-jsdom": "^30.2.0",
+ "lodash": "^4.17.21",
"nhsuk-frontend": "^10.0.0",
"outdent": "^0.8.0",
- "prettier": "^3.2.5",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "regenerator-runtime": "^0.13.7",
- "rollup": "^4.13.0",
- "rollup-plugin-dts": "^6.1.0",
- "rollup-plugin-peer-deps-external": "^2.2.4",
+ "prettier": "^3.6.2",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "rollup": "^4.52.4",
"rollup-plugin-preserve-directives": "^0.4.0",
- "rollup-plugin-tsconfig-paths": "^1.5.2",
- "sass": "^1.53.0",
- "storybook": "^8.0.5",
- "ts-jest": "^29.1.2",
- "typescript": "5.3.3",
- "vite": "^4.5.3",
- "vite-tsconfig-paths": "^4.3.2"
- },
- "dependencies": {
- "classnames": "^2.2.6"
+ "sass-embedded": "^1.93.2",
+ "storybook": "^9.1.10",
+ "tslib": "^2.8.1",
+ "typescript": "^5.9.3",
+ "typescript-eslint": "^8.45.0",
+ "vite": "^7.1.9",
+ "vite-tsconfig-paths": "^5.1.4"
},
"peerDependencies": {
+ "classnames": ">=2.5.0",
"nhsuk-frontend": ">=10.0.0 <11.0.0",
- "react": ">=16.8.0",
- "react-dom": ">=16.8.0"
+ "react": ">=18.2.0",
+ "react-dom": ">=18.2.0",
+ "tslib": ">=2.8.0"
},
- "packageManager": "yarn@4.1.1"
+ "packageManager": "yarn@4.10.3"
}
diff --git a/rollup.config.js b/rollup.config.js
new file mode 100644
index 00000000..fadfee86
--- /dev/null
+++ b/rollup.config.js
@@ -0,0 +1,81 @@
+import { join } from 'node:path';
+import { babel } from '@rollup/plugin-babel';
+import commonjs from '@rollup/plugin-commonjs';
+import { nodeResolve } from '@rollup/plugin-node-resolve';
+import typescript from '@rollup/plugin-typescript';
+import preserveDirectives from 'rollup-plugin-preserve-directives';
+import { defineConfig } from 'rollup';
+import packageJson from './package.json' with { type: 'json' };
+import tsBuildConfig from './tsconfig.build.json' with { type: 'json' };
+
+const { outDir } = tsBuildConfig.compilerOptions;
+const external = Object.keys(packageJson.peerDependencies);
+
+export default defineConfig(
+ /** @satisfies {OutputOptions[]} */ ([
+ {
+ entryFileNames: '[name].cjs',
+ format: 'cjs',
+ },
+ {
+ entryFileNames: '[name].js',
+ format: 'esm',
+ },
+ ]).map(
+ /**
+ * Rollup options for each module format
+ *
+ * @returns {RollupOptions}
+ */
+ (options) => ({
+ input: 'src/index.ts',
+ output: [
+ {
+ ...options,
+ dir: join(outDir, options.format),
+ preserveModules: true,
+ preserveModulesRoot: 'src',
+ sourcemap: true,
+ sourcemapExcludeSources: true,
+ },
+ ],
+ external: ['react/jsx-runtime', ...external],
+ jsx: /** @type {const} */ ('react-jsx'),
+ treeshake: false,
+ plugins: [
+ nodeResolve({
+ browser: true,
+ }),
+ commonjs(),
+ typescript({
+ tsconfig: 'tsconfig.build.json',
+ compilerOptions: {
+ emitDeclarationOnly: true,
+ outDir: join(outDir, options.format),
+ },
+ }),
+ preserveDirectives(),
+ babel({
+ babelHelpers: 'bundled',
+ exclude: 'node_modules/**',
+ }),
+ ],
+
+ // Handle warnings as errors
+ onwarn(warning) {
+ const { code, message } = warning;
+
+ // Skip warnings about "use client" directives
+ if (code === 'MODULE_LEVEL_DIRECTIVE' && message.includes(`"use client"`)) {
+ return;
+ }
+
+ throw new Error(warning.message, { cause: warning });
+ },
+ }),
+ ),
+);
+
+/**
+ * @import { OutputOptions, RollupOptions } from 'rollup'
+ */
diff --git a/rollup.config.mjs b/rollup.config.mjs
deleted file mode 100644
index 7dc3c972..00000000
--- a/rollup.config.mjs
+++ /dev/null
@@ -1,89 +0,0 @@
-import resolve from '@rollup/plugin-node-resolve';
-import commonjs from '@rollup/plugin-commonjs';
-import typescript from '@rollup/plugin-typescript';
-import terser from '@rollup/plugin-terser';
-import external from 'rollup-plugin-peer-deps-external';
-import { dts } from 'rollup-plugin-dts';
-import tsPaths from 'rollup-plugin-tsconfig-paths';
-import preserveDirectives from 'rollup-plugin-preserve-directives';
-
-import tsBuildConfig from './bundle-base.tsconfig.json' with { type: 'json' };
-import packageJson from './package.json' with { type: 'json' };
-
-// suppresses warnings printed to console as part of bundling components with directives present.
-const onWarnSuppression = {
- onwarn(warning, warn) {
- if (warning.code === 'MODULE_LEVEL_DIRECTIVE' && warning.message.includes(`"use client"`)) {
- return;
- }
- warn(warning);
- },
-};
-
-const commonPlugins = [external(), tsPaths(), resolve(), commonjs()];
-
-export default [
- // cjs export
- {
- input: 'src/index.ts',
- output: [
- {
- file: packageJson.main,
- format: 'cjs',
- sourcemap: true,
- },
- ],
- plugins: [
- ...commonPlugins,
- typescript({
- tsconfig: 'bundle-base.tsconfig.json',
- compilerOptions: {
- declaration: false,
- },
- }),
- terser(),
- ],
- ...onWarnSuppression,
- },
- // esm export
- {
- input: 'src/index.ts',
- output: [
- {
- dir: packageJson.module,
- format: 'esm',
- sourcemap: true,
- preserveModules: true,
- preserveModulesRoot: 'src',
- },
- ],
- plugins: [
- ...commonPlugins,
- typescript({
- tsconfig: 'bundle-base.tsconfig.json',
- compilerOptions: {
- declaration: true,
- declarationDir: 'dist/esm',
- emitDeclarationOnly: true,
- outDir: 'dist/esm',
- },
- }),
- preserveDirectives(),
- terser({ compress: { directives: false } }),
- ],
- ...onWarnSuppression,
- },
- // type bundling
- {
- input: 'src/index.ts',
- output: [{ file: 'dist/index.d.ts', format: 'esm' }],
- external: [],
- plugins: [
- dts({
- compilerOptions: {
- paths: tsBuildConfig.compilerOptions.paths,
- },
- }),
- ],
- },
-];
diff --git a/src/__mocks__/styleMock.ts b/src/__mocks__/styleMock.ts
deleted file mode 100644
index f053ebf7..00000000
--- a/src/__mocks__/styleMock.ts
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = {};
diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts
index 2f325f87..f2061729 100644
--- a/src/__tests__/index.test.ts
+++ b/src/__tests__/index.test.ts
@@ -1,10 +1,11 @@
-import * as index from '../index';
+import * as index from '..';
describe('Index', () => {
it('contains all expected elements', () => {
const sortedIndex = Object.keys(index).sort((a, b) => a.localeCompare(b));
expect(sortedIndex).toEqual([
+ '__esModule', // Synthetic default export
'ActionLink',
'ArrowLeftIcon',
'ArrowRightCircleIcon',
@@ -14,15 +15,20 @@ describe('Index', () => {
'Breadcrumb',
'Button',
'Card',
+ 'CardContext',
+ 'cardTypeIsCareCard',
'CharacterCount',
'Checkboxes',
+ 'CheckboxesContext',
'ChevronRightCircleIcon',
+ 'childIsOfComponentType',
'Clearfix',
'Col',
'Container',
'ContentsList',
'CrossIcon',
'DateInput',
+ 'DateInputContext',
'Details',
'DoAndDontList',
'ErrorMessage',
@@ -30,10 +36,15 @@ describe('Index', () => {
'Fieldset',
'Footer',
'Form',
+ 'FormContext',
'FormGroup',
+ 'FormGroupContext',
'Header',
+ 'HeaderContext',
+ 'HeadingLevel',
'Hero',
'HintText',
+ 'Icon',
'Images',
'InsetText',
'Label',
@@ -41,7 +52,9 @@ describe('Index', () => {
'Legend',
'NavAZ',
'Pagination',
+ 'Panel',
'Radios',
+ 'RadiosContext',
'ReadingWidth',
'ReviewDate',
'Row',
@@ -50,6 +63,9 @@ describe('Index', () => {
'SkipLink',
'SummaryList',
'Table',
+ 'TableContext',
+ 'TableSection',
+ 'TableSectionContext',
'Tabs',
'Tag',
'Textarea',
diff --git a/src/components/content-presentation/details/Details.tsx b/src/components/content-presentation/details/Details.tsx
index b5dcfc1b..12c169d6 100644
--- a/src/components/content-presentation/details/Details.tsx
+++ b/src/components/content-presentation/details/Details.tsx
@@ -1,5 +1,5 @@
-import React, { ComponentPropsWithoutRef, FC, forwardRef } from 'react';
import classNames from 'classnames';
+import { forwardRef, type ComponentPropsWithoutRef, type FC } from 'react';
export interface DetailsProps extends ComponentPropsWithoutRef<'details'> {
expander?: boolean;
@@ -40,7 +40,7 @@ DetailsSummary.displayName = 'Details.Summary';
DetailsText.displayName = 'Details.Text';
ExpanderGroup.displayName = 'Details.ExpanderGroup';
-export default Object.assign(DetailsComponent, {
+export const Details = Object.assign(DetailsComponent, {
Summary: DetailsSummary,
Text: DetailsText,
ExpanderGroup,
diff --git a/src/components/content-presentation/details/__tests__/Details.test.tsx b/src/components/content-presentation/details/__tests__/Details.test.tsx
index b9e237a0..5e558eea 100644
--- a/src/components/content-presentation/details/__tests__/Details.test.tsx
+++ b/src/components/content-presentation/details/__tests__/Details.test.tsx
@@ -1,7 +1,7 @@
-import React, { createRef } from 'react';
import { render } from '@testing-library/react';
-import { renderClient, renderServer } from '@util/components';
-import Details from '../';
+import { createRef } from 'react';
+import { Details } from '..';
+import { renderClient, renderServer } from '#util/components';
describe('Details', () => {
it('matches snapshot', async () => {
diff --git a/src/components/content-presentation/details/__tests__/__snapshots__/Details.test.tsx.snap b/src/components/content-presentation/details/__tests__/__snapshots__/Details.test.tsx.snap
index 51e90f28..2850e566 100644
--- a/src/components/content-presentation/details/__tests__/__snapshots__/Details.test.tsx.snap
+++ b/src/components/content-presentation/details/__tests__/__snapshots__/Details.test.tsx.snap
@@ -1,4 +1,4 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`Details Details.ExpanderGroup matches snapshot: Details.ExpanderGroup 1`] = `
diff --git a/src/components/content-presentation/details/index.ts b/src/components/content-presentation/details/index.ts
index 72e3cee2..c55898bc 100644
--- a/src/components/content-presentation/details/index.ts
+++ b/src/components/content-presentation/details/index.ts
@@ -1 +1 @@
-export { default } from './Details';
+export * from './Details.js';
diff --git a/src/components/content-presentation/do-and-dont-list/DoAndDontList.tsx b/src/components/content-presentation/do-and-dont-list/DoAndDontList.tsx
index 9573da7f..7c2a12db 100644
--- a/src/components/content-presentation/do-and-dont-list/DoAndDontList.tsx
+++ b/src/components/content-presentation/do-and-dont-list/DoAndDontList.tsx
@@ -1,14 +1,16 @@
-import React, {
- ComponentPropsWithoutRef,
- FC,
+'use client';
+
+import classNames from 'classnames';
+import {
createContext,
- useContext,
- ReactNode,
forwardRef,
+ useContext,
+ type ComponentPropsWithoutRef,
+ type FC,
+ type ReactNode,
} from 'react';
-import classNames from 'classnames';
-import { Tick, Cross } from '@components/content-presentation/icons';
-import HeadingLevel, { HeadingLevelProps } from '@components/utils/HeadingLevel';
+import { CrossIcon, TickIcon } from '../icons/index.js';
+import { HeadingLevel, type HeadingLevelProps } from '#components/utils/HeadingLevel.js';
type ListType = 'do' | 'dont';
@@ -53,12 +55,12 @@ const DoAndDontItem: FC
= ({ prefixText, listItemType, child
{(listItemType || listItem) === 'do' ? (
<>
-
+
{actualPrefix}
>
) : (
<>
-
+
{actualPrefix}
>
)}
@@ -70,6 +72,6 @@ const DoAndDontItem: FC = ({ prefixText, listItemType, child
DoAndDontListComponent.displayName = 'DoAndDontList';
DoAndDontItem.displayName = 'DoAndDontList.Item';
-export default Object.assign(DoAndDontListComponent, {
+export const DoAndDontList = Object.assign(DoAndDontListComponent, {
Item: DoAndDontItem,
});
diff --git a/src/components/content-presentation/do-and-dont-list/__tests__/DoAndDontList.test.tsx b/src/components/content-presentation/do-and-dont-list/__tests__/DoAndDontList.test.tsx
index 129c2020..f7519b4f 100644
--- a/src/components/content-presentation/do-and-dont-list/__tests__/DoAndDontList.test.tsx
+++ b/src/components/content-presentation/do-and-dont-list/__tests__/DoAndDontList.test.tsx
@@ -1,7 +1,7 @@
-import React, { createRef } from 'react';
import { render } from '@testing-library/react';
-import DoAndDontList from '../';
-import { renderClient, renderServer } from '@util/components';
+import { createRef } from 'react';
+import { DoAndDontList } from '..';
+import { renderClient, renderServer } from '#util/components';
describe('DoAndDontList', () => {
describe('list type "do"', () => {
diff --git a/src/components/content-presentation/do-and-dont-list/__tests__/__snapshots__/DoAndDontList.test.tsx.snap b/src/components/content-presentation/do-and-dont-list/__tests__/__snapshots__/DoAndDontList.test.tsx.snap
index f45722b7..549880e7 100644
--- a/src/components/content-presentation/do-and-dont-list/__tests__/__snapshots__/DoAndDontList.test.tsx.snap
+++ b/src/components/content-presentation/do-and-dont-list/__tests__/__snapshots__/DoAndDontList.test.tsx.snap
@@ -1,4 +1,4 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`DoAndDontList DoDontList.Item matches snapshot: DoDontList.Item 1`] = `
diff --git a/src/components/content-presentation/do-and-dont-list/index.ts b/src/components/content-presentation/do-and-dont-list/index.ts
index f6db6b15..bd8c9192 100644
--- a/src/components/content-presentation/do-and-dont-list/index.ts
+++ b/src/components/content-presentation/do-and-dont-list/index.ts
@@ -1 +1 @@
-export { default } from './DoAndDontList';
+export * from './DoAndDontList.js';
diff --git a/src/components/content-presentation/hero/Hero.tsx b/src/components/content-presentation/hero/Hero.tsx
index ccecc19c..1020ac53 100644
--- a/src/components/content-presentation/hero/Hero.tsx
+++ b/src/components/content-presentation/hero/Hero.tsx
@@ -1,7 +1,7 @@
-import React, { ComponentPropsWithoutRef, FC, forwardRef } from 'react';
import classNames from 'classnames';
-import { Container, Row, Col } from '../../layout';
-import HeadingLevel, { HeadingLevelProps } from '@components/utils/HeadingLevel';
+import { forwardRef, type ComponentPropsWithoutRef, type FC } from 'react';
+import { Col, Container, Row } from '#components/layout/index.js';
+import { HeadingLevel, type HeadingLevelProps } from '#components/utils/HeadingLevel.js';
export interface HeroContentProps extends ComponentPropsWithoutRef<'div'> {
hasImage: boolean;
@@ -73,7 +73,7 @@ HeroComponent.displayName = 'Hero';
HeroHeading.displayName = 'Hero.Heading';
HeroText.displayName = 'Hero.Text';
-export default Object.assign(HeroComponent, {
+export const Hero = Object.assign(HeroComponent, {
Heading: HeroHeading,
Text: HeroText,
});
diff --git a/src/components/content-presentation/hero/__tests__/Hero.test.tsx b/src/components/content-presentation/hero/__tests__/Hero.test.tsx
index 6678abd3..39c661c7 100644
--- a/src/components/content-presentation/hero/__tests__/Hero.test.tsx
+++ b/src/components/content-presentation/hero/__tests__/Hero.test.tsx
@@ -1,6 +1,6 @@
-import React, { createRef } from 'react';
import { render } from '@testing-library/react';
-import Hero from '..';
+import { createRef } from 'react';
+import { Hero } from '..';
describe('Hero', () => {
it('matches snapshot', () => {
diff --git a/src/components/content-presentation/hero/__tests__/__snapshots__/Hero.test.tsx.snap b/src/components/content-presentation/hero/__tests__/__snapshots__/Hero.test.tsx.snap
index e371cb6c..245ec3d2 100644
--- a/src/components/content-presentation/hero/__tests__/__snapshots__/Hero.test.tsx.snap
+++ b/src/components/content-presentation/hero/__tests__/__snapshots__/Hero.test.tsx.snap
@@ -1,4 +1,4 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`Hero Hero.Heading matches snapshot: Hero.Heading 1`] = `
diff --git a/src/components/content-presentation/hero/index.ts b/src/components/content-presentation/hero/index.ts
index 9c292bc0..60b8a72d 100644
--- a/src/components/content-presentation/hero/index.ts
+++ b/src/components/content-presentation/hero/index.ts
@@ -1 +1 @@
-export { default } from './Hero';
+export * from './Hero.js';
diff --git a/src/components/content-presentation/icons/BaseIcon.tsx b/src/components/content-presentation/icons/Icon.tsx
similarity index 78%
rename from src/components/content-presentation/icons/BaseIcon.tsx
rename to src/components/content-presentation/icons/Icon.tsx
index 04e7c1bb..ccbbb90d 100644
--- a/src/components/content-presentation/icons/BaseIcon.tsx
+++ b/src/components/content-presentation/icons/Icon.tsx
@@ -1,7 +1,7 @@
-import React, { ComponentPropsWithoutRef, FC } from 'react';
import classNames from 'classnames';
+import { type ComponentPropsWithoutRef, type FC } from 'react';
-export interface BaseIconSVGProps extends ComponentPropsWithoutRef<'svg'> {
+export interface IconProps extends ComponentPropsWithoutRef<'svg'> {
title?: string;
modifier?:
| 'arrow-left'
@@ -16,10 +16,10 @@ export interface BaseIconSVGProps extends ComponentPropsWithoutRef<'svg'> {
/**
* @deprecated Use `modifier` instead.
*/
- iconType?: BaseIconSVGProps['modifier'];
+ iconType?: IconProps['modifier'];
}
-export const BaseIconSVG: FC
= ({
+export const Icon: FC = ({
className,
children,
iconType,
diff --git a/src/components/content-presentation/icons/__tests__/Icons.test.tsx b/src/components/content-presentation/icons/__tests__/Icons.test.tsx
index 9cdff5e4..41ad773e 100644
--- a/src/components/content-presentation/icons/__tests__/Icons.test.tsx
+++ b/src/components/content-presentation/icons/__tests__/Icons.test.tsx
@@ -1,10 +1,11 @@
-import React from 'react';
import { render } from '@testing-library/react';
-import * as Icons from '../';
+import * as Icons from '..';
describe('Icons', () => {
it('all icons match snapshots', () => {
- for (const [name, Icon] of Object.entries(Icons)) {
+ for (const [name, Icon] of Object.entries(Icons).filter(
+ ([, Icon]) => Icon instanceof Function,
+ )) {
const { container } = render();
expect(container).toMatchSnapshot(name);
}
diff --git a/src/components/content-presentation/icons/__tests__/__snapshots__/Icons.test.tsx.snap b/src/components/content-presentation/icons/__tests__/__snapshots__/Icons.test.tsx.snap
index f3214c39..49438204 100644
--- a/src/components/content-presentation/icons/__tests__/__snapshots__/Icons.test.tsx.snap
+++ b/src/components/content-presentation/icons/__tests__/__snapshots__/Icons.test.tsx.snap
@@ -1,6 +1,6 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
-exports[`Icons all icons match snapshots: ArrowLeft 1`] = `
+exports[`Icons all icons match snapshots: ArrowLeftIcon 1`] = `