Skip to content

Commit 7a4a9c5

Browse files
committed
[New]: add attributes rule option for no-static-element-interactions which allows custom mapping of attributes to native ones
1 parent cc2c3d1 commit 7a4a9c5

File tree

3 files changed

+65
-32
lines changed

3 files changed

+65
-32
lines changed

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

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,24 @@ const expectedError = {
2929

3030
const ruleName = 'no-static-element-interactions';
3131

32-
const customOptions = [{
33-
a: {
34-
attributes: {
35-
href: ['to', 'href'],
36-
},
37-
},
38-
button: {
39-
attributes: {
40-
onClick: ['onClick', 'handleClick'],
41-
},
32+
const customOptions = [
33+
{
34+
attributes: [
35+
{
36+
components: ['a', 'Link'],
37+
attributes: {
38+
href: ['to', 'href'],
39+
},
40+
},
41+
{
42+
components: ['button', 'Button'],
43+
attributes: {
44+
onClick: ['onClick', 'handleClick'],
45+
},
46+
},
47+
],
4248
},
43-
}];
49+
];
4450

4551
const componentsSettings = {
4652
'jsx-a11y': {

docs/rules/no-static-element-interactions.md

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,25 @@ Indicate the element's role with the `role` attribute:
2121
onClick={onClickHandler}
2222
onKeyPress={onKeyPressHandler}
2323
role="button"
24-
tabindex="0">
24+
tabindex="0"
25+
>
2526
Save
2627
</div>
2728
```
2829

2930
Common interactive roles include:
3031

31-
1. `button`
32-
1. `link`
33-
1. `checkbox`
34-
1. `menuitem`
35-
1. `menuitemcheckbox`
36-
1. `menuitemradio`
37-
1. `option`
38-
1. `radio`
39-
1. `searchbox`
40-
1. `switch`
41-
1. `textbox`
32+
1. `button`
33+
1. `link`
34+
1. `checkbox`
35+
1. `menuitem`
36+
1. `menuitemcheckbox`
37+
1. `menuitemradio`
38+
1. `option`
39+
1. `radio`
40+
1. `searchbox`
41+
1. `switch`
42+
1. `textbox`
4243

4344
Note: Adding a role to your element does **not** add behavior. When a semantic HTML element like `<button>` is used, then it will also respond to Enter key presses when it has focus. The developer is responsible for providing the expected behavior of an element that the role suggests it would have: focusability and key press support.
4445

@@ -47,12 +48,16 @@ Note: Adding a role to your element does **not** add behavior. When a semantic H
4748
If your element is catching bubbled click or key events from descendant elements, there are no appropriate roles for your element: you will have to deactivate the rule. Consider explaining the reason for disabling the rule as well.
4849

4950
```jsx
50-
{/* The <div> element has a child <button> element that allows keyboard interaction */}
51-
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
51+
{
52+
/* The <div> element has a child <button> element that allows keyboard interaction */
53+
}
54+
{
55+
/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */
56+
}
5257
<div onClick={this.handleButtonClick}>
5358
<button>Save</button>
5459
<button>Cancel</button>
55-
</div>
60+
</div>;
5661
```
5762

5863
Do not use the role `presentation` on the element: it removes the element's semantics, and may also remove its children's semantics, creating big issues with assistive technology.
@@ -74,11 +79,23 @@ You may configure which handler props should be taken into account when applying
7479
'onKeyUp',
7580
],
7681
allowExpressionValues: true,
82+
attributes: [
83+
{
84+
components: ['a', 'Link'],
85+
attributes: {
86+
href: ['to', 'href'],
87+
},
88+
},
89+
],
7790
},
7891
],
7992
```
8093

81-
Adjust the list of handler prop names in the handlers array to increase or decrease the coverage surface of this rule in your codebase.
94+
### `handlers`
95+
96+
Adjust the list of handler prop names in the `handlers` array to increase or decrease the coverage surface of this rule in your codebase.
97+
98+
### `allowExpressionValues`
8299

83100
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`:
84101

@@ -88,6 +105,16 @@ The `allowExpressionValues` option determines whether the `role` attribute is al
88105
<div role={isButton ? "button" : "link"} onClick={() => {}} />;
89106
```
90107

108+
### `attributes`
109+
110+
The `attributes` array allows to set custom attributes for a given list of components. This is useful in cases where you are utilizing libraries that may have different prop names mapped to a native attribute (e.g., `to` being used for `href`)
111+
112+
```jsx
113+
// these will be valid given the `attributes` option above
114+
<a onClick={() => {}} className="foo" to="some/path" />
115+
<Link onClick={() => {}} className="foo" to="some/path" />
116+
```
117+
91118
### Succeed
92119

93120
```jsx

src/util/isInteractiveElement.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ function checkIsInteractiveElement(tagName, attributes, options = {}): boolean {
7878
}
7979

8080
function isInteractiveElementWithCustomOptions() {
81-
const elementConfig = options[tagName];
81+
const matchingConfig = options.attributes.find((config) => config.components && config.components.includes(tagName));
8282

83-
if (!elementConfig) return false;
83+
if (!matchingConfig) return false;
8484

85-
return Object.keys(elementConfig.attributes).some((standardAttr) => {
86-
const customAttrs = elementConfig.attributes[standardAttr];
85+
return Object.keys(matchingConfig.attributes).some((standardAttr) => {
86+
const customAttrs = matchingConfig.attributes[standardAttr];
8787

8888
const validCustomAttr = customAttrs.find((customAttr) => {
8989
if (customAttr === standardAttr) return false;
@@ -109,7 +109,7 @@ function checkIsInteractiveElement(tagName, attributes, options = {}): boolean {
109109
}
110110

111111
// Checks if there are custom options for this element
112-
if (options && Object.keys(options).length > 0) {
112+
if (options && options.attributes && options.attributes.length > 0) {
113113
return isInteractiveElementWithCustomOptions();
114114
}
115115

0 commit comments

Comments
 (0)