[change] modernize CheckBox

Rewrite CheckBox to use function components and hooks.
Rewrite the tests to replace enzyme with testing-library.
This commit is contained in:
Nicolas Gallagher
2020-02-04 14:38:19 -08:00
parent 999c2ad122
commit fdf53df301
3 changed files with 75 additions and 83 deletions
@@ -2,59 +2,62 @@
import CheckBox from '../';
import React from 'react';
import { shallow } from 'enzyme';
import { render } from '@testing-library/react';
const checkboxSelector = 'input[type="checkbox"]';
function findCheckbox(container) {
return container.firstChild.querySelector('input');
}
describe('CheckBox', () => {
describe('disabled', () => {
test('when "false" a default checkbox is rendered', () => {
const component = shallow(<CheckBox />);
expect(component.find(checkboxSelector).prop('disabled')).toBe(undefined);
const { container } = render(<CheckBox />);
expect(findCheckbox(container).disabled).toBe(false);
});
test('when "true" a disabled checkbox is rendered', () => {
const component = shallow(<CheckBox disabled />);
expect(component.find(checkboxSelector).prop('disabled')).toBe(true);
const { container } = render(<CheckBox disabled />);
expect(findCheckbox(container).disabled).toBe(true);
});
});
describe('onChange', () => {
test('is called with the event object', () => {
const onChange = jest.fn();
const component = shallow(<CheckBox onChange={onChange} value={false} />);
component.find('input').simulate('change', { nativeEvent: { target: { checked: true } } });
expect(onChange).toHaveBeenCalledWith({
nativeEvent: { target: { checked: true }, value: true }
});
const { container } = render(<CheckBox onChange={onChange} value={false} />);
const checkbox = findCheckbox(container);
checkbox.click(); // Needed to get ReactDOM to trigger 'change' event
expect(onChange).toHaveBeenCalled();
});
});
describe('onValueChange', () => {
test('when value is "false" it receives "true"', () => {
const onValueChange = jest.fn();
const component = shallow(<CheckBox onValueChange={onValueChange} value={false} />);
component.find('input').simulate('change', { nativeEvent: { target: { checked: true } } });
const { container } = render(<CheckBox onValueChange={onValueChange} value={false} />);
const checkbox = findCheckbox(container);
checkbox.click(); // Needed to get ReactDOM to trigger 'change' event
expect(onValueChange).toHaveBeenCalledWith(true);
});
test('when value is "true" it receives "false"', () => {
const onValueChange = jest.fn();
const component = shallow(<CheckBox onValueChange={onValueChange} value />);
component.find('input').simulate('change', { nativeEvent: { target: { checked: false } } });
const { container } = render(<CheckBox onValueChange={onValueChange} value />);
const checkbox = findCheckbox(container);
checkbox.click(); // Needed to get ReactDOM to trigger 'change' event
expect(onValueChange).toHaveBeenCalledWith(false);
});
});
describe('value', () => {
test('when "false" an unchecked checkbox is rendered', () => {
const component = shallow(<CheckBox value={false} />);
expect(component.find(checkboxSelector).prop('checked')).toBe(false);
const { container } = render(<CheckBox value={false} />);
expect(findCheckbox(container).checked).toBe(false);
});
test('when "true" a checked checkbox is rendered', () => {
const component = shallow(<CheckBox value />);
expect(component.find(checkboxSelector).prop('checked')).toBe(true);
const { container } = render(<CheckBox value />);
expect(findCheckbox(container).checked).toBe(true);
});
});
});
+52 -63
View File
@@ -10,12 +10,11 @@
import type { ViewProps } from '../View';
import applyNativeMethods from '../../modules/applyNativeMethods';
import createElement from '../createElement';
import StyleSheet from '../StyleSheet';
import UIManager from '../UIManager';
import View from '../View';
import React from 'react';
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
type CheckBoxProps = {
...ViewProps,
@@ -26,74 +25,64 @@ type CheckBoxProps = {
value?: boolean
};
class CheckBox extends React.Component<CheckBoxProps> {
_checkboxElement: HTMLInputElement;
const CheckBox = forwardRef<CheckBoxProps, *>((props, ref) => {
const { color, disabled, onChange, onValueChange, style, value, ...other } = props;
static displayName = 'CheckBox';
const checkboxRef = useRef(null);
blur() {
UIManager.blur(this._checkboxElement);
}
useImperativeHandle(
ref,
() => {
return {
blur() {
UIManager.blur(checkboxRef.current);
},
focus() {
UIManager.focus(checkboxRef.current);
}
};
},
[checkboxRef]
);
focus() {
UIManager.focus(this._checkboxElement);
}
render() {
const {
color,
disabled,
/* eslint-disable */
onChange,
onValueChange,
/* eslint-enable */
style,
value,
...other
} = this.props;
const fakeControl = (
<View
style={[
styles.fakeControl,
value && styles.fakeControlChecked,
// custom color
value && color && { backgroundColor: color, borderColor: color },
disabled && styles.fakeControlDisabled,
value && disabled && styles.fakeControlCheckedAndDisabled
]}
/>
);
const nativeControl = createElement('input', {
checked: value,
disabled: disabled,
onChange: this._handleChange,
ref: this._setCheckboxRef,
style: [styles.nativeControl, styles.cursorInherit],
type: 'checkbox'
});
return (
<View {...other} style={[styles.root, style, disabled && styles.cursorDefault]}>
{fakeControl}
{nativeControl}
</View>
);
}
_handleChange = (event: Object) => {
const { onChange, onValueChange } = this.props;
function handleChange(event: Object) {
const value = event.nativeEvent.target.checked;
event.nativeEvent.value = value;
onChange && onChange(event);
onValueChange && onValueChange(value);
};
}
_setCheckboxRef = element => {
this._checkboxElement = element;
};
}
const fakeControl = (
<View
style={[
styles.fakeControl,
value && styles.fakeControlChecked,
// custom color
value && color && { backgroundColor: color, borderColor: color },
disabled && styles.fakeControlDisabled,
value && disabled && styles.fakeControlCheckedAndDisabled
]}
/>
);
const nativeControl = createElement('input', {
checked: value,
disabled: disabled,
onChange: handleChange,
ref: checkboxRef,
style: [styles.nativeControl, styles.cursorInherit],
type: 'checkbox'
});
return (
<View {...other} ref={ref} style={[styles.root, style, disabled && styles.cursorDefault]}>
{fakeControl}
{nativeControl}
</View>
);
});
CheckBox.displayName = 'CheckBox';
const styles = StyleSheet.create({
root: {
@@ -143,4 +132,4 @@ const styles = StyleSheet.create({
}
});
export default applyNativeMethods(CheckBox);
export default CheckBox;
+1 -1
View File
@@ -136,7 +136,7 @@ const Switch = forwardRef<SwitchProps, *>((props, ref) => {
});
return (
<View {...other} style={rootStyle}>
<View {...other} ref={ref} style={rootStyle}>
<View style={trackStyle} />
<View ref={thumbRef} style={thumbStyle} />
{nativeControl}