Skip to content

Commit e712500

Browse files
committed
All tests passing locally on latest aria-query changes (model-aria-better branch)
1 parent b7542e4 commit e712500

13 files changed

+164
-141
lines changed

__mocks__/genInteractives.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ const interactiveElementsMap = {
1616
a: [{prop: 'href', value: '#'}],
1717
area: [{prop: 'href', value: '#'}],
1818
button: [],
19-
form: [],
2019
input: [],
2120
'input[type=\"button\"]': [{prop: 'type', value: 'button'}],
2221
'input[type=\"checkbox\"]': [{prop: 'type', value: 'checkbox'}],
@@ -43,7 +42,9 @@ const interactiveElementsMap = {
4342
menuitem:[],
4443
option: [],
4544
select: [],
45+
'table[role="grid"]': [{prop: 'role', value: 'grid'}],
4646
'td[role="gridcell"]': [{prop: 'role', value: 'gridcell'}],
47+
tr: [],
4748
textarea: [],
4849
};
4950

@@ -56,6 +57,7 @@ const nonInteractiveElementsMap = {
5657
dt: [],
5758
fieldset: [],
5859
figure: [],
60+
form: [],
5961
frame: [],
6062
h1: [],
6163
h2: [],
@@ -71,10 +73,10 @@ const nonInteractiveElementsMap = {
7173
nav: [],
7274
ol: [],
7375
table: [],
76+
td: [],
7477
tbody: [],
7578
tfoot: [],
7679
thead: [],
77-
tr: [],
7880
ul: [],
7981
};
8082

@@ -104,11 +106,15 @@ const nonAbstractRoles = roleNames
104106

105107
const interactiveRoles = roleNames
106108
.filter(role => !roles.get(role).abstract)
107-
.filter(role => roles.get(role).interactive);
109+
.filter(role => roles.get(role).superClass.some(
110+
klasses => klasses.includes('widget')),
111+
);
108112

109113
const nonInteractiveRoles = roleNames
110114
.filter(role => !roles.get(role).abstract)
111-
.filter(role => !roles.get(role).interactive);
115+
.filter(role => !roles.get(role).superClass.some(
116+
klasses => klasses.includes('widget')),
117+
);
112118

113119
export function genElementSymbol (
114120
openingElement: Object,

__tests__/src/rules/no-noninteractive-element-handlers-test.js

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const expectedError = {
2828

2929
ruleTester.run('no-noninteractive-element-handlers', rule, {
3030
valid: [
31+
{ code: '<TestComponent onClick={doFoo} />' },
32+
{ code: '<Button onClick={doFoo} />' },
3133
{ code: '<div />;' },
3234
{ code: '<div className="foo" />;' },
3335
{ code: '<div className="foo" {...props} />;' },
@@ -57,33 +59,36 @@ ruleTester.run('no-noninteractive-element-handlers', rule, {
5759
{ code: '<input type="time" onClick={() => void 0} />' },
5860
{ code: '<input type="url" onClick={() => void 0} />' },
5961
{ code: '<input type="week" onClick={() => void 0} />' },
60-
/* End all flavors of input */
6162
{ code: '<input type="hidden" onClick={() => void 0} />' },
63+
/* End all flavors of input */
64+
{ code: '<a tabIndex="0" onClick={() => void 0} />' },
65+
{ code: '<a onClick={() => void 0} href="http://x.y.z" />' },
66+
{ code: '<a onClick={() => void 0} href="http://x.y.z" tabIndex="0" />' },
6267
{ code: '<button onClick={() => void 0} className="foo" />' },
6368
{ code: '<menuitem onClick={() => {}} />;' },
6469
{ code: '<option onClick={() => void 0} className="foo" />' },
6570
{ code: '<select onClick={() => void 0} className="foo" />' },
6671
{ code: '<textarea onClick={() => void 0} className="foo" />' },
67-
{ code: '<a tabIndex="0" onClick={() => void 0} />' },
68-
{ code: '<a onClick={() => void 0} href="http://x.y.z" />' },
69-
{ code: '<a onClick={() => void 0} href="http://x.y.z" tabIndex="0" />' },
70-
{ code: '<input onClick={() => void 0} type="hidden" />;' },
71-
{ code: '<form onClick={() => {}} />;' },
72-
{ code: '<TestComponent onClick={doFoo} />' },
73-
{ code: '<Button onClick={doFoo} />' },
72+
{ code: '<tr onClick={() => {}} />;' },
7473
/* HTML elements attributed with an interactive role */
7574
{ code: '<div role="button" onClick={() => {}} />;' },
7675
{ code: '<div role="checkbox" onClick={() => {}} />;' },
7776
{ code: '<div role="columnheader" onClick={() => {}} />;' },
7877
{ code: '<div role="combobox" onClick={() => {}} />;' },
79-
{ code: '<div role="form" onClick={() => {}} />;' },
78+
{ code: '<div role="grid" onClick={() => {}} />;' },
8079
{ code: '<div role="gridcell" onClick={() => {}} />;' },
8180
{ code: '<div role="link" onClick={() => {}} />;' },
81+
{ code: '<div role="listbox" onClick={() => {}} />;' },
82+
{ code: '<div role="menu" onClick={() => {}} />;' },
83+
{ code: '<div role="menubar" onClick={() => {}} />;' },
8284
{ code: '<div role="menuitem" onClick={() => {}} />;' },
8385
{ code: '<div role="menuitemcheckbox" onClick={() => {}} />;' },
8486
{ code: '<div role="menuitemradio" onClick={() => {}} />;' },
8587
{ code: '<div role="option" onClick={() => {}} />;' },
88+
{ code: '<div role="progressbar" onClick={() => {}} />;' },
8689
{ code: '<div role="radio" onClick={() => {}} />;' },
90+
{ code: '<div role="radiogroup" onClick={() => {}} />;' },
91+
{ code: '<div role="row" onClick={() => {}} />;' },
8792
{ code: '<div role="rowheader" onClick={() => {}} />;' },
8893
{ code: '<div role="searchbox" onClick={() => {}} />;' },
8994
{ code: '<div role="slider" onClick={() => {}} />;' },
@@ -105,6 +110,9 @@ ruleTester.run('no-noninteractive-element-handlers', rule, {
105110
{ code: '<div role="sectionhead" onClick={() => {}} />;' },
106111
{ code: '<div role="select" onClick={() => {}} />;' },
107112
{ code: '<div role="structure" onClick={() => {}} />;' },
113+
{ code: '<div role="tablist" onClick={() => {}} />;' },
114+
{ code: '<div role="tree" onClick={() => {}} />;' },
115+
{ code: '<div role="treegrid" onClick={() => {}} />;' },
108116
{ code: '<div role="widget" onClick={() => {}} />;' },
109117
{ code: '<div role="window" onClick={() => {}} />;' },
110118
].map(parserOptionsMapper),
@@ -148,6 +156,7 @@ ruleTester.run('no-noninteractive-element-handlers', rule, {
148156
{ code: '<figcaption onClick={() => {}} />;', errors: [expectedError] },
149157
{ code: '<font onClick={() => {}} />;', errors: [expectedError] },
150158
{ code: '<footer onClick={() => {}} />;', errors: [expectedError] },
159+
{ code: '<form onClick={() => {}} />;', errors: [expectedError] },
151160
{ code: '<frameset onClick={() => {}} />;', errors: [expectedError] },
152161
{ code: '<head onClick={() => {}} />;', errors: [expectedError] },
153162
{ code: '<header onClick={() => {}} />;', errors: [expectedError] },
@@ -235,7 +244,6 @@ ruleTester.run('no-noninteractive-element-handlers', rule, {
235244
{ code: '<tbody onClick={() => {}} />;', errors: [expectedError] },
236245
{ code: '<tfoot onClick={() => {}} />;', errors: [expectedError] },
237246
{ code: '<thead onClick={() => {}} />;', errors: [expectedError] },
238-
{ code: '<tr onClick={() => {}} />;', errors: [expectedError] },
239247
{ code: '<ul onClick={() => {}} />;', errors: [expectedError] },
240248
/* HTML elements attributed with a non-interactive role */
241249
{ code: '<div role="alert" onClick={() => {}} />;', errors: [expectedError] },
@@ -252,38 +260,29 @@ ruleTester.run('no-noninteractive-element-handlers', rule, {
252260
{ code: '<div role="document" onClick={() => {}} />;', errors: [expectedError] },
253261
{ code: '<div role="feed" onClick={() => {}} />;', errors: [expectedError] },
254262
{ code: '<div role="figure" onClick={() => {}} />;', errors: [expectedError] },
255-
{ code: '<div role="grid" onClick={() => {}} />;', errors: [expectedError] },
263+
{ code: '<div role="form" onClick={() => {}} />;', errors: [expectedError] },
256264
{ code: '<div role="group" onClick={() => {}} />;', errors: [expectedError] },
257265
{ code: '<div role="heading" onClick={() => {}} />;', errors: [expectedError] },
258266
{ code: '<div role="img" onClick={() => {}} />;', errors: [expectedError] },
259267
{ code: '<div role="list" onClick={() => {}} />;', errors: [expectedError] },
260-
{ code: '<div role="listbox" onClick={() => {}} />;', errors: [expectedError] },
261268
{ code: '<div role="listitem" onClick={() => {}} />;', errors: [expectedError] },
262269
{ code: '<div role="log" onClick={() => {}} />;', errors: [expectedError] },
263270
{ code: '<div role="main" onClick={() => {}} />;', errors: [expectedError] },
264271
{ code: '<div role="marquee" onClick={() => {}} />;', errors: [expectedError] },
265272
{ code: '<div role="math" onClick={() => {}} />;', errors: [expectedError] },
266-
{ code: '<div role="menu" onClick={() => {}} />;', errors: [expectedError] },
267-
{ code: '<div role="menubar" onClick={() => {}} />;', errors: [expectedError] },
268273
{ code: '<div role="navigation" onClick={() => {}} />;', errors: [expectedError] },
269274
{ code: '<div role="note" onClick={() => {}} />;', errors: [expectedError] },
270-
{ code: '<div role="progressbar" onClick={() => {}} />;', errors: [expectedError] },
271-
{ code: '<div role="radiogroup" onClick={() => {}} />;', errors: [expectedError] },
272275
{ code: '<div role="region" onClick={() => {}} />;', errors: [expectedError] },
273-
{ code: '<div role="row" onClick={() => {}} />;', errors: [expectedError] },
274276
{ code: '<div role="rowgroup" onClick={() => {}} />;', errors: [expectedError] },
275277
{ code: '<div role="search" onClick={() => {}} />;', errors: [expectedError] },
276278
{ code: '<div role="separator" onClick={() => {}} />;', errors: [expectedError] },
277279
{ code: '<div role="scrollbar" onClick={() => {}} />;', errors: [expectedError] },
278280
{ code: '<div role="status" onClick={() => {}} />;', errors: [expectedError] },
279281
{ code: '<div role="table" onClick={() => {}} />;', errors: [expectedError] },
280-
{ code: '<div role="tablist" onClick={() => {}} />;', errors: [expectedError] },
281282
{ code: '<div role="tabpanel" onClick={() => {}} />;', errors: [expectedError] },
282283
{ code: '<div role="term" onClick={() => {}} />;', errors: [expectedError] },
283284
{ code: '<div role="timer" onClick={() => {}} />;', errors: [expectedError] },
284285
{ code: '<div role="toolbar" onClick={() => {}} />;', errors: [expectedError] },
285286
{ code: '<div role="tooltip" onClick={() => {}} />;', errors: [expectedError] },
286-
{ code: '<div role="tree" onClick={() => {}} />;', errors: [expectedError] },
287-
{ code: '<div role="treegrid" onClick={() => {}} />;', errors: [expectedError] },
288287
].map(parserOptionsMapper),
289288
});

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ ruleTester.run('no-static-element-interactions', rule, {
179179
{ code: '<div role="table" onClick={() => {}} />;' },
180180
{ code: '<div role="tablist" onClick={() => {}} />;' },
181181
{ code: '<div role="tabpanel" onClick={() => {}} />;' },
182+
{ code: '<td onClick={() => {}} />;' },
182183
{ code: '<div role="term" onClick={() => {}} />;' },
183184
{ code: '<div role="timer" onClick={() => {}} />;' },
184185
{ code: '<div role="toolbar" onClick={() => {}} />;' },
@@ -274,7 +275,6 @@ ruleTester.run('no-static-element-interactions', rule, {
274275
{ code: '<sub onClick={() => {}} />;', errors: [expectedError] },
275276
{ code: '<summary onClick={() => {}} />;', errors: [expectedError] },
276277
{ code: '<sup onClick={() => {}} />;', errors: [expectedError] },
277-
{ code: '<td onClick={() => {}} />;', errors: [expectedError] },
278278
{ code: '<th onClick={() => {}} />;', errors: [expectedError] },
279279
{ code: '<time onClick={() => {}} />;', errors: [expectedError] },
280280
{ code: '<title onClick={() => {}} />;', errors: [expectedError] },

__tests__/src/rules/role-has-required-aria-props-test.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import rule from '../../../src/rules/role-has-required-aria-props';
2121
const ruleTester = new RuleTester();
2222

2323
const errorMessage = (role) => {
24-
const requiredProps = roles.get(role).requiredProps.toString().toLowerCase();
24+
const requiredProps = Object.keys(roles.get(role).requiredProps);
2525

2626
return {
2727
message: `Elements with the ARIA role "${role}" must have the following ` +
@@ -33,8 +33,11 @@ const errorMessage = (role) => {
3333

3434
// Create basic test cases using all valid role types.
3535
const basicValidityTests = [...roles.keys()].map((role) => {
36-
const { requiredProps } = roles.get(role);
37-
const propChain = requiredProps.join(' ').toLowerCase();
36+
const {
37+
requiredProps: requiredPropKeyValues,
38+
} = roles.get(role);
39+
const requiredProps = Object.keys(requiredPropKeyValues);
40+
const propChain = requiredProps.join(' ');
3841

3942
return {
4043
code: `<div role="${role.toLowerCase()}" ${propChain} />`,
@@ -43,17 +46,15 @@ const basicValidityTests = [...roles.keys()].map((role) => {
4346

4447
ruleTester.run('role-has-required-aria-props', rule, {
4548
valid: [
49+
{ code: '<Bar baz />' },
4650
// Variables should pass, as we are only testing literals.
4751
{ code: '<div />' },
4852
{ code: '<div></div>' },
4953
{ code: '<div role={role} />' },
5054
{ code: '<div role={role || "button"} />' },
5155
{ code: '<div role={role || "foobar"} />' },
52-
{ code: '<div role="tabpanel row" />' },
53-
{
54-
code: '<span role="checkbox" aria-checked="false" aria-labelledby="foo" tabindex="0"></span>',
55-
},
56-
{ code: '<Bar baz />' },
56+
{ code: '<div role="row" />' },
57+
{ code: '<span role="checkbox" aria-checked="false" aria-labelledby="foo" tabindex="0"></span>' },
5758
].concat(basicValidityTests).map(parserOptionsMapper),
5859

5960
invalid: [

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ const errorMessage = (attr, role, tag, isImplicit) => ({
3939
const nonAbstractRoles = [...roles.keys()].filter(role => roles.get(role).abstract === false);
4040

4141
const createTests = rolesNames => rolesNames.reduce((tests, role) => {
42-
const validPropsForRole = roles.get(role.toLowerCase()).props;
42+
const {
43+
props: propKeyValues,
44+
} = roles.get(role);
45+
const validPropsForRole = Object.keys(propKeyValues);
4346
const invalidPropsForRole = [...aria.keys()]
4447
.map(attribute => attribute.toLowerCase())
4548
.filter(attribute => validPropsForRole.indexOf(attribute) === -1);

src/rules/role-has-required-aria-props.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ module.exports = {
2727
create: context => ({
2828
JSXAttribute: (attribute) => {
2929
const name = propName(attribute);
30-
const normalizedName = name ? name.toUpperCase() : '';
30+
const normalizedName = name ? name.toLowerCase() : '';
3131

32-
if (normalizedName !== 'ROLE') {
32+
if (normalizedName !== 'role') {
3333
return;
3434
}
3535

@@ -47,12 +47,14 @@ module.exports = {
4747
.filter(val => [...roles.keys()].indexOf(val) > -1);
4848

4949
validRoles.forEach((role) => {
50-
const { requiredProps } = roles.get(role);
50+
const {
51+
requiredProps: requiredPropKeyValues,
52+
} = roles.get(role);
53+
const requiredProps = Object.keys(requiredPropKeyValues);
5154

5255
if (requiredProps.length > 0) {
5356
const hasRequiredProps = requiredProps
5457
.every(prop => getProp(attribute.parent.attributes, prop));
55-
5658
if (hasRequiredProps === false) {
5759
context.report({
5860
node: attribute,

src/rules/role-supports-aria-props.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,17 @@ module.exports = {
4646
// This actually isn't true - should fix in future release.
4747
if (
4848
typeof roleValue !== 'string'
49-
|| roles.get(roleValue.toLowerCase()) === undefined
49+
|| roles.get(roleValue) === undefined
5050
) {
5151
return;
5252
}
5353

5454
// Make sure it has no aria-* properties defined outside of its property set.
55-
const propertySet = roles.get(roleValue.toLowerCase()).props;
55+
const {
56+
props: propKeyValues,
57+
} = roles.get(roleValue);
58+
const propertySet = Object.keys(propKeyValues);
5659
const invalidAriaPropsForRole = [...aria.keys()]
57-
.map(attribute => attribute.toLowerCase())
5860
.filter(attribute => propertySet.indexOf(attribute) === -1);
5961

6062
node.attributes.forEach((prop) => {
@@ -63,9 +65,7 @@ module.exports = {
6365
}
6466

6567
const name = propName(prop);
66-
const normalizedName = name ? name.toLowerCase() : '';
67-
68-
if (invalidAriaPropsForRole.indexOf(normalizedName) > -1) {
68+
if (invalidAriaPropsForRole.indexOf(name) > -1) {
6969
context.report({
7070
node,
7171
message: errorMessage(name, roleValue, type, isImplicit),

src/util/getImplicitRole.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ import implicitRoles from './implicitRoles';
99
* @returns {String} - String representing the node's implicit role or '' if it doesn't exist.
1010
*/
1111
export default function getImplicitRole(type, attributes) {
12-
const normalizedType = type.toUpperCase();
13-
14-
if (implicitRoles[normalizedType]) {
15-
return implicitRoles[normalizedType](attributes);
12+
if (implicitRoles[type]) {
13+
return implicitRoles[type](attributes);
1614
}
1715

1816
return '';

0 commit comments

Comments
 (0)