diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..82a9a73 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +.eslintrc.js \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..f307fb1 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "endOfLine": "lf", + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "printWidth": 100 +} diff --git a/examples/simple.js b/examples/simple.js index 7b944b2..e7c87dc 100644 --- a/examples/simple.js +++ b/examples/simple.js @@ -25,7 +25,6 @@ export default class Simple extends React.Component {
void; @@ -11,7 +11,7 @@ interface SwitchProps { checkedChildren?: React.ReactNode; unCheckedChildren?: React.ReactNode; onChange?: SwitchChangeEventHandler; - onMouseUp: MouseEventHandler; + onMouseUp: React.MouseEventHandler; onClick?: SwitchClickEventHandler; tabIndex?: number; checked?: boolean; @@ -22,143 +22,115 @@ interface SwitchProps { title?: string; } -interface SwitchState { - checked: boolean; -} - -class Switch extends Component { - private node: React.RefObject; +const Switch = React.forwardRef((props, ref) => { + const mergedRef = (ref as any) || React.createRef(); - static defaultProps = { - prefixCls: 'rc-switch', - checkedChildren: null, - unCheckedChildren: null, - className: '', - defaultChecked: false, - }; - - constructor(props) { - super(props); - let checked = false; - if ('checked' in props) { - checked = !!props.checked; - } else { - checked = !!props.defaultChecked; - } - this.state = { checked }; - this.node = React.createRef(); + let initChecked = false; + if ('checked' in props) { + initChecked = !!props.checked; + } else { + initChecked = !!props.defaultChecked; } + const [checked, setChecked] = React.useState(initChecked); - componentDidMount() { - const { autoFocus, disabled } = this.props; + React.useEffect(() => { + const { autoFocus, disabled } = props; if (autoFocus && !disabled) { - this.focus(); + focus(); } - } + }, [props.autoFocus, props.disabled]); - static getDerivedStateFromProps(nextProps) { - const { checked } = nextProps; - const newState: Partial = {}; - if ('checked' in nextProps) { - newState.checked = !!checked; + React.useEffect(() => { + if ('checked' in props) { + setChecked(!!props.checked); } - return newState; - } + }, [props.checked]); - setChecked(checked, e) { - const { disabled, onChange } = this.props; + const setInternalChecked = (checked, e) => { + const { disabled, onChange } = props; if (disabled) { return; } - if (!('checked' in this.props)) { - this.setState({ - checked, - }); + if (!('checked' in props)) { + setChecked(checked); } if (onChange) { onChange(checked, e); } - } + }; - handleClick = e => { - const { checked } = this.state; - const { onClick } = this.props; + const handleClick = e => { + const { onClick } = props; const newChecked = !checked; - this.setChecked(newChecked, e); + setInternalChecked(newChecked, e); if (onClick) { onClick(newChecked, e); } }; - handleKeyDown = e => { + const handleKeyDown = e => { if (e.keyCode === 37) { // Left - this.setChecked(false, e); + setInternalChecked(false, e); } else if (e.keyCode === 39) { // Right - this.setChecked(true, e); + setInternalChecked(true, e); } }; // Handle auto focus when click switch in Chrome - handleMouseUp = e => { - const { onMouseUp } = this.props; - this.blur(); - if (onMouseUp) { - onMouseUp(e); + const handleMouseUp = e => { + (mergedRef.current as any).blur(); + if (props.onMouseUp) { + props.onMouseUp(e); } }; - focus() { - if (this.node.current) { - this.node.current.focus(); - } - } - - blur() { - if (this.node.current) { - this.node.current.blur(); - } - } - - render() { - const { - className, - prefixCls, - disabled, - loadingIcon, - checkedChildren, - unCheckedChildren, - onChange, - ...restProps - } = this.props; - const { checked } = this.state; - const switchClassName = classNames({ - [className]: !!className, - [prefixCls]: true, - [`${prefixCls}-checked`]: checked, - [`${prefixCls}-disabled`]: disabled, - }); - return ( - - ); - } -} + const { + className, + prefixCls, + disabled, + loadingIcon, + checkedChildren, + unCheckedChildren, + onChange, + ...restProps + } = props; + + const switchClassName = classNames({ + [className]: !!className, + [prefixCls]: true, + [`${prefixCls}-checked`]: checked, + [`${prefixCls}-disabled`]: disabled, + }); + + return ( + + ); +}); + +Switch.displayName = 'Switch'; + +Switch.defaultProps = { + prefixCls: 'rc-switch', + checkedChildren: null, + unCheckedChildren: null, + className: '', + defaultChecked: false, +}; export default Switch; diff --git a/tests/index.spec.js b/tests/index.spec.js index 575aaa7..c88692d 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -4,35 +4,44 @@ import { mount } from 'enzyme'; import Switch from '../index'; describe('rc-switch', () => { - let switcher; - beforeEach(() => { - switcher = mount(); - }); + function createSwitch(props = {}) { + return mount( + } + unCheckedChildren={} + {...props} + />, + ); + } it('works', () => { - expect(switcher.state().checked).toBe(false); - switcher.simulate('click'); - expect(switcher.state().checked).toBe(true); + const wrapper = createSwitch(); + expect(wrapper.exists('.unchecked')).toBeTruthy(); + wrapper.simulate('click'); + expect(wrapper.exists('.checked')).toBeTruthy(); }); it('should be checked upon right key and unchecked on left key', () => { - expect(switcher.state().checked).toBe(false); - switcher.simulate('keydown', { keyCode: 39 }); - expect(switcher.state().checked).toBe(true); - switcher.simulate('keydown', { keyCode: 37 }); - expect(switcher.state().checked).toBe(false); + const wrapper = createSwitch(); + expect(wrapper.exists('.unchecked')).toBeTruthy(); + wrapper.simulate('keydown', { keyCode: 39 }); + expect(wrapper.exists('.checked')).toBeTruthy(); + wrapper.simulate('keydown', { keyCode: 37 }); + expect(wrapper.exists('.unchecked')).toBeTruthy(); }); it('should change from an initial checked state of true to false on click', () => { - const wrapper = mount(); - expect(wrapper.state().checked).toBe(true); + const onChange = jest.fn(); + const wrapper = createSwitch({ defaultChecked: true, onChange }); + expect(wrapper.exists('.checked')).toBeTruthy(); wrapper.simulate('click'); - expect(wrapper.state().checked).toBe(false); + expect(wrapper.exists('.unchecked')).toBeTruthy(); + expect(onChange.mock.calls.length).toBe(1); }); it('should support onClick', () => { const onClick = jest.fn(); - const wrapper = mount(); + const wrapper = createSwitch({ onClick }); wrapper.simulate('click'); expect(onClick).toHaveBeenCalledWith(true, expect.objectContaining({ type: 'click' })); expect(onClick.mock.calls.length).toBe(1); @@ -43,10 +52,10 @@ describe('rc-switch', () => { it('should not toggle when clicked in a disabled state', () => { const onChange = jest.fn(); - const wrapper = mount(); - expect(wrapper.state().checked).toBe(true); + const wrapper = createSwitch({ disabled: true, checked: true, onChange }); + expect(wrapper.exists('.checked')).toBeTruthy(); wrapper.simulate('click'); - expect(wrapper.state().checked).toBe(true); + expect(wrapper.exists('.checked')).toBeTruthy(); expect(onChange.mock.calls.length).toBe(0); }); @@ -59,8 +68,11 @@ describe('rc-switch', () => { const container = document.createElement('div'); document.body.appendChild(container); const handleFocus = jest.fn(); - const wrapper = mount(, { attachTo: container }); - wrapper.instance().focus(); + const ref = React.createRef(); + mount(, { + attachTo: container, + }); + ref.current.focus(); expect(handleFocus).toHaveBeenCalled(); }); @@ -68,9 +80,12 @@ describe('rc-switch', () => { const container = document.createElement('div'); document.body.appendChild(container); const handleBlur = jest.fn(); - const wrapper = mount(, { attachTo: container }); - wrapper.instance().focus(); - wrapper.instance().blur(); + const ref = React.createRef(); + mount(, { + attachTo: container, + }); + ref.current.focus(); + ref.current.blur(); expect(handleBlur).toHaveBeenCalled(); }); @@ -81,4 +96,18 @@ describe('rc-switch', () => { mount(, { attachTo: container }); expect(handleFocus).toHaveBeenCalled(); }); + + it('disabled', () => { + const wrapper = createSwitch({ disabled: true }); + expect(wrapper.exists('.unchecked')).toBeTruthy(); + wrapper.simulate('keydown', { keyCode: 39 }); + expect(wrapper.exists('.unchecked')).toBeTruthy(); + }); + + it('onMouseUp', () => { + const onMouseUp = jest.fn(); + const wrapper = createSwitch({ onMouseUp }); + wrapper.simulate('mouseup'); + expect(onMouseUp).toHaveBeenCalled(); + }); });