Skip to content

Commit 964644f

Browse files
octatonebeefancohen
authored andcommitted
scope no-onchange to select menu elements (#61)
Fixes #55
1 parent 4f4ecc2 commit 964644f

File tree

4 files changed

+37
-16
lines changed

4 files changed

+37
-16
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Then configure the rules you want to use under the rules section.
8484
- [mouse-events-have-key-events](docs/rules/mouse-events-have-key-events.md): Enforce that `onMouseOver`/`onMouseOut` are accompanied by `onFocus`/`onBlur` for keyboard-only users.
8585
- [no-access-key](docs/rules/no-access-key.md): Enforce that the `accessKey` prop is not used on any element to avoid complications with keyboard commands used by a screenreader.
8686
- [no-marquee](docs/rules/no-marquee.md): Enforce `<marquee>` elements are not used.
87-
- [no-onchange](docs/rules/no-onchange.md): Enforce that `onBlur` is used instead of `onChange`.
87+
- [no-onchange](docs/rules/no-onchange.md): Enforce usage of `onBlur` over `onChange` on select menus for accessibility.
8888
- [onclick-has-focus](docs/rules/onclick-has-focus.md): Enforce that elements with `onClick` handlers must be focusable.
8989
- [onclick-has-role](docs/rules/onclick-has-role.md): Enforce that non-interactive, visible elements (such as `<div>`) that have click handlers use the role attribute.
9090
- [role-has-required-aria-props](docs/rules/role-has-required-aria-props.md): Enforce that elements with ARIA roles must have all required attributes for that role.

docs/rules/no-onchange.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# no-onchange
22

3-
Enforce usage of `onBlur` over/in parallel with `onChange` for accessibility. `onBlur` **should** be used instead of `onChange`, unless absolutely necessary and it causes no negative consequences for keyboard only or screen reader users. `onBlur` is a more declarative action by the user: for instance in a dropdown, using the arrow keys to toggle between options will trigger the `onChange` event in some browsers. Regardless, when a change of context results from an `onBlur` event or an `onChange` event, the user should be notified of the change unless it occurs below the currently focused element.
3+
Enforce usage of `onBlur` over/in parallel with `onChange` on select menu elements for accessibility. `onBlur` **should** be used instead of `onChange`, unless absolutely necessary and it causes no negative consequences for keyboard only or screen reader users. `onBlur` is a more declarative action by the user: for instance in a dropdown, using the arrow keys to toggle between options will trigger the `onChange` event in some browsers. Regardless, when a change of context results from an `onBlur` event or an `onChange` event, the user should be notified of the change unless it occurs below the currently focused element.
44

55
#### References
66
1. [onChange Event Accessibility Issues](http://cita.disability.uiuc.edu/html-best-practices/auto/onchange.php)
@@ -12,11 +12,16 @@ This rule takes no arguments.
1212

1313
### Succeed
1414
```jsx
15-
<input onBlur={updateModel} />
16-
<input onBlur={handleOnBlur} onChange={handleOnChange} />
15+
<select onBlur={updateModel}>
16+
<option/>
17+
</select>
18+
19+
<select>
20+
<option onBlur={handleOnBlur} onChange={handleOnChange} />
21+
</select>
1722
```
1823

1924
### Fail
2025
```jsx
21-
<input onChange={updateModel} />
26+
<select onChange={updateModel} />
2227
```

src/rules/no-onchange.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,25 @@
77
// Rule Definition
88
// ----------------------------------------------------------------------------
99

10-
import { getProp } from 'jsx-ast-utils';
10+
import { getProp, elementType } from 'jsx-ast-utils';
1111

1212
const errorMessage = 'onBlur must be used instead of onchange, ' +
1313
'unless absolutely necessary and it causes no negative consequences ' +
1414
'for keyboard only or screen reader users.';
1515

16+
const applicableTypes = [
17+
'select',
18+
'option',
19+
];
20+
1621
module.exports = context => ({
1722
JSXOpeningElement: node => {
23+
const nodeType = elementType(node);
24+
25+
if (applicableTypes.indexOf(nodeType) === -1) {
26+
return;
27+
}
28+
1829
const onChange = getProp(node.attributes, 'onChange');
1930
const hasOnBlur = getProp(node.attributes, 'onBlur') !== undefined;
2031

tests/src/rules/no-onchange.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* @fileoverview Enforce usage of onBlur over onChange for accessibility.
2+
* @fileoverview Enforce usage of onBlur over onChange on select menus for accessibility.
33
* @author Ethan Cohen
44
*/
55

@@ -32,16 +32,21 @@ const expectedError = {
3232

3333
ruleTester.run('no-onchange', rule, {
3434
valid: [
35-
{ code: '<div onBlur={() => {}} />;', parserOptions },
36-
{ code: '<div onBlur={handleOnBlur} />;', parserOptions },
37-
{ code: '<div />;', parserOptions },
38-
{ code: '<div onBlur={() => {}} onChange={() => {}} />;', parserOptions },
39-
{ code: '<div {...props} />', parserOptions },
35+
{ code: '<select onBlur={() => {}} />;', parserOptions },
36+
{ code: '<select onBlur={handleOnBlur} />;', parserOptions },
37+
{ code: '<option />;', parserOptions },
38+
{ code: '<option onBlur={() => {}} onChange={() => {}} />;', parserOptions },
39+
{ code: '<option {...props} />', parserOptions },
40+
{ code: '<input onChange={() => {}} />;', parserOptions },
41+
{ code: '<input onChange={handleOnChange} />;', parserOptions },
42+
{ code: '<input />;', parserOptions },
43+
{ code: '<input onChange={() => {}} onChange={() => {}} />;', parserOptions },
44+
{ code: '<input {...props} />', parserOptions },
4045
],
4146
invalid: [
42-
{ code: '<div onChange={() => {}} />;', errors: [expectedError], parserOptions },
43-
{ code: '<div onChange={handleOnChange} />;', errors: [expectedError], parserOptions },
44-
{ code: '<input onChange={() => {}} />', errors: [expectedError], parserOptions },
45-
{ code: '<input onChange={() => {}} {...props} />', errors: [expectedError], parserOptions },
47+
{ code: '<select onChange={() => {}} />;', errors: [expectedError], parserOptions },
48+
{ code: '<select onChange={handleOnChange} />;', errors: [expectedError], parserOptions },
49+
{ code: '<option onChange={() => {}} />', errors: [expectedError], parserOptions },
50+
{ code: '<option onChange={() => {}} {...props} />', errors: [expectedError], parserOptions },
4651
],
4752
});

0 commit comments

Comments
 (0)