|
1 | | -import React, { HTMLProps, PureComponent, SyntheticEvent } from 'react'; |
| 1 | +import React, { HTMLProps, PureComponent } from 'react'; |
2 | 2 | import classNames from 'classnames'; |
3 | 3 | 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'; |
13 | 8 |
|
14 | 9 | interface CheckboxesProps extends HTMLProps<HTMLDivElement>, FormElementProps { |
15 | 10 | idPrefix?: string; |
16 | | - onChange?: (e: CheckboxesEvent) => any; |
17 | | -} |
18 | | - |
19 | | -interface CheckboxesState { |
20 | | - name: string; |
21 | 11 | } |
22 | 12 |
|
23 | | -interface Checkboxes { |
24 | | - boxCount: number; |
25 | | -} |
| 13 | +type CheckboxesState = { |
| 14 | + conditionalBoxes: Array<string>; |
| 15 | +}; |
26 | 16 |
|
27 | 17 | 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[]) { |
29 | 25 | super(props, ...rest); |
30 | 26 | this.state = { |
31 | | - name: props.name || generateRandomName('checkbox'), |
| 27 | + conditionalBoxes: [], |
32 | 28 | }; |
33 | | - this.boxCount = 0; |
34 | 29 | } |
35 | 30 |
|
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 => { |
37 | 64 | 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]; |
41 | 67 | } |
42 | 68 | this.boxCount += 1; |
43 | | - return `${idPrefix || name}-${this.boxCount}`; |
| 69 | + this.boxIds[reference] = `${idPrefix || id}-${this.boxCount}`; |
| 70 | + return this.boxIds[reference]; |
44 | 71 | }; |
45 | 72 |
|
46 | | - static contextType = FormContext; |
| 73 | + resetBoxIds = () => { |
| 74 | + this.boxCount = 0; |
| 75 | + this.boxIds = {}; |
| 76 | + }; |
47 | 77 |
|
48 | | - static Box: React.FC<BoxProps> = Box; |
| 78 | + static Box = Box; |
49 | 79 |
|
50 | 80 | 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; |
71 | 82 | 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> |
93 | 109 | ); |
94 | 110 | } |
95 | 111 | } |
|
0 commit comments