Skip to content

Commit

Permalink
Simplify Fieldset component and introduce FormGroup
Browse files Browse the repository at this point in the history
  • Loading branch information
jakeb-nhs committed Oct 9, 2024
1 parent 6d09861 commit 8e0552f
Show file tree
Hide file tree
Showing 33 changed files with 371 additions and 154 deletions.
46 changes: 26 additions & 20 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,65 @@

## Unreleased

This version updates nhsuk-frontend to version 9.

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-5.0.md).

## 4.1.3 - 23 September 2024

:wrench: **Fixes**
* Remove the unnecessary aria-labelledby tags from radio items. PR [#253](https://github.com/NHSDigital/nhsuk-react-components/pull/253)

- Remove the unnecessary aria-labelledby tags from radio items. PR [#253](https://github.com/NHSDigital/nhsuk-react-components/pull/253)

## 4.1.2 - 3 September 2024

:wrench: **Fixes**

* Fix issues with SkipLink (always set the href) and bring into line with NHSUK frontend. PR [#248](https://github.com/NHSDigital/nhsuk-react-components/pull/248)
- Fix issues with SkipLink (always set the href) and bring into line with NHSUK frontend. PR [#248](https://github.com/NHSDigital/nhsuk-react-components/pull/248)

## 4.1.1 - 9 August 2024

:wrench: **Fixes**

* Remove the unnecessary aria-labelledby tags from DateInput fields. PR [#246](https://github.com/NHSDigital/nhsuk-react-components/pull/246)
- Remove the unnecessary aria-labelledby tags from DateInput fields. PR [#246](https://github.com/NHSDigital/nhsuk-react-components/pull/246)

## 4.1.0 - 11 June 2024

:wrench: **Fixes**

* Add js shims for buttons. PR [#231](https://github.com/NHSDigital/nhsuk-react-components/pull/231), Fixes [#218](https://github.com/NHSDigital/nhsuk-react-components/issues/218)
* Fix errors not being linked to inputs. PR [#230](https://github.com/NHSDigital/nhsuk-react-components/pull/230), Fixes [#227](https://github.com/NHSDigital/nhsuk-react-components/issues/227)
* Fix inputs incorrectly using `aria-labelledby`. PR [#230](https://github.com/NHSDigital/nhsuk-react-components/pull/230), Fixes [#212](https://github.com/NHSDigital/nhsuk-react-components/issues/212)
* Update Storybook docs for several components.
- Add js shims for buttons. PR [#231](https://github.com/NHSDigital/nhsuk-react-components/pull/231), Fixes [#218](https://github.com/NHSDigital/nhsuk-react-components/issues/218)
- Fix errors not being linked to inputs. PR [#230](https://github.com/NHSDigital/nhsuk-react-components/pull/230), Fixes [#227](https://github.com/NHSDigital/nhsuk-react-components/issues/227)
- Fix inputs incorrectly using `aria-labelledby`. PR [#230](https://github.com/NHSDigital/nhsuk-react-components/pull/230), Fixes [#212](https://github.com/NHSDigital/nhsuk-react-components/issues/212)
- Update Storybook docs for several components.

:new: **New features**

* Added a CHANGELOG to keep track of changes between releases. [Keep a changelog](https://keepachangelog.com)
* Added support for `preventDoubleClick` debouncing on buttons. PR [#231](https://github.com/NHSDigital/nhsuk-react-components/pull/231)
* Error summaries now automatically set role, tabindex, and aria-labelledby. PR [#229](https://github.com/NHSDigital/nhsuk-react-components/pull/237), Fixes [#228](https://github.com/NHSDigital/nhsuk-react-components/issues/229)
* Storybook link in readme now points to latest version. PR [#226](https://github.com/NHSDigital/nhsuk-react-components/pull/226)
- Added a CHANGELOG to keep track of changes between releases. [Keep a changelog](https://keepachangelog.com)
- Added support for `preventDoubleClick` debouncing on buttons. PR [#231](https://github.com/NHSDigital/nhsuk-react-components/pull/231)
- Error summaries now automatically set role, tabindex, and aria-labelledby. PR [#229](https://github.com/NHSDigital/nhsuk-react-components/pull/237), Fixes [#228](https://github.com/NHSDigital/nhsuk-react-components/issues/229)
- Storybook link in readme now points to latest version. PR [#226](https://github.com/NHSDigital/nhsuk-react-components/pull/226)

## 4.0.2 - 21 May 2024

:wrench: **Fixes**

* Fix error message role by @edwardhorsford in [#219](https://github.com/NHSDigital/nhsuk-react-components/pull/219)
- Fix error message role by @edwardhorsford in [#219](https://github.com/NHSDigital/nhsuk-react-components/pull/219)

## 4.0.1 - 20 May 2024

:wrench: **Fixes**

* Fix issue with the footer copyright not being rendered in the correct location if there are multiple link columns by @jakeb-nhs in [#223](https://github.com/NHSDigital/nhsuk-react-components/pull/223)
- Fix issue with the footer copyright not being rendered in the correct location if there are multiple link columns by @jakeb-nhs in [#223](https://github.com/NHSDigital/nhsuk-react-components/pull/223)

## 4.0.0 - 15 May 2024

This version updates nhsuk-frontend to version 8.

For a full list of changes in this release please refer to the [migration doc](https://github.com/NHSDigital/nhsuk-react-components/blob/feature/nhsuk-frontend-v8/docs/upgrade-to-4.0.md).
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-4.0.md).

* Migrate enzyme to react-testing-library by @JoshuaBates-NHS in [#198](https://github.com/NHSDigital/nhsuk-react-components/pull/198)
* Allow support for module directives in build process by @JoshuaBates-NHS in [#199](https://github.com/NHSDigital/nhsuk-react-components/pull/199)
* Update modified components since NHS UK frontend v5 by @jakeb-nhs in [#197](https://github.com/NHSDigital/nhsuk-react-components/pull/197)
* Add new components since NHS UK frontend v5 by @jakeb-nhs in [#202](https://github.com/NHSDigital/nhsuk-react-components/pull/202)
* Migrate some patterns to components, rework removed components from frontend v8 by @jakeb-nhs in [#203](https://github.com/NHSDigital/nhsuk-react-components/pull/203)
* Improve unit test coverage by @jakeb-nhs in [#204](https://github.com/NHSDigital/nhsuk-react-components/pull/204)
- Migrate enzyme to react-testing-library by @JoshuaBates-NHS in [#198](https://github.com/NHSDigital/nhsuk-react-components/pull/198)
- Allow support for module directives in build process by @JoshuaBates-NHS in [#199](https://github.com/NHSDigital/nhsuk-react-components/pull/199)
- Update modified components since NHS UK frontend v5 by @jakeb-nhs in [#197](https://github.com/NHSDigital/nhsuk-react-components/pull/197)
- Add new components since NHS UK frontend v5 by @jakeb-nhs in [#202](https://github.com/NHSDigital/nhsuk-react-components/pull/202)
- Migrate some patterns to components, rework removed components from frontend v8 by @jakeb-nhs in [#203](https://github.com/NHSDigital/nhsuk-react-components/pull/203)
- Improve unit test coverage by @jakeb-nhs in [#204](https://github.com/NHSDigital/nhsuk-react-components/pull/204)
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ class GetStartedButton extends PureComponent {

## Upgrading

* [Upgrading to 1.0](/docs/upgrade-to-1.0.md)
* [Upgrading to 2.0](/docs/upgrade-to-2.0.md)
* [Upgrading to 3.0](/docs/upgrade-to-3.0.md)
* [Upgrading to 4.0](/docs/upgrade-to-4.0.md)
- [Upgrading to 1.0](/docs/upgrade-to-1.0.md)
- [Upgrading to 2.0](/docs/upgrade-to-2.0.md)
- [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)

## Maintainers

Expand Down
42 changes: 42 additions & 0 deletions docs/upgrade-to-5.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Upgrading to 5.0

## Breaking changes

## New Features

### FormGroup

In order to provide consumers of this library more control over when to render fieldsets, some behaviour of the previous `Fieldset` component has been extracted into a new `FormGroup` component. This makes `FormGroup` responsible for rendering the error decorator line for groups of inputs, and `Fieldset` is now a simpler component which makes use of this component.

No changes are required for existing usages of `Fieldset`. For examples of the usage of `FormGroup`, please see storybook.

For example, this:

```
<Fieldset>
<Fieldset.Legend>What is your address?</Fieldset.Legend>
<TextInput id="address-1" />
<TextInput id="address-2" />
</Fieldset>
```

Would become this:

```
<InputGroup>
<Fieldset>
<Fieldset.Legend>What is your address?</Fieldset.Legend>
<TextInput id="address-1" />
<TextInput id="address-2" />
</Fieldset>
</InputGroup>
```

Which also allows consumers to omit the `Fieldset` if rendering a single input:

```
<InputGroup>
<HeadingLevel headingLevel="h3">What is your address?</HeadingLevel>
<TextInput id="address-1" />
</InputGroup>
```
1 change: 1 addition & 0 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('Index', () => {
'Fieldset',
'Footer',
'Form',
'FormGroup',
'Header',
'Hero',
'HintText',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import React, { FC, HTMLProps, createContext, useContext, ReactNode } from 'react';
import classNames from 'classnames';
import { Tick, Cross } from '@components/content-presentation/icons';
import HeadingLevel, { HeadingLevelType } from '@util/HeadingLevel';
import HeadingLevel, { HeadingLevelType } from '@components/utils/HeadingLevel';

type ListType = 'do' | 'dont';

Expand Down
2 changes: 1 addition & 1 deletion src/components/content-presentation/hero/Hero.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { FC, HTMLProps } from 'react';
import classNames from 'classnames';
import { Container, Row, Col } from '../../layout';
import HeadingLevel, { HeadingLevelType } from '@util/HeadingLevel';
import HeadingLevel, { HeadingLevelType } from '@components/utils/HeadingLevel';

interface HeroContentProps extends HTMLProps<HTMLDivElement> {
hasImage: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { FC, ComponentProps, HTMLProps } from 'react';
import classNames from 'classnames';
import HeadingLevel from '@util/HeadingLevel';
import HeadingLevel from '@components/utils/HeadingLevel';

export interface TablePanelProps extends HTMLProps<HTMLDivElement> {
heading?: string;
Expand Down
2 changes: 1 addition & 1 deletion src/components/content-presentation/tabs/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';
import classNames from 'classnames';
import React, { FC, HTMLAttributes, useEffect } from 'react';
import HeadingLevel, { HeadingLevelType } from '@util/HeadingLevel';
import HeadingLevel, { HeadingLevelType } from '@components/utils/HeadingLevel';
import TabsJs from '@resources/tabs';

type TabsProps = HTMLAttributes<HTMLDivElement>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { fireEvent, render } from '@testing-library/react';
import Tabs from '../Tabs';
import { HeadingLevelType } from '@util/HeadingLevel';
import { HeadingLevelType } from '@components/utils/HeadingLevel';

describe('The tabs component', () => {
it('Matches the snapshot', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { FC, HTMLProps } from 'react';
import classNames from 'classnames';
import HeadingLevel, { HeadingLevelType } from '@util/HeadingLevel';
import HeadingLevel, { HeadingLevelType } from '@components/utils/HeadingLevel';

interface WarningCalloutLabelProps extends HTMLProps<HTMLHeadingElement> {
headingLevel?: HeadingLevelType;
Expand Down
6 changes: 3 additions & 3 deletions src/components/form-elements/checkboxes/Checkboxes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import React, { HTMLProps, useEffect } from 'react';
import classNames from 'classnames';
import { FormElementProps } from '@util/types/FormTypes';
import FormGroup from '@util/FormGroup';
import SingleInputFormGroup from '@components/utils/SingleInputFormGroup';
import CheckboxContext, { ICheckboxContext } from './CheckboxContext';
import Box from './components/Box';
import Divider from './components/Divider';
Expand Down Expand Up @@ -53,7 +53,7 @@ const Checkboxes = ({ children, idPrefix, ...rest }: CheckboxesProps) => {
};

return (
<FormGroup<CheckboxesProps> inputType="checkboxes" {...rest}>
<SingleInputFormGroup<CheckboxesProps> inputType="checkboxes" {...rest}>
{/* eslint-disable-next-line @typescript-eslint/no-unused-vars */}
{({ className, name, id, idPrefix, error, ...restRenderProps }) => {
resetCheckboxIds();
Expand All @@ -69,7 +69,7 @@ const Checkboxes = ({ children, idPrefix, ...rest }: CheckboxesProps) => {
</div>
);
}}
</FormGroup>
</SingleInputFormGroup>
);
};

Expand Down
9 changes: 6 additions & 3 deletions src/components/form-elements/date-input/DateInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import React, { HTMLProps, ChangeEvent, useEffect, useState } from 'react';
import classNames from 'classnames';
import { DayInput, MonthInput, YearInput } from './components/IndividualDateInputs';
import FormGroup from '@util/FormGroup';
import SingleInputFormGroup from '@components/utils/SingleInputFormGroup';
import DateInputContext, { IDateInputContext } from './DateInputContext';
import { FormElementProps } from '@util/types/FormTypes';

Expand Down Expand Up @@ -90,7 +90,10 @@ const DateInput = ({
};

return (
<FormGroup<Omit<DateInputProps, 'value' | 'defaultValue'>> inputType="dateinput" {...rest}>
<SingleInputFormGroup<Omit<DateInputProps, 'value' | 'defaultValue'>>
inputType="dateinput"
{...rest}
>
{/* eslint-disable-next-line @typescript-eslint/no-unused-vars */}
{({ className, name, id, error, autoSelectNext, ...restRenderProps }) => {
const contextValue: IDateInputContext = {
Expand All @@ -116,7 +119,7 @@ const DateInput = ({
</div>
);
}}
</FormGroup>
</SingleInputFormGroup>
);
};

Expand Down
63 changes: 7 additions & 56 deletions src/components/form-elements/fieldset/Fieldset.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { FC, HTMLProps, MutableRefObject, useMemo, useState } from 'react';
import React, { FC, HTMLProps, MutableRefObject } from 'react';
import classNames from 'classnames';
import { NHSUKSize } from '@util/types/NHSUKTypes';
import HeadingLevel, { HeadingLevelType } from '@util/HeadingLevel';
import FieldsetContext, { IFieldsetContext } from './FieldsetContext';
import HeadingLevel, { HeadingLevelType } from '@components/utils/HeadingLevel';
import FormGroup from '@components/utils/FormGroup';

interface LegendProps extends Omit<HTMLProps<HTMLLegendElement>, 'size'> {
isPageHeading?: boolean;
Expand Down Expand Up @@ -44,60 +44,11 @@ interface FieldsetProps extends HTMLProps<HTMLFieldSetElement> {
disableErrorLine?: boolean;
}

const FieldSet = ({ className, disableErrorLine, fieldsetRef, ...rest }: FieldsetProps) => {
const [registeredComponents, setRegisteredComponents] = useState<string[]>([]);
const [erroredComponents, setErroredComponents] = useState<string[]>([]);

const passError = (componentId: string, error: boolean): void => {
const existingError = erroredComponents.includes(componentId);
if (existingError && !error) {
setErroredComponents(erroredComponents.filter((id) => id !== componentId));
return;
}
if (!existingError && error) {
setErroredComponents([...erroredComponents, componentId]);
}
};

const registerComponent = (componentId: string, deregister = false): void => {
let newComponents = [...registeredComponents];
if (deregister) {
newComponents = newComponents.filter((id) => id !== componentId);
} else if (!registeredComponents.includes(componentId)) {
newComponents = [...newComponents, componentId];
}
setRegisteredComponents(newComponents);
};

const contextValue: IFieldsetContext = useMemo(() => {
return {
isFieldset: true,
registerComponent: registerComponent,
passError: passError,
};
}, [registerComponent, passError]);

const containsFormElements = registeredComponents.length > 0;
const containsError = erroredComponents.length > 0;

const FieldSet = ({ className, fieldsetRef, disableErrorLine, ...rest }: FieldsetProps) => {
return (
<FieldsetContext.Provider value={contextValue}>
{containsFormElements ? (
<div
className={classNames('nhsuk-form-group', {
'nhsuk-form-group--error': disableErrorLine ? false : containsError,
})}
>
<fieldset
className={classNames('nhsuk-fieldset', className)}
ref={fieldsetRef}
{...rest}
/>
</div>
) : (
<fieldset className={classNames('nhsuk-fieldset', className)} ref={fieldsetRef} {...rest} />
)}
</FieldsetContext.Provider>
<FormGroup enableErrorLine={!disableErrorLine}>
<fieldset className={classNames('nhsuk-fieldset', className)} ref={fieldsetRef} {...rest} />
</FormGroup>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import TextInput from '@components/form-elements/text-input';

describe('Fieldset', () => {
it('matches snapshot', () => {
const { container } = render(<Fieldset>Text</Fieldset>);
const { container } = render(
<Fieldset>
<TextInput id="test-input" />
</Fieldset>,
);

expect(container).toMatchSnapshot('Fieldset');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,23 @@ exports[`Fieldset Fieldset.Legend matches snapshot: FieldsetLegend 1`] = `

exports[`Fieldset matches snapshot: Fieldset 1`] = `
<div>
<fieldset
class="nhsuk-fieldset"
<div
class="nhsuk-form-group"
>
Text
</fieldset>
<fieldset
class="nhsuk-fieldset"
>
<div
class="nhsuk-form-group"
>
<input
class="nhsuk-input"
id="test-input"
name="test-input"
type="text"
/>
</div>
</fieldset>
</div>
</div>
`;
Loading

0 comments on commit 8e0552f

Please sign in to comment.