Skip to content

Commit 6d2ffd3

Browse files
committed
Use the existing role and DOM json files.
1 parent 339e0da commit 6d2ffd3

File tree

11 files changed

+294
-335
lines changed

11 files changed

+294
-335
lines changed

.babelrc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
{
2-
"presets": ["es2015"]
2+
"presets": ["es2015"],
3+
"plugins": [
4+
"transform-object-rest-spread"
5+
]
36
}

__mocks__/genInteractives.js

Lines changed: 20 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -1,212 +1,45 @@
11
import JSXAttributeMock from 'JSXAttributeMock';
22
import JSXElementMock from 'JSXElementMock';
3+
import DOMElements from '../src/util/attributes/DOM.json';
4+
import roles from '../src/util/attributes/role.json';
5+
6+
const pureInteractiveElements = {};
7+
Object.keys(DOMElements)
8+
.filter(name => DOMElements[name].interactive === true)
9+
.forEach(name => pureInteractiveElements[name] = []);
310

411
const interactiveElementsMap = {
12+
...pureInteractiveElements,
513
a: [
614
{prop: 'href', value: '#'}
715
],
816
area: [
917
{prop: 'href', value: '#'}
1018
],
11-
button: [],
1219
input: [
1320
{prop: 'type', value: 'text'}
1421
],
15-
option: [],
16-
select: [],
17-
textarea: [],
1822
};
1923

24+
const pureNonInteractiveElementsMap = {};
25+
Object.keys(DOMElements)
26+
.filter(name => !DOMElements[name].interactive)
27+
.forEach(name => pureNonInteractiveElementsMap[name] = []);
28+
2029
const nonInteractiveElementsMap = {
21-
html: [],
22-
base: [],
23-
head: [],
24-
link: [],
25-
meta: [],
26-
style: [],
27-
title: [],
28-
address: [],
29-
article: [],
30-
aside: [],
31-
footer: [],
32-
header: [],
33-
h1: [],
34-
h2: [],
35-
h3: [],
36-
h4: [],
37-
h5: [],
38-
h6: [],
39-
hgroup: [],
40-
nav: [],
41-
section: [],
42-
dd: [],
43-
dl: [],
44-
dt: [],
45-
figcaption: [],
46-
figure: [],
47-
hr: [],
48-
li: [],
49-
main: [],
50-
ol: [],
51-
p: [],
52-
pre: [],
53-
ul: [],
54-
a: [],
55-
abbr: [],
56-
b: [],
57-
bdi: [],
58-
bdo: [],
59-
br: [],
60-
cite: [],
61-
code: [],
62-
data: [],
63-
dfn: [],
64-
em: [],
65-
i: [],
66-
kbd: [],
67-
mark: [],
68-
q: [],
69-
rp: [],
70-
rt: [],
71-
rtc: [],
72-
ruby: [],
73-
s: [],
74-
samp: [],
75-
small: [],
76-
strong: [],
77-
sub: [],
78-
sup: [],
79-
time: [],
80-
u: [],
81-
var: [],
82-
wbr: [],
83-
area: [],
84-
audio: [],
85-
img: [],
86-
map: [],
87-
track: [],
88-
video: [],
89-
embed: [],
90-
object: [],
91-
param: [],
92-
source: [],
93-
canvas: [],
94-
noscript: [],
95-
script: [],
96-
del: [],
97-
ins: [],
98-
caption: [],
99-
col: [],
100-
colgroup: [],
101-
table: [],
102-
tbody: [],
103-
td: [],
104-
tfoot: [],
105-
th: [],
106-
thead: [],
107-
tr: [],
108-
datalist: [],
109-
fieldset: [],
110-
form: [],
30+
...pureNonInteractiveElementsMap,
11131
input: [
11232
{prop: 'type', value: 'hidden'}
11333
],
114-
label: [],
115-
legend: [],
116-
meter: [],
117-
optgroup: [],
118-
output: [],
119-
progress: [],
120-
details: [],
121-
dialog: [],
122-
menu: [],
123-
menuitem: [],
124-
summary: [],
125-
content: [],
126-
acronym: [],
127-
applet: [],
128-
big: [],
129-
blink: [],
130-
center: [],
131-
dir: [],
132-
font: [],
133-
frame: [],
134-
frameset: [],
135-
keygen: [],
136-
marquee: [],
137-
noembed: [],
138-
spacer: [],
139-
strike: [],
140-
tt: [],
141-
xmp: [],
14234
};
14335

144-
const interactiveRoles = [
145-
'button',
146-
'checkbox',
147-
'link',
148-
'menuitem',
149-
'menuitemcheckbox',
150-
'menuitemradio',
151-
'option',
152-
'radio',
153-
'spinbutton',
154-
'tab',
155-
'textbox',
156-
];
36+
const interactiveRoles = Object.keys(roles).filter(
37+
role => roles[role].interactive === true
38+
);
15739

158-
const nonInteractiveRoles = [
159-
'alert',
160-
'alertdialog',
161-
'dialog',
162-
'gridcell',
163-
'log',
164-
'marquee',
165-
'progressbar',
166-
'scrollbar',
167-
'slider',
168-
'status',
169-
'tabpanel',
170-
'timer',
171-
'tooltip',
172-
'treeitem',
173-
'combobox',
174-
'grid',
175-
'listbox',
176-
'menu',
177-
'menubar',
178-
'radiogroup',
179-
'tablist',
180-
'tree',
181-
'treegrid',
182-
'article',
183-
'columnheader',
184-
'definition',
185-
'directory',
186-
'document',
187-
'group',
188-
'heading',
189-
'img',
190-
'list',
191-
'listitem',
192-
'math',
193-
'note',
194-
'presentation',
195-
'region',
196-
'row',
197-
'rowgroup',
198-
'rowheader',
199-
'separator',
200-
'toolbar',
201-
'application',
202-
'banner',
203-
'complementary',
204-
'contentinfo',
205-
'form',
206-
'main',
207-
'navigation',
208-
'search',
209-
];
40+
const nonInteractiveRoles = Object.keys(roles).filter(
41+
role => roles[role].interactive === false
42+
);
21043

21144
export function genInteractiveElements () {
21245
const elements = [];

__tests__/src/rules/onclick-has-focus-test.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const parserOptions = {
2525
const ruleTester = new RuleTester();
2626

2727
const expectedError = {
28-
message: 'Elements with onClick handlers must be focusable. ' +
28+
message: 'Interactive elements with onClick handlers must be focusable. ' +
2929
'Either set the tabIndex property to a valid value (usually 0), ' +
3030
'or use an element type which is inherently focusable such as `button`.',
3131
type: 'JSXOpeningElement',
@@ -47,6 +47,7 @@ ruleTester.run('onclick-has-focus', rule, {
4747
{ code: '<div onClick={() => void 0} tabIndex={undefined} />;', parserOptions },
4848
{ code: '<div onClick={() => void 0} tabIndex="bad" />;', parserOptions },
4949
{ code: '<div onClick={() => void 0} role={undefined} />;', parserOptions },
50+
{ code: '<div role="section" onClick={() => void 0} />', parserOptions },
5051
{ code: '<div onClick={() => void 0} aria-hidden={false} />;', parserOptions },
5152
{ code: '<div onClick={() => void 0} {...props} />;', parserOptions },
5253
{ code: '<input type="text" onClick={() => void 0} />', parserOptions },
@@ -96,6 +97,7 @@ ruleTester.run('onclick-has-focus', rule, {
9697
{ code: '<div role="option" tabIndex="0" onClick={() => void 0} />', parserOptions },
9798
{ code: '<div role="radio" tabIndex="0" onClick={() => void 0} />', parserOptions },
9899
{ code: '<div role="spinbutton" tabIndex="0" onClick={() => void 0} />', parserOptions },
100+
{ code: '<div role="switch" tabIndex="0" onClick={() => void 0} />', parserOptions },
99101
{ code: '<div role="tab" tabIndex="0" onClick={() => void 0} />', parserOptions },
100102
{ code: '<div role="textbox" tabIndex="0" onClick={() => void 0} />', parserOptions },
101103
{ code: '<Foo.Bar onClick={() => void 0} aria-hidden={false} />;', parserOptions },
@@ -158,6 +160,11 @@ ruleTester.run('onclick-has-focus', rule, {
158160
errors: [expectedError],
159161
parserOptions,
160162
},
163+
{
164+
code: '<div role="switch" onClick={() => void 0} />',
165+
errors: [expectedError],
166+
parserOptions,
167+
},
161168
{
162169
code: '<div role="tab" onClick={() => void 0} />',
163170
errors: [expectedError],

__tests__/src/util/isInteractiveElement-test.js

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,28 @@ describe('isInteractiveElement', () => {
1515
});
1616
});
1717
describe('non-interactive elements', () => {
18-
it('should not identify them as interactive elements', () => {
19-
genNonInteractiveElements().forEach(
20-
({
21-
type,
22-
openingElement,
23-
}) => expect(isInteractiveElement(
24-
elementType(openingElement),
25-
openingElement.attributes,
26-
)).toBe(false)
27-
);
28-
});
18+
19+
genNonInteractiveElements().forEach(
20+
({ openingElement }) => {
21+
it(`should not identify \`${openingElement.name.name}\` as an interactive element`, () => {
22+
expect(isInteractiveElement(
23+
elementType(openingElement),
24+
openingElement.attributes,
25+
)).toBe(false);
26+
});
27+
}
28+
);
2929
});
3030
describe('interactive elements', () => {
31-
it('should identify them as interactive elements', () => {
32-
genInteractiveElements().forEach(
33-
({
34-
type,
35-
openingElement,
36-
}) => expect(isInteractiveElement(
37-
elementType(openingElement),
38-
openingElement.attributes,
39-
)).toBe(true)
40-
);
41-
});
31+
genInteractiveElements().forEach(
32+
({ openingElement }) => {
33+
it(`should identify \`${openingElement.name.name}\` as an interactive element`, () => {
34+
expect(isInteractiveElement(
35+
elementType(openingElement),
36+
openingElement.attributes,
37+
)).toBe(true);
38+
});
39+
}
40+
);
4241
});
4342
});
Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-env mocha */
22
import assert from 'assert';
3-
import { elementType } from 'jsx-ast-utils';
3+
import { elementType, getProp, getLiteralPropValue } from 'jsx-ast-utils';
44
import isInteractiveRole from '../../../src/util/isInteractiveRole';
55
import {
66
genInteractiveRoleElements,
@@ -15,34 +15,36 @@ describe('isInteractiveRole', () => {
1515
});
1616
});
1717
describe('elements with a non-interactive role', () => {
18-
it('should not identify them as interactive role elements', () => {
19-
genNonInteractiveRoleElements().forEach(
20-
({
21-
type,
22-
openingElement,
23-
}) => expect(isInteractiveRole(
24-
elementType(openingElement),
25-
openingElement.attributes,
26-
)).toBe(false)
27-
);
28-
});
18+
genNonInteractiveRoleElements().forEach(
19+
({ openingElement }) => {
20+
const attributes = openingElement.attributes;
21+
const role = getLiteralPropValue(getProp(attributes, 'role')).toLowerCase();
22+
it(`should not identify \`${role}\` as an interactive role element`, () => {
23+
expect(isInteractiveRole(
24+
elementType(openingElement),
25+
attributes,
26+
)).toBe(false)
27+
});
28+
}
29+
);
2930
});
3031
describe('elements without a role', () => {
3132
it('should not identify them as interactive role elements', () => {
3233
expect(isInteractiveRole('div', [])).toBe(false);
3334
});
3435
});
3536
describe('elements with an interactive role', () => {
36-
it('should identify them as interactive role elements', () => {
37-
genInteractiveRoleElements().forEach(
38-
({
39-
type,
40-
openingElement,
41-
}) => expect(isInteractiveRole(
42-
elementType(openingElement),
43-
openingElement.attributes,
44-
)).toBe(true)
45-
);
46-
});
37+
genInteractiveRoleElements().forEach(
38+
({ openingElement }) => {
39+
const attributes = openingElement.attributes;
40+
const role = getLiteralPropValue(getProp(attributes, 'role')).toLowerCase();
41+
it(`should identify \`${role}\` as an interactive role element`, () => {
42+
expect(isInteractiveRole(
43+
elementType(openingElement),
44+
attributes,
45+
)).toBe(true)
46+
});
47+
}
48+
);
4749
});
4850
});

0 commit comments

Comments
 (0)