10000 [New] Introduce a plugin-wide setting for custom components. · jsx-eslint/eslint-plugin-jsx-a11y@64dcac6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 64dcac6

Browse files
committed
[New] Introduce a plugin-wide setting for custom components.
For #174
1 parent 1826628 commit 64dcac6

File tree

71 files changed

+1317
-691
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1317
-691
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,24 @@ Add `plugin:jsx-a11y/recommended` or `plugin:jsx-a11y/strict` in `extends`:
8989
}
9090
```
9191

92+
To enable your custom components to be checked as DOM elements, you can set global settings in your
93+
configuration file by mapping each custom component name to a DOM element type.
94+
95+
```json
96+
{
97+
"settings": {
98+
"jsx-a11y": {
99+
"components": {
100+
"CityInput": "input",
101+
"CustomButton": "button",
102+
"MyButton": "button",
103+
"RoundButton": "button"
104+
}
105+
}
106+
}
107+
}
108+
```
109+
92110
## Supported Rules
93111

94112
<!-- AUTO-GENERATED-CONTENT:START (LIST) -->

__tests__/__util__/parserOptionsMapper.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export default function parserOptionsMapper({
1111
errors,
1212
options = [],
1313
parserOptions = {},
14+
settings,
1415
}) {
1516
return {
1617
code,
@@ -20,5 +21,6 @@ export default function parserOptionsMapper({
2021
...defaultParserOptions,
2122
...parserOptions,
2223
},
24+
settings,
2325
};
2426
}

__tests__/__util__/ruleOptionsMapperFactory.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ type ESLintTestRunnerTestCase = {
66
code: string,
77
errors: ?Array<{ message: string, type: string }>,
88
options: ?Array<mixed>,
9-
parserOptions: ?Array<mixed>
9+
parserOptions: ?Array<mixed>,
10+
settings?: {[string]: mixed},
1011
};
1112

1213
type RuleOptionsMapperFactoryType = (
@@ -15,7 +16,7 @@ type RuleOptionsMapperFactoryType = (
1516

1617
export default function ruleOptionsMapperFactory(ruleOptions: Array<mixed> = []): RuleOptionsMapperFactoryType {
1718
// eslint-disable-next-line
18-
return ({ code, errors, options, parserOptions }: ESLintTestRunnerTestCase): ESLintTestRunnerTestCase => {
19+
return ({ code, errors, options, parserOptions, settings }: ESLintTestRunnerTestCase): ESLintTestRunnerTestCase => {
1920
return {
2021
code,
2122
errors,
@@ -25,6 +26,7 @@ export default function ruleOptionsMapperFactory(ruleOptions: Array<mixed> = [])
2526
...item,
2627
}], [{}]),
2728
parserOptions,
29+
settings,
2830
};
2931
};
3032
}

__tests__/src/rules/accessible-emoji-test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ ruleTester.run('accessible-emoji', rule, {
3737
{ code: '<span aria-hidden="true">🐼</span>' },
3838
{ code: '<span aria-hidden>🐼</span>' },
3939
{ code: '<div aria-hidden="true">🐼</div>' },
40+
{ code: '<input type="hidden">🐼</input>' },
41+
{
42+
code: '<CustomInput type="hidden">🐼</CustomInput>',
43+
settings: { 'jsx-a11y': { components: { CustomInput: 'input' } } },
44+
},
4045
].map(parserOptionsMapper),
4146
invalid: [
4247
{ code: '<span>🐼</span>', errors: [expectedError] },
@@ -46,5 +51,6 @@ ruleTester.run('accessible-emoji', rule, {
4651
{ code: '<i role="img" aria-labelledby="id1">🐼</i>', errors: [expectedError] },
4752
{ code: '<Foo>🐼</Foo>', errors: [expectedError] },
4853
{ code: '<span aria-hidden="false">🐼</span>', errors: [expectedError] },
54+
{ code: '<CustomInput type="hidden">🐼</CustomInput>', errors: [expectedError] },
4955
].map(parserOptionsMapper),
5056
});

__tests__/src/rules/alt-text-test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ const areaError = 'Each area of an image map must have a text alternative throug
4242

4343
const inputImageError = '<input> elements with type="image" must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.';
4444

45+
const componentsSettings = {
46+
'jsx-a11y': {
47+
components: {
48+
Input: 'input',
49+
},
50+
},
51+
};
52+
4553
const array = [{
4654
img: ['Thumbnail', 'Image'],
4755
object: ['Object'],
@@ -112,6 +120,7 @@ ruleTester.run('alt-text', rule, {
112120
{ code: '<input type="image" alt="This is descriptive!" />' },
113121
{ code: '<input type="image" alt={altText} />' },
114122
{ code: '<InputImage />' },
123+
{ code: '<Input type="image" alt="" />', settings: componentsSettings },
115124

116125
// CUSTOM ELEMENT TESTS FOR ARRAY OPTION TESTS
117126
{ code: '<Thumbnail alt="foo" />;', options: array },
@@ -263,5 +272,6 @@ ruleTester.run('alt-text', rule, {
263272
{ code: '<InputImage alt={undefined} />', errors: [inputImageError], options: array },
264273
{ code: '<InputImage>Foo</InputImage>', errors: [inputImageError], options: array },
265274
{ code: '<InputImage {...this.props} />', errors: [inputImageError], options: array },
275+
{ code: '<Input type="image" />', errors: [inputImageError], settings: componentsSettings },
266276
].map(parserOptionsMapper),
267277
});

__tests__/src/rules/anchor-has-content-test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,20 @@ ruleTester.run('anchor-has-content', rule, {
3131
{ code: '<a>{foo.bar}</a>' },
3232
{ code: '<a dangerouslySetInnerHTML={{ __html: "foo" }} />' },
3333
{ code: '<a children={children} />' },
34+
{ code: '<Link />' },
35+
{
36+
code: '<Link>foo</Link>',
37+
settings: { 'jsx-a11y': { components: { Link: 'a' } } },
38+
},
3439
].map(parserOptionsMapper),
3540
invalid: [
3641
{ code: '<a />', errors: [expectedError] },
3742
{ code: '<a><Bar aria-hidden /></a>', errors: [expectedError] },
3843
{ code: '<a>{undefined}</a>', errors: [expectedError] },
44+
{
45+
code: '<Link />',
46+
errors: [expectedError],
47+
settings: { 'jsx-a11y': { components: { Link: 'a' } } },
48+
},
3949
].map(parserOptionsMapper),
4050
});

__tests__/src/rules/anchor-is-valid-test.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,19 @@ const componentsAndSpecialLinkAndNoHrefAspect = [{
7878
aspects: ['noHref'],
7979
}];
8080

81+
const componentsSettings = {
82+
'jsx-a11y': {
83+
components: {
84+
Anchor: 'a',
85+
Link: 'a',
86+
},
87+
},
88+
};
89+
8190
ruleTester.run('anchor-is-valid', rule, {
8291
valid: [
8392
// DEFAULT ELEMENT 'a' TESTS
84-
{ code: '<Anchor />;' },
93+
{ code: '<Anchor />' },
8594
{ code: '<a {...props} />' },
8695
{ code: '<a href="foo" />' },
8796
{ code: '<a href={foo} />' },
@@ -119,6 +128,7 @@ ruleTester.run('anchor-is-valid', rule, {
119128
{ code: '<Link href={`#foo`}/>', options: components },
120129
{ code: '<Link href={"foo"}/>', options: components },
121130
{ code: '<Link href="#foo" />', options: components },
131+
{ code: '<Link href="#foo" />', settings: componentsSettings },
122132

123133
// CUSTOM PROP TESTS
124134
{ code: '<a {...props} />', options: specialLink },
@@ -332,6 +342,11 @@ ruleTester.run('anchor-is-valid', rule, {
332342
errors: [preferButtonexpectedError],
333343
options: components,
334344
},
345+
{
346+
code: '<Link href="#" onClick={() => void 0} />',
347+
errors: [preferButtonexpectedError],
348+
settings: componentsSettings,
349+
},
335350

336351
// CUSTOM PROP TESTS
337352
// NO HREF

__tests__/src/rules/aria-activedescendant-has-tabindex-test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ ruleTester.run('aria-activedescendant-has-tabindex', rule, {
3636
{
3737
code: '<CustomComponent aria-activedescendant={someID} tabIndex={-1} />;',
3838
},
39+
{
40+
code: '<CustomComponent aria-activedescendant={someID} tabIndex={0} />;',
41+
settings: { 'jsx-a11y': { components: { CustomComponent: 'div' } } },
42+
},
3943
{
4044
code: '<div />;',
4145
},
@@ -81,5 +85,10 @@ ruleTester.run('aria-activedescendant-has-tabindex', rule, {
8185
code: '<div aria-activedescendant={someID} />;',
8286
errors: [expectedError],
8387
},
88+
{
89+
code: '<CustomComponent aria-activedescendant={someID} />;',
90+
errors: [expectedError],
91+
settings: { 'jsx-a11y': { components: { CustomComponent: 'div' } } },
92+
},
8493
].map(parserOptionsMapper),
8594
});

__tests__/src/rules/aria-role-test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ const ignoreNonDOMSchema = [{
4747
ignoreNonDOM: true,
4848
}];
4949

50+
const customDivSettings = {
51+
'jsx-a11y': {
52+
components: {
53+
Div: 'div',
54+
},
55+
},
56+
};
57+
5058
ruleTester.run('aria-role', rule, {
5159
valid: [
5260
// Variables should pass, as we are only testing literals.
@@ -66,6 +74,11 @@ ruleTester.run('aria-role', rule, {
6674
{ code: '<Foo role="bar" />', options: ignoreNonDOMSchema },
6775
{ code: '<fakeDOM role="bar" />', options: ignoreNonDOMSchema },
6876
{ code: '<img role="presentation" />', options: ignoreNonDOMSchema },
77+
{
78+
code: '<Div role="button" />',
79+
errors: [errorMessage],
80+
settings: customDivSettings,
81+
},
6982
].concat(validTests).map(parserOptionsMapper),
7083

7184
invalid: [
@@ -82,5 +95,12 @@ ruleTester.run('aria-role', rule, {
8295
{ code: '<div role={null}></div>', errors: [errorMessage] },
8396
{ code: '<Foo role="datepicker" />', errors: [errorMessage] },
8497
{ code: '<Foo role="Button" />', errors: [errorMessage] },
98+
{ code: '<Div role="Button" />', errors: [errorMessage], settings: customDivSettings },
99+
{
100+
code: '<Div role="Button" />',
101+
errors: [errorMessage],
102+
options: ignoreNonDOMSchema,
103+
settings: customDivSettings,
104+
},
85105
].concat(invalidTests).map(parserOptionsMapper),
86106
});

__tests__/src/rules/aria-unsupported-elements-test.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ const invalidRoleValidityTests = domElements
5454
.map((reservedElem) => ({
5555
code: `<${reservedElem} role {...props} />`,
5656
errors: [errorMessage('role')],
57-
}));
57+
})).concat({
58+
code: '<Meta aria-hidden />',
59+
errors: [errorMessage('aria-hidden')],
60+
settings: { 'jsx-a11y' 4F56 : { components: { Meta: 'meta' } } },
61+
});
5862

5963
const invalidAriaValidityTests = domElements
6064
.filter((element) => Boolean(dom.get(element).reserved))

0 commit comments

Comments
 (0)
0