@arcaauth/eslint-plugin-jsx-a11y 6.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +17 -0
- package/.eslintrc +44 -0
- package/CHANGELOG.md +774 -0
- package/LICENSE.md +8 -0
- package/README.md +423 -0
- package/__mocks__/IdentifierMock.js +15 -0
- package/__mocks__/JSXAttributeMock.js +39 -0
- package/__mocks__/JSXElementMock.js +37 -0
- package/__mocks__/JSXExpressionContainerMock.js +15 -0
- package/__mocks__/JSXSpreadAttributeMock.js +18 -0
- package/__mocks__/JSXTextMock.js +17 -0
- package/__mocks__/LiteralMock.js +17 -0
- package/__mocks__/genInteractives.js +218 -0
- package/__tests__/__util__/axeMapping.js +6 -0
- package/__tests__/__util__/helpers/getESLintCoreRule.js +9 -0
- package/__tests__/__util__/helpers/parsers.js +186 -0
- package/__tests__/__util__/parserOptionsMapper.js +53 -0
- package/__tests__/__util__/ruleOptionsMapperFactory.js +33 -0
- package/__tests__/index-test.js +40 -0
- package/__tests__/src/rules/accessible-emoji-test.js +66 -0
- package/__tests__/src/rules/alt-text-test.js +291 -0
- package/__tests__/src/rules/anchor-ambiguous-text-test.js +117 -0
- package/__tests__/src/rules/anchor-has-content-test.js +54 -0
- package/__tests__/src/rules/anchor-is-valid-test.js +532 -0
- package/__tests__/src/rules/aria-activedescendant-has-tabindex-test.js +95 -0
- package/__tests__/src/rules/aria-props-test.js +69 -0
- package/__tests__/src/rules/aria-proptypes-test.js +311 -0
- package/__tests__/src/rules/aria-role-test.js +118 -0
- package/__tests__/src/rules/aria-unsupported-elements-test.js +75 -0
- package/__tests__/src/rules/autocomplete-valid-test.js +77 -0
- package/__tests__/src/rules/click-events-have-key-events-test.js +77 -0
- package/__tests__/src/rules/control-has-associated-label-test.js +327 -0
- package/__tests__/src/rules/heading-has-content-test.js +85 -0
- package/__tests__/src/rules/html-has-lang-test.js +42 -0
- package/__tests__/src/rules/iframe-has-title-test.js +55 -0
- package/__tests__/src/rules/img-redundant-alt-test.js +137 -0
- package/__tests__/src/rules/interactive-supports-focus-test.js +267 -0
- package/__tests__/src/rules/label-has-associated-control-test.js +243 -0
- package/__tests__/src/rules/label-has-for-test.js +235 -0
- package/__tests__/src/rules/lang-test.js +59 -0
- package/__tests__/src/rules/media-has-caption-test.js +220 -0
- package/__tests__/src/rules/mouse-events-have-key-events-test.js +154 -0
- package/__tests__/src/rules/no-access-key-test.js +48 -0
- package/__tests__/src/rules/no-aria-hidden-on-focusable-test.js +44 -0
- package/__tests__/src/rules/no-autofocus-test.js +68 -0
- package/__tests__/src/rules/no-distracting-elements-test.js +51 -0
- package/__tests__/src/rules/no-interactive-element-to-noninteractive-role-test.js +405 -0
- package/__tests__/src/rules/no-noninteractive-element-interactions-test.js +502 -0
- package/__tests__/src/rules/no-noninteractive-element-to-interactive-role-test.js +500 -0
- package/__tests__/src/rules/no-noninteractive-tabindex-test.js +123 -0
- package/__tests__/src/rules/no-onchange-test.js +57 -0
- package/__tests__/src/rules/no-redundant-roles-test.js +98 -0
- package/__tests__/src/rules/no-static-element-interactions-test.js +501 -0
- package/__tests__/src/rules/prefer-tag-over-role-test.js +63 -0
- package/__tests__/src/rules/role-has-required-aria-props-test.js +134 -0
- package/__tests__/src/rules/role-supports-aria-props-test.js +570 -0
- package/__tests__/src/rules/scope-test.js +50 -0
- package/__tests__/src/rules/tabindex-no-positive-test.js +55 -0
- package/__tests__/src/util/attributesComparator-test.js +91 -0
- package/__tests__/src/util/getAccessibleChildText-test.js +174 -0
- package/__tests__/src/util/getComputedRole-test.js +71 -0
- package/__tests__/src/util/getElementType-test.js +154 -0
- package/__tests__/src/util/getExplicitRole-test.js +35 -0
- package/__tests__/src/util/getImplicitRole-test.js +25 -0
- package/__tests__/src/util/getSuggestion-test.js +33 -0
- package/__tests__/src/util/getTabIndex-test.js +85 -0
- package/__tests__/src/util/hasAccessibleChild-test.js +157 -0
- package/__tests__/src/util/implicitRoles/input-test.js +87 -0
- package/__tests__/src/util/implicitRoles/menu-test.js +20 -0
- package/__tests__/src/util/implicitRoles/menuitem-test.js +38 -0
- package/__tests__/src/util/isAbstractRole-test.js +51 -0
- package/__tests__/src/util/isContentEditable-test.js +52 -0
- package/__tests__/src/util/isDOMElement-test.js +30 -0
- package/__tests__/src/util/isDisabledElement-test.js +88 -0
- package/__tests__/src/util/isFocusable-test.js +111 -0
- package/__tests__/src/util/isInteractiveElement-test.js +104 -0
- package/__tests__/src/util/isInteractiveRole-test.js +59 -0
- package/__tests__/src/util/isNonInteractiveElement-test.js +97 -0
- package/__tests__/src/util/isNonInteractiveRole-test.js +59 -0
- package/__tests__/src/util/isNonLiteralProperty-test.js +52 -0
- package/__tests__/src/util/isSemanticRoleElement-test.js +72 -0
- package/__tests__/src/util/mayContainChildComponent-test.js +219 -0
- package/__tests__/src/util/mayHaveAccessibleLabel-test.js +256 -0
- package/__tests__/src/util/parserOptionsMapper-test.js +93 -0
- package/__tests__/src/util/schemas-test.js +35 -0
- package/docs/rules/accessible-emoji.md +30 -0
- package/docs/rules/alt-text.md +168 -0
- package/docs/rules/anchor-ambiguous-text.md +91 -0
- package/docs/rules/anchor-has-content.md +64 -0
- package/docs/rules/anchor-is-valid.md +270 -0
- package/docs/rules/aria-activedescendant-has-tabindex.md +52 -0
- package/docs/rules/aria-props.md +29 -0
- package/docs/rules/aria-proptypes.md +30 -0
- package/docs/rules/aria-role.md +51 -0
- package/docs/rules/aria-unsupported-elements.md +30 -0
- package/docs/rules/autocomplete-valid.md +49 -0
- package/docs/rules/click-events-have-key-events.md +28 -0
- package/docs/rules/control-has-associated-label.md +113 -0
- package/docs/rules/heading-has-content.md +67 -0
- package/docs/rules/html-has-lang.md +31 -0
- package/docs/rules/iframe-has-title.md +37 -0
- package/docs/rules/img-redundant-alt.md +48 -0
- package/docs/rules/interactive-supports-focus.md +156 -0
- package/docs/rules/label-has-associated-control.md +152 -0
- package/docs/rules/label-has-for.md +130 -0
- package/docs/rules/lang.md +31 -0
- package/docs/rules/media-has-caption.md +48 -0
- package/docs/rules/mouse-events-have-key-events.md +58 -0
- package/docs/rules/no-access-key.md +30 -0
- package/docs/rules/no-aria-hidden-on-focusable.md +37 -0
- package/docs/rules/no-autofocus.md +43 -0
- package/docs/rules/no-distracting-elements.md +41 -0
- package/docs/rules/no-interactive-element-to-noninteractive-role.md +73 -0
- package/docs/rules/no-noninteractive-element-interactions.md +145 -0
- package/docs/rules/no-noninteractive-element-to-interactive-role.md +76 -0
- package/docs/rules/no-noninteractive-tabindex.md +115 -0
- package/docs/rules/no-onchange.md +36 -0
- package/docs/rules/no-redundant-roles.md +46 -0
- package/docs/rules/no-static-element-interactions.md +114 -0
- package/docs/rules/prefer-tag-over-role.md +32 -0
- package/docs/rules/role-has-required-aria-props.md +31 -0
- package/docs/rules/role-supports-aria-props.md +39 -0
- package/docs/rules/scope.md +30 -0
- package/docs/rules/tabindex-no-positive.md +32 -0
- package/lib/configs/flat-config-base.js +11 -0
- package/lib/configs/legacy-config-base.js +9 -0
- package/lib/index.js +209 -0
- package/lib/rules/accessible-emoji.js +63 -0
- package/lib/rules/alt-text.js +218 -0
- package/lib/rules/anchor-ambiguous-text.js +64 -0
- package/lib/rules/anchor-has-content.js +60 -0
- package/lib/rules/anchor-is-valid.js +122 -0
- package/lib/rules/aria-activedescendant-has-tabindex.js +66 -0
- package/lib/rules/aria-props.js +59 -0
- package/lib/rules/aria-proptypes.js +114 -0
- package/lib/rules/aria-role.js +89 -0
- package/lib/rules/aria-unsupported-elements.js +64 -0
- package/lib/rules/autocomplete-valid.js +67 -0
- package/lib/rules/click-events-have-key-events.js +68 -0
- package/lib/rules/control-has-associated-label.js +103 -0
- package/lib/rules/heading-has-content.js +61 -0
- package/lib/rules/html-has-lang.js +50 -0
- package/lib/rules/iframe-has-title.js +50 -0
- package/lib/rules/img-redundant-alt.js +88 -0
- package/lib/rules/interactive-supports-focus.js +87 -0
- package/lib/rules/label-has-associated-control.js +127 -0
- package/lib/rules/label-has-for.js +150 -0
- package/lib/rules/lang.js +68 -0
- package/lib/rules/media-has-caption.js +96 -0
- package/lib/rules/mouse-events-have-key-events.js +94 -0
- package/lib/rules/no-access-key.js +43 -0
- package/lib/rules/no-aria-hidden-on-focusable.js +47 -0
- package/lib/rules/no-autofocus.js +62 -0
- package/lib/rules/no-distracting-elements.js +54 -0
- package/lib/rules/no-interactive-element-to-noninteractive-role.js +81 -0
- package/lib/rules/no-noninteractive-element-interactions.js +95 -0
- package/lib/rules/no-noninteractive-element-to-interactive-role.js +80 -0
- package/lib/rules/no-noninteractive-tabindex.js +109 -0
- package/lib/rules/no-onchange.js +52 -0
- package/lib/rules/no-redundant-roles.js +86 -0
- package/lib/rules/no-static-element-interactions.js +102 -0
- package/lib/rules/prefer-tag-over-role.js +75 -0
- package/lib/rules/role-has-required-aria-props.js +88 -0
- package/lib/rules/role-supports-aria-props.js +78 -0
- package/lib/rules/scope.js +58 -0
- package/lib/rules/tabindex-no-positive.js +53 -0
- package/lib/util/attributesComparator.js +34 -0
- package/lib/util/getAccessibleChildText.js +55 -0
- package/lib/util/getComputedRole.js +19 -0
- package/lib/util/getElementType.js +30 -0
- package/lib/util/getExplicitRole.js +27 -0
- package/lib/util/getImplicitRole.js +24 -0
- package/lib/util/getSuggestion.js +32 -0
- package/lib/util/getTabIndex.js +34 -0
- package/lib/util/hasAccessibleChild.js +30 -0
- package/lib/util/implicitRoles/a.js +17 -0
- package/lib/util/implicitRoles/area.js +17 -0
- package/lib/util/implicitRoles/article.js +13 -0
- package/lib/util/implicitRoles/aside.js +13 -0
- package/lib/util/implicitRoles/body.js +13 -0
- package/lib/util/implicitRoles/button.js +13 -0
- package/lib/util/implicitRoles/datalist.js +13 -0
- package/lib/util/implicitRoles/details.js +13 -0
- package/lib/util/implicitRoles/dialog.js +13 -0
- package/lib/util/implicitRoles/form.js +13 -0
- package/lib/util/implicitRoles/h1.js +13 -0
- package/lib/util/implicitRoles/h2.js +13 -0
- package/lib/util/implicitRoles/h3.js +13 -0
- package/lib/util/implicitRoles/h4.js +13 -0
- package/lib/util/implicitRoles/h5.js +13 -0
- package/lib/util/implicitRoles/h6.js +13 -0
- package/lib/util/implicitRoles/hr.js +13 -0
- package/lib/util/implicitRoles/img.js +31 -0
- package/lib/util/implicitRoles/index.js +82 -0
- package/lib/util/implicitRoles/input.js +38 -0
- package/lib/util/implicitRoles/li.js +13 -0
- package/lib/util/implicitRoles/link.js +17 -0
- package/lib/util/implicitRoles/menu.js +19 -0
- package/lib/util/implicitRoles/menuitem.js +28 -0
- package/lib/util/implicitRoles/meter.js +13 -0
- package/lib/util/implicitRoles/nav.js +13 -0
- package/lib/util/implicitRoles/ol.js +13 -0
- package/lib/util/implicitRoles/option.js +13 -0
- package/lib/util/implicitRoles/output.js +13 -0
- package/lib/util/implicitRoles/progress.js +13 -0
- package/lib/util/implicitRoles/section.js +13 -0
- package/lib/util/implicitRoles/select.js +13 -0
- package/lib/util/implicitRoles/tbody.js +13 -0
- package/lib/util/implicitRoles/textarea.js +13 -0
- package/lib/util/implicitRoles/tfoot.js +13 -0
- package/lib/util/implicitRoles/thead.js +13 -0
- package/lib/util/implicitRoles/ul.js +13 -0
- package/lib/util/isAbstractRole.js +23 -0
- package/lib/util/isContentEditable.js +13 -0
- package/lib/util/isDOMElement.js +15 -0
- package/lib/util/isDisabledElement.js +23 -0
- package/lib/util/isFocusable.js +23 -0
- package/lib/util/isHiddenFromScreenReader.js +26 -0
- package/lib/util/isInteractiveElement.js +116 -0
- package/lib/util/isInteractiveRole.js +54 -0
- package/lib/util/isNonInteractiveElement.js +131 -0
- package/lib/util/isNonInteractiveRole.js +55 -0
- package/lib/util/isNonLiteralProperty.js +29 -0
- package/lib/util/isPresentationRole.js +13 -0
- package/lib/util/isSemanticRoleElement.js +54 -0
- package/lib/util/mayContainChildComponent.js +50 -0
- package/lib/util/mayHaveAccessibleLabel.js +95 -0
- package/lib/util/schemas.js +52 -0
- package/package.json +120 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Disallow inherently non-interactive elements to be assigned
|
|
3
|
+
* interactive roles.
|
|
4
|
+
* @author Jesse Beach
|
|
5
|
+
* @author $AUTHOR
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// -----------------------------------------------------------------------------
|
|
9
|
+
// Requirements
|
|
10
|
+
// -----------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
import { RuleTester } from 'eslint';
|
|
13
|
+
import { configs } from '../../../src/index';
|
|
14
|
+
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
|
15
|
+
import parsers from '../../__util__/helpers/parsers';
|
|
16
|
+
import rule from '../../../src/rules/no-noninteractive-element-to-interactive-role';
|
|
17
|
+
import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory';
|
|
18
|
+
|
|
19
|
+
// -----------------------------------------------------------------------------
|
|
20
|
+
// Tests
|
|
21
|
+
// -----------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
const ruleTester = new RuleTester();
|
|
24
|
+
|
|
25
|
+
const errorMessage = 'Non-interactive elements should not be assigned interactive roles.';
|
|
26
|
+
|
|
27
|
+
const expectedError = {
|
|
28
|
+
message: errorMessage,
|
|
29
|
+
type: 'JSXAttribute',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const ruleName = 'jsx-a11y/no-noninteractive-element-to-interactive-role';
|
|
33
|
+
|
|
34
|
+
const componentsSettings = {
|
|
35
|
+
'jsx-a11y': {
|
|
36
|
+
components: {
|
|
37
|
+
Article: 'article',
|
|
38
|
+
Input: 'input',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const alwaysValid = [
|
|
44
|
+
{ code: '<TestComponent onClick={doFoo} />' },
|
|
45
|
+
{ code: '<Button onClick={doFoo} />' },
|
|
46
|
+
/* Interactive elements */
|
|
47
|
+
{ code: '<a tabIndex="0" role="button" />' },
|
|
48
|
+
{ code: '<a href="http://x.y.z" role="button" />' },
|
|
49
|
+
{ code: '<a href="http://x.y.z" tabIndex="0" role="button" />' },
|
|
50
|
+
{ code: '<area role="button" />;' },
|
|
51
|
+
{ code: '<area role="menuitem" />;' },
|
|
52
|
+
{ code: '<button className="foo" role="button" />' },
|
|
53
|
+
{ code: '<body role="button" />;' },
|
|
54
|
+
{ code: '<frame role="button" />;' },
|
|
55
|
+
{ code: '<td role="button" />;' },
|
|
56
|
+
{ code: '<frame role="menuitem" />;' },
|
|
57
|
+
{ code: '<td role="menuitem" />;' },
|
|
58
|
+
/* All flavors of input */
|
|
59
|
+
{ code: '<input role="button" />' },
|
|
60
|
+
{ code: '<input type="button" role="button" />' },
|
|
61
|
+
{ code: '<input type="checkbox" role="button" />' },
|
|
62
|
+
{ code: '<input type="color" role="button" />' },
|
|
63
|
+
{ code: '<input type="date" role="button" />' },
|
|
64
|
+
{ code: '<input type="datetime" role="button" />' },
|
|
65
|
+
{ code: '<input type="datetime-local" role="button" />' },
|
|
66
|
+
{ code: '<input type="email" role="button" />' },
|
|
67
|
+
{ code: '<input type="file" role="button" />' },
|
|
68
|
+
{ code: '<input type="hidden" role="button" />' },
|
|
69
|
+
{ code: '<input type="image" role="button" />' },
|
|
70
|
+
{ code: '<input type="month" role="button" />' },
|
|
71
|
+
{ code: '<input type="number" role="button" />' },
|
|
72
|
+
{ code: '<input type="password" role="button" />' },
|
|
73
|
+
{ code: '<input type="radio" role="button" />' },
|
|
74
|
+
{ code: '<input type="range" role="button" />' },
|
|
75
|
+
{ code: '<input type="reset" role="button" />' },
|
|
76
|
+
{ code: '<input type="search" role="button" />' },
|
|
77
|
+
{ code: '<input type="submit" role="button" />' },
|
|
78
|
+
{ code: '<input type="tel" role="button" />' },
|
|
79
|
+
{ code: '<input type="text" role="button" />' },
|
|
80
|
+
{ code: '<input type="time" role="button" />' },
|
|
81
|
+
{ code: '<input type="url" role="button" />' },
|
|
82
|
+
{ code: '<input type="week" role="button" />' },
|
|
83
|
+
{ code: '<input type="hidden" role="img" />' },
|
|
84
|
+
/* End all flavors of input */
|
|
85
|
+
{ code: '<menuitem role="button" />;' },
|
|
86
|
+
{ code: '<option className="foo" role="button" />' },
|
|
87
|
+
{ code: '<select className="foo" role="button" />' },
|
|
88
|
+
{ code: '<textarea className="foo" role="button" />' },
|
|
89
|
+
{ code: '<tr role="button" />;' },
|
|
90
|
+
{ code: '<tr role="presentation" />;' },
|
|
91
|
+
/* Interactive elements */
|
|
92
|
+
{ code: '<a tabIndex="0" role="img" />' },
|
|
93
|
+
{ code: '<a href="http://x.y.z" role="img" />' },
|
|
94
|
+
{ code: '<a href="http://x.y.z" tabIndex="0" role="img" />' },
|
|
95
|
+
/* All flavors of input */
|
|
96
|
+
{ code: '<input role="img" />' },
|
|
97
|
+
{ code: '<input type="img" role="img" />' },
|
|
98
|
+
{ code: '<input type="checkbox" role="img" />' },
|
|
99
|
+
{ code: '<input type="color" role="img" />' },
|
|
100
|
+
{ code: '<input type="date" role="img" />' },
|
|
101
|
+
{ code: '<input type="datetime" role="img" />' },
|
|
102
|
+
{ code: '<input type="datetime-local" role="img" />' },
|
|
103
|
+
{ code: '<input type="email" role="img" />' },
|
|
104
|
+
{ code: '<input type="file" role="img" />' },
|
|
105
|
+
{ code: '<input type="hidden" role="button" />' },
|
|
106
|
+
{ code: '<input type="image" role="img" />' },
|
|
107
|
+
{ code: '<input type="month" role="img" />' },
|
|
108
|
+
{ code: '<input type="number" role="img" />' },
|
|
109
|
+
{ code: '<input type="password" role="img" />' },
|
|
110
|
+
{ code: '<input type="radio" role="img" />' },
|
|
111
|
+
{ code: '<input type="range" role="img" />' },
|
|
112
|
+
{ code: '<input type="reset" role="img" />' },
|
|
113
|
+
{ code: '<input type="search" role="img" />' },
|
|
114
|
+
{ code: '<input type="submit" role="img" />' },
|
|
115
|
+
{ code: '<input type="tel" role="img" />' },
|
|
116
|
+
{ code: '<input type="text" role="img" />' },
|
|
117
|
+
{ code: '<input type="time" role="img" />' },
|
|
118
|
+
{ code: '<input type="url" role="img" />' },
|
|
119
|
+
{ code: '<input type="week" role="img" />' },
|
|
120
|
+
/* End all flavors of input */
|
|
121
|
+
{ code: '<menuitem role="img" />;' },
|
|
122
|
+
{ code: '<option className="foo" role="img" />' },
|
|
123
|
+
{ code: '<select className="foo" role="img" />' },
|
|
124
|
+
{ code: '<textarea className="foo" role="img" />' },
|
|
125
|
+
{ code: '<tr role="img" />;' },
|
|
126
|
+
/* Interactive elements */
|
|
127
|
+
{ code: '<a tabIndex="0" role="listitem" />' },
|
|
128
|
+
{ code: '<a href="http://x.y.z" role="listitem" />' },
|
|
129
|
+
{ code: '<a href="http://x.y.z" tabIndex="0" role="listitem" />' },
|
|
130
|
+
/* All flavors of input */
|
|
131
|
+
{ code: '<input role="listitem" />' },
|
|
132
|
+
{ code: '<input type="listitem" role="listitem" />' },
|
|
133
|
+
{ code: '<input type="checkbox" role="listitem" />' },
|
|
134
|
+
{ code: '<input type="color" role="listitem" />' },
|
|
135
|
+
{ code: '<input type="date" role="listitem" />' },
|
|
136
|
+
{ code: '<input type="datetime" role="listitem" />' },
|
|
137
|
+
{ code: '<input type="datetime-local" role="listitem" />' },
|
|
138
|
+
{ code: '<input type="email" role="listitem" />' },
|
|
139
|
+
{ code: '<input type="file" role="listitem" />' },
|
|
140
|
+
{ code: '<input type="image" role="listitem" />' },
|
|
141
|
+
{ code: '<input type="month" role="listitem" />' },
|
|
142
|
+
{ code: '<input type="number" role="listitem" />' },
|
|
143
|
+
{ code: '<input type="password" role="listitem" />' },
|
|
144
|
+
{ code: '<input type="radio" role="listitem" />' },
|
|
145
|
+
{ code: '<input type="range" role="listitem" />' },
|
|
146
|
+
{ code: '<input type="reset" role="listitem" />' },
|
|
147
|
+
{ code: '<input type="search" role="listitem" />' },
|
|
148
|
+
{ code: '<input type="submit" role="listitem" />' },
|
|
149
|
+
{ code: '<input type="tel" role="listitem" />' },
|
|
150
|
+
{ code: '<input type="text" role="listitem" />' },
|
|
151
|
+
{ code: '<input type="time" role="listitem" />' },
|
|
152
|
+
{ code: '<input type="url" role="listitem" />' },
|
|
153
|
+
{ code: '<input type="week" role="listitem" />' },
|
|
154
|
+
/* End all flavors of input */
|
|
155
|
+
{ code: '<menuitem role="listitem" />;' },
|
|
156
|
+
{ code: '<option className="foo" role="listitem" />' },
|
|
157
|
+
{ code: '<select className="foo" role="listitem" />' },
|
|
158
|
+
{ code: '<textarea className="foo" role="listitem" />' },
|
|
159
|
+
{ code: '<tr role="listitem" />;' },
|
|
160
|
+
/* HTML elements with neither an interactive or non-interactive valence (static) */
|
|
161
|
+
{ code: '<acronym role="button" />;' },
|
|
162
|
+
{ code: '<applet role="button" />;' },
|
|
163
|
+
{ code: '<audio role="button" />;' },
|
|
164
|
+
{ code: '<b role="button" />;' },
|
|
165
|
+
{ code: '<base role="button" />;' },
|
|
166
|
+
{ code: '<bdi role="button" />;' },
|
|
167
|
+
{ code: '<bdo role="button" />;' },
|
|
168
|
+
{ code: '<big role="button" />;' },
|
|
169
|
+
{ code: '<blink role="button" />;' },
|
|
170
|
+
{ code: '<canvas role="button" />;' },
|
|
171
|
+
{ code: '<center role="button" />;' },
|
|
172
|
+
{ code: '<cite role="button" />;' },
|
|
173
|
+
{ code: '<col role="button" />;' },
|
|
174
|
+
{ code: '<colgroup role="button" />;' },
|
|
175
|
+
{ code: '<content role="button" />;' },
|
|
176
|
+
{ code: '<data role="button" />;' },
|
|
177
|
+
{ code: '<datalist role="button" />;' },
|
|
178
|
+
{ code: '<div role="button" />;' },
|
|
179
|
+
{ code: '<div className="foo" role="button" />;' },
|
|
180
|
+
{ code: '<div className="foo" {...props} role="button" />;' },
|
|
181
|
+
{ code: '<div aria-hidden role="button" />;' },
|
|
182
|
+
{ code: '<div aria-hidden={true} role="button" />;' },
|
|
183
|
+
{ code: '<div role="button" />;' },
|
|
184
|
+
{ code: '<div role={undefined} role="button" />;' },
|
|
185
|
+
{ code: '<div {...props} role="button" />;' },
|
|
186
|
+
{ code: '<div onKeyUp={() => void 0} aria-hidden={false} role="button" />;' },
|
|
187
|
+
{ code: '<embed role="button" />;' },
|
|
188
|
+
{ code: '<font role="button" />;' },
|
|
189
|
+
{ code: '<frameset role="button" />;' },
|
|
190
|
+
{ code: '<head role="button" />;' },
|
|
191
|
+
{ code: '<header role="button" />;' },
|
|
192
|
+
{ code: '<hgroup role="button" />;' },
|
|
193
|
+
{ code: '<i role="button" />;' },
|
|
194
|
+
{ code: '<kbd role="button" />;' },
|
|
195
|
+
{ code: '<keygen role="button" />;' },
|
|
196
|
+
{ code: '<link role="button" />;' },
|
|
197
|
+
{ code: '<map role="button" />;' },
|
|
198
|
+
{ code: '<meta role="button" />;' },
|
|
199
|
+
{ code: '<noembed role="button" />;' },
|
|
200
|
+
{ code: '<noscript role="button" />;' },
|
|
201
|
+
{ code: '<object role="button" />;' },
|
|
202
|
+
{ code: '<param role="button" />;' },
|
|
203
|
+
{ code: '<picture role="button" />;' },
|
|
204
|
+
{ code: '<q role="button" />;' },
|
|
205
|
+
{ code: '<rp role="button" />;' },
|
|
206
|
+
{ code: '<rt role="button" />;' },
|
|
207
|
+
{ code: '<rtc role="button" />;' },
|
|
208
|
+
{ code: '<s role="button" />;' },
|
|
209
|
+
{ code: '<samp role="button" />;' },
|
|
210
|
+
{ code: '<script role="button" />;' },
|
|
211
|
+
{ code: '<small role="button" />;' },
|
|
212
|
+
{ code: '<source role="button" />;' },
|
|
213
|
+
{ code: '<spacer role="button" />;' },
|
|
214
|
+
{ code: '<span role="button" />;' },
|
|
215
|
+
{ code: '<strike role="button" />;' },
|
|
216
|
+
{ code: '<style role="button" />;' },
|
|
217
|
+
{ code: '<summary role="button" />;' },
|
|
218
|
+
{ code: '<th role="button" />;' },
|
|
219
|
+
{ code: '<title role="button" />;' },
|
|
220
|
+
{ code: '<track role="button" />;' },
|
|
221
|
+
{ code: '<tt role="button" />;' },
|
|
222
|
+
{ code: '<u role="button" />;' },
|
|
223
|
+
{ code: '<var role="button" />;' },
|
|
224
|
+
{ code: '<video role="button" />;' },
|
|
225
|
+
{ code: '<wbr role="button" />;' },
|
|
226
|
+
{ code: '<xmp role="button" />;' },
|
|
227
|
+
/* HTML elements attributed with an interactive role */
|
|
228
|
+
{ code: '<div role="button" />;' },
|
|
229
|
+
{ code: '<div role="checkbox" />;' },
|
|
230
|
+
{ code: '<div role="columnheader" />;' },
|
|
231
|
+
{ code: '<div role="combobox" />;' },
|
|
232
|
+
{ code: '<div role="grid" />;' },
|
|
233
|
+
{ code: '<div role="gridcell" />;' },
|
|
234
|
+
{ code: '<div role="link" />;' },
|
|
235
|
+
{ code: '<div role="listbox" />;' },
|
|
236
|
+
{ code: '<div role="menu" />;' },
|
|
237
|
+
{ code: '<div role="menubar" />;' },
|
|
238
|
+
{ code: '<div role="menuitem" />;' },
|
|
239
|
+
{ code: '<div role="menuitemcheckbox" />;' },
|
|
240
|
+
{ code: '<div role="menuitemradio" />;' },
|
|
241
|
+
{ code: '<div role="option" />;' },
|
|
242
|
+
{ code: '<div role="progressbar" />;' },
|
|
243
|
+
{ code: '<div role="radio" />;' },
|
|
244
|
+
{ code: '<div role="radiogroup" />;' },
|
|
245
|
+
{ code: '<div role="row" />;' },
|
|
246
|
+
{ code: '<div role="rowheader" />;' },
|
|
247
|
+
{ code: '<div role="searchbox" />;' },
|
|
248
|
+
{ code: '<div role="slider" />;' },
|
|
249
|
+
{ code: '<div role="spinbutton" />;' },
|
|
250
|
+
{ code: '<div role="switch" />;' },
|
|
251
|
+
{ code: '<div role="tab" />;' },
|
|
252
|
+
{ code: '<div role="textbox" />;' },
|
|
253
|
+
{ code: '<div role="treeitem" />;' },
|
|
254
|
+
/* Presentation is a special case role that indicates intentional static semantics */
|
|
255
|
+
{ code: '<div role="presentation" />;' },
|
|
256
|
+
/* HTML elements attributed with an abstract role */
|
|
257
|
+
{ code: '<div role="command" />;' },
|
|
258
|
+
{ code: '<div role="composite" />;' },
|
|
259
|
+
{ code: '<div role="input" />;' },
|
|
260
|
+
{ code: '<div role="landmark" />;' },
|
|
261
|
+
{ code: '<div role="range" />;' },
|
|
262
|
+
{ code: '<div role="roletype" />;' },
|
|
263
|
+
{ code: '<div role="section" />;' },
|
|
264
|
+
{ code: '<div role="sectionhead" />;' },
|
|
265
|
+
{ code: '<div role="select" />;' },
|
|
266
|
+
{ code: '<div role="structure" />;' },
|
|
267
|
+
{ code: '<div role="tablist" />;' },
|
|
268
|
+
{ code: '<div role="toolbar" />;' },
|
|
269
|
+
{ code: '<div role="tree" />;' },
|
|
270
|
+
{ code: '<div role="treegrid" />;' },
|
|
271
|
+
{ code: '<div role="widget" />;' },
|
|
272
|
+
{ code: '<div role="window" />;' },
|
|
273
|
+
/* HTML elements with an inherent non-interactive role, assigned an
|
|
274
|
+
* interactive role. */
|
|
275
|
+
{ code: '<main role="listitem" />;' },
|
|
276
|
+
{ code: '<a role="listitem" />' },
|
|
277
|
+
{ code: '<a role="listitem" />;' },
|
|
278
|
+
{ code: '<a role="button" />' },
|
|
279
|
+
{ code: '<a role="button" />;' },
|
|
280
|
+
{ code: '<a role="menuitem" />' },
|
|
281
|
+
{ code: '<a role="menuitem" />;' },
|
|
282
|
+
{ code: '<area role="listitem" />;' },
|
|
283
|
+
{ code: '<article role="listitem" />;' },
|
|
284
|
+
{ code: '<article role="listitem" />;' },
|
|
285
|
+
{ code: '<dd role="listitem" />;' },
|
|
286
|
+
{ code: '<dfn role="listitem" />;' },
|
|
287
|
+
{ code: '<dt role="listitem" />;' },
|
|
288
|
+
{ code: '<fieldset role="listitem" />;' },
|
|
289
|
+
{ code: '<figure role="listitem" />;' },
|
|
290
|
+
{ code: '<form role="listitem" />;' },
|
|
291
|
+
{ code: '<frame role="listitem" />;' },
|
|
292
|
+
{ code: '<h1 role="listitem" />;' },
|
|
293
|
+
{ code: '<h2 role="listitem" />;' },
|
|
294
|
+
{ code: '<h3 role="listitem" />;' },
|
|
295
|
+
{ code: '<h4 role="listitem" />;' },
|
|
296
|
+
{ code: '<h5 role="listitem" />;' },
|
|
297
|
+
{ code: '<h6 role="listitem" />;' },
|
|
298
|
+
{ code: '<hr role="listitem" />;' },
|
|
299
|
+
{ code: '<img role="listitem" />;' },
|
|
300
|
+
{ code: '<li role="listitem" />;' },
|
|
301
|
+
{ code: '<li role="presentation" />;' },
|
|
302
|
+
{ code: '<nav role="listitem" />;' },
|
|
303
|
+
{ code: '<ol role="listitem" />;' },
|
|
304
|
+
{ code: '<table role="listitem" />;' },
|
|
305
|
+
{ code: '<tbody role="listitem" />;' },
|
|
306
|
+
{ code: '<td role="listitem" />;' },
|
|
307
|
+
{ code: '<tfoot role="listitem" />;' },
|
|
308
|
+
{ code: '<thead role="listitem" />;' },
|
|
309
|
+
{ code: '<ul role="listitem" />;' },
|
|
310
|
+
/* HTML elements attributed with a non-interactive role */
|
|
311
|
+
{ code: '<div role="alert" />;' },
|
|
312
|
+
{ code: '<div role="alertdialog" />;' },
|
|
313
|
+
{ code: '<div role="application" />;' },
|
|
314
|
+
{ code: '<div role="article" />;' },
|
|
315
|
+
{ code: '<div role="banner" />;' },
|
|
316
|
+
{ code: '<div role="cell" />;' },
|
|
317
|
+
{ code: '<div role="complementary" />;' },
|
|
318
|
+
{ code: '<div role="contentinfo" />;' },
|
|
319
|
+
{ code: '<div role="definition" />;' },
|
|
320
|
+
{ code: '<div role="dialog" />;' },
|
|
321
|
+
{ code: '<div role="directory" />;' },
|
|
322
|
+
{ code: '<div role="document" />;' },
|
|
323
|
+
{ code: '<div role="feed" />;' },
|
|
324
|
+
{ code: '<div role="figure" />;' },
|
|
325
|
+
{ code: '<div role="form" />;' },
|
|
326
|
+
{ code: '<div role="group" />;' },
|
|
327
|
+
{ code: '<div role="heading" />;' },
|
|
328
|
+
{ code: '<div role="img" />;' },
|
|
329
|
+
{ code: '<div role="list" />;' },
|
|
330
|
+
{ code: '<div role="listitem" />;' },
|
|
331
|
+
{ code: '<div role="log" />;' },
|
|
332
|
+
{ code: '<div role="main" />;' },
|
|
333
|
+
{ code: '<div role="marquee" />;' },
|
|
334
|
+
{ code: '<div role="math" />;' },
|
|
335
|
+
{ code: '<div role="navigation" />;' },
|
|
336
|
+
{ code: '<div role="note" />;' },
|
|
337
|
+
{ code: '<div role="region" />;' },
|
|
338
|
+
{ code: '<div role="rowgroup" />;' },
|
|
339
|
+
{ code: '<div role="search" />;' },
|
|
340
|
+
{ code: '<div role="separator" />;' },
|
|
341
|
+
{ code: '<div role="scrollbar" />;' },
|
|
342
|
+
{ code: '<div role="status" />;' },
|
|
343
|
+
{ code: '<div role="table" />;' },
|
|
344
|
+
{ code: '<div role="tabpanel" />;' },
|
|
345
|
+
{ code: '<div role="term" />;' },
|
|
346
|
+
{ code: '<div role="timer" />;' },
|
|
347
|
+
{ code: '<div role="tooltip" />;' },
|
|
348
|
+
{ code: '<ul role="list" />;' },
|
|
349
|
+
/* Custom components */
|
|
350
|
+
{ code: '<Article role="button" />' },
|
|
351
|
+
{ code: '<Input role="button" />', settings: componentsSettings },
|
|
352
|
+
];
|
|
353
|
+
|
|
354
|
+
const neverValid = [
|
|
355
|
+
/* HTML elements with an inherent non-interactive role, assigned an
|
|
356
|
+
* interactive role. */
|
|
357
|
+
{ code: '<address role="button" />;', errors: [expectedError] },
|
|
358
|
+
{ code: '<article role="button" />;', errors: [expectedError] },
|
|
359
|
+
{ code: '<aside role="button" />;', errors: [expectedError] },
|
|
360
|
+
{ code: '<blockquote role="button" />;', errors: [expectedError] },
|
|
361
|
+
{ code: '<br role="button" />;', errors: [expectedError] },
|
|
362
|
+
{ code: '<caption role="button" />;', errors: [expectedError] },
|
|
363
|
+
{ code: '<code role="button" />;', errors: [expectedError] },
|
|
364
|
+
{ code: '<dd role="button" />;', errors: [expectedError] },
|
|
365
|
+
{ code: '<del role="button" />;', errors: [expectedError] },
|
|
366
|
+
{ code: '<details role="button" />;', errors: [expectedError] },
|
|
367
|
+
{ code: '<dfn role="button" />;', errors: [expectedError] },
|
|
368
|
+
{ code: '<dir role="button" />;', errors: [expectedError] },
|
|
369
|
+
{ code: '<dl role="button" />;', errors: [expectedError] },
|
|
370
|
+
{ code: '<dt role="button" />;', errors: [expectedError] },
|
|
371
|
+
{ code: '<em role="button" />;', errors: [expectedError] },
|
|
372
|
+
{ code: '<fieldset role="button" />;', errors: [expectedError] },
|
|
373
|
+
{ code: '<figcaption role="button" />;', errors: [expectedError] },
|
|
374
|
+
{ code: '<figure role="button" />;', errors: [expectedError] },
|
|
375
|
+
{ code: '<footer role="button" />;', errors: [expectedError] },
|
|
376
|
+
{ code: '<form role="button" />;', errors: [expectedError] },
|
|
377
|
+
{ code: '<h1 role="button" />;', errors: [expectedError] },
|
|
378
|
+
{ code: '<h2 role="button" />;', errors: [expectedError] },
|
|
379
|
+
{ code: '<h3 role="button" />;', errors: [expectedError] },
|
|
380
|
+
{ code: '<h4 role="button" />;', errors: [expectedError] },
|
|
381
|
+
{ code: '<h5 role="button" />;', errors: [expectedError] },
|
|
382
|
+
{ code: '<h6 role="button" />;', errors: [expectedError] },
|
|
383
|
+
{ code: '<hr role="button" />;', errors: [expectedError] },
|
|
384
|
+
{ code: '<html role="button" />;', errors: [expectedError] },
|
|
385
|
+
{ code: '<iframe role="button" />;', errors: [expectedError] },
|
|
386
|
+
{ code: '<img role="button" />;', errors: [expectedError] },
|
|
387
|
+
{ code: '<ins role="button" />;', errors: [expectedError] },
|
|
388
|
+
{ code: '<label role="button" />;', errors: [expectedError] },
|
|
389
|
+
{ code: '<legend role="button" />;', errors: [expectedError] },
|
|
390
|
+
{ code: '<li role="button" />;', errors: [expectedError] },
|
|
391
|
+
{ code: '<main role="button" />;', errors: [expectedError] },
|
|
392
|
+
{ code: '<mark role="button" />;', errors: [expectedError] },
|
|
393
|
+
{ code: '<marquee role="button" />;', errors: [expectedError] },
|
|
394
|
+
{ code: '<menu role="button" />;', errors: [expectedError] },
|
|
395
|
+
{ code: '<meter role="button" />;', errors: [expectedError] },
|
|
396
|
+
{ code: '<nav role="button" />;', errors: [expectedError] },
|
|
397
|
+
{ code: '<ol role="button" />;', errors: [expectedError] },
|
|
398
|
+
{ code: '<optgroup role="button" />;', errors: [expectedError] },
|
|
399
|
+
{ code: '<output role="button" />;', errors: [expectedError] },
|
|
400
|
+
{ code: '<pre role="button" />;', errors: [expectedError] },
|
|
401
|
+
{ code: '<progress role="button" />;', errors: [expectedError] },
|
|
402
|
+
{ code: '<ruby role="button" />;', errors: [expectedError] },
|
|
403
|
+
{ code: '<strong role="button" />;', errors: [expectedError] },
|
|
404
|
+
{ code: '<sub role="button" />;', errors: [expectedError] },
|
|
405
|
+
{ code: '<sup role="button" />;', errors: [expectedError] },
|
|
406
|
+
{ code: '<table role="button" />;', errors: [expectedError] },
|
|
407
|
+
{ code: '<tbody role="button" />;', errors: [expectedError] },
|
|
408
|
+
{ code: '<tfoot role="button" />;', errors: [expectedError] },
|
|
409
|
+
{ code: '<thead role="button" />;', errors: [expectedError] },
|
|
410
|
+
{ code: '<time role="button" />;', errors: [expectedError] },
|
|
411
|
+
{ code: '<ul role="button" />;', errors: [expectedError] },
|
|
412
|
+
/* HTML elements with an inherent non-interactive role, assigned an
|
|
413
|
+
* interactive role. */
|
|
414
|
+
{ code: '<main role="menuitem" />;', errors: [expectedError] },
|
|
415
|
+
{ code: '<article role="menuitem" />;', errors: [expectedError] },
|
|
416
|
+
{ code: '<dd role="menuitem" />;', errors: [expectedError] },
|
|
417
|
+
{ code: '<dfn role="menuitem" />;', errors: [expectedError] },
|
|
418
|
+
{ code: '<dt role="menuitem" />;', errors: [expectedError] },
|
|
419
|
+
{ code: '<fieldset role="menuitem" />;', errors: [expectedError] },
|
|
420
|
+
{ code: '<figure role="menuitem" />;', errors: [expectedError] },
|
|
421
|
+
{ code: '<form role="menuitem" />;', errors: [expectedError] },
|
|
422
|
+
{ code: '<h1 role="menuitem" />;', errors: [expectedError] },
|
|
423
|
+
{ code: '<h2 role="menuitem" />;', errors: [expectedError] },
|
|
424
|
+
{ code: '<h3 role="menuitem" />;', errors: [expectedError] },
|
|
425
|
+
{ code: '<h4 role="menuitem" />;', errors: [expectedError] },
|
|
426
|
+
{ code: '<h5 role="menuitem" />;', errors: [expectedError] },
|
|
427
|
+
{ code: '<h6 role="menuitem" />;', errors: [expectedError] },
|
|
428
|
+
{ code: '<hr role="menuitem" />;', errors: [expectedError] },
|
|
429
|
+
{ code: '<img role="menuitem" />;', errors: [expectedError] },
|
|
430
|
+
{ code: '<nav role="menuitem" />;', errors: [expectedError] },
|
|
431
|
+
{ code: '<ol role="menuitem" />;', errors: [expectedError] },
|
|
432
|
+
{ code: '<p role="button" />;', errors: [expectedError] },
|
|
433
|
+
{ code: '<section role="button" aria-label="Aardvark" />;', errors: [expectedError] },
|
|
434
|
+
{ code: '<table role="menuitem" />;', errors: [expectedError] },
|
|
435
|
+
{ code: '<tbody role="menuitem" />;', errors: [expectedError] },
|
|
436
|
+
{ code: '<tfoot role="menuitem" />;', errors: [expectedError] },
|
|
437
|
+
{ code: '<thead role="menuitem" />;', errors: [expectedError] },
|
|
438
|
+
/* Custom components */
|
|
439
|
+
{ code: '<Article role="button" />', errors: [expectedError], settings: componentsSettings },
|
|
440
|
+
];
|
|
441
|
+
|
|
442
|
+
const recommendedOptions = (configs.recommended.rules[ruleName][1] || {});
|
|
443
|
+
ruleTester.run(`${ruleName}:recommended`, rule, {
|
|
444
|
+
valid: parsers.all([].concat(
|
|
445
|
+
...alwaysValid,
|
|
446
|
+
{ code: '<ul role="menu" />;' },
|
|
447
|
+
{ code: '<ul role="menubar" />;' },
|
|
448
|
+
{ code: '<ul role="radiogroup" />;' },
|
|
449
|
+
{ code: '<ul role="tablist" />;' },
|
|
450
|
+
{ code: '<ul role="tree" />;' },
|
|
451
|
+
{ code: '<ul role="treegrid" />;' },
|
|
452
|
+
{ code: '<ol role="menu" />;' },
|
|
453
|
+
{ code: '<ol role="menubar" />;' },
|
|
454
|
+
{ code: '<ol role="radiogroup" />;' },
|
|
455
|
+
{ code: '<ol role="tablist" />;' },
|
|
456
|
+
{ code: '<ol role="tree" />;' },
|
|
457
|
+
{ code: '<ol role="treegrid" />;' },
|
|
458
|
+
{ code: '<li role="tab" />;' },
|
|
459
|
+
{ code: '<li role="menuitem" />;' },
|
|
460
|
+
{ code: '<li role="menuitemcheckbox" />;' },
|
|
461
|
+
{ code: '<li role="menuitemradio" />;' },
|
|
462
|
+
{ code: '<li role="row" />;' },
|
|
463
|
+
{ code: '<li role="treeitem" />;' },
|
|
464
|
+
{ code: '<Component role="treeitem" />;' },
|
|
465
|
+
{ code: '<fieldset role="radiogroup" />;' },
|
|
466
|
+
{ code: '<fieldset role="presentation" />;' },
|
|
467
|
+
))
|
|
468
|
+
.map(ruleOptionsMapperFactory(recommendedOptions))
|
|
469
|
+
.map(parserOptionsMapper),
|
|
470
|
+
invalid: parsers.all([].concat(
|
|
471
|
+
...neverValid,
|
|
472
|
+
))
|
|
473
|
+
.map(ruleOptionsMapperFactory(recommendedOptions))
|
|
474
|
+
.map(parserOptionsMapper),
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
ruleTester.run(`${ruleName}:strict`, rule, {
|
|
478
|
+
valid: parsers.all([].concat(
|
|
479
|
+
...alwaysValid,
|
|
480
|
+
)).map(parserOptionsMapper),
|
|
481
|
+
invalid: parsers.all([].concat(
|
|
482
|
+
...neverValid,
|
|
483
|
+
{ code: '<ul role="menu" />;', errors: [expectedError] },
|
|
484
|
+
{ code: '<ul role="menubar" />;', errors: [expectedError] },
|
|
485
|
+
{ code: '<ul role="radiogroup" />;', errors: [expectedError] },
|
|
486
|
+
{ code: '<ul role="tablist" />;', errors: [expectedError] },
|
|
487
|
+
{ code: '<ul role="tree" />;', errors: [expectedError] },
|
|
488
|
+
{ code: '<ul role="treegrid" />;', errors: [expectedError] },
|
|
489
|
+
{ code: '<ol role="menu" />;', errors: [expectedError] },
|
|
490
|
+
{ code: '<ol role="menubar" />;', errors: [expectedError] },
|
|
491
|
+
{ code: '<ol role="radiogroup" />;', errors: [expectedError] },
|
|
492
|
+
{ code: '<ol role="tablist" />;', errors: [expectedError] },
|
|
493
|
+
{ code: '<ol role="tree" />;', errors: [expectedError] },
|
|
494
|
+
{ code: '<ol role="treegrid" />;', errors: [expectedError] },
|
|
495
|
+
{ code: '<li role="tab" />;', errors: [expectedError] },
|
|
496
|
+
{ code: '<li role="menuitem" />;', errors: [expectedError] },
|
|
497
|
+
{ code: '<li role="row" />;', errors: [expectedError] },
|
|
498
|
+
{ code: '<li role="treeitem" />;', errors: [expectedError] },
|
|
499
|
+
)).map(parserOptionsMapper),
|
|
500
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Disallow tabindex on static and noninteractive elements
|
|
3
|
+
* @author jessebeach
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// -----------------------------------------------------------------------------
|
|
7
|
+
// Requirements
|
|
8
|
+
// -----------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
import { RuleTester } from 'eslint';
|
|
11
|
+
import { configs } from '../../../src/index';
|
|
12
|
+
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
|
13
|
+
import parsers from '../../__util__/helpers/parsers';
|
|
14
|
+
import rule from '../../../src/rules/no-noninteractive-tabindex';
|
|
15
|
+
import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory';
|
|
16
|
+
|
|
17
|
+
// -----------------------------------------------------------------------------
|
|
18
|
+
// Tests
|
|
19
|
+
// -----------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
const ruleTester = new RuleTester();
|
|
22
|
+
|
|
23
|
+
const ruleName = 'no-noninteractive-tabindex';
|
|
24
|
+
|
|
25
|
+
const expectedError = {
|
|
26
|
+
message: '`tabIndex` should only be declared on interactive elements.',
|
|
27
|
+
type: 'JSXAttribute',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const componentsSettings = {
|
|
31
|
+
'jsx-a11y': {
|
|
32
|
+
components: {
|
|
33
|
+
Article: 'article',
|
|
34
|
+
MyButton: 'button',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const alwaysValid = [
|
|
40
|
+
{ code: '<MyButton tabIndex={0} />' },
|
|
41
|
+
{ code: '<button />' },
|
|
42
|
+
{ code: '<button tabIndex="0" />' },
|
|
43
|
+
{ code: '<button tabIndex={0} />' },
|
|
44
|
+
{ code: '<div />' },
|
|
45
|
+
{ code: '<div tabIndex="-1" />' },
|
|
46
|
+
{ code: '<div role="button" tabIndex="0" />' },
|
|
47
|
+
{ code: '<div role="article" tabIndex="-1" />' },
|
|
48
|
+
{ code: '<article tabIndex="-1" />' },
|
|
49
|
+
{ code: '<Article tabIndex="-1" />', settings: componentsSettings },
|
|
50
|
+
{ code: '<MyButton tabIndex={0} />', settings: componentsSettings },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const neverValid = [
|
|
54
|
+
{ code: '<div tabIndex="0" />', errors: [expectedError] },
|
|
55
|
+
{ code: '<div role="article" tabIndex="0" />', errors: [expectedError] },
|
|
56
|
+
{ code: '<article tabIndex="0" />', errors: [expectedError] },
|
|
57
|
+
{ code: '<article tabIndex={0} />', errors: [expectedError] },
|
|
58
|
+
{ code: '<Article tabIndex={0} />', errors: [expectedError], settings: componentsSettings },
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const recommendedOptions = (
|
|
62
|
+
configs.recommended.rules[`jsx-a11y/${ruleName}`][1] || {}
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
ruleTester.run(`${ruleName}:recommended`, rule, {
|
|
66
|
+
valid: parsers.all([].concat(
|
|
67
|
+
...alwaysValid,
|
|
68
|
+
{ code: '<div role="tabpanel" tabIndex="0" />' },
|
|
69
|
+
// Expressions should pass in recommended mode
|
|
70
|
+
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} tabIndex="0" />;' },
|
|
71
|
+
// Cases for allowExpressionValues set to true
|
|
72
|
+
{
|
|
73
|
+
code: '<div role={BUTTON} onClick={() => {}} tabIndex="0" />;',
|
|
74
|
+
options: [{ allowExpressionValues: true }],
|
|
75
|
+
},
|
|
76
|
+
// Specific case for ternary operator with literals on both side
|
|
77
|
+
{
|
|
78
|
+
code: '<div role={isButton ? "button" : "link"} onClick={() => {}} tabIndex="0" />;',
|
|
79
|
+
options: [{ allowExpressionValues: true }],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
code: '<div role={isButton ? "button" : LINK} onClick={() => {}} tabIndex="0" />;',
|
|
83
|
+
options: [{ allowExpressionValues: true }],
|
|
84
|
+
errors: [expectedError],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
code: '<div role={isButton ? BUTTON : LINK} onClick={() => {}} tabIndex="0"/>;',
|
|
88
|
+
options: [{ allowExpressionValues: true }],
|
|
89
|
+
errors: [expectedError],
|
|
90
|
+
},
|
|
91
|
+
))
|
|
92
|
+
.map(ruleOptionsMapperFactory(recommendedOptions))
|
|
93
|
+
.map(parserOptionsMapper),
|
|
94
|
+
invalid: parsers.all([].concat(
|
|
95
|
+
...neverValid,
|
|
96
|
+
))
|
|
97
|
+
.map(ruleOptionsMapperFactory(recommendedOptions))
|
|
98
|
+
.map(parserOptionsMapper),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
ruleTester.run(`${ruleName}:strict`, rule, {
|
|
102
|
+
valid: parsers.all([].concat(
|
|
103
|
+
...alwaysValid,
|
|
104
|
+
)).map(parserOptionsMapper),
|
|
105
|
+
invalid: parsers.all([].concat(
|
|
106
|
+
...neverValid,
|
|
107
|
+
{ code: '<div role="tabpanel" tabIndex="0" />', errors: [expectedError] },
|
|
108
|
+
// Expressions should fail in strict mode
|
|
109
|
+
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} tabIndex="0" />;', errors: [expectedError] },
|
|
110
|
+
// Cases for allowExpressionValues set to false
|
|
111
|
+
{
|
|
112
|
+
code: '<div role={BUTTON} onClick={() => {}} tabIndex="0" />;',
|
|
113
|
+
options: [{ allowExpressionValues: false }],
|
|
114
|
+
errors: [expectedError],
|
|
115
|
+
},
|
|
116
|
+
// Specific case for ternary operator with literals on both side
|
|
117
|
+
{
|
|
118
|
+
code: '<div role={isButton ? "button" : "link"} onClick={() => {}} tabIndex="0" />;',
|
|
119
|
+
options: [{ allowExpressionValues: false }],
|
|
120
|
+
errors: [expectedError],
|
|
121
|
+
},
|
|
122
|
+
)).map(parserOptionsMapper),
|
|
123
|
+
});
|