Skip to content

Commit 364a8b9

Browse files
committed
Fix accessibility issues with checkbox nodes
Resolves #281.
1 parent 7aaaf02 commit 364a8b9

File tree

5 files changed

+63
-19
lines changed

5 files changed

+63
-19
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# CHANGELOG
22

3+
## [v1.7.2](https://github.com/jakezatecky/react-checkbox-tree/compare/v1.7.1...v1.7.2) (2021-08-09)
4+
5+
### Bug Fixes
6+
7+
* [#281]: Fix accessibility issues with checkbox nodes
8+
39
## [v1.7.1](https://github.com/jakezatecky/react-checkbox-tree/compare/v1.7.0...v1.7.1) (2021-06-08)
410

511
### Build

src/js/TreeNode.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class TreeNode extends React.PureComponent {
5151
super(props);
5252

5353
this.onCheck = this.onCheck.bind(this);
54+
this.onCheckboxKeyPress = this.onCheckboxKeyPress.bind(this);
55+
this.onCheckboxKeyUp = this.onCheckboxKeyUp.bind(this);
5456
this.onClick = this.onClick.bind(this);
5557
this.onExpand = this.onExpand.bind(this);
5658
}
@@ -61,6 +63,23 @@ class TreeNode extends React.PureComponent {
6163
onCheck({ value, checked: this.getCheckState({ toggle: true }) });
6264
}
6365

66+
onCheckboxKeyPress(event) {
67+
const { which } = event;
68+
69+
// Prevent browser scroll when pressing space on the checkbox
70+
if (which === 32) {
71+
event.preventDefault();
72+
}
73+
}
74+
75+
onCheckboxKeyUp(event) {
76+
const { keyCode } = event;
77+
78+
if ([13, 32].includes(keyCode)) {
79+
this.onCheck();
80+
}
81+
}
82+
6483
onClick() {
6584
const {
6685
expandOnClick,
@@ -217,7 +236,15 @@ class TreeNode extends React.PureComponent {
217236
onClick={this.onCheck}
218237
onChange={() => {}}
219238
/>
220-
<span className="rct-checkbox">
239+
<span
240+
aria-checked={checked === 1}
241+
aria-disabled={disabled}
242+
className="rct-checkbox"
243+
role="checkbox"
244+
tabIndex={0}
245+
onKeyPress={this.onCheckboxKeyPress}
246+
onKeyUp={this.onCheckboxKeyUp}
247+
>
221248
{this.renderCheckboxIcon()}
222249
</span>
223250
{!clickable ? children : null}

src/less/react-checkbox-tree.less

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,6 @@
2828
line-height: normal;
2929
color: inherit;
3030

31-
&:focus {
32-
outline: none;
33-
}
34-
3531
&:disabled {
3632
cursor: not-allowed;
3733
}
@@ -49,7 +45,8 @@
4945
background: @rct-label-hover;
5046
}
5147

52-
&:active {
48+
&:active,
49+
&:focus {
5350
background: @rct-label-active;
5451
}
5552
}
@@ -127,6 +124,7 @@
127124
}
128125

129126
.rct-collapse {
127+
align-self: stretch;
130128
border: 0;
131129
background: none;
132130
line-height: normal;

src/scss/react-checkbox-tree.scss

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ $rct-clickable-focus: rgba($rct-icon-color, .2) !default;
3434
line-height: normal;
3535
color: inherit;
3636

37-
&:focus {
38-
outline: none;
39-
}
40-
4137
&:disabled {
4238
cursor: not-allowed;
4339
}
@@ -55,7 +51,8 @@ $rct-clickable-focus: rgba($rct-icon-color, .2) !default;
5551
background: $rct-label-hover;
5652
}
5753

58-
&:active {
54+
&:active,
55+
&:focus {
5956
background: $rct-label-active;
6057
}
6158
}
@@ -133,6 +130,7 @@ $rct-clickable-focus: rgba($rct-icon-color, .2) !default;
133130
}
134131

135132
.rct-collapse {
133+
align-self: stretch;
136134
border: 0;
137135
background: none;
138136
line-height: normal;

test/TreeNode.js

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,8 @@ describe('<TreeNode />', () => {
226226
<TreeNode {...baseProps} icons={{ uncheck: <span className="other-uncheck" /> }} />,
227227
);
228228

229-
assert.isTrue(wrapper.contains(
230-
<span className="rct-checkbox">
231-
<span className="other-uncheck" />
232-
</span>,
229+
assert.isTrue(wrapper.find('.rct-checkbox').contains(
230+
<span className="other-uncheck" />,
233231
));
234232
});
235233
});
@@ -254,10 +252,8 @@ describe('<TreeNode />', () => {
254252
<TreeNode {...baseProps} />,
255253
);
256254

257-
assert.isTrue(wrapper.contains(
258-
<span className="rct-checkbox">
259-
<span className="rct-icon rct-icon-uncheck" />
260-
</span>,
255+
assert.isTrue(wrapper.find('.rct-checkbox').contains(
256+
<span className="rct-icon rct-icon-uncheck" />,
261257
));
262258
});
263259

@@ -392,6 +388,25 @@ describe('<TreeNode />', () => {
392388
assert.isTrue(actual.checked);
393389
});
394390

391+
it('should trigger on key press', () => {
392+
let actual = {};
393+
394+
const wrapper = shallow(
395+
<TreeNode
396+
{...baseProps}
397+
checked={2}
398+
value="jupiter"
399+
onCheck={(node) => {
400+
actual = node;
401+
}}
402+
/>,
403+
);
404+
405+
wrapper.find('.rct-checkbox').simulate('keyup', { keyCode: 32 });
406+
407+
assert.isTrue(actual.checked);
408+
});
409+
395410
describe('optimisticToggle', () => {
396411
it('should toggle a partially-checked node to unchecked', () => {
397412
let actual = {};

0 commit comments

Comments
 (0)