@@ -25,9 +25,10 @@ const parserOptions = {
25
25
const ruleTester = new RuleTester ( ) ;
26
26
27
27
const expectedError = {
28
- message : 'Elements with onClick handlers must be focusable. ' +
29
- 'Either set the tabIndex property to a valid value (usually 0), ' +
30
- 'or use an element type which is inherently focusable such as `button`.' ,
28
+ message : 'An non-interactive element with an onClick handler and an ' +
29
+ 'interactive role must be focusable. Either set the tabIndex property to ' +
30
+ 'a valid value (usually 0) or use an element type which is inherently ' +
31
+ 'focusable such as `button`.' ,
31
32
type : 'JSXOpeningElement' ,
32
33
} ;
33
34
@@ -43,94 +44,159 @@ ruleTester.run('onclick-has-focus', rule, {
43
44
{ code : '<div aria-hidden={1 <= 2} onClick={() => void 0} />' , parserOptions } ,
44
45
{ code : '<div aria-hidden={2 > 1} onClick={() => void 0} />' , parserOptions } ,
45
46
{ code : '<div aria-hidden={2 >= 1} onClick={() => void 0} />' , parserOptions } ,
47
+ { code : '<div onClick={() => void 0} />;' , parserOptions } ,
48
+ { code : '<div onClick={() => void 0} tabIndex={undefined} />;' , parserOptions } ,
49
+ { code : '<div onClick={() => void 0} tabIndex="bad" />;' , parserOptions } ,
50
+ { code : '<div onClick={() => void 0} role={undefined} />;' , parserOptions } ,
51
+ { code : '<div role="section" onClick={() => void 0} />' , parserOptions } ,
52
+ { code : '<div onClick={() => void 0} aria-hidden={false} />;' , parserOptions } ,
53
+ { code : '<div onClick={() => void 0} {...props} />;' , parserOptions } ,
46
54
{ code : '<input type="text" onClick={() => void 0} />' , parserOptions } ,
47
55
{ code : '<input type="hidden" onClick={() => void 0} tabIndex="-1" />' , parserOptions } ,
48
56
{ code : '<input type="hidden" onClick={() => void 0} tabIndex={-1} />' , parserOptions } ,
49
57
{ code : '<input onClick={() => void 0} />' , parserOptions } ,
58
+ { code : '<input onClick={() => void 0} role="combobox" />' , parserOptions } ,
50
59
{ code : '<button onClick={() => void 0} className="foo" />' , parserOptions } ,
51
60
{ code : '<option onClick={() => void 0} className="foo" />' , parserOptions } ,
52
61
{ code : '<select onClick={() => void 0} className="foo" />' , parserOptions } ,
53
62
{ code : '<area href="#" onClick={() => void 0} className="foo" />' , parserOptions } ,
63
+ { code : '<area onClick={() => void 0} className="foo" />' , parserOptions } ,
54
64
{ code : '<textarea onClick={() => void 0} className="foo" />' , parserOptions } ,
65
+ { code : '<a onClick="showNextPage();">Next page</a>' , parserOptions } ,
66
+ { code : '<a onClick="showNextPage();" tabIndex={undefined}>Next page</a>' , parserOptions } ,
67
+ { code : '<a onClick="showNextPage();" tabIndex="bad">Next page</a>' , parserOptions } ,
68
+ { code : '<a onClick={() => void 0} />' , parserOptions } ,
55
69
{ code : '<a tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
56
70
{ code : '<a tabIndex={dynamicTabIndex} onClick={() => void 0} />' , parserOptions } ,
57
71
{ code : '<a tabIndex={0} onClick={() => void 0} />' , parserOptions } ,
58
72
{ code : '<a role="button" href="#" onClick={() => void 0} />' , parserOptions } ,
59
73
{ code : '<a onClick={() => void 0} href="http://x.y.z" />' , parserOptions } ,
60
74
{ code : '<a onClick={() => void 0} href="http://x.y.z" tabIndex="0" />' , parserOptions } ,
61
75
{ code : '<a onClick={() => void 0} href="http://x.y.z" tabIndex={0} />' , parserOptions } ,
76
+ { code : '<a onClick={() => void 0} href="http://x.y.z" role="button" />' , parserOptions } ,
62
77
{ code : '<TestComponent onClick={doFoo} />' , parserOptions } ,
63
78
{ code : '<input onClick={() => void 0} type="hidden" />;' , parserOptions } ,
79
+ { code : '<span onClick="submitForm();">Submit</span>' , errors : [ expectedError ] , parserOptions } ,
80
+ { code : '<span onClick="submitForm();" tabIndex={undefined}>Submit</span>' , parserOptions } ,
81
+ { code : '<span onClick="submitForm();" tabIndex="bad">Submit</span>' , parserOptions } ,
64
82
{ code : '<span onClick="doSomething();" tabIndex="0">Click me!</span>' , parserOptions } ,
65
83
{ code : '<span onClick="doSomething();" tabIndex={0}>Click me!</span>' , parserOptions } ,
66
84
{ code : '<span onClick="doSomething();" tabIndex="-1">Click me too!</span>' , parserOptions } ,
67
85
{
68
86
code : '<a href="javascript:void(0);" onClick="doSomething();">Click ALL the things!</a>' ,
69
87
parserOptions,
70
88
} ,
89
+ { code : '<section onClick={() => void 0} />;' , parserOptions } ,
90
+ { code : '<main onClick={() => void 0} />;' , parserOptions } ,
91
+ { code : '<article onClick={() => void 0} />;' , parserOptions } ,
92
+ { code : '<header onClick={() => void 0} />;' , parserOptions } ,
93
+ { code : '<footer onClick={() => void 0} />;' , parserOptions } ,
94
+ { code : '<div role="button" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
95
+ { code : '<div role="checkbox" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
96
+ { code : '<div role="link" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
97
+ { code : '<div role="menuitem" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
98
+ { code : '<div role="menuitemcheckbox" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
99
+ { code : '<div role="menuitemradio" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
100
+ { code : '<div role="option" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
101
+ { code : '<div role="radio" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
102
+ { code : '<div role="spinbutton" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
103
+ { code : '<div role="switch" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
104
+ { code : '<div role="tab" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
105
+ { code : '<div role="textbox" tabIndex="0" onClick={() => void 0} />' , parserOptions } ,
71
106
{ code : '<Foo.Bar onClick={() => void 0} aria-hidden={false} />;' , parserOptions } ,
72
107
{ code : '<Input onClick={() => void 0} type="hidden" />;' , parserOptions } ,
73
108
] ,
74
109
75
110
invalid : [
76
- { code : '<span onClick="submitForm();">Submit</span>' , errors : [ expectedError ] , parserOptions } ,
77
111
{
78
- code : '<span onClick="submitForm();" tabIndex={undefined}>Submit</span>' ,
112
+ code : '<span role="button" onClick={() => void 0} />' ,
113
+ errors : [ expectedError ] ,
114
+ parserOptions,
115
+ } ,
116
+ {
117
+ code : '<a role="button" onClick={() => void 0} />' ,
118
+ errors : [ expectedError ] ,
119
+ parserOptions,
120
+ } ,
121
+ {
122
+ code : '<div role="button" onClick={() => void 0} />' ,
123
+ errors : [ expectedError ] ,
124
+ parserOptions,
125
+ } ,
126
+ {
127
+ code : '<div role="checkbox" onClick={() => void 0} />' ,
128
+ errors : [ expectedError ] ,
129
+ parserOptions,
130
+ } ,
131
+ {
132
+ code : '<div role="link" onClick={() => void 0} />' ,
133
+ errors : [ expectedError ] ,
134
+ parserOptions,
135
+ } ,
136
+ {
137
+ code : '<div role="gridcell" onClick={() => void 0} />' ,
138
+ errors : [ expectedError ] ,
139
+ parserOptions,
140
+ } ,
141
+ {
142
+ code : '<div role="menuitem" onClick={() => void 0} />' ,
143
+ errors : [ expectedError ] ,
144
+ parserOptions,
145
+ } ,
146
+ {
147
+ code : '<div role="menuitemcheckbox" onClick={() => void 0} />' ,
148
+ errors : [ expectedError ] ,
149
+ parserOptions,
150
+ } ,
151
+ {
152
+ code : '<div role="menuitemradio" onClick={() => void 0} />' ,
79
153
errors : [ expectedError ] ,
80
154
parserOptions,
81
155
} ,
82
156
{
83
- code : '<span onClick="submitForm();" tabIndex="bad">Submit</span >' ,
157
+ code : '<div role="option" onClick={() => void 0} / >' ,
84
158
errors : [ expectedError ] ,
85
159
parserOptions,
86
160
} ,
87
- { code : '<a onClick="showNextPage();">Next page</a>' , errors : [ expectedError ] , parserOptions } ,
88
161
{
89
- code : '<a onClick="showNextPage();" tabIndex={undefined}>Next page</a >' ,
162
+ code : '<div role="radio" onClick={() => void 0} / >' ,
90
163
errors : [ expectedError ] ,
91
164
parserOptions,
92
165
} ,
93
166
{
94
- code : '<a onClick="showNextPage();" tabIndex="bad">Next page</a >' ,
167
+ code : '<div role="searchbox" onClick={() => void 0} / >' ,
95
168
errors : [ expectedError ] ,
96
169
parserOptions,
97
170
} ,
98
171
{
99
- code : '<a onClick={() => void 0} />' ,
172
+ code : '<div role="slider" onClick={() => void 0} />' ,
100
173
errors : [ expectedError ] ,
101
174
parserOptions,
102
175
} ,
103
176
{
104
- code : '<area onClick={() => void 0} className="foo" />' ,
177
+ code : '<div role="spinbutton" onClick={() => void 0} />' ,
105
178
errors : [ expectedError ] ,
106
179
parserOptions,
107
180
} ,
108
- { code : '<div onClick={() => void 0} />;' , errors : [ expectedError ] , parserOptions } ,
109
181
{
110
- code : '<div onClick={() => void 0} tabIndex={undefined} />; ' ,
182
+ code : '<div role="switch" onClick={() => void 0} /> ' ,
111
183
errors : [ expectedError ] ,
112
184
parserOptions,
113
185
} ,
114
186
{
115
- code : '<div onClick={() => void 0} tabIndex="bad" />; ' ,
187
+ code : '<div role="tab" onClick={() => void 0} /> ' ,
116
188
errors : [ expectedError ] ,
117
189
parserOptions,
118
190
} ,
119
191
{
120
- code : '<div onClick={() => void 0} role={undefined} />; ' ,
192
+ code : '<div role="textbox" onClick={() => void 0} /> ' ,
121
193
errors : [ expectedError ] ,
122
194
parserOptions,
123
195
} ,
124
196
{
125
- code : '<div onClick={() => void 0} aria-hidden={false} />; ' ,
197
+ code : '<div role="treeitem" onClick={() => void 0} /> ' ,
126
198
errors : [ expectedError ] ,
127
199
parserOptions,
128
200
} ,
129
- { code : '<div onClick={() => void 0} {...props} />;' , errors : [ expectedError ] , parserOptions } ,
130
- { code : '<section onClick={() => void 0} />;' , errors : [ expectedError ] , parserOptions } ,
131
- { code : '<main onClick={() => void 0} />;' , errors : [ expectedError ] , parserOptions } ,
132
- { code : '<article onClick={() => void 0} />;' , errors : [ expectedError ] , parserOptions } ,
133
- { code : '<header onClick={() => void 0} />;' , errors : [ expectedError ] , parserOptions } ,
134
- { code : '<footer onClick={() => void 0} />;' , errors : [ expectedError ] , parserOptions } ,
135
201
] ,
136
202
} ) ;
0 commit comments