Skip to content

Commit 12d063b

Browse files
authored
Merge pull request #17 from NHSDigital/tjc-addLabelsToFormElements
Add labels to Form Elements
2 parents 3a1ce72 + 87f13ca commit 12d063b

File tree

35 files changed

+416
-220
lines changed

35 files changed

+416
-220
lines changed

src/components/button/Button.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,15 @@ export const Button: React.FC<ButtonProps> = ({
3535
)}
3636
disabled={disabled}
3737
aria-disabled={disabled ? 'true' : 'false'}
38-
type={type || 'submit'}
38+
type={type}
3939
{...rest}
4040
/>
4141
);
4242

43+
Button.defaultProps = {
44+
type: 'submit',
45+
};
46+
4347
export const ButtonLink: React.FC<ButtonLinkProps> = ({
4448
className,
4549
role,
@@ -58,15 +62,20 @@ export const ButtonLink: React.FC<ButtonLinkProps> = ({
5862
{ 'nhsuk-button--reverse': reverse },
5963
className,
6064
)}
61-
role={role || 'button'}
65+
role={role}
6266
aria-disabled={disabled ? 'true' : 'false'}
63-
draggable={draggable || false}
67+
draggable={draggable}
6468
{...rest}
6569
>
6670
{children}
6771
</a>
6872
);
6973

74+
ButtonLink.defaultProps = {
75+
role: 'button',
76+
draggable: false,
77+
};
78+
7079
const ButtonWrapper: React.FC<ButtonLinkProps | ButtonProps> = ({ href, as, ...rest }) => {
7180
if (as === 'a') {
7281
return <ButtonLink href={href} {...(rest as ButtonLinkProps)} />;

src/components/care-card/CareCard.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import React, { HTMLProps, createContext, useContext } from 'react';
22
import classNames from 'classnames';
3+
import { CareCardType } from '../../util/types/NHSUKTypes';
34
import HeadingLevel, { HeadingLevelType } from '../../util/HeadingLevel';
45

5-
type CareCardType = 'non-urgent' | 'urgent' | 'immediate';
6-
76
interface CareCardProps extends HTMLProps<HTMLDivElement> {
87
type: CareCardType;
98
}

src/components/care-card/__tests__/CareCard.tests.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ describe('CareCard', () => {
5454
});
5555

5656
it('renders with correct classNames', () => {
57-
const nonUrgentCard = shallow(<CareCard type="non-urgent"></CareCard>);
58-
const urgentCard = shallow(<CareCard type="urgent"></CareCard>);
59-
const immediateCard = shallow(<CareCard type="immediate"></CareCard>);
57+
const nonUrgentCard = shallow(<CareCard type="non-urgent" />);
58+
const urgentCard = shallow(<CareCard type="urgent" />);
59+
const immediateCard = shallow(<CareCard type="immediate" />);
6060

6161
expect(nonUrgentCard.hasClass('nhsuk-care-card--non-urgent')).toBeTruthy();
6262
expect(urgentCard.hasClass('nhsuk-care-card--urgent')).toBeTruthy();

src/components/checkboxes/Checkboxes.tsx

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import React, { HTMLProps, PureComponent, SyntheticEvent } from 'react';
22
import classNames from 'classnames';
3+
import { FormElementProps } from '../../util/types/FormTypes';
4+
import LabelBlock from '../../util/LabelBlock';
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';
79

810
interface CheckboxesEvent extends SyntheticEvent<HTMLInputElement> {
911
target: HTMLInputElement;
1012
}
1113

12-
interface CheckboxesProps extends HTMLProps<HTMLDivElement> {
13-
error?: string | boolean;
14+
interface CheckboxesProps extends HTMLProps<HTMLDivElement>, FormElementProps {
1415
idPrefix?: string;
1516
onChange?: (e: CheckboxesEvent) => any;
1617
}
@@ -27,7 +28,7 @@ class Checkboxes extends PureComponent<CheckboxesProps, CheckboxesState> {
2728
constructor(props: CheckboxesProps, ...rest: any[]) {
2829
super(props, ...rest);
2930
this.state = {
30-
name: props.name || `checkbox_${(Math.random() + 1).toString(36).substring(2, 7)}`,
31+
name: props.name || generateRandomName('checkbox'),
3132
};
3233
this.boxCount = 0;
3334
}
@@ -47,7 +48,19 @@ class Checkboxes extends PureComponent<CheckboxesProps, CheckboxesState> {
4748
static Box: React.FC<BoxProps> = Box;
4849

4950
render() {
50-
const { error, className, id, children, idPrefix, ...rest } = this.props;
51+
const {
52+
error,
53+
className,
54+
id,
55+
children,
56+
idPrefix,
57+
label,
58+
labelProps,
59+
errorProps,
60+
hint,
61+
hintProps,
62+
...rest
63+
} = this.props;
5164
const { name } = this.state;
5265
const { isForm, setError } = this.context;
5366

@@ -56,14 +69,22 @@ class Checkboxes extends PureComponent<CheckboxesProps, CheckboxesState> {
5669
}
5770

5871
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>
72+
<>
73+
<LabelBlock
74+
elementId={id}
75+
label={label}
76+
labelProps={labelProps}
77+
error={error}
78+
errorProps={errorProps}
79+
hint={hint}
80+
hintProps={hintProps}
81+
/>
82+
<CheckboxContext.Provider value={{ isCheckbox: true, name, getBoxId: this.getBoxId }}>
83+
<div className={classNames('nhsuk-checkboxes', className)} id={id} {...rest}>
84+
{children}
85+
</div>
86+
</CheckboxContext.Provider>
87+
</>
6788
);
6889
}
6990
}

src/components/contents-list/ContentsList.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ interface ContentsListItemProps extends HTMLProps<HTMLAnchorElement> {
88
const ContentsListItem: React.FC<ContentsListItemProps> = ({ className, current, ...rest }) => (
99
<li className={classNames('nhsuk-contents-list__item', className)}>
1010
{current ? (
11-
<span className="nhsuk-contents-list__current" {...rest}></span>
11+
<span className="nhsuk-contents-list__current" {...rest} />
1212
) : (
13-
<a className="nhsuk-contents-list__link" {...rest}></a>
13+
<a className="nhsuk-contents-list__link" {...rest} />
1414
)}
1515
</li>
1616
);

src/components/date-input/DateInput.tsx

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ 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 { FormElementProps } from '../../util/types/FormTypes';
12+
import { generateRandomName } from '../../util/RandomName';
13+
import LabelBlock from '../../util/LabelBlock';
1414

1515
interface IDateInputContext {
1616
isDateInput: boolean;
@@ -96,15 +96,15 @@ DateInputInput.defaultProps = {
9696
pattern: '[0-9]*',
9797
};
9898

99-
const DateInputDay: React.FC<HTMLProps<HTMLInputElement>> = props => (
99+
const DateInputDay: React.FC<Omit<DateInputInputProps, 'dateInputType'>> = props => (
100100
<DateInputInput dateInputType="Day" {...props} />
101101
);
102102

103-
const DateInputMonth: React.FC<HTMLProps<HTMLInputElement>> = props => (
103+
const DateInputMonth: React.FC<Omit<DateInputInputProps, 'dateInputType'>> = props => (
104104
<DateInputInput dateInputType="Month" {...props} />
105105
);
106106

107-
const DateInputYear: React.FC<HTMLProps<HTMLInputElement>> = props => (
107+
const DateInputYear: React.FC<Omit<DateInputInputProps, 'dateInputType'>> = props => (
108108
<DateInputInput dateInputType="Year" {...props} />
109109
);
110110

@@ -123,13 +123,10 @@ type DateInputValue = {
123123
year: string;
124124
};
125125

126-
interface DateInputProps extends Omit<HTMLProps<HTMLDivElement>, 'onChange'> {
126+
interface DateInputProps extends Omit<HTMLProps<HTMLDivElement>, 'onChange'>, FormElementProps {
127127
autoSelectNext?: boolean;
128128
onChange?: (e: DateInputEvent) => any;
129129
autoCompletePrefix?: string;
130-
error?: string;
131-
hint?: string;
132-
labelHtmlFor?: string;
133130
}
134131

135132
type DateInputState = {
@@ -146,7 +143,7 @@ class DateInput extends PureComponent<DateInputProps, DateInputState> {
146143
constructor(props: DateInputProps, ...rest: any[]) {
147144
super(props, ...rest);
148145
this.state = {
149-
name: props.name || `dateinput_${(Math.random() + 1).toString(36).substring(2, 7)}`,
146+
name: props.name || generateRandomName('dateinput'),
150147
value: { day: '', month: '', year: '' },
151148
};
152149
this.monthRef = null;
@@ -211,10 +208,12 @@ class DateInput extends PureComponent<DateInputProps, DateInputState> {
211208
children,
212209
autoSelectNext,
213210
autoCompletePrefix,
214-
labelHtmlFor,
215211
hint,
216212
error,
217213
label,
214+
labelProps,
215+
errorProps,
216+
hintProps,
218217
...rest
219218
} = this.props;
220219
const { name } = this.state;
@@ -228,13 +227,20 @@ class DateInput extends PureComponent<DateInputProps, DateInputState> {
228227

229228
return (
230229
<>
231-
{label ? <Label htmlFor={labelHtmlFor || id}>{label}</Label> : null}
232-
{hint ? <Hint>{hint}</Hint> : null}
233-
{error ? <ErrorMessage>{error}</ErrorMessage> : null}
230+
<LabelBlock
231+
elementId={id}
232+
label={label}
233+
labelProps={labelProps}
234+
hint={hint}
235+
hintProps={hintProps}
236+
error={error}
237+
errorProps={errorProps}
238+
/>
234239
<div
240+
id={id}
235241
className={classNames('nhsuk-date-input', className)}
236-
{...rest}
237242
onChange={this.onChange}
243+
{...rest}
238244
>
239245
<DateInputContext.Provider value={contextValue}>
240246
{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: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
import React, { HTMLProps } from 'react';
22
import classNames from 'classnames';
3+
import { NHSUKSize } from '../../util/types/NHSUKTypes';
34
import HeadingLevel, { HeadingLevelType } from '../../util/HeadingLevel';
45

5-
interface LegendProps extends HTMLProps<HTMLLegendElement> {
6+
interface LegendProps extends Omit<HTMLProps<HTMLLegendElement>, 'size'> {
67
isPageHeading?: boolean;
78
headingLevel?: HeadingLevelType;
9+
size?: NHSUKSize;
810
}
911

1012
const Legend: React.FC<LegendProps> = ({
1113
className,
1214
children,
1315
isPageHeading,
1416
headingLevel,
17+
size,
1518
...rest
1619
}) => (
1720
<legend
1821
className={classNames(
1922
'nhsuk-fieldset__legend',
2023
{
21-
'nhsuk-fieldset__legend--xl': isPageHeading,
24+
'nhsuk-fieldset__legend--xl': isPageHeading && !size,
2225
},
26+
{ [`nhsuk-fieldset__legend--${size}`]: size },
2327
className,
2428
)}
2529
{...rest}

src/components/header/Header.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const BaseHeaderLogo: React.FC<OrganisationalLogoProps> = props => {
2020
};
2121

2222
const HeaderContainer: React.FC<HTMLProps<HTMLDivElement>> = ({ className, ...rest }) => (
23-
<Container className={classNames('nhsuk-header__container', className)} {...rest}></Container>
23+
<Container className={classNames('nhsuk-header__container', className)} {...rest} />
2424
);
2525

2626
interface HeaderProps extends HTMLProps<HTMLDivElement> {
@@ -41,12 +41,19 @@ interface HeaderState {
4141

4242
class Header extends PureComponent<HeaderProps, HeaderState> {
4343
static Logo = BaseHeaderLogo;
44+
4445
static Search = Search;
46+
4547
static Nav = Nav;
48+
4649
static NavItem = NavItem;
50+
4751
static Container = HeaderContainer;
52+
4853
static Content = Content;
54+
4955
static MenuToggle = MenuToggle;
56+
5057
static ServiceName = TransactionalServiceName;
5158

5259
static defaultProps = {
@@ -72,11 +79,11 @@ class Header extends PureComponent<HeaderProps, HeaderState> {
7279
};
7380

7481
toggleMenu = () => {
75-
this.setState({ menuOpen: !this.state.menuOpen });
82+
this.setState(state => ({ menuOpen: !state.menuOpen }));
7683
};
7784

7885
toggleSearch = () => {
79-
this.setState({ searchOpen: !this.state.searchOpen });
86+
this.setState(state => ({ searchOpen: !state.searchOpen }));
8087
};
8188

8289
render() {

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;

0 commit comments

Comments
 (0)