Skip to content

Commit 799dbc2

Browse files
committed
Merge branch 'master' of github.com:NHSDigital/nhsuk-react-components
2 parents c72ca7d + d6b04ab commit 799dbc2

37 files changed

+1677
-1169
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nhsuk-react-components",
3-
"version": "1.1.3",
3+
"version": "1.1.4-test.3",
44
"author": {
55
"email": "[email protected]",
66
"name": "Thomas Judd-Cooper",
@@ -22,6 +22,7 @@
2222
"@types/enzyme": "^3.10.3",
2323
"@types/enzyme-adapter-react-16": "^1.0.5",
2424
"@types/jest": "^24.0.23",
25+
"@types/jest-axe": "^3.2.2",
2526
"@types/node": "^12.12.12",
2627
"@types/react": "^16.9.3",
2728
"@types/react-dom": "^16.9.3",
@@ -45,6 +46,7 @@
4546
"eslint-plugin-react": "^7.14.3",
4647
"eslint-plugin-react-hooks": "^1.7.0",
4748
"jest": "^24.9.0",
49+
"jest-axe": "^3.4.0",
4850
"nhsuk-frontend": "^3.0.2",
4951
"node-sass": "^4.12.0",
5052
"prettier": "^1.18.2",

src/components/checkboxes/Box.tsx

Lines changed: 0 additions & 89 deletions
This file was deleted.
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { createContext } from 'react';
22

33
export interface ICheckboxContext {
4-
isCheckbox: boolean;
54
name: string;
6-
getBoxId: () => string | undefined;
5+
getBoxId: (reference: string) => string | undefined;
6+
setConditional: (boxReference: string, hasConditional: boolean) => void;
7+
leaseReference: () => string;
8+
unleaseReference: (reference: string) => void;
79
}
810

911
export default createContext<ICheckboxContext>({
10-
isCheckbox: false,
1112
name: '',
1213
getBoxId: () => undefined,
14+
setConditional: () => {},
15+
leaseReference: () => '',
16+
unleaseReference: () => {},
1317
});

src/components/checkboxes/Checkboxes.tsx

Lines changed: 85 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,111 @@
1-
import React, { HTMLProps, PureComponent, SyntheticEvent } from 'react';
1+
import React, { HTMLProps, PureComponent } from 'react';
22
import classNames from 'classnames';
33
import { FormElementProps } from '../../util/types/FormTypes';
4-
import LabelBlock from '../../util/LabelBlock';
5-
import { generateRandomName } from '../../util/RandomName';
6-
import FormContext from '../form/FormContext';
7-
import CheckboxContext from './CheckboxContext';
8-
import Box, { BoxProps } from './Box';
9-
10-
interface CheckboxesEvent extends SyntheticEvent<HTMLInputElement> {
11-
target: HTMLInputElement;
12-
}
4+
import FormGroup from '../../util/FormGroup';
5+
import CheckboxContext, { ICheckboxContext } from './CheckboxContext';
6+
import Box from './components/Box';
7+
import { generateRandomName } from '../../util/RandomID';
138

149
interface CheckboxesProps extends HTMLProps<HTMLDivElement>, FormElementProps {
1510
idPrefix?: string;
16-
onChange?: (e: CheckboxesEvent) => any;
17-
}
18-
19-
interface CheckboxesState {
20-
name: string;
2111
}
2212

23-
interface Checkboxes {
24-
boxCount: number;
25-
}
13+
type CheckboxesState = {
14+
conditionalBoxes: Array<string>;
15+
};
2616

2717
class Checkboxes extends PureComponent<CheckboxesProps, CheckboxesState> {
28-
constructor(props: CheckboxesProps, ...rest: any[]) {
18+
private boxCount: number = 0;
19+
20+
private boxReferences: Array<string> = [];
21+
22+
private boxIds: Record<string, string> = {};
23+
24+
constructor(props: {}, ...rest: any[]) {
2925
super(props, ...rest);
3026
this.state = {
31-
name: props.name || generateRandomName('checkbox'),
27+
conditionalBoxes: [],
3228
};
33-
this.boxCount = 0;
3429
}
3530

36-
getBoxId = (): string | undefined => {
31+
leaseReference = (): string => {
32+
const reference = generateRandomName();
33+
if (this.boxReferences.includes(reference)) {
34+
return this.leaseReference();
35+
}
36+
this.boxReferences.push(reference);
37+
return reference;
38+
};
39+
40+
unleaseReference = (reference: string) => {
41+
this.boxReferences = this.boxReferences.filter(ref => ref !== reference);
42+
};
43+
44+
setConditional = (boxReference: string, hasConditional: boolean) => {
45+
this.setState(state => {
46+
const currentHasConditional = state.conditionalBoxes.includes(boxReference);
47+
if (currentHasConditional && hasConditional === false) {
48+
return {
49+
...state,
50+
conditionalBoxes: state.conditionalBoxes.filter(ref => ref !== boxReference),
51+
};
52+
}
53+
if (!currentHasConditional && hasConditional === true) {
54+
return {
55+
...state,
56+
conditionalBoxes: [...state.conditionalBoxes, boxReference],
57+
};
58+
}
59+
return state;
60+
});
61+
};
62+
63+
getBoxId = (id: string, reference: string): string => {
3764
const { idPrefix } = this.props;
38-
const { name } = this.state;
39-
if (!name && !idPrefix) {
40-
return undefined;
65+
if (reference in this.boxIds) {
66+
return this.boxIds[reference];
4167
}
4268
this.boxCount += 1;
43-
return `${idPrefix || name}-${this.boxCount}`;
69+
this.boxIds[reference] = `${idPrefix || id}-${this.boxCount}`;
70+
return this.boxIds[reference];
4471
};
4572

46-
static contextType = FormContext;
73+
resetBoxIds = () => {
74+
this.boxCount = 0;
75+
this.boxIds = {};
76+
};
4777

48-
static Box: React.FC<BoxProps> = Box;
78+
static Box = Box;
4979

5080
render() {
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;
64-
const { name } = this.state;
65-
const { isForm, setError } = this.context;
66-
67-
if (isForm) {
68-
setError(name, Boolean(error));
69-
}
70-
81+
const { children, ...rest } = this.props;
7182
return (
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
84-
className={classNames('nhsuk-checkboxes', className)}
85-
id={id}
86-
aria-describedby={hint ? `${id}--hint` : undefined}
87-
{...rest}
88-
>
89-
{children}
90-
</div>
91-
</CheckboxContext.Provider>
92-
</>
83+
<FormGroup<CheckboxesProps> inputType="checkboxes" {...rest}>
84+
{({ className, name, id, idPrefix, ...restRenderProps }) => {
85+
this.resetBoxIds();
86+
const containsConditional = this.state.conditionalBoxes.length > 0;
87+
const contextValue: ICheckboxContext = {
88+
name,
89+
getBoxId: reference => this.getBoxId(id, reference),
90+
setConditional: this.setConditional,
91+
leaseReference: this.leaseReference,
92+
unleaseReference: this.unleaseReference,
93+
};
94+
return (
95+
<div
96+
className={classNames(
97+
'nhsuk-checkboxes',
98+
{ 'nhsuk-checkboxes--conditional': containsConditional },
99+
className,
100+
)}
101+
id={id}
102+
{...restRenderProps}
103+
>
104+
<CheckboxContext.Provider value={contextValue}>{children}</CheckboxContext.Provider>
105+
</div>
106+
);
107+
}}
108+
</FormGroup>
93109
);
94110
}
95111
}

0 commit comments

Comments
 (0)