Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added documentation and tests cases for allowExpressionValues option #870

Merged
merged 3 commits into from Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 33 additions & 1 deletion __tests__/src/rules/no-noninteractive-tabindex-test.js
Expand Up @@ -65,8 +65,28 @@ ruleTester.run(`${ruleName}:recommended`, rule, {
valid: [
...alwaysValid,
{ code: '<div role="tabpanel" tabIndex="0" />' },
// Expressions should fail in strict mode
// Expressions should pass in recommended mode
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} tabIndex="0" />;' },
// Cases for allowExpressionValues set to true
{
code: '<div role={BUTTON} onClick={() => {}} tabIndex="0" />;',
options: [{ allowExpressionValues: true }],
},
// Specific case for ternary operator with literals on both side
{
code: '<div role={isButton ? "button" : "link"} onClick={() => {}} tabIndex="0" />;',
options: [{ allowExpressionValues: true }],
},
{
code: '<div role={isButton ? "button" : LINK} onClick={() => {}} tabIndex="0" />;',
options: [{ allowExpressionValues: true }],
errors: [expectedError],
},
{
code: '<div role={isButton ? BUTTON : LINK} onClick={() => {}} tabIndex="0"/>;',
options: [{ allowExpressionValues: true }],
errors: [expectedError],
},
]
.map(ruleOptionsMapperFactory(recommendedOptions))
.map(parserOptionsMapper),
Expand All @@ -86,5 +106,17 @@ ruleTester.run(`${ruleName}:strict`, rule, {
{ code: '<div role="tabpanel" tabIndex="0" />', errors: [expectedError] },
// Expressions should fail in strict mode
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} tabIndex="0" />;', errors: [expectedError] },
// Cases for allowExpressionValues set to false
{
code: '<div role={BUTTON} onClick={() => {}} tabIndex="0" />;',
options: [{ allowExpressionValues: false }],
errors: [expectedError],
},
// Specific case for ternary operator with literals on both side
{
code: '<div role={isButton ? "button" : "link"} onClick={() => {}} tabIndex="0" />;',
options: [{ allowExpressionValues: false }],
errors: [expectedError],
},
].map(parserOptionsMapper),
});
21 changes: 21 additions & 0 deletions __tests__/src/rules/no-static-element-interactions-test.js
Expand Up @@ -429,11 +429,26 @@ ruleTester.run(`${ruleName}:recommended`, rule, {
// Expressions should pass in recommended mode
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} />;' },
{ code: '<div {...this.props} role={this.props.role} onKeyPress={e => this.handleKeyPress(e)}>{this.props.children}</div>' },
// Cases for allowExpressionValues set to true
{
code: '<div role={BUTTON} onClick={() => {}} />;',
options: [{ allowExpressionValues: true }],
},
// Specific case for ternary operator with literals on both side
{
code: '<div role={isButton ? "button" : "link"} onClick={() => {}} />;',
options: [{ allowExpressionValues: true }],
},
{
code: '<div role={isButton ? "button" : LINK} onClick={() => {}} />;',
options: [{ allowExpressionValues: true }],
errors: [expectedError],
},
{
code: '<div role={isButton ? BUTTON : LINK} onClick={() => {}} />;',
options: [{ allowExpressionValues: true }],
errors: [expectedError],
},
)
.map(ruleOptionsMapperFactory(recommendedOptions))
.map(parserOptionsMapper),
Expand Down Expand Up @@ -470,6 +485,12 @@ ruleTester.run(`${ruleName}:strict`, rule, {
// Expressions should fail in strict mode
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} />;', errors: [expectedError] },
{ code: '<div {...this.props} role={this.props.role} onKeyPress={e => this.handleKeyPress(e)}>{this.props.children}</div>', errors: [expectedError] },
// Cases for allowExpressionValues set to false
{
code: '<div role={BUTTON} onClick={() => {}} />;',
options: [{ allowExpressionValues: false }],
errors: [expectedError],
},
// Specific case for ternary operator with literals on both side
{
code: '<div role={isButton ? "button" : "link"} onClick={() => {}} />;',
Expand Down
9 changes: 8 additions & 1 deletion docs/rules/no-noninteractive-tabindex.md
Expand Up @@ -50,7 +50,7 @@ If you know that a particular element will be scrollable, you might want to add

```jsx
// eslint-disable-next-line no-noninteractive-tabindex
<pre tabIndex="0">
<pre tabIndex="0">
<code>{someLongCode}</code>
</pre>
```
Expand All @@ -65,9 +65,16 @@ The recommended options for this rule allow `tabIndex` on elements with the noni
{
tags: [],
roles: ['tabpanel'],
allowExpressionValues: true,
},
]
```
The `allowExpressionValues` option determines whether the `role` attribute is allowed to be assigned using an expression. For example, the following would pass in recommended mode if `allowExpressionValues` is set to be `true`:
```jsx
<div role={ROLE_BUTTON} onClick={() => {}} tabIndex="0" />;
// In case of a conditional expression, there should be literals on both sides of ternary operator
<div role={isButton ? "button" : "link"} onClick={() => {}} tabIndex="0" />;
```

### Succeed
```jsx
Expand Down
10 changes: 9 additions & 1 deletion docs/rules/no-static-element-interactions.md
Expand Up @@ -55,7 +55,7 @@ Do not use the role `presentation` on the element: it removes the element's sema

## Rule details

You may configure which handler props should be taken into account when applying this rule. The recommended configuration includes the following 6 handlers.
You may configure which handler props should be taken into account when applying this rule. The recommended configuration includes the following 6 handlers and the `allowExpressionValues` option.

```javascript
'jsx-a11y/no-static-element-interactions': [
Expand All @@ -69,12 +69,20 @@ You may configure which handler props should be taken into account when applying
'onKeyDown',
'onKeyUp',
],
allowExpressionValues: true,
},
],
```

Adjust the list of handler prop names in the handlers array to increase or decrease the coverage surface of this rule in your codebase.

The `allowExpressionValues` option determines whether the `role` attribute is allowed to be assigned using an expression. For example, the following would pass in recommended mode if `allowExpressionValues` is set to be `true`:
```jsx
<div role={ROLE_BUTTON} onClick={() => {}} />;
// In case of a conditional expression, there should be literals on both sides of ternary operator
<div role={isButton ? "button" : "link"} onClick={() => {}} />;
```

### Succeed

```jsx
Expand Down
12 changes: 12 additions & 0 deletions src/rules/no-noninteractive-tabindex.js
Expand Up @@ -82,6 +82,18 @@ export default ({
allowExpressionValues === true
&& isNonLiteralProperty(attributes, 'role')
) {
// Special case if role is assigned using ternary with literals on both side
const roleProp = getProp(attributes, 'role');
if (roleProp && roleProp.type === 'JSXAttribute' && roleProp.value.type === 'JSXExpressionContainer') {
if (roleProp.value.expression.type === 'ConditionalExpression') {
if (
roleProp.value.expression.consequent.type === 'Literal'
&& roleProp.value.expression.alternate.type === 'Literal'
) {
return;
}
}
}
return;
}
if (
Expand Down