Skip to content

Commit 9ee1242

Browse files
authored
[new] - Add no-redundant-roles rule (#150)
* [new] - Add no-redundant-roles rule Fixes #149
1 parent d394b4c commit 9ee1242

File tree

5 files changed

+117
-0
lines changed

5 files changed

+117
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ You can also enable all the recommended rules at once. Add `plugin:jsx-a11y/reco
111111
- [no-autofocus](docs/rules/no-autofocus.md): Enforce autoFocus prop is not used.
112112
- [no-distracting-elements](docs/rules/no-distracting-elements.md): Enforce distracting elements are not used.
113113
- [no-onchange](docs/rules/no-onchange.md): Enforce usage of `onBlur` over `onChange` on select menus for accessibility.
114+
- [no-redundant-roles](docs/rules/no-redundant-roles.md): Enforce explicit role property is not the same as implicit/default role property on element.
114115
- [no-static-element-interactions](docs/rules/no-static-element-interactions.md): Enforce non-interactive elements have no interactive handlers.
115116
- [onclick-has-focus](docs/rules/onclick-has-focus.md): Enforce that elements with `onClick` handlers must be focusable.
116117
- [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.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* eslint-env jest */
2+
/**
3+
* @fileoverview Enforce explicit role property is not the
4+
* same as implicit default role property on element.
5+
* @author Ethan Cohen <@evcohen>
6+
*/
7+
8+
// -----------------------------------------------------------------------------
9+
// Requirements
10+
// -----------------------------------------------------------------------------
11+
12+
import { RuleTester } from 'eslint';
13+
import rule from '../../../src/rules/no-redundant-roles';
14+
15+
const parserOptions = {
16+
ecmaVersion: 6,
17+
ecmaFeatures: {
18+
jsx: true,
19+
},
20+
};
21+
22+
// -----------------------------------------------------------------------------
23+
// Tests
24+
// -----------------------------------------------------------------------------
25+
26+
const ruleTester = new RuleTester();
27+
28+
const expectedError = (element, implicitRole) => ({
29+
message: `The element ${element} has an implicit role of ${implicitRole}. Defining this explicitly is redundant and should be avoided.`,
30+
type: 'JSXOpeningElement',
31+
});
32+
33+
ruleTester.run('no-redundant-roles', rule, {
34+
valid: [
35+
{ code: '<div />;', parserOptions },
36+
{ code: '<button role="main" />', parserOptions },
37+
{ code: '<MyComponent role="button" />', parserOptions },
38+
{ code: '<button role={`${foo}button`} />', parserOptions },
39+
],
40+
invalid: [
41+
{ code: '<button role="button" />', errors: [expectedError('button', 'button')], parserOptions },
42+
{ code: '<body role="DOCUMENT" />', errors: [expectedError('body', 'document')], parserOptions },
43+
{ code: '<button role={`${undefined}button`} />', errors: [expectedError('button', 'button')], parserOptions },
44+
],
45+
});

docs/rules/no-redundant-roles.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# no-redundant-roles
2+
3+
Some HTML elements have native semantics that are implemented by the browser. This includes default/implicit ARIA roles. Setting an ARIA role that matches its default/implicit role is redundant since it is already set by the browser.
4+
5+
#### References
6+
1. [w3](https://www.w3.org/TR/html5/dom.html#aria-role-attribute)
7+
8+
## Rule details
9+
10+
This rule takes no arguments.
11+
12+
### Succeed
13+
```jsx
14+
<div />
15+
<button role="presentation" />
16+
<MyComponent role="main" />
17+
```
18+
19+
### Fail
20+
```jsx
21+
<button role="button" />
22+
<img role="img" src="foo.jpg" />
23+
```

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ module.exports = {
2323
'no-autofocus': require('./rules/no-autofocus'),
2424
'no-distracting-elements': require('./rules/no-distracting-elements'),
2525
'no-onchange': require('./rules/no-onchange'),
26+
'no-redundant-roles': require('./rules/no-redundant-roles'),
2627
'no-static-element-interactions': require('./rules/no-static-element-interactions'),
2728
'onclick-has-focus': require('./rules/onclick-has-focus'),
2829
'onclick-has-role': require('./rules/onclick-has-role'),
@@ -59,6 +60,7 @@ module.exports = {
5960
'jsx-a11y/no-autofocus': 'error',
6061
'jsx-a11y/no-distracting-elements': 'error',
6162
'jsx-a11y/no-onchange': 'error',
63+
'jsx-a11y/no-redundant-roles': 'error',
6264
'jsx-a11y/no-static-element-interactions': 'warn',
6365
'jsx-a11y/onclick-has-focus': 'error',
6466
'jsx-a11y/onclick-has-role': 'error',

src/rules/no-redundant-roles.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* @fileoverview Enforce explicit role property is not the
3+
* same as implicit/default role property on element.
4+
* @author Ethan Cohen <@evcohen>
5+
*/
6+
7+
// ----------------------------------------------------------------------------
8+
// Rule Definition
9+
// ----------------------------------------------------------------------------
10+
11+
import { elementType, getProp, getLiteralPropValue } from 'jsx-ast-utils';
12+
import { generateObjSchema } from '../util/schemas';
13+
import getImplicitRole from '../util/getImplicitRole';
14+
15+
const errorMessage = (element, implicitRole) =>
16+
`The element ${element} has an implicit role of ${implicitRole}. Defining this explicitly is redundant and should be avoided.`;
17+
18+
const schema = generateObjSchema();
19+
20+
module.exports = {
21+
meta: {
22+
docs: {},
23+
schema: [schema],
24+
},
25+
26+
create: context => ({
27+
JSXOpeningElement: (node) => {
28+
const type = elementType(node);
29+
const implicitRole = getImplicitRole(type, node.attributes);
30+
31+
if (implicitRole === '') {
32+
return;
33+
}
34+
35+
const role = getProp(node.attributes, 'role');
36+
const roleValue = getLiteralPropValue(role);
37+
38+
if (typeof roleValue === 'string' && roleValue.toUpperCase() === implicitRole.toUpperCase()) {
39+
context.report({
40+
node,
41+
message: errorMessage(type, implicitRole.toLowerCase()),
42+
});
43+
}
44+
},
45+
}),
46+
};

0 commit comments

Comments
 (0)