Skip to content

Commit 0551b95

Browse files
committed
1. Bind error, errorProps, label, labelProps, hint, hintProps consistently to each form component.
2. Add sizing selections to the Label component ("s", "m" etc.) 3. Add sizing selections to the Fieldset.Legend component
1 parent 8339a6e commit 0551b95

File tree

11 files changed

+233
-113
lines changed

11 files changed

+233
-113
lines changed

src/components/checkboxes/Checkboxes.tsx

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
import React, { HTMLProps, PureComponent, SyntheticEvent } from 'react';
22
import classNames from 'classnames';
3+
import LabelBlock from '../utils/LabelBlock';
4+
import { HintProps } from '../hint/Hint';
5+
import { generateRandomName } from '../../util/RandomName';
36
import FormContext from '../form/FormContext';
4-
import ErrorMessage from '../error-message';
57
import CheckboxContext from './CheckboxContext';
68
import Box, { BoxProps } from './Box';
9+
import { LabelProps } from '../label/Label';
10+
import { ErrorMessageProps } from '../error-message/ErrorMessage';
711

812
interface CheckboxesEvent extends SyntheticEvent<HTMLInputElement> {
913
target: HTMLInputElement;
1014
}
1115

1216
interface CheckboxesProps extends HTMLProps<HTMLDivElement> {
13-
error?: string | boolean;
1417
idPrefix?: string;
1518
onChange?: (e: CheckboxesEvent) => any;
19+
label?: string;
20+
labelProps?: LabelProps;
21+
hint?: string;
22+
hintProps?: HintProps;
23+
error?: string | boolean;
24+
errorProps?: ErrorMessageProps;
1625
}
1726

1827
interface CheckboxesState {
@@ -27,7 +36,7 @@ class Checkboxes extends PureComponent<CheckboxesProps, CheckboxesState> {
2736
constructor(props: CheckboxesProps, ...rest: any[]) {
2837
super(props, ...rest);
2938
this.state = {
30-
name: props.name || `checkbox_${(Math.random() + 1).toString(36).substring(2, 7)}`,
39+
name: props.name || generateRandomName('checkbox'),
3140
};
3241
this.boxCount = 0;
3342
}
@@ -47,7 +56,19 @@ class Checkboxes extends PureComponent<CheckboxesProps, CheckboxesState> {
4756
static Box: React.FC<BoxProps> = Box;
4857

4958
render() {
50-
const { error, className, id, children, idPrefix, ...rest } = this.props;
59+
const {
60+
error,
61+
className,
62+
id,
63+
children,
64+
idPrefix,
65+
label,
66+
labelProps,
67+
errorProps,
68+
hint,
69+
hintProps,
70+
...rest
71+
} = this.props;
5172
const { name } = this.state;
5273
const { isForm, setError } = this.context;
5374

@@ -56,14 +77,22 @@ class Checkboxes extends PureComponent<CheckboxesProps, CheckboxesState> {
5677
}
5778

5879
return (
59-
<CheckboxContext.Provider value={{ isCheckbox: true, name, getBoxId: this.getBoxId }}>
60-
<div className={classNames('nhsuk-checkboxes', className)} id={id} {...rest}>
61-
{error && typeof error === 'string' ? (
62-
<ErrorMessage id={id ? `${id}--error` : undefined}>{error}</ErrorMessage>
63-
) : null}
64-
{children}
65-
</div>
66-
</CheckboxContext.Provider>
80+
<>
81+
<LabelBlock
82+
elementId={id}
83+
label={label}
84+
labelProps={labelProps}
85+
error={error}
86+
errorProps={errorProps}
87+
hint={hint}
88+
hintProps={hintProps}
89+
/>
90+
<CheckboxContext.Provider value={{ isCheckbox: true, name, getBoxId: this.getBoxId }}>
91+
<div className={classNames('nhsuk-checkboxes', className)} id={id} {...rest}>
92+
{children}
93+
</div>
94+
</CheckboxContext.Provider>
95+
</>
6796
);
6897
}
6998
}

src/components/date-input/DateInput.tsx

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import React, {
88
SyntheticEvent,
99
} from 'react';
1010
import classNames from 'classnames';
11-
import ErrorMessage from '../error-message';
12-
import Hint from '../hint';
13-
import Label from '../label';
11+
import { generateRandomName } from '../../util/RandomName';
12+
import { HintProps } from '../hint/Hint';
13+
import { ErrorMessageProps } from '../error-message/ErrorMessage';
14+
import { LabelProps } from '../label/Label';
15+
import LabelBlock from '../utils/LabelBlock';
1416

1517
interface IDateInputContext {
1618
isDateInput: boolean;
@@ -127,9 +129,12 @@ interface DateInputProps extends Omit<HTMLProps<HTMLDivElement>, 'onChange'> {
127129
autoSelectNext?: boolean;
128130
onChange?: (e: DateInputEvent) => any;
129131
autoCompletePrefix?: string;
132+
label?: string;
133+
labelProps?: LabelProps;
130134
error?: string;
135+
errorProps?: ErrorMessageProps;
131136
hint?: string;
132-
labelHtmlFor?: string;
137+
hintProps?: HintProps;
133138
}
134139

135140
type DateInputState = {
@@ -146,7 +151,7 @@ class DateInput extends PureComponent<DateInputProps, DateInputState> {
146151
constructor(props: DateInputProps, ...rest: any[]) {
147152
super(props, ...rest);
148153
this.state = {
149-
name: props.name || `dateinput_${(Math.random() + 1).toString(36).substring(2, 7)}`,
154+
name: props.name || generateRandomName('dateinput'),
150155
value: { day: '', month: '', year: '' },
151156
};
152157
this.monthRef = null;
@@ -211,10 +216,12 @@ class DateInput extends PureComponent<DateInputProps, DateInputState> {
211216
children,
212217
autoSelectNext,
213218
autoCompletePrefix,
214-
labelHtmlFor,
215219
hint,
216220
error,
217221
label,
222+
labelProps,
223+
errorProps,
224+
hintProps,
218225
...rest
219226
} = this.props;
220227
const { name } = this.state;
@@ -228,13 +235,20 @@ class DateInput extends PureComponent<DateInputProps, DateInputState> {
228235

229236
return (
230237
<>
231-
{label ? <Label htmlFor={labelHtmlFor || id}>{label}</Label> : null}
232-
{hint ? <Hint>{hint}</Hint> : null}
233-
{error ? <ErrorMessage>{error}</ErrorMessage> : null}
238+
<LabelBlock
239+
elementId={id}
240+
label={label}
241+
labelProps={labelProps}
242+
hint={hint}
243+
hintProps={hintProps}
244+
error={error}
245+
errorProps={errorProps}
246+
/>
234247
<div
248+
id={id}
235249
className={classNames('nhsuk-date-input', className)}
236-
{...rest}
237250
onChange={this.onChange}
251+
{...rest}
238252
>
239253
<DateInputContext.Provider value={contextValue}>
240254
{children || (

src/components/error-message/ErrorMessage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { HTMLProps } from 'react';
22
import classNames from 'classnames';
33

4-
interface ErrorMessageProps extends HTMLProps<HTMLSpanElement> {
4+
export interface ErrorMessageProps extends HTMLProps<HTMLSpanElement> {
55
visuallyHiddenText?: false | string;
66
}
77

src/components/fieldset/Fieldset.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,26 @@ import React, { HTMLProps } from 'react';
22
import classNames from 'classnames';
33
import HeadingLevel, { HeadingLevelType } from '../../util/HeadingLevel';
44

5-
interface LegendProps extends HTMLProps<HTMLLegendElement> {
5+
interface LegendProps extends Omit<HTMLProps<HTMLLegendElement>, 'size'> {
66
isPageHeading?: boolean;
77
headingLevel?: HeadingLevelType;
8+
size?: 's' | 'm' | 'l' | 'xl';
89
}
910

10-
const Legend: React.FC<LegendProps> = ({ className, children, isPageHeading, headingLevel }) => (
11+
const Legend: React.FC<LegendProps> = ({
12+
className,
13+
children,
14+
isPageHeading,
15+
headingLevel,
16+
size,
17+
}) => (
1118
<legend
1219
className={classNames(
1320
'nhsuk-fieldset__legend',
1421
{
15-
'nhsuk-fieldset__legend--xl': isPageHeading,
22+
'nhsuk-fieldset__legend--xl': isPageHeading && !size,
1623
},
24+
{ [`nhsuk-fieldset__legend--${size}`]: size },
1725
className,
1826
)}
1927
>
@@ -36,7 +44,7 @@ interface Fieldset extends React.FC<HTMLProps<HTMLFieldSetElement>> {
3644
}
3745

3846
const Fieldset: Fieldset = ({ className, ...rest }) => (
39-
<fieldset className={classNames('nhsuk-fieldset', className)} {...rest}></fieldset>
47+
<fieldset className={classNames('nhsuk-fieldset', className)} {...rest} />
4048
);
4149

4250
Fieldset.Legend = Legend;

src/components/hint/Hint.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import React, { HTMLProps } from 'react';
22
import classNames from 'classnames';
33

4-
const Hint: React.FC<HTMLProps<HTMLSpanElement>> = ({ className, ...rest }) => (
5-
<span className={classNames('nhsuk-hint', className)} {...rest}></span>
4+
export type HintProps = HTMLProps<HTMLSpanElement>;
5+
6+
const Hint: React.FC<HintProps> = ({ className, ...rest }) => (
7+
<span className={classNames('nhsuk-hint', className)} {...rest} />
68
);
79

810
export default Hint;

src/components/input/Input.tsx

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,37 @@
11
import React, { HTMLProps, useState, useEffect } from 'react';
22
import classNames from 'classnames';
3-
import Hint from '../hint';
3+
import { generateRandomName } from '../../util/RandomName';
4+
import LabelBlock from '../utils/LabelBlock';
5+
import { LabelProps } from '../label/Label';
6+
import { HintProps } from '../hint/Hint';
7+
import { ErrorMessageProps } from '../error-message/ErrorMessage';
48
import { useFormContext } from '../form/FormContext';
5-
import ErrorMessage from '../error-message';
69

710
interface InputProps extends HTMLProps<HTMLInputElement> {
8-
labelProps?: HTMLProps<HTMLLabelElement>;
11+
label?: string;
12+
labelProps?: LabelProps;
913
hint?: string;
14+
hintProps?: HintProps;
1015
error?: boolean | string;
16+
errorProps?: ErrorMessageProps;
1117
width?: '2' | '3' | '4' | '5' | '10';
1218
}
1319

1420
const Input: React.FC<InputProps> = ({
1521
className,
16-
children,
22+
label,
1723
id,
1824
ref,
1925
labelProps,
2026
hint,
21-
width,
27+
hintProps,
2228
error,
29+
errorProps,
30+
width,
2331
...rest
2432
}) => {
2533
const { isForm, setError } = useFormContext();
26-
const [name] = useState<string>(
27-
rest.name || `input_${(Math.random() + 1).toString(36).substring(2, 7)}`,
28-
);
34+
const [name] = useState<string>(rest.name || generateRandomName('input'));
2935

3036
useEffect(() => {
3137
if (isForm) {
@@ -35,19 +41,15 @@ const Input: React.FC<InputProps> = ({
3541

3642
return (
3743
<>
38-
{children ? (
39-
<label
40-
className={classNames('nhsuk-label', labelProps ? labelProps.className : undefined)}
41-
htmlFor={id}
42-
{...labelProps}
43-
>
44-
{children}
45-
</label>
46-
) : null}
47-
{hint ? <Hint id={id ? `${id}-label` : undefined}>{hint}</Hint> : null}
48-
{error && typeof error === 'string' ? (
49-
<ErrorMessage id={id ? `${id}-error` : undefined}>{error}</ErrorMessage>
50-
) : null}
44+
<LabelBlock
45+
elementId={id}
46+
label={label}
47+
labelProps={labelProps}
48+
hint={hint}
49+
hintProps={hintProps}
50+
error={error}
51+
errorProps={errorProps}
52+
/>
5153
<input
5254
className={classNames(
5355
'nhsuk-input',

src/components/label/Label.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
11
import React, { HTMLProps } from 'react';
22
import classNames from 'classnames';
33

4-
interface LabelProps extends HTMLProps<HTMLLabelElement> {
4+
export interface LabelProps extends Omit<HTMLProps<HTMLLabelElement>, 'size'> {
55
bold?: boolean;
66
isPageHeading?: boolean;
7+
size?: 's ' | 'm' | 'l' | 'xl';
78
}
89

9-
const BaseLabel: React.FC<LabelProps> = ({ className, bold, isPageHeading, ...rest }) => (
10+
const BaseLabel: React.FC<LabelProps> = ({ className, bold, size, isPageHeading, ...rest }) => (
1011
<label
1112
className={classNames(
1213
'nhsuk-label',
13-
{ 'nhsuk-label--s': bold },
14-
{ 'nhsuk-label--xl': isPageHeading },
14+
{ 'nhsuk-label--s': bold && !size },
15+
{ 'nhsuk-label--xl': isPageHeading && !size },
16+
{ [`nhsuk-label--${size}`]: size },
1517
className,
1618
)}
1719
{...rest}
18-
></label>
20+
/>
1921
);
2022

2123
const Label: React.FC<LabelProps> = ({ isPageHeading, ...rest }) => {
2224
if (isPageHeading) {
2325
return (
2426
<h1 className="nhsuk-label-wrapper">
25-
<BaseLabel isPageHeading {...rest}></BaseLabel>
27+
<BaseLabel isPageHeading {...rest} />
2628
</h1>
2729
);
2830
}
29-
return <BaseLabel {...rest}></BaseLabel>;
31+
return <BaseLabel {...rest} />;
3032
};
3133

3234
export default Label;

0 commit comments

Comments
 (0)