Skip to content

Commit 3760167

Browse files
Add support for form group beforeInput and afterInput
1 parent 17e627e commit 3760167

File tree

14 files changed

+178
-143
lines changed

14 files changed

+178
-143
lines changed

src/components/form-elements/character-count/CharacterCount.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@ import {
1414
import { FormGroup } from '#components/utils/index.js';
1515
import { type FormElementProps } from '#util/types/FormTypes.js';
1616

17-
export interface CharacterCountProps
18-
extends
19-
ComponentPropsWithoutRef<'textarea'>,
20-
Omit<FormElementProps, 'fieldsetProps' | 'legend' | 'legendProps'> {
17+
export interface CharacterCountElementProps extends ComponentPropsWithoutRef<'textarea'> {
2118
maxLength?: number;
2219
maxWords?: number;
2320
threshold?: number;
2421
}
2522

23+
export type CharacterCountProps = CharacterCountElementProps &
24+
Omit<
25+
FormElementProps<CharacterCountElementProps, 'textarea'>,
26+
'fieldsetProps' | 'legend' | 'legendProps'
27+
>;
28+
2629
export const CharacterCount = forwardRef<HTMLTextAreaElement, CharacterCountProps>(
2730
({ maxLength, maxWords, threshold, formGroupProps, ...rest }, forwardedRef) => {
2831
const moduleRef = useRef<HTMLDivElement>(null);
@@ -47,7 +50,7 @@ export const CharacterCount = forwardRef<HTMLTextAreaElement, CharacterCountProp
4750
}
4851

4952
return (
50-
<FormGroup<CharacterCountProps>
53+
<FormGroup<CharacterCountProps, 'textarea'>
5154
inputType="textarea"
5255
formGroupProps={{
5356
...formGroupProps,

src/components/form-elements/checkboxes/Checkboxes.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ import { FormGroup } from '#components/utils/index.js';
1818
import { generateRandomName } from '#util/tools/index.js';
1919
import { type FormElementProps } from '#util/types/FormTypes.js';
2020

21-
export interface CheckboxesProps
22-
extends ComponentPropsWithoutRef<'div'>, Omit<FormElementProps, 'label' | 'labelProps'> {
21+
export interface CheckboxesElementProps extends ComponentPropsWithoutRef<'div'> {
2322
idPrefix?: string;
2423
small?: boolean;
2524
}
2625

26+
export type CheckboxesProps = CheckboxesElementProps &
27+
Omit<FormElementProps<CheckboxesElementProps, 'div'>, 'label' | 'labelProps'>;
28+
2729
const CheckboxesComponent = forwardRef<HTMLDivElement, CheckboxesProps>((props, forwardedRef) => {
2830
const { children, idPrefix, ...rest } = props;
2931

@@ -82,7 +84,7 @@ const CheckboxesComponent = forwardRef<HTMLDivElement, CheckboxesProps>((props,
8284
}
8385

8486
return (
85-
<FormGroup<CheckboxesProps> inputType="checkboxes" {...rest}>
87+
<FormGroup<CheckboxesProps, 'div'> inputType="checkboxes" {...rest}>
8688
{({ className, small, name, id, idPrefix, error, ...restRenderProps }) => {
8789
resetCheckboxIds();
8890
const contextValue: ICheckboxesContext = {

src/components/form-elements/checkboxes/components/CheckboxesItem.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,19 @@ import { HintText } from '#components/form-elements/hint-text/index.js';
1717
import { Label } from '#components/form-elements/label/index.js';
1818
import { type ComponentPropsWithDataAttributes, type FormElementProps } from '#util/types/index.js';
1919

20-
export interface CheckboxesItemProps
21-
extends
22-
ComponentPropsWithoutRef<'input'>,
23-
Pick<FormElementProps, 'hint' | 'hintProps' | 'labelProps'> {
20+
export interface CheckboxesItemElementProps extends ComponentPropsWithoutRef<'input'> {
2421
conditional?: ReactNode;
2522
forceShowConditional?: boolean;
2623
conditionalProps?: ComponentPropsWithRef<'div'>;
2724
exclusive?: boolean;
2825
}
2926

27+
export type CheckboxesItemProps = CheckboxesItemElementProps &
28+
Omit<
29+
FormElementProps<CheckboxesItemElementProps, 'input'>,
30+
'fieldsetProps' | 'label' | 'legend' | 'legendProps'
31+
>;
32+
3033
export const CheckboxesItem = forwardRef<HTMLInputElement, CheckboxesItemProps>(
3134
(props, forwardedRef) => {
3235
const {

src/components/form-elements/date-input/DateInput.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,21 @@ export interface DateInputElement extends Omit<HTMLInputElement, 'value' | 'onCh
3636
onChange?: EventHandler<DateInputChangeEvent>;
3737
}
3838

39-
export interface DateInputProps
40-
extends
41-
Omit<ComponentPropsWithoutRef<'div'>, 'defaultValue' | 'onChange'>,
42-
Omit<FormElementProps, 'label' | 'labelProps'> {
39+
export interface DateInputElementProps extends Omit<
40+
ComponentPropsWithoutRef<'div'>,
41+
'defaultValue' | 'onChange'
42+
> {
4343
value?: Partial<DateInputValue>;
4444
defaultValue?: Partial<DateInputValue>;
4545
onChange?: EventHandler<DateInputChangeEvent>;
4646
}
4747

48+
export type DateInputProps = DateInputElementProps &
49+
Omit<
50+
FormElementProps<Omit<ComponentPropsWithoutRef<'div'>, 'defaultValue' | 'onChange'>, 'div'>,
51+
'label' | 'labelProps'
52+
>;
53+
4854
export type DateInputType = 'day' | 'month' | 'year';
4955

5056
const DateInputComponent = forwardRef<HTMLDivElement, DateInputProps>(
@@ -78,7 +84,7 @@ const DateInputComponent = forwardRef<HTMLDivElement, DateInputProps>(
7884
};
7985

8086
return (
81-
<FormGroup<Omit<DateInputProps, 'value' | 'defaultValue'>>
87+
<FormGroup<Omit<DateInputProps, 'value' | 'defaultValue'>, 'div'>
8288
formGroupProps={{ ...formGroupProps, ref: moduleRef }}
8389
fieldsetProps={{ role: 'group' }}
8490
inputType="dateinput"

src/components/form-elements/date-input/components/DateInputField.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@ import { DateInputContext, type IDateInputContext } from '../DateInputContext.js
1414
import { Label } from '#components/form-elements/label/index.js';
1515
import { type FormElementProps } from '#util/types/FormTypes.js';
1616

17-
export interface IndividualDateInputProps
18-
extends ComponentPropsWithoutRef<'input'>, Pick<FormElementProps, 'label' | 'labelProps'> {
17+
export interface IndividualDateInputElementProps extends ComponentPropsWithoutRef<'input'> {
1918
error?: string | ReactElement | false;
2019
inputType: 'day' | 'month' | 'year';
2120
}
2221

22+
export type IndividualDateInputProps = IndividualDateInputElementProps &
23+
Omit<
24+
FormElementProps<IndividualDateInputElementProps, 'input'>,
25+
'error' | 'fieldsetProps' | 'legend' | 'legendProps'
26+
>;
27+
2328
const labels: Record<'day' | 'month' | 'year', string> = {
2429
day: 'Day',
2530
month: 'Month',

src/components/form-elements/radios/Radios.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ import { FormGroup } from '#components/utils/index.js';
1818
import { generateRandomName } from '#util/tools/index.js';
1919
import { type FormElementProps } from '#util/types/FormTypes.js';
2020

21-
export interface RadiosProps
22-
extends ComponentPropsWithoutRef<'div'>, Omit<FormElementProps, 'label' | 'labelProps'> {
21+
export interface RadiosElementProps extends ComponentPropsWithoutRef<'div'> {
2322
idPrefix?: string;
2423
inline?: boolean;
2524
small?: boolean;
2625
}
2726

27+
export type RadiosProps = RadiosElementProps &
28+
Omit<FormElementProps<RadiosElementProps, 'div'>, 'label' | 'labelProps'>;
29+
2830
const RadiosComponent = forwardRef<HTMLDivElement, RadiosProps>((props, forwardedRef) => {
2931
const { children, idPrefix, ...rest } = props;
3032

@@ -89,7 +91,7 @@ const RadiosComponent = forwardRef<HTMLDivElement, RadiosProps>((props, forwarde
8991
}
9092

9193
return (
92-
<FormGroup<RadiosProps> inputType="radios" {...rest}>
94+
<FormGroup<RadiosProps, 'div'> inputType="radios" {...rest}>
9395
{({ className, inline, small, name, id, error, ...restRenderProps }) => {
9496
resetRadioIds();
9597
const contextValue: IRadiosContext = {

src/components/form-elements/radios/components/RadiosItem.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@ import { HintText } from '#components/form-elements/hint-text/index.js';
1717
import { Label } from '#components/form-elements/label/index.js';
1818
import { type FormElementProps } from '#util/types/FormTypes.js';
1919

20-
export interface RadiosItemProps
21-
extends
22-
ComponentPropsWithoutRef<'input'>,
23-
Pick<FormElementProps, 'hint' | 'hintProps' | 'labelProps'> {
20+
export interface RadiosItemElementProps extends ComponentPropsWithoutRef<'input'> {
2421
conditional?: ReactNode;
2522
forceShowConditional?: boolean;
2623
conditionalProps?: ComponentPropsWithRef<'div'>;
2724
}
2825

26+
export type RadiosItemProps = RadiosItemElementProps &
27+
Omit<
28+
FormElementProps<RadiosItemElementProps, 'input'>,
29+
'fieldsetProps' | 'label' | 'legend' | 'legendProps'
30+
>;
31+
2932
export const RadiosItem = forwardRef<HTMLInputElement, RadiosItemProps>((props, forwardedRef) => {
3033
const {
3134
className,

src/components/form-elements/select/Select.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import { forwardRef, type ComponentPropsWithoutRef } from 'react';
66
import { FormGroup } from '#components/utils/index.js';
77
import { type FormElementProps } from '#util/types/FormTypes.js';
88

9-
export type SelectProps = ComponentPropsWithoutRef<'select'> &
10-
Omit<FormElementProps, 'fieldsetProps' | 'legend' | 'legendProps'>;
9+
export type SelectElementProps = ComponentPropsWithoutRef<'select'>;
10+
11+
export type SelectProps = SelectElementProps &
12+
Omit<FormElementProps<SelectElementProps, 'select'>, 'fieldsetProps' | 'legend' | 'legendProps'>;
1113

1214
const SelectComponent = forwardRef<HTMLSelectElement, SelectProps>(
1315
({ children, ...rest }, forwardedRef) => (
14-
<FormGroup<SelectProps> inputType="select" {...rest}>
16+
<FormGroup<SelectProps, 'select'> inputType="select" {...rest}>
1517
{({ className, error, ...restRenderProps }) => (
1618
<select
1719
className={classNames('nhsuk-select', { 'nhsuk-select--error': error }, className)}

src/components/form-elements/text-input/TextInput.tsx

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,19 @@ import { FormGroup } from '#components/utils/index.js';
77
import { type FormElementProps } from '#util/types/FormTypes.js';
88
import { type InputWidth } from '#util/types/NHSUKTypes.js';
99

10-
export interface TextInputProps
11-
extends
12-
ComponentPropsWithoutRef<'input'>,
13-
Omit<FormElementProps, 'fieldsetProps' | 'legend' | 'legendProps'> {
10+
export interface TextInputElementProps extends ComponentPropsWithoutRef<'input'> {
1411
width?: InputWidth;
1512
code?: boolean;
1613
prefix?: string;
1714
suffix?: string;
1815
}
1916

17+
export type TextInputProps = TextInputElementProps &
18+
Omit<
19+
FormElementProps<TextInputElementProps, 'input'>,
20+
'fieldsetProps' | 'legend' | 'legendProps'
21+
>;
22+
2023
const TextInputPrefix: FC<Pick<TextInputProps, 'prefix'>> = ({ prefix }) => (
2124
<div className="nhsuk-input__prefix" aria-hidden="true">
2225
{prefix}
@@ -29,10 +32,17 @@ const TextInputSuffix: FC<Pick<TextInputProps, 'suffix'>> = ({ suffix }) => (
2932
</div>
3033
);
3134

32-
export const TextInput = forwardRef<HTMLInputElement, TextInputProps>((props, forwardedRef) => (
33-
<FormGroup<TextInputProps> {...props} inputType="input">
34-
{({ width, className, code, error, type = 'text', prefix, suffix, ...rest }) => {
35-
const Input = (
35+
export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
36+
({ prefix, suffix, formGroupProps, ...props }, forwardedRef) => (
37+
<FormGroup<TextInputProps, 'input'>
38+
{...props}
39+
formGroupProps={{
40+
...formGroupProps,
41+
beforeInput: prefix ? () => <TextInputPrefix prefix={prefix} /> : undefined,
42+
afterInput: suffix ? () => <TextInputSuffix suffix={suffix} /> : undefined,
43+
}}
44+
>
45+
{({ width, className, code, error, type = 'text', prefix, suffix, ...rest }) => (
3646
<input
3747
className={classNames(
3848
'nhsuk-input',
@@ -45,19 +55,9 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>((props, fo
4555
type={type}
4656
{...rest}
4757
/>
48-
);
49-
50-
return prefix || suffix ? (
51-
<div className="nhsuk-input__wrapper">
52-
{prefix ? <TextInputPrefix prefix={prefix} /> : null}
53-
{Input}
54-
{suffix ? <TextInputSuffix suffix={suffix} /> : null}
55-
</div>
56-
) : (
57-
Input
58-
);
59-
}}
60-
</FormGroup>
61-
));
58+
)}
59+
</FormGroup>
60+
),
61+
);
6262

6363
TextInput.displayName = 'TextInput';

src/components/form-elements/text-input/__tests__/TextInput.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ describe('TextInput', () => {
149149
({ prefix, suffix }) => {
150150
const { container } = render(<TextInput prefix={prefix} suffix={suffix} />);
151151

152-
const prefixElement = container.querySelector('.nhsuk-input__wrapper > .nhsuk-input__prefix');
153-
const suffixElement = container.querySelector('.nhsuk-input__wrapper > .nhsuk-input__suffix');
152+
const prefixElement = container.querySelector('.nhsuk-input-wrapper > .nhsuk-input__prefix');
153+
const suffixElement = container.querySelector('.nhsuk-input-wrapper > .nhsuk-input__suffix');
154154

155155
if (prefix) {
156156
expect(prefixElement).not.toBeNull();
@@ -167,10 +167,10 @@ describe('TextInput', () => {
167167
}
168168

169169
if (!prefix && !suffix) {
170-
expect(container.querySelector('.nhsuk-input__wrapper')).toBeNull();
170+
expect(container.querySelector('.nhsuk-input-wrapper')).toBeNull();
171171
expect(container.querySelector('.nhsuk-input')).not.toBeNull();
172172
} else {
173-
expect(container.querySelector('.nhsuk-input__wrapper > .nhsuk-input')).not.toBeNull();
173+
expect(container.querySelector('.nhsuk-input-wrapper > .nhsuk-input')).not.toBeNull();
174174
}
175175
},
176176
);

0 commit comments

Comments
 (0)