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..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 + ), @@ -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 @@ -719,6 +720,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/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', 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/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/src/components/content-presentation/icons/Icon.tsx b/src/components/content-presentation/icons/Icon.tsx index e5f12b75..871adb18 100644 --- a/src/components/content-presentation/icons/Icon.tsx +++ b/src/components/content-presentation/icons/Icon.tsx @@ -1,9 +1,10 @@ +/* eslint-disable @typescript-eslint/no-deprecated */ import classNames from 'classnames'; 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 +15,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) => ( - + ); diff --git a/src/components/content-presentation/tag/Tag.tsx b/src/components/content-presentation/tag/Tag.tsx index 60d83a6c..3f090c23 100644 --- a/src/components/content-presentation/tag/Tag.tsx +++ b/src/components/content-presentation/tag/Tag.tsx @@ -1,8 +1,9 @@ +/* eslint-disable @typescript-eslint/no-deprecated */ import classNames from 'classnames'; import { type ComponentPropsWithoutRef, type FC } from 'react'; export interface TagProps extends ComponentPropsWithoutRef<'strong'> { - modifier?: + colour?: | 'white' | 'grey' | 'green' @@ -15,14 +16,37 @@ 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']; + + border?: boolean; + noBorder?: boolean; } -export const Tag: FC = ({ className, color, modifier = color, ...rest }) => ( +export const Tag: FC = ({ + className, + 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/src/components/form-elements/button/Button.tsx b/src/components/form-elements/button/Button.tsx index 7bcd2442..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'; @@ -26,24 +27,48 @@ export interface ButtonLinkProps extends ButtonBaseProps, AsElementLink((props, forwa warning, login, small, + variant, type = 'submit', preventDoubleClick, onClick, @@ -119,6 +145,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> = ({