From 57347eb37606dcb222e6195ad72d3849c7b307d0 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 16 Oct 2025 16:55:38 +0100 Subject: [PATCH 1/7] Update data attributes to use string values --- .../form-elements/character-count/CharacterCount.tsx | 6 +++--- .../checkboxes/components/CheckboxesItem.tsx | 6 +++--- src/util/types/FormTypes.ts | 10 ++-------- src/util/types/NHSUKTypes.ts | 6 +++--- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/components/form-elements/character-count/CharacterCount.tsx b/src/components/form-elements/character-count/CharacterCount.tsx index 5d9723fc4..10c7bcd56 100644 --- a/src/components/form-elements/character-count/CharacterCount.tsx +++ b/src/components/form-elements/character-count/CharacterCount.tsx @@ -41,9 +41,9 @@ export const CharacterCount = forwardRef, @@ -54,10 +54,10 @@ export const CheckboxesItem = forwardRef( useEffect(() => () => unleaseReference(checkboxReference)); - const inputProps: HTMLAttributesWithData = rest; + const inputProps: ComponentPropsWithDataAttributes<'input'> = rest; if (exclusive) { - inputProps['data-checkbox-exclusive'] = true; + inputProps['data-checkbox-exclusive'] = 'true'; } return ( diff --git a/src/util/types/FormTypes.ts b/src/util/types/FormTypes.ts index 6054eac59..9f6d3149f 100644 --- a/src/util/types/FormTypes.ts +++ b/src/util/types/FormTypes.ts @@ -1,10 +1,9 @@ -import { type ComponentPropsWithRef } from 'react'; - import { type ErrorMessageProps } from '#components/form-elements/error-message/index.js'; import { type FieldsetProps } from '#components/form-elements/fieldset/index.js'; import { type HintTextProps } from '#components/form-elements/hint-text/index.js'; import { type LabelProps } from '#components/form-elements/label/index.js'; import { type LegendProps } from '#components/form-elements/legend/index.js'; +import { type ComponentPropsWithDataAttributes } from '#util/types/NHSUKTypes.js'; export interface FormElementProps { 'fieldsetProps'?: FieldsetProps; @@ -16,12 +15,7 @@ export interface FormElementProps { 'errorProps'?: ErrorMessageProps; 'hint'?: string; 'hintProps'?: HintTextProps; - 'formGroupProps'?: ComponentPropsWithRef<'div'> & { - 'data-module'?: string; - 'data-maxlength'?: number; - 'data-maxwords'?: number; - 'data-threshold'?: number; - }; + 'formGroupProps'?: ComponentPropsWithDataAttributes<'div'>; 'id'?: string; 'name'?: string; 'aria-describedby'?: string; diff --git a/src/util/types/NHSUKTypes.ts b/src/util/types/NHSUKTypes.ts index 862fdfbdb..77259880b 100644 --- a/src/util/types/NHSUKTypes.ts +++ b/src/util/types/NHSUKTypes.ts @@ -1,4 +1,4 @@ -import { type HTMLAttributes } from 'react'; +import { type ComponentProps, type ElementType } from 'react'; export type NHSUKSize = 's' | 'm' | 'l' | 'xl'; @@ -16,6 +16,6 @@ export type ColWidth = | 'one-third' | 'one-quarter'; -export type HTMLAttributesWithData = HTMLAttributes & { - [key: `data-${string}`]: unknown; +export type ComponentPropsWithDataAttributes = ComponentProps & { + [key: `data-${string}`]: string | undefined; }; From aa994f635446713aa9fa5b0baa289a3aedaf8b5a Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 16 Oct 2025 12:00:24 +0100 Subject: [PATCH 2/7] Allow unused vars as object rest siblings --- eslint.config.js | 11 +++++++++++ .../form-elements/checkboxes/Checkboxes.tsx | 1 - src/components/form-elements/radios/Radios.tsx | 1 - src/components/utils/__tests__/FormGroup.test.tsx | 4 ---- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 8660f6396..8ae6aae0a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -43,6 +43,17 @@ export default defineConfig([ 'import/no-named-as-default-member': 'off', 'import/no-unresolved': 'off', 'import/no-unused-modules': 'off', + + // Prefer rules that are type aware + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + ignoreRestSiblings: true, + }, + ], }, settings: { 'import/resolver': { diff --git a/src/components/form-elements/checkboxes/Checkboxes.tsx b/src/components/form-elements/checkboxes/Checkboxes.tsx index 1e17e60c9..fc2751d57 100644 --- a/src/components/form-elements/checkboxes/Checkboxes.tsx +++ b/src/components/form-elements/checkboxes/Checkboxes.tsx @@ -71,7 +71,6 @@ const CheckboxesComponent = forwardRef((props, return ( inputType="checkboxes" {...rest}> - {/* eslint-disable-next-line @typescript-eslint/no-unused-vars */} {({ className, name, id, idPrefix, error, ...restRenderProps }) => { resetCheckboxIds(); const contextValue: ICheckboxesContext = { diff --git a/src/components/form-elements/radios/Radios.tsx b/src/components/form-elements/radios/Radios.tsx index b69470ab7..e9ad91419 100644 --- a/src/components/form-elements/radios/Radios.tsx +++ b/src/components/form-elements/radios/Radios.tsx @@ -78,7 +78,6 @@ const RadiosComponent = forwardRef((props, forwarde return ( inputType="radios" {...rest}> - {/* eslint-disable-next-line @typescript-eslint/no-unused-vars */} {({ className, inline, name, id, error, ...restRenderProps }) => { resetRadioIds(); const contextValue: IRadiosContext = { diff --git a/src/components/utils/__tests__/FormGroup.test.tsx b/src/components/utils/__tests__/FormGroup.test.tsx index dc26c29ad..3fef09028 100644 --- a/src/components/utils/__tests__/FormGroup.test.tsx +++ b/src/components/utils/__tests__/FormGroup.test.tsx @@ -220,7 +220,6 @@ describe('FormGroup', () => { it('string component', async () => { const { container } = await renderClient( inputType="input" error="Oh no there's an error!"> - {/* eslint-disable-next-line @typescript-eslint/no-unused-vars */} {({ error, ...rest }) => } , { className: 'nhsuk-form-group' }, @@ -245,7 +244,6 @@ describe('FormGroup', () => { hint="This is a hint" label="Form Label" > - {/* eslint-disable-next-line @typescript-eslint/no-unused-vars */} {({ error, ...rest }) => } , @@ -264,7 +262,6 @@ describe('FormGroup', () => { error="This is an error" hint="This is a hint" > - {/* eslint-disable-next-line @typescript-eslint/no-unused-vars */} {({ error, ...rest }) => } , { className: 'nhsuk-form-group' }, @@ -279,7 +276,6 @@ describe('FormGroup', () => { it('should have no aria-describedby when there is no hint or label', async () => { const { container } = await renderClient( inputType="input"> - {/* eslint-disable-next-line @typescript-eslint/no-unused-vars */} {({ error, ...rest }) => } , { className: 'nhsuk-form-group' }, From 7ff7ac00852205a4a9549f32089a86330cf4a50b Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 16 Oct 2025 10:02:40 +0100 Subject: [PATCH 3/7] Fix missing character count max length --- .../character-count/__tests__/CharacterCount.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/form-elements/character-count/__tests__/CharacterCount.test.tsx b/src/components/form-elements/character-count/__tests__/CharacterCount.test.tsx index 09e9581b9..7241b6c09 100644 --- a/src/components/form-elements/character-count/__tests__/CharacterCount.test.tsx +++ b/src/components/form-elements/character-count/__tests__/CharacterCount.test.tsx @@ -50,7 +50,7 @@ describe('Character Count', () => { const fieldRef = createRef(); const { container } = await renderClient( - , + , { moduleName: 'nhsuk-character-count' }, ); From 36752606235b1642104366b776912a94548c8904 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Wed, 15 Oct 2025 17:32:45 +0100 Subject: [PATCH 4/7] Update to NHS.UK frontend v10.1.0 --- package.json | 4 ++-- yarn.lock | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index dbc7199c1..e8598639e 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "jest-axe": "^10.0.0", "jest-environment-jsdom": "^30.2.0", "lodash": "^4.17.21", - "nhsuk-frontend": "^10.0.0", + "nhsuk-frontend": "^10.1.0", "outdent": "^0.8.0", "prettier": "^3.6.2", "react": "^19.2.0", @@ -124,7 +124,7 @@ }, "peerDependencies": { "classnames": ">=2.5.0", - "nhsuk-frontend": ">=10.0.0 <11.0.0", + "nhsuk-frontend": ">=10.1.0 <11.0.0", "react": ">=18.2.0", "react-dom": ">=18.2.0", "tslib": ">=2.8.0" diff --git a/yarn.lock b/yarn.lock index ca78d0cb8..7678068d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8464,10 +8464,10 @@ __metadata: languageName: node linkType: hard -"nhsuk-frontend@npm:^10.0.0": - version: 10.0.0 - resolution: "nhsuk-frontend@npm:10.0.0" - checksum: 10c0/5a24cc7b56188d18bbd86c040e39b420546451f1b955eed176f4acd5f3b75d6ec5eef86b8ab970cff1b4741f2f599debae03bf4be75c3f6a903cfce205e88501 +"nhsuk-frontend@npm:^10.1.0": + version: 10.1.0 + resolution: "nhsuk-frontend@npm:10.1.0" + checksum: 10c0/adf8ccc240c3514b865ae361b4e29f7ad11051f5c4585970cbd2580f61dbe5f1818d53f3b8eef3825aaf72f9ea3df647f4b0cc57d60092a3edc7583665374f91 languageName: node linkType: hard @@ -8514,7 +8514,7 @@ __metadata: jest-axe: "npm:^10.0.0" jest-environment-jsdom: "npm:^30.2.0" lodash: "npm:^4.17.21" - nhsuk-frontend: "npm:^10.0.0" + nhsuk-frontend: "npm:^10.1.0" outdent: "npm:^0.8.0" prettier: "npm:^3.6.2" react: "npm:^19.2.0" @@ -8530,7 +8530,7 @@ __metadata: vite-tsconfig-paths: "npm:^5.1.4" peerDependencies: classnames: ">=2.5.0" - nhsuk-frontend: ">=10.0.0 <11.0.0" + nhsuk-frontend: ">=10.1.0 <11.0.0" react: ">=18.2.0" react-dom: ">=18.2.0" tslib: ">=2.8.0" From a6fdc0c8f42e8b4545d4be330dc1f87a86f5c0ff Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Wed, 15 Oct 2025 18:28:52 +0100 Subject: [PATCH 5/7] Add support for small radios and checkboxes --- .../form-elements/checkboxes/Checkboxes.tsx | 9 +++++++-- src/components/form-elements/radios/Radios.tsx | 14 +++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/components/form-elements/checkboxes/Checkboxes.tsx b/src/components/form-elements/checkboxes/Checkboxes.tsx index fc2751d57..9b2264876 100644 --- a/src/components/form-elements/checkboxes/Checkboxes.tsx +++ b/src/components/form-elements/checkboxes/Checkboxes.tsx @@ -13,6 +13,7 @@ export interface CheckboxesProps extends ComponentPropsWithoutRef<'div'>, Omit { idPrefix?: string; + small?: boolean; } const CheckboxesComponent = forwardRef((props, forwardedRef) => { @@ -71,7 +72,7 @@ const CheckboxesComponent = forwardRef((props, return ( inputType="checkboxes" {...rest}> - {({ className, name, id, idPrefix, error, ...restRenderProps }) => { + {({ className, small, name, id, idPrefix, error, ...restRenderProps }) => { resetCheckboxIds(); const contextValue: ICheckboxesContext = { name, @@ -81,7 +82,11 @@ const CheckboxesComponent = forwardRef((props, }; return (
, Omit { - inline?: boolean; idPrefix?: string; + inline?: boolean; + small?: boolean; } const RadiosComponent = forwardRef((props, forwardedRef) => { @@ -78,7 +79,7 @@ const RadiosComponent = forwardRef((props, forwarde return ( inputType="radios" {...rest}> - {({ className, inline, name, id, error, ...restRenderProps }) => { + {({ className, inline, small, name, id, error, ...restRenderProps }) => { resetRadioIds(); const contextValue: IRadiosContext = { getRadioId: (reference) => getRadioId(id, reference), @@ -91,7 +92,14 @@ const RadiosComponent = forwardRef((props, forwarde return (
Date: Thu, 16 Oct 2025 17:18:12 +0100 Subject: [PATCH 6/7] Add small form control examples --- stories/Form Elements/Checkboxes.stories.tsx | 18 +++++++ stories/Form Elements/Radios.stories.tsx | 49 ++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/stories/Form Elements/Checkboxes.stories.tsx b/stories/Form Elements/Checkboxes.stories.tsx index 7972b20c2..6857694b2 100644 --- a/stories/Form Elements/Checkboxes.stories.tsx +++ b/stories/Form Elements/Checkboxes.stories.tsx @@ -67,6 +67,24 @@ export const WithHintText: Story = { ), }; +export const Small: Story = { + args: { + ...Standard.args, + legendProps: { isPageHeading: true, size: 'm' }, + small: true, + }, + render: Standard.render, +}; + +export const SmallWithHintText: Story = { + args: { + ...WithHintText.args, + legendProps: { isPageHeading: true, size: 'm' }, + small: true, + }, + render: WithHintText.render, +}; + export const WithDisabledItem: Story = { render: (args) => ( diff --git a/stories/Form Elements/Radios.stories.tsx b/stories/Form Elements/Radios.stories.tsx index e7b0dc3e6..65a416454 100644 --- a/stories/Form Elements/Radios.stories.tsx +++ b/stories/Form Elements/Radios.stories.tsx @@ -118,6 +118,55 @@ export const RadiosWithHintsOnItems: Story = { ), }; +export const SmallRadios: Story = { + args: { + ...StandardRadios.args, + legendProps: { isPageHeading: true, size: 'm' }, + idPrefix: 'small', + small: true, + }, + render: StandardRadios.render, +}; + +export const SmallInlineRadios: Story = { + args: { + ...InlineRadios.args, + legendProps: { isPageHeading: true, size: 'm' }, + small: true, + }, + render: InlineRadios.render, +}; + +export const SmallRadiosWithConditionalContent: Story = { + args: { + ...RadiosWithConditionalContent.args, + legendProps: { isPageHeading: true, size: 'm' }, + idPrefix: 'small-conditional', + small: true, + }, + render: RadiosWithConditionalContent.render, +}; + +export const SmallRadiosWithADivider: Story = { + args: { + ...RadiosWithADivider.args, + legendProps: { isPageHeading: true, size: 'm' }, + idPrefix: 'small-divider', + small: true, + }, + render: RadiosWithADivider.render, +}; + +export const SmallRadiosWithHintsOnItems: Story = { + args: { + ...RadiosWithHintsOnItems.args, + legendProps: { isPageHeading: true, size: 'm' }, + idPrefix: 'small-hints', + small: true, + }, + render: RadiosWithHintsOnItems.render, +}; + export const DisabledRadios: Story = { args: { idPrefix: 'disabled', From adda0cb75c32486dc16699487c2cd0a4bd1c0741 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Wed, 15 Oct 2025 18:30:13 +0100 Subject: [PATCH 7/7] Update upgrade guide --- docs/upgrade-to-6.0.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/upgrade-to-6.0.md b/docs/upgrade-to-6.0.md index c4d165720..ddc790a12 100644 --- a/docs/upgrade-to-6.0.md +++ b/docs/upgrade-to-6.0.md @@ -18,6 +18,10 @@ The updated [header](https://service-manual.nhs.uk/design-system/components/head - 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 +#### Smaller versions of radio buttons and checkboxes + +You can now use smaller versions of the [radios](https://service-manual.nhs.uk/design-system/components/radios) and [checkboxes](https://service-manual.nhs.uk/design-system/components/checkboxes) components by adding the `small` prop. + ### Panel component The [panel](https://service-manual.nhs.uk/design-system/components/panel) component from NHS.UK frontend v9.3.0 has been added: