Skip to content

Commit de80629

Browse files
committed
fix(Switch): checked derived state, keyboard accessibility
- fix(Switch): checked props and state out of sync - thanks @gravitymedianet @jinixx - fix(Switch): does not provide any keyboard accessibility - thanks @roastery-zz close #44
1 parent 2adf1f8 commit de80629

File tree

2 files changed

+42
-13
lines changed

2 files changed

+42
-13
lines changed

src/Switch.js

+40-11
Original file line numberDiff line numberDiff line change
@@ -45,35 +45,53 @@ const defaultProps = {
4545
class AppSwitch extends Component {
4646
constructor(props) {
4747
super(props);
48-
this.onChange = this.onChange.bind(this);
48+
this.handleChange = this.handleChange.bind(this);
49+
this.handleKeyDown = this.handleKeyDown.bind(this);
50+
this.handleKeyUp = this.handleKeyUp.bind(this);
4951
this.state = {
5052
checked: this.props.defaultChecked || this.props.checked,
5153
selected: []
5254
};
5355
}
5456

55-
onChange(event) {
56-
const target = event.target;
57+
toggleState(check) {
5758
this.setState({
58-
checked: target.checked,
59+
checked: check
5960
})
61+
}
62+
63+
handleChange(event) {
64+
const target = event.target;
65+
this.toggleState(target.checked)
6066

6167
if (this.props.onChange) {
6268
this.props.onChange(event);
6369
}
6470
}
6571

66-
componentDidUpdate(prevProps) {
67-
if (this.props.checked !== prevProps.checked) {
68-
this.setState({
69-
checked: this.props.checked
70-
})
72+
handleKeyDown(event) {
73+
if (event.key === ' ') {
74+
event.preventDefault();
75+
}
76+
}
77+
78+
handleKeyUp(event) {
79+
if (event.key === 'Enter' || event.key === ' ') {
80+
this.toggleState(!this.state.checked);
81+
}
82+
}
83+
84+
componentDidUpdate(prevProps, prevState) {
85+
if (this.props.checked !== prevState.checked) {
86+
this.toggleState(this.props.checked)
7187
}
7288
}
7389

7490
render() {
7591
const { className, disabled, color, name, label, outline, size, required, type, value, dataOn, dataOff, variant, ...attributes } = this.props;
7692

93+
const tabindex = attributes.tabIndex
94+
delete attributes.tabIndex
7795
delete attributes.checked
7896
delete attributes.defaultChecked
7997
delete attributes.onChange
@@ -98,8 +116,19 @@ class AppSwitch extends Component {
98116
);
99117

100118
return (
101-
<label className={classes}>
102-
<input type={type} className={inputClasses} onChange={this.onChange} checked={this.state.checked} name={name} required={required} disabled={disabled} value={value} {...attributes} />
119+
<label className={classes} tabIndex={tabindex} onKeyUp={this.handleKeyUp} onKeyDown={this.handleKeyDown}>
120+
<input type={type}
121+
className={inputClasses}
122+
onChange={this.handleChange}
123+
checked={this.state.checked}
124+
name={name}
125+
required={required}
126+
disabled={disabled}
127+
value={value}
128+
aria-checked={this.state.checked}
129+
aria-disabled={disabled}
130+
aria-readonly={disabled}
131+
{...attributes} />
103132
<span className={sliderClasses} data-checked={dataOn} data-unchecked={dataOff}></span>
104133
</label>
105134
);

tests/Switch.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ describe('AppSwitch', () => {
6060
expect(onChangeMock.called).toBe(true);
6161
});
6262

63-
it('should call onChange()', () => {
64-
const onChange = spy(AppSwitch.prototype, 'onChange');
63+
it('should call handleChange()', () => {
64+
const onChange = spy(AppSwitch.prototype, 'handleChange');
6565
const event = { target: { checked: true } };
6666
const wrapper = shallow(<AppSwitch label size="lg" />);
6767
expect(wrapper.find('input').props().checked).toBe(false);

0 commit comments

Comments
 (0)