Skip to content

Commit 5349a4c

Browse files
committed
change Checkbox/CheckboxGroup component to FC
1 parent fb44d30 commit 5349a4c

File tree

2 files changed

+136
-152
lines changed

2 files changed

+136
-152
lines changed

src/scripts/Checkbox.tsx

Lines changed: 29 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,42 @@
1-
import React, { Component, InputHTMLAttributes } from 'react';
1+
import React, { FC, InputHTMLAttributes, Ref, useContext } from 'react';
22
import classnames from 'classnames';
33
import { FormElement, FormElementProps } from './FormElement';
4+
import { CheckboxGroupContext, CheckboxValueType } from './CheckboxGroup';
45

6+
/**
7+
*
8+
*/
59
export type CheckboxProps = {
610
label?: string;
711
required?: boolean;
812
error?: FormElementProps['error'];
9-
totalCols?: number;
1013
cols?: number;
11-
grouped?: boolean;
1214
name?: string;
13-
value?: string | number;
15+
value?: CheckboxValueType;
1416
checked?: boolean;
1517
defaultChecked?: boolean;
16-
checkboxRef?: (node: HTMLLabelElement | null) => void;
18+
checkboxRef?: Ref<HTMLLabelElement>;
1719
} & InputHTMLAttributes<HTMLInputElement>;
1820

19-
export class Checkbox extends Component<CheckboxProps> {
20-
node: HTMLDivElement | HTMLLabelElement | null = null;
21-
22-
componentWillReceiveProps(nextProps: Readonly<CheckboxProps>) {
23-
if (this.node) {
24-
const input = this.node.getElementsByTagName('input')[0];
25-
if (
26-
nextProps.defaultChecked !== undefined &&
27-
nextProps.defaultChecked !== input.checked
28-
) {
29-
input.checked = nextProps.defaultChecked;
30-
}
31-
}
32-
}
33-
34-
renderCheckbox({ className, label, checkboxRef, ...props }: CheckboxProps) {
35-
const checkClassNames = classnames(className, 'slds-checkbox');
36-
return (
37-
<label
38-
ref={(node) => {
39-
this.node = node;
40-
if (checkboxRef) checkboxRef(node);
41-
}}
42-
className={checkClassNames}
43-
>
44-
<input type='checkbox' {...props} />
45-
<span className='slds-checkbox_faux' />
46-
<span className='slds-form-element__label'>{label}</span>
47-
</label>
48-
);
49-
}
50-
51-
render() {
52-
const { grouped, required, error, totalCols, cols, ...props } = this.props;
53-
const formElemProps = { required, error, totalCols, cols };
54-
return grouped ? (
55-
this.renderCheckbox(props)
56-
) : (
57-
<FormElement
58-
formElementRef={(node) => (this.node = node)}
59-
{...formElemProps}
60-
>
61-
{this.renderCheckbox(props)}
62-
</FormElement>
63-
);
64-
}
65-
}
21+
/**
22+
*
23+
*/
24+
export const Checkbox: FC<CheckboxProps> = (props) => {
25+
const { className, label, required, error, cols, checkboxRef, ...rprops } =
26+
props;
27+
const { grouped } = useContext(CheckboxGroupContext);
28+
const formElemProps = { required, error, cols };
29+
const checkClassNames = classnames(className, 'slds-checkbox');
30+
const check = (
31+
<label ref={checkboxRef} className={checkClassNames}>
32+
<input type='checkbox' {...rprops} />
33+
<span className='slds-checkbox_faux' />
34+
<span className='slds-form-element__label'>{label}</span>
35+
</label>
36+
);
37+
return grouped ? (
38+
check
39+
) : (
40+
<FormElement {...formElemProps}>{check}</FormElement>
41+
);
42+
};

src/scripts/CheckboxGroup.tsx

Lines changed: 107 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,122 @@
1-
import React, { FieldsetHTMLAttributes } from 'react';
1+
import React, {
2+
createContext,
3+
FC,
4+
FieldsetHTMLAttributes,
5+
useCallback,
6+
useContext,
7+
useMemo,
8+
useRef,
9+
} from 'react';
210
import classnames from 'classnames';
311
import { FormElementProps } from './FormElement';
12+
import { FieldSetColumnContext } from './FieldSet';
413

5-
export type CheckboxGroupProps<ValueType extends string | number> = {
14+
/**
15+
*
16+
*/
17+
export type CheckboxValueType = string | number;
18+
19+
/**
20+
*
21+
*/
22+
export const CheckboxGroupContext = createContext<{ grouped?: boolean }>({});
23+
24+
/**
25+
*
26+
*/
27+
export type CheckboxGroupProps = {
628
label?: string;
729
required?: boolean;
830
error?: FormElementProps['error'];
931
name?: string;
10-
totalCols?: number;
1132
cols?: number;
12-
onValueChange?: (values: ValueType[]) => void;
33+
onValueChange?: (values: CheckboxValueType[]) => void;
1334
} & FieldsetHTMLAttributes<HTMLFieldSetElement>;
1435

15-
export class CheckboxGroup<
16-
ValueType extends string | number
17-
> extends React.Component<CheckboxGroupProps<ValueType>> {
18-
static isFormElement = true;
19-
20-
private nodes: { [key: string]: any } = {};
21-
22-
constructor(props: Readonly<CheckboxGroupProps<ValueType>>) {
23-
super(props);
24-
25-
this.onChange = this.onChange.bind(this);
26-
this.renderControl = this.renderControl.bind(this);
27-
}
36+
/**
37+
*
38+
*/
39+
export const CheckboxGroup: FC<CheckboxGroupProps> = (props) => {
40+
const {
41+
className,
42+
label,
43+
cols,
44+
style,
45+
required,
46+
error,
47+
onValueChange,
48+
onChange: onChange_,
49+
children,
50+
...rprops
51+
} = props;
52+
const { totalCols } = useContext(FieldSetColumnContext);
53+
const controlElRef = useRef<HTMLDivElement | null>(null);
2854

29-
onChange(e: React.FormEvent<HTMLFieldSetElement>) {
30-
if (this.props.onValueChange) {
31-
const values: ValueType[] = [];
32-
React.Children.forEach(this.props.children, (check: any, i) => {
33-
const el = check.props.ref || this.nodes[`check${i + 1}`];
34-
const checkEl = el && el.querySelector('input[type=checkbox]');
35-
if (checkEl && checkEl.checked) {
36-
values.push(check.props.value);
55+
const onChange = useCallback(
56+
(e: React.FormEvent<HTMLFieldSetElement>) => {
57+
if (onValueChange) {
58+
const checkboxes =
59+
controlElRef.current?.querySelectorAll<HTMLInputElement>(
60+
'input[type=checkbox]'
61+
);
62+
if (!checkboxes) {
63+
return;
3764
}
38-
});
39-
this.props.onValueChange(values);
40-
}
41-
if (this.props.onChange) {
42-
this.props.onChange(e);
43-
}
44-
}
65+
const values = [...checkboxes]
66+
.filter((checkbox) => checkbox.checked)
67+
.map((checkbox) => checkbox.value);
68+
onValueChange?.(values);
69+
}
70+
onChange_?.(e);
71+
},
72+
[onChange_, onValueChange]
73+
);
4574

46-
renderControl(checkbox: any, i: number) {
47-
const props: any = { grouped: true };
48-
if (checkbox.props.ref) {
49-
props.ref = checkbox.props.ref;
50-
} else {
51-
props.checkboxRef = (node: any) => (this.nodes[`check${i + 1}`] = node);
52-
}
53-
if (this.props.name) {
54-
props.name = this.props.name;
55-
}
56-
return React.cloneElement(checkbox, props);
57-
}
75+
const grpClassNames = classnames(
76+
className,
77+
'slds-form-element',
78+
{
79+
'slds-has-error': error,
80+
'slds-is-required': required,
81+
},
82+
typeof totalCols === 'number'
83+
? `slds-size_${cols || 1}-of-${totalCols}`
84+
: null
85+
);
86+
const grpStyles =
87+
typeof totalCols === 'number'
88+
? { display: 'inline-block', ...style }
89+
: style;
90+
const errorMessage = error
91+
? typeof error === 'string'
92+
? error
93+
: typeof error === 'object'
94+
? error.message
95+
: undefined
96+
: undefined;
97+
const grpCtx = useMemo(() => ({ grouped: true }), []);
5898

59-
render() {
60-
const {
61-
className,
62-
label,
63-
totalCols,
64-
cols,
65-
style,
66-
required,
67-
error,
68-
children,
69-
...props
70-
} = this.props;
71-
const grpClassNames = classnames(
72-
className,
73-
'slds-form-element',
74-
{
75-
'slds-has-error': error,
76-
'slds-is-required': required,
77-
},
78-
typeof totalCols === 'number'
79-
? `slds-size_${cols || 1}-of-${totalCols}`
80-
: null
81-
);
82-
const grpStyles =
83-
typeof totalCols === 'number'
84-
? { display: 'inline-block', ...style }
85-
: style;
86-
const errorMessage = error
87-
? typeof error === 'string'
88-
? error
89-
: typeof error === 'object'
90-
? error.message
91-
: undefined
92-
: undefined;
99+
return (
100+
<fieldset
101+
className={grpClassNames}
102+
style={grpStyles}
103+
{...rprops}
104+
onChange={onChange}
105+
>
106+
<legend className='slds-form-element__label'>
107+
{label}
108+
{required ? <abbr className='slds-required'>*</abbr> : undefined}
109+
</legend>
110+
<div className='slds-form-element__control' ref={controlElRef}>
111+
<CheckboxGroupContext.Provider value={grpCtx}>
112+
{children}
113+
</CheckboxGroupContext.Provider>
114+
{errorMessage ? (
115+
<div className='slds-form-element__help'>{errorMessage}</div>
116+
) : undefined}
117+
</div>
118+
</fieldset>
119+
);
120+
};
93121

94-
delete props.onChange;
95-
return (
96-
<fieldset
97-
className={grpClassNames}
98-
style={grpStyles}
99-
onChange={this.onChange}
100-
{...props}
101-
>
102-
<legend className='slds-form-element__label'>
103-
{label}
104-
{required ? <abbr className='slds-required'>*</abbr> : undefined}
105-
</legend>
106-
<div className='slds-form-element__control'>
107-
{React.Children.map(children, this.renderControl)}
108-
{errorMessage ? (
109-
<div className='slds-form-element__help'>{errorMessage}</div>
110-
) : undefined}
111-
</div>
112-
</fieldset>
113-
);
114-
}
115-
}
122+
(CheckboxGroup as unknown as { isFormElement?: boolean }).isFormElement = true;

0 commit comments

Comments
 (0)