From 79d8ad4918c84a3b0e0bb7a48909e70d7a77d9a8 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Mon, 15 Jun 2026 13:06:08 +0100 Subject: [PATCH 1/9] Update to NHS.UK frontend v10.5.2 --- package.json | 2 +- yarn.lock | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index cc6052b8..4f4e7faf 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "jest": "^30.4.2", "jest-axe": "^10.0.0", "jest-environment-jsdom": "^30.4.1", - "nhsuk-frontend": "^10.3.1", + "nhsuk-frontend": "^10.5.2", "outdent": "^0.8.0", "prettier": "^3.8.3", "react": "^19.2.7", diff --git a/yarn.lock b/yarn.lock index 7c7885b4..5dba0e9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8435,10 +8435,27 @@ __metadata: languageName: node linkType: hard -"nhsuk-frontend@npm:^10.3.1": - version: 10.3.1 - resolution: "nhsuk-frontend@npm:10.3.1" - checksum: 10c0/66941f6dd2ce4c275c66b54880e6b7e9ae96b3b3479a1797c5bba4a7d58f7b8c029189e51f6628c017b3d173c81996c910545a4373c04766f139a994f13ac82d +"nhsuk-frontend@npm:^10.5.2": + version: 10.5.2 + resolution: "nhsuk-frontend@npm:10.5.2" + peerDependencies: + "@prettier/sync": ^0.6.0 + highlight.js: ^11.0.0 + nunjucks: ^3.0.0 + outdent: ^0.8.0 + slug: ^9.0.0 || ^11.0.0 + peerDependenciesMeta: + "@prettier/sync": + optional: true + highlight.js: + optional: true + nunjucks: + optional: true + outdent: + optional: true + slug: + optional: true + checksum: 10c0/d21c0b9aa1371af8fba5f538448bddafde0c4de2a4f56c0fe668ab7accef10790f97d495b6ed94255dc10dce8f11d52381b8536bcd8e8765597a636f3c7890f5 languageName: node linkType: hard @@ -8486,7 +8503,7 @@ __metadata: jest: "npm:^30.4.2" jest-axe: "npm:^10.0.0" jest-environment-jsdom: "npm:^30.4.1" - nhsuk-frontend: "npm:^10.3.1" + nhsuk-frontend: "npm:^10.5.2" outdent: "npm:^0.8.0" prettier: "npm:^3.8.3" react: "npm:^19.2.7" From b56bd0db777cf1a4d610e03ce246a056ff2de26c Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Mon, 15 Jun 2026 13:13:37 +0100 Subject: [PATCH 2/9] Prefer browser `` element --- stories/Navigation/SkipLink.stories.tsx | 30 ++++++------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/stories/Navigation/SkipLink.stories.tsx b/stories/Navigation/SkipLink.stories.tsx index bd4a5c4d..bdabb602 100644 --- a/stories/Navigation/SkipLink.stories.tsx +++ b/stories/Navigation/SkipLink.stories.tsx @@ -1,27 +1,9 @@ import { Markdown } from '@storybook/addon-docs/blocks'; import { type Meta, type StoryObj } from '@storybook/react-vite'; -import { type FC, type ReactNode } from 'react'; -import { HintText } from '#components/form-elements/hint-text/index.js'; import { Col, Container, Row } from '#components/layout/index.js'; import { SkipLink } from '#components/navigation/skip-link/index.js'; -const CodeText: FC<{ children: ReactNode }> = ({ children }) => ( - - {children} - -); - const meta: Meta = { title: 'Navigation/Skip link', component: SkipLink, @@ -41,11 +23,13 @@ const meta: Meta = { }, render: (args) => ( <> - - Press - tab - to show the SkipLink - +

+ Press{' '} + + Tab + {' '} + to show the skip link +

From e8e4df10e0e8e2a8a73f2cd95f9c25628fd8126e Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Mon, 15 Jun 2026 14:32:00 +0100 Subject: [PATCH 3/9] Configure ESLint to warn on deprecations --- eslint.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/eslint.config.js b/eslint.config.js index 346db8a4..b4074fe4 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -36,6 +36,7 @@ export default defineConfig([ 'no-redeclare': 'off', 'no-undef': 'off', 'no-unused-vars': 'off', + '@typescript-eslint/no-deprecated': 'warn', '@typescript-eslint/no-redeclare': 'error', '@typescript-eslint/no-unused-vars': [ 'error', From c0ad523d5ece02d67a77f71be4d40e5b5cfa4465 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Mon, 15 Jun 2026 13:16:39 +0100 Subject: [PATCH 4/9] Deprecate tag component prop `modifier` for `colour` --- .storybook/preview.tsx | 2 -- .storybook/storybook.d.ts | 1 - .storybook/storybook.scss | 12 ----------- docs/upgrade-to-6.0.md | 9 ++++++++ .../content-presentation/tag/Tag.tsx | 21 ++++++++++++++----- stories/Content Presentation/Tag.stories.tsx | 20 +++++++++--------- stories/Navigation/Card.stories.tsx | 2 +- 7 files changed, 36 insertions(+), 31 deletions(-) delete mode 100644 .storybook/storybook.d.ts delete mode 100644 .storybook/storybook.scss diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index d553f10c..572274c1 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -3,8 +3,6 @@ import { type Preview } from '@storybook/react-vite'; import { Col, Container, Row } from '#components/layout/index.js'; -import './storybook.scss'; - const preview: Preview = { decorators: (Story, { parameters }) => parameters.width === false ? ( diff --git a/.storybook/storybook.d.ts b/.storybook/storybook.d.ts deleted file mode 100644 index d5cf927a..00000000 --- a/.storybook/storybook.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module '*.scss'; diff --git a/.storybook/storybook.scss b/.storybook/storybook.scss deleted file mode 100644 index 1cf1ac12..00000000 --- a/.storybook/storybook.scss +++ /dev/null @@ -1,12 +0,0 @@ -.tag-wrapper { - display: flex; - flex-direction: column; - - & > .nhsuk-tag { - margin-bottom: 10px; - - &:last-of-type { - margin-bottom: 0; - } - } -} diff --git a/docs/upgrade-to-6.0.md b/docs/upgrade-to-6.0.md index 917edfb2..b4198559 100644 --- a/docs/upgrade-to-6.0.md +++ b/docs/upgrade-to-6.0.md @@ -719,6 +719,15 @@ If you are using the `Table.Panel` child component, you must migrate to the feat + Other conditions like impetigo ``` +### Tags + +To align with NHS.UK frontend, you must rename the `Tag` prop `color` to `colour`: + +```patch +- In progress ++ In progress +``` + ### Textarea You must rename the `Textarea` prop `textareaRef` to `ref` for consistency with other components: diff --git a/src/components/content-presentation/tag/Tag.tsx b/src/components/content-presentation/tag/Tag.tsx index 60d83a6c..f7da2e66 100644 --- a/src/components/content-presentation/tag/Tag.tsx +++ b/src/components/content-presentation/tag/Tag.tsx @@ -2,7 +2,7 @@ import classNames from 'classnames'; import { type ComponentPropsWithoutRef, type FC } from 'react'; export interface TagProps extends ComponentPropsWithoutRef<'strong'> { - modifier?: + colour?: | 'white' | 'grey' | 'green' @@ -15,14 +15,25 @@ export interface TagProps extends ComponentPropsWithoutRef<'strong'> { | 'yellow'; /** - * @deprecated Use `modifier` instead. + * @deprecated Use `colour` instead. */ - color?: TagProps['modifier']; + color?: TagProps['colour']; + + /** + * @deprecated Use `colour` instead. + */ + modifier?: TagProps['colour']; } -export const Tag: FC = ({ className, color, modifier = color, ...rest }) => ( +export const Tag: FC = ({ + className, + modifier, + color, + colour = color ?? modifier, + ...rest +}) => ( ); diff --git a/stories/Content Presentation/Tag.stories.tsx b/stories/Content Presentation/Tag.stories.tsx index 3e343f54..d1fa8342 100644 --- a/stories/Content Presentation/Tag.stories.tsx +++ b/stories/Content Presentation/Tag.stories.tsx @@ -34,34 +34,34 @@ export const Colours: Story = { render: () => ( <>

- In progress + In progress

- Inactive + Inactive

- New + New

- Active + Active

- Pending + Pending

- Received + Received

- Sent + Sent

- Rejected + Rejected

- Declined + Declined

- Delayed + Delayed

), diff --git a/stories/Navigation/Card.stories.tsx b/stories/Navigation/Card.stories.tsx index 59bed22c..dbeea71a 100644 --- a/stories/Navigation/Card.stories.tsx +++ b/stories/Navigation/Card.stories.tsx @@ -438,7 +438,7 @@ export const FeatureCardWithNestedCardAndSummaryList: Story = { Response - Follow up requested + Follow up requested From 023a158eecb91f92e5401f294f75f4eec6501325 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Mon, 15 Jun 2026 13:21:14 +0100 Subject: [PATCH 5/9] Deprecate icon component prop `modifier` for `name` --- docs/upgrade-to-6.0.md | 7 ++++--- .../content-presentation/icons/Icon.tsx | 19 +++++++++++++------ .../icons/individual/ArrowLeftIcon.tsx | 2 +- .../icons/individual/ArrowRightCircleIcon.tsx | 2 +- .../icons/individual/ArrowRightIcon.tsx | 2 +- .../individual/ChevronRightCircleIcon.tsx | 2 +- .../icons/individual/CrossIcon.tsx | 2 +- .../icons/individual/SearchIcon.tsx | 2 +- .../icons/individual/TickIcon.tsx | 2 +- .../icons/individual/UserIcon.tsx | 2 +- 10 files changed, 25 insertions(+), 17 deletions(-) diff --git a/docs/upgrade-to-6.0.md b/docs/upgrade-to-6.0.md index b4198559..40be75fa 100644 --- a/docs/upgrade-to-6.0.md +++ b/docs/upgrade-to-6.0.md @@ -333,10 +333,11 @@ To align with NHS.UK frontend, icons unused by components have been removed: - `ChevronLeftIcon` - `ChevronRightIcon` - `ChevronDownIcon` -- `CloseIcon` - `EmdashIcon` and `SmallEmdashIcon` -- `MinusIcon` -- `PlusIcon` + +With the following icons renamed: + +- `CloseIcon` renamed to `CrossIcon` ### Back link diff --git a/src/components/content-presentation/icons/Icon.tsx b/src/components/content-presentation/icons/Icon.tsx index e5f12b75..cf1184ce 100644 --- a/src/components/content-presentation/icons/Icon.tsx +++ b/src/components/content-presentation/icons/Icon.tsx @@ -3,7 +3,7 @@ import { type ComponentPropsWithoutRef, type FC } from 'react'; export interface IconProps extends ComponentPropsWithoutRef<'svg'> { title?: string; - modifier?: + name?: | 'arrow-left' | 'arrow-right' | 'arrow-right-circle' @@ -14,18 +14,25 @@ export interface IconProps extends ComponentPropsWithoutRef<'svg'> { | 'user'; /** - * @deprecated Use `modifier` instead. + * @deprecated Use `name` instead. */ - iconType?: IconProps['modifier']; + iconType?: IconProps['name']; + + /** + * @deprecated Use `name` instead. + */ + modifier?: IconProps['name']; } export const Icon: FC = ({ className, children, + name, iconType, - modifier = iconType - ?.replace('nhsuk-icon__', '') // NHS.UK frontend v9.x - .replace('nhsuk-icon--', ''), // NHS.UK frontend v10.x + modifier = name ?? + iconType + ?.replace('nhsuk-icon__', '') // NHS.UK frontend v9.x + .replace('nhsuk-icon--', ''), // NHS.UK frontend v10.x title, ...rest }) => ( diff --git a/src/components/content-presentation/icons/individual/ArrowLeftIcon.tsx b/src/components/content-presentation/icons/individual/ArrowLeftIcon.tsx index 350316a9..b4d50a75 100644 --- a/src/components/content-presentation/icons/individual/ArrowLeftIcon.tsx +++ b/src/components/content-presentation/icons/individual/ArrowLeftIcon.tsx @@ -3,7 +3,7 @@ import { type FC } from 'react'; import { Icon, type IconProps } from '../Icon.js'; export const ArrowLeftIcon: FC = (props) => ( - + ); diff --git a/src/components/content-presentation/icons/individual/ArrowRightCircleIcon.tsx b/src/components/content-presentation/icons/individual/ArrowRightCircleIcon.tsx index 61430c66..8dad2c51 100644 --- a/src/components/content-presentation/icons/individual/ArrowRightCircleIcon.tsx +++ b/src/components/content-presentation/icons/individual/ArrowRightCircleIcon.tsx @@ -3,7 +3,7 @@ import { type FC } from 'react'; import { Icon, type IconProps } from '../Icon.js'; export const ArrowRightCircleIcon: FC = (props) => ( - + ); diff --git a/src/components/content-presentation/icons/individual/ArrowRightIcon.tsx b/src/components/content-presentation/icons/individual/ArrowRightIcon.tsx index ed40ae24..f117c615 100644 --- a/src/components/content-presentation/icons/individual/ArrowRightIcon.tsx +++ b/src/components/content-presentation/icons/individual/ArrowRightIcon.tsx @@ -3,7 +3,7 @@ import { type FC } from 'react'; import { Icon, type IconProps } from '../Icon.js'; export const ArrowRightIcon: FC = (props) => ( - + ); diff --git a/src/components/content-presentation/icons/individual/ChevronRightCircleIcon.tsx b/src/components/content-presentation/icons/individual/ChevronRightCircleIcon.tsx index caf3dec4..01e9e622 100644 --- a/src/components/content-presentation/icons/individual/ChevronRightCircleIcon.tsx +++ b/src/components/content-presentation/icons/individual/ChevronRightCircleIcon.tsx @@ -3,7 +3,7 @@ import { type FC } from 'react'; import { Icon, type IconProps } from '../Icon.js'; export const ChevronRightCircleIcon: FC = (props) => ( - + ); diff --git a/src/components/content-presentation/icons/individual/CrossIcon.tsx b/src/components/content-presentation/icons/individual/CrossIcon.tsx index c871d581..18848843 100644 --- a/src/components/content-presentation/icons/individual/CrossIcon.tsx +++ b/src/components/content-presentation/icons/individual/CrossIcon.tsx @@ -3,7 +3,7 @@ import { type FC } from 'react'; import { Icon, type IconProps } from '../Icon.js'; export const CrossIcon: FC = (props) => ( - + ); diff --git a/src/components/content-presentation/icons/individual/SearchIcon.tsx b/src/components/content-presentation/icons/individual/SearchIcon.tsx index ab5ac215..7f96d4b1 100644 --- a/src/components/content-presentation/icons/individual/SearchIcon.tsx +++ b/src/components/content-presentation/icons/individual/SearchIcon.tsx @@ -3,7 +3,7 @@ import { type FC } from 'react'; import { Icon, type IconProps } from '../Icon.js'; export const SearchIcon: FC = (props) => ( - + ); diff --git a/src/components/content-presentation/icons/individual/TickIcon.tsx b/src/components/content-presentation/icons/individual/TickIcon.tsx index 55804ff6..eaf64a03 100644 --- a/src/components/content-presentation/icons/individual/TickIcon.tsx +++ b/src/components/content-presentation/icons/individual/TickIcon.tsx @@ -3,7 +3,7 @@ import { type FC } from 'react'; import { Icon, type IconProps } from '../Icon.js'; export const TickIcon: FC = (props) => ( - + ); diff --git a/src/components/content-presentation/icons/individual/UserIcon.tsx b/src/components/content-presentation/icons/individual/UserIcon.tsx index 752fc28e..31e8ca1a 100644 --- a/src/components/content-presentation/icons/individual/UserIcon.tsx +++ b/src/components/content-presentation/icons/individual/UserIcon.tsx @@ -3,7 +3,7 @@ import { type FC } from 'react'; import { Icon, type IconProps } from '../Icon.js'; export const UserIcon: FC = (props) => ( - + ); From 7dc19f74dbea8cf2e04499898f8701663be9e3c1 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Mon, 15 Jun 2026 13:19:05 +0100 Subject: [PATCH 6/9] Update tag component to support `noBorder` or `border` props --- .../content-presentation/tag/Tag.tsx | 14 ++++- .../tag/__tests__/Tag.test.tsx | 4 +- stories/Content Presentation/Tag.stories.tsx | 61 +++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/components/content-presentation/tag/Tag.tsx b/src/components/content-presentation/tag/Tag.tsx index f7da2e66..63f4a0d4 100644 --- a/src/components/content-presentation/tag/Tag.tsx +++ b/src/components/content-presentation/tag/Tag.tsx @@ -23,6 +23,9 @@ export interface TagProps extends ComponentPropsWithoutRef<'strong'> { * @deprecated Use `colour` instead. */ modifier?: TagProps['colour']; + + border?: boolean; + noBorder?: boolean; } export const Tag: FC = ({ @@ -30,10 +33,19 @@ export const Tag: FC = ({ modifier, color, colour = color ?? modifier, + border = true, + noBorder = false, ...rest }) => ( ); diff --git a/src/components/content-presentation/tag/__tests__/Tag.test.tsx b/src/components/content-presentation/tag/__tests__/Tag.test.tsx index 26d2b1c8..bb930dd7 100644 --- a/src/components/content-presentation/tag/__tests__/Tag.test.tsx +++ b/src/components/content-presentation/tag/__tests__/Tag.test.tsx @@ -16,7 +16,7 @@ describe('Tag', () => { expect(container.querySelector('strong.nhsuk-tag')).toBeTruthy(); }); - it.each['modifier']>([ + it.each['colour']>([ 'white', 'grey', 'green', @@ -28,7 +28,7 @@ describe('Tag', () => { 'orange', 'yellow', ])('adds colour class %s ', (colour) => { - const { container } = render(); + const { container } = render(); expect(container.querySelector(`strong.nhsuk-tag.nhsuk-tag--${colour}`)).toBeTruthy(); }); diff --git a/stories/Content Presentation/Tag.stories.tsx b/stories/Content Presentation/Tag.stories.tsx index d1fa8342..d4ccedb7 100644 --- a/stories/Content Presentation/Tag.stories.tsx +++ b/stories/Content Presentation/Tag.stories.tsx @@ -66,3 +66,64 @@ export const Colours: Story = { ), }; + +export const ColoursWithoutBorder: Story = { + name: 'Tag colours without border', + globals: { + backgrounds: { value: 'dark' }, + }, + render: () => ( + <> +

+ + In progress + +

+

+ + Inactive + +

+

+ + New + +

+

+ + Active + +

+

+ + Pending + +

+

+ + Received + +

+

+ + Sent + +

+

+ + Rejected + +

+

+ + Declined + +

+

+ + Delayed + +

+ + ), +}; From 206a7823a6ad96226c44626f8edb561fc5903e90 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Mon, 15 Jun 2026 14:34:18 +0100 Subject: [PATCH 7/9] Update button component to support `variant` prop --- docs/upgrade-to-6.0.md | 4 +- .../form-elements/button/Button.tsx | 42 ++++- .../button/__tests__/Button.test.tsx | 82 +++++---- .../__snapshots__/Button.test.tsx.snap | 166 +++++++++--------- .../password-input/PasswordInput.tsx | 2 +- .../Content Presentation/Panel.stories.tsx | 6 +- stories/Form Elements/Select.stories.tsx | 4 +- stories/Form Elements/TextInput.stories.tsx | 4 +- stories/Navigation/Card.stories.tsx | 4 +- 9 files changed, 181 insertions(+), 133 deletions(-) diff --git a/docs/upgrade-to-6.0.md b/docs/upgrade-to-6.0.md index 40be75fa..849a2dbd 100644 --- a/docs/upgrade-to-6.0.md +++ b/docs/upgrade-to-6.0.md @@ -48,7 +48,7 @@ You can now use smaller versions of [buttons](https://service-manual.nhs.uk/desi By default, the secondary button is transparent and has no colour. -You can now make the [button](https://service-manual.nhs.uk/design-system/components/button) component white when you use it on darker backgrounds by adding the `secondarySolid` prop. +You can now make the [button](https://service-manual.nhs.uk/design-system/components/button) component white when you use it on darker backgrounds by setting the `variant` prop to `'secondary-solid'`. #### Add inline buttons to text inputs and select menus @@ -58,7 +58,7 @@ You can now add inline buttons to text inputs and select menus using the `formGr + ), diff --git a/src/components/form-elements/button/Button.tsx b/src/components/form-elements/button/Button.tsx index 7bcd2442..297c5ba4 100644 --- a/src/components/form-elements/button/Button.tsx +++ b/src/components/form-elements/button/Button.tsx @@ -26,24 +26,48 @@ export interface ButtonLinkProps extends ButtonBaseProps, AsElementLink((props, forwa warning, login, small, + variant, type = 'submit', preventDoubleClick, onClick, @@ -119,6 +144,7 @@ const ButtonLinkComponent = forwardRef( warning, login, small, + variant, preventDoubleClick, onClick, ...rest diff --git a/src/components/form-elements/button/__tests__/Button.test.tsx b/src/components/form-elements/button/__tests__/Button.test.tsx index eccfa7ce..6c499f47 100644 --- a/src/components/form-elements/button/__tests__/Button.test.tsx +++ b/src/components/form-elements/button/__tests__/Button.test.tsx @@ -2,38 +2,38 @@ import { type ComponentProps, type Ref, createRef, forwardRef } from 'react'; import { renderClient, renderServer } from '#util/components'; -import { Button } from '..'; +import { Button, type ButtonProps } from '..'; -const buttonTypes = [ +const buttonTypes: Pick, 'variant' | 'children' | 'className'>[] = [ { - type: 'secondary', - text: 'Find my location', + variant: 'secondary', + children: 'Find my location', className: 'nhsuk-button--secondary', }, { - type: 'secondarySolid', - text: 'Find my location', + variant: 'secondary-solid', + children: 'Find my location', className: 'nhsuk-button--secondary-solid', }, { - type: 'reverse', - text: 'Log out', + variant: 'reverse', + children: 'Log out', className: 'nhsuk-button--reverse', }, { - type: 'warning', - text: 'Yes, delete this vaccine', + variant: 'warning', + children: 'Yes, delete this vaccine', className: 'nhsuk-button--warning', }, { - type: 'login', - text: 'Continue to NHS login', + variant: 'login', + children: 'Continue to NHS login', className: 'nhsuk-button--login', }, { - type: 'small', - text: 'Search', - className: 'nhsuk-button--small', + variant: 'brand', + children: 'Continue to NHS login', + className: 'nhsuk-button--brand', }, ]; @@ -132,7 +132,7 @@ describe('Button', () => { expect(buttonEl2.dataset).toHaveProperty('customLink', 'true'); }); - describe('disabled', () => { + describe('Disabled', () => { it('matches snapshot', async () => { const { container } = await renderClient(, { moduleName: 'nhsuk-button', @@ -154,25 +154,35 @@ describe('Button', () => { }); }); - describe('button types', () => { - describe.each(buttonTypes)('$type', ({ type, className }) => { + describe('Variants', () => { + describe.each(buttonTypes)('$variant', ({ className, ...props }) => { it('matches snapshot', async () => { - const { container } = await renderClient( - , - { moduleName: 'nhsuk-button' }, - ); + const { container } = await renderClient(, { + moduleName: 'nhsuk-button', + }); expect(container).toMatchSnapshot(); }); - it(`adds correct classes for type - ${type}`, async () => { + it('adds correct classes for variant', async () => { + const { modules } = await renderClient(, { + moduleName: 'nhsuk-button', + }); + + const [buttonEl] = modules; + expect(buttonEl).toHaveClass(className); + }); + + it('adds small size class', async () => { const { modules } = await renderClient( - , + , { moduleName: 'nhsuk-button' }, ); const [buttonEl] = modules; - expect(buttonEl).toHaveClass(className); + expect(buttonEl).toHaveClass('nhsuk-button--small'); }); }); }); @@ -243,11 +253,11 @@ describe('Button as a link', () => { expect(buttonEl).toHaveTextContent('Find my location'); }); - describe('button types', () => { - describe.each(buttonTypes)('$type', ({ type, className }) => { + describe('Variants', () => { + describe.each(buttonTypes)('$variant', ({ className, ...props }) => { it('matches snapshot', async () => { const { container } = await renderClient( - , { moduleName: 'nhsuk-button' }, @@ -256,9 +266,9 @@ describe('Button as a link', () => { expect(container).toMatchSnapshot(); }); - it(`adds correct classes for type - ${type}`, async () => { + it('adds correct classes for variant', async () => { const { modules } = await renderClient( - , { moduleName: 'nhsuk-button' }, @@ -267,6 +277,18 @@ describe('Button as a link', () => { const [buttonEl] = modules; expect(buttonEl).toHaveClass(className); }); + + it('adds small size class', async () => { + const { modules } = await renderClient( + , + { moduleName: 'nhsuk-button' }, + ); + + const [buttonEl] = modules; + expect(buttonEl).toHaveClass('nhsuk-button--small'); + }); }); }); }); diff --git a/src/components/form-elements/button/__tests__/__snapshots__/Button.test.tsx.snap b/src/components/form-elements/button/__tests__/__snapshots__/Button.test.tsx.snap index a7841f2a..99054be7 100644 --- a/src/components/form-elements/button/__tests__/__snapshots__/Button.test.tsx.snap +++ b/src/components/form-elements/button/__tests__/__snapshots__/Button.test.tsx.snap @@ -1,99 +1,102 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Button as a link button types login matches snapshot 1`] = ` +exports[`Button Disabled matches snapshot: DisabledButton 1`] = ` `; -exports[`Button as a link button types reverse matches snapshot 1`] = ` +exports[`Button Variants login matches snapshot 1`] = ` `; -exports[`Button as a link button types secondary matches snapshot 1`] = ` +exports[`Button Variants reverse matches snapshot 1`] = ` `; -exports[`Button as a link button types secondarySolid matches snapshot 1`] = ` +exports[`Button Variants secondary matches snapshot 1`] = ` `; -exports[`Button as a link button types small matches snapshot 1`] = ` +exports[`Button Variants secondary-solid matches snapshot 1`] = ` `; -exports[`Button as a link button types warning matches snapshot 1`] = ` +exports[`Button Variants warning matches snapshot 1`] = ` `; -exports[`Button as a link matches snapshot: LinkButton 1`] = ` +exports[`Button as a link Variants brand matches snapshot 1`] = `
`; -exports[`Button button types login matches snapshot 1`] = ` +exports[`Button as a link Variants login matches snapshot 1`] = ` `; -exports[`Button button types reverse matches snapshot 1`] = ` +exports[`Button as a link Variants reverse matches snapshot 1`] = `
- +
`; -exports[`Button button types secondary matches snapshot 1`] = ` +exports[`Button as a link Variants secondary matches snapshot 1`] = `
- +
`; -exports[`Button button types secondarySolid matches snapshot 1`] = ` +exports[`Button as a link Variants secondary-solid matches snapshot 1`] = `
- -
-`; - -exports[`Button button types small matches snapshot 1`] = ` -
- +
`; -exports[`Button button types warning matches snapshot 1`] = ` +exports[`Button as a link Variants warning matches snapshot 1`] = `
- +
`; -exports[`Button disabled matches snapshot: DisabledButton 1`] = ` +exports[`Button as a link matches snapshot: LinkButton 1`] = `
- + Find my location +
`; diff --git a/src/components/form-elements/password-input/PasswordInput.tsx b/src/components/form-elements/password-input/PasswordInput.tsx index 9089e9b2..f354ad76 100644 --- a/src/components/form-elements/password-input/PasswordInput.tsx +++ b/src/components/form-elements/password-input/PasswordInput.tsx @@ -40,7 +40,7 @@ const PasswordInputButton: FC> = ({ Cancel @@ -70,7 +70,7 @@ export const InterruptionPanelCancel: Story = {

Cancelling your appointment cannot be undone.

- Change my weight @@ -91,7 +91,7 @@ export const InterruptionPanelContinue: Story = { You entered your weight as 21.4 kilograms. This is lower than expected.

- Change my weight diff --git a/stories/Form Elements/Select.stories.tsx b/stories/Form Elements/Select.stories.tsx index 936e229c..6d2dc902 100644 --- a/stories/Form Elements/Select.stories.tsx +++ b/stories/Form Elements/Select.stories.tsx @@ -94,7 +94,7 @@ export const SelectWithButton: Story = { args: { formGroupProps: { afterInput: ( - ), @@ -108,7 +108,7 @@ export const SelectWithButtonAndError: Story = { error: 'Select a location', formGroupProps: { afterInput: ( - ), diff --git a/stories/Form Elements/TextInput.stories.tsx b/stories/Form Elements/TextInput.stories.tsx index 0e1265af..051a0b72 100644 --- a/stories/Form Elements/TextInput.stories.tsx +++ b/stories/Form Elements/TextInput.stories.tsx @@ -148,7 +148,7 @@ export const WithButton: Story = { spellCheck: false, formGroupProps: { afterInput: ( - ), @@ -167,7 +167,7 @@ export const WithButtonAndError: Story = { spellCheck: false, formGroupProps: { afterInput: ( - ), diff --git a/stories/Navigation/Card.stories.tsx b/stories/Navigation/Card.stories.tsx index dbeea71a..7cc186a9 100644 --- a/stories/Navigation/Card.stories.tsx +++ b/stories/Navigation/Card.stories.tsx @@ -151,7 +151,7 @@ export const WithSummaryListAndButton: Story = { 15 March 1984 - @@ -416,7 +416,7 @@ export const FeatureCardWithNestedCardAndSummaryList: Story = { Sarah Philips (Mum) would like to speak to a member of the team about other options for their child's vaccination. - From ed70ae8ce849fd31baf3d3ad3e70bff9e806d150 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Mon, 15 Jun 2026 14:55:20 +0100 Subject: [PATCH 8/9] Update details component to support `reverse` prop --- .../content-presentation/details/Details.tsx | 12 ++- .../details/__tests__/Details.test.tsx | 9 ++ .../Content Presentation/Details.stories.tsx | 90 +++++++++++++++---- 3 files changed, 94 insertions(+), 17 deletions(-) diff --git a/src/components/content-presentation/details/Details.tsx b/src/components/content-presentation/details/Details.tsx index 0ce3aab5..1809aea9 100644 --- a/src/components/content-presentation/details/Details.tsx +++ b/src/components/content-presentation/details/Details.tsx @@ -3,12 +3,20 @@ import { type ComponentPropsWithoutRef, type FC, forwardRef } from 'react'; export interface DetailsProps extends ComponentPropsWithoutRef<'details'> { expander?: boolean; + variant?: 'reverse'; } const DetailsComponent = forwardRef( - ({ className, expander, ...rest }, forwardedRef) => ( + ({ className, expander, variant, ...rest }, forwardedRef) => (
diff --git a/src/components/content-presentation/details/__tests__/Details.test.tsx b/src/components/content-presentation/details/__tests__/Details.test.tsx index cca0bd47..fc826723 100644 --- a/src/components/content-presentation/details/__tests__/Details.test.tsx +++ b/src/components/content-presentation/details/__tests__/Details.test.tsx @@ -47,6 +47,15 @@ describe('Details', () => { expect(detailsEl).toHaveClass('nhsuk-expander'); }); + it('adds reverse variant', async () => { + const { modules } = await renderClient(
, { + className: 'nhsuk-details', + }); + + const [detailsEl] = modules; + expect(detailsEl).toHaveClass('nhsuk-details--reverse'); + }); + it('forwards refs', async () => { const ref = createRef(); diff --git a/stories/Content Presentation/Details.stories.tsx b/stories/Content Presentation/Details.stories.tsx index a453bbbe..46c9f2f3 100644 --- a/stories/Content Presentation/Details.stories.tsx +++ b/stories/Content Presentation/Details.stories.tsx @@ -18,13 +18,6 @@ const meta: Meta = { ), }, }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - name: 'Details default', argTypes: { expander: { table: { @@ -32,8 +25,15 @@ export const Default: Story = { }, }, }, - render: ({ expander }) => ( -
+}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + name: 'Details default', + render: (args) => ( +
Where can I find my NHS number?

An NHS number is a 10 digit number, like 485 777 3456.

@@ -53,16 +53,73 @@ export const Default: Story = { ), }; +export const Reverse: Story = { + name: 'Reverse', + args: { + variant: 'reverse', + }, + globals: { + backgrounds: { value: 'dark' }, + }, + render: (args) => ( +
+ Opening times + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Day of the week + + Opening hours +
Monday9am to 6pm
Tuesday9am to 6pm
Wednesday9am to 6pm
Thursday9am to 6pm
Friday9am to 6pm
Saturday9am to 1pm
SundayClosed
+
+
+ ), +}; + export const Expander: Story = { name: 'Expander', args: { expander: true, }, - render: ({ expander }) => ( -
+ render: (args) => ( +
Opening times - +
@@ -109,9 +166,12 @@ export const Expander: Story = { export const ExpanderGroup: Story = { name: 'Expander group', + args: { + expander: true, + }, render: (args) => ( -
+
How to measure your blood glucose levels

@@ -122,12 +182,12 @@ export const ExpanderGroup: Story = {

  • a blood glucose metre
  • small needles called lancets
  • -
  • a plastic pen to hold the lancest
  • +
  • a plastic pen to hold the lancets
  • small test strips
-
+
When to check your blood glucose level

Try to check your blood:

From 30779ff63df9992d7d1ba06c1f7162b903362b5e Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Mon, 15 Jun 2026 14:58:07 +0100 Subject: [PATCH 9/9] Ignore deprecated props until next major release --- src/components/content-presentation/icons/Icon.tsx | 1 + src/components/content-presentation/tag/Tag.tsx | 1 + src/components/form-elements/button/Button.tsx | 1 + 3 files changed, 3 insertions(+) diff --git a/src/components/content-presentation/icons/Icon.tsx b/src/components/content-presentation/icons/Icon.tsx index cf1184ce..871adb18 100644 --- a/src/components/content-presentation/icons/Icon.tsx +++ b/src/components/content-presentation/icons/Icon.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-deprecated */ import classNames from 'classnames'; import { type ComponentPropsWithoutRef, type FC } from 'react'; diff --git a/src/components/content-presentation/tag/Tag.tsx b/src/components/content-presentation/tag/Tag.tsx index 63f4a0d4..3f090c23 100644 --- a/src/components/content-presentation/tag/Tag.tsx +++ b/src/components/content-presentation/tag/Tag.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-deprecated */ import classNames from 'classnames'; import { type ComponentPropsWithoutRef, type FC } from 'react'; diff --git a/src/components/form-elements/button/Button.tsx b/src/components/form-elements/button/Button.tsx index 297c5ba4..b6007a73 100644 --- a/src/components/form-elements/button/Button.tsx +++ b/src/components/form-elements/button/Button.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-deprecated */ 'use client'; import classNames from 'classnames';