Skip to content

Commit f121a78

Browse files
authored
Merge branch 'master' into another-attempt-at-eslint-v6
2 parents 91f17be + f3de162 commit f121a78

File tree

6 files changed

+35
-11
lines changed

6 files changed

+35
-11
lines changed

__tests__/src/rules/no-noninteractive-tabindex-test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ ruleTester.run(`${ruleName}:recommended`, rule, {
5454
valid: [
5555
...alwaysValid,
5656
{ code: '<div role="tabpanel" tabIndex="0" />' },
57+
// Expressions should fail in strict mode
58+
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} tabIndex="0" />;' },
5759
]
5860
.map(ruleOptionsMapperFactory(recommendedOptions))
5961
.map(parserOptionsMapper),
@@ -71,5 +73,7 @@ ruleTester.run(`${ruleName}:strict`, rule, {
7173
invalid: [
7274
...neverValid,
7375
{ code: '<div role="tabpanel" tabIndex="0" />', errors: [expectedError] },
76+
// Expressions should fail in strict mode
77+
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} tabIndex="0" />;', errors: [expectedError] },
7478
].map(parserOptionsMapper),
7579
});

__tests__/src/rules/no-static-element-interactions-test.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ const alwaysValid = [
9797
{ code: '<div role="treeitem" onClick={() => {}} />;' },
9898
/* Presentation is a special case role that indicates intentional static semantics */
9999
{ code: '<div role="presentation" onClick={() => {}} />;' },
100+
{ code: '<div role="presentation" onKeyDown={() => {}} />;' },
100101
/* HTML elements with an inherent, non-interactive role */
101102
{ code: '<main onClick={() => void 0} />;' },
102103
{ code: '<article onClick={() => {}} />;' },
@@ -257,7 +258,6 @@ const alwaysValid = [
257258
{ code: '<div onAnimationEnd={() => {}} />;' },
258259
{ code: '<div onAnimationIteration={() => {}} />;' },
259260
{ code: '<div onTransitionEnd={() => {}} />;' },
260-
{ code: '<div {...this.props} role={this.props.role} onKeyPress={e => this.handleKeyPress(e)}>{this.props.children}</div>' },
261261
];
262262

263263
const neverValid = [
@@ -412,6 +412,9 @@ ruleTester.run(`${ruleName}:recommended`, rule, {
412412
{ code: '<div onAnimationEnd={() => {}} />;' },
413413
{ code: '<div onAnimationIteration={() => {}} />;' },
414414
{ code: '<div onTransitionEnd={() => {}} />;' },
415+
// Expressions should pass in recommended mode
416+
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} />;' },
417+
{ code: '<div {...this.props} role={this.props.role} onKeyPress={e => this.handleKeyPress(e)}>{this.props.children}</div>' },
415418
]
416419
.map(ruleOptionsMapperFactory(recommendedOptions))
417420
.map(parserOptionsMapper),
@@ -445,5 +448,8 @@ ruleTester.run(`${ruleName}:strict`, rule, {
445448
{ code: '<div onMouseMove={() => {}} />;', errors: [expectedError] },
446449
{ code: '<div onMouseOut={() => {}} />;', errors: [expectedError] },
447450
{ code: '<div onMouseOver={() => {}} />;', errors: [expectedError] },
451+
// Expressions should fail in strict mode
452+
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} />;', errors: [expectedError] },
453+
{ code: '<div {...this.props} role={this.props.role} onKeyPress={e => this.handleKeyPress(e)}>{this.props.children}</div>', errors: [expectedError] },
448454
].map(parserOptionsMapper),
449455
});

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"eslint-plugin-import": "^2.18.0",
4444
"estraverse": "^4.2.0",
4545
"expect": "^24.3.1",
46-
"flow-bin": "^0.100.0",
46+
"flow-bin": "^0.101.0",
4747
"in-publish": "^2.0.0",
4848
"jest": "^24.0.0",
4949
"jscodeshift": "^0.6.0",
@@ -65,7 +65,7 @@
6565
"damerau-levenshtein": "^1.0.4",
6666
"emoji-regex": "^7.0.2",
6767
"has": "^1.0.3",
68-
"jsx-ast-utils": "^2.0.1"
68+
"jsx-ast-utils": "^2.2.0"
6969
},
7070
"peerDependencies": {
7171
"eslint": "^3 || ^4 || ^5 || ^6"

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,15 @@ module.exports = {
170170
{
171171
tags: [],
172172
roles: ['tabpanel'],
173+
allowExpressionValues: true,
173174
},
174175
],
175176
'jsx-a11y/no-onchange': 'error',
176177
'jsx-a11y/no-redundant-roles': 'error',
177178
'jsx-a11y/no-static-element-interactions': [
178179
'error',
179180
{
181+
allowExpressionValues: true,
180182
handlers: [
181183
'onClick',
182184
'onMouseDown',

src/rules/no-noninteractive-tabindex.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import includes from 'array-includes';
1919
import type { ESLintContext } from '../../flow/eslint';
2020
import isInteractiveElement from '../util/isInteractiveElement';
2121
import isInteractiveRole from '../util/isInteractiveRole';
22+
import isNonLiteralProperty from '../util/isNonLiteralProperty';
2223
import { generateObjSchema, arraySchema } from '../util/schemas';
2324
import getTabIndex from '../util/getTabIndex';
2425

@@ -67,10 +68,17 @@ module.exports = {
6768
const {
6869
tags,
6970
roles,
71+
allowExpressionValues,
7072
} = (options[0] || {});
73+
if (tags && includes(tags, type)) {
74+
return;
75+
}
76+
if (roles && includes(roles, role)) {
77+
return;
78+
}
7179
if (
72-
(tags && includes(tags, type))
73-
|| (roles && includes(roles, role))
80+
allowExpressionValues === true
81+
&& isNonLiteralProperty(attributes, 'role')
7482
) {
7583
return;
7684
}

src/rules/no-static-element-interactions.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ import isInteractiveElement from '../util/isInteractiveElement';
2626
import isInteractiveRole from '../util/isInteractiveRole';
2727
import isNonInteractiveElement from '../util/isNonInteractiveElement';
2828
import isNonInteractiveRole from '../util/isNonInteractiveRole';
29-
import isPresentationRole from '../util/isPresentationRole';
3029
import isNonLiteralProperty from '../util/isNonLiteralProperty';
30+
import isPresentationRole from '../util/isPresentationRole';
3131

3232
const errorMessage = 'Static HTML elements with event handlers require a role.';
3333

@@ -55,11 +55,12 @@ module.exports = {
5555
JSXOpeningElement: (node: JSXOpeningElement) => {
5656
const { attributes } = node;
5757
const type = elementType(node);
58-
const interactiveProps = options[0]
59-
? options[0].handlers
60-
: defaultInteractiveProps;
58+
const {
59+
allowExpressionValues,
60+
handlers = defaultInteractiveProps,
61+
} = (options[0] || {});
6162

62-
const hasInteractiveProps = interactiveProps
63+
const hasInteractiveProps = handlers
6364
.some(prop => (
6465
hasProp(attributes, prop)
6566
&& getPropValue(getProp(attributes, prop)) != null
@@ -91,7 +92,10 @@ module.exports = {
9192
return;
9293
}
9394

94-
if (isNonLiteralProperty(attributes, 'role')) {
95+
if (
96+
allowExpressionValues === true
97+
&& isNonLiteralProperty(attributes, 'role')
98+
) {
9599
// This rule has no opinion about non-literal roles.
96100
return;
97101
}

0 commit comments

Comments
 (0)