Skip to content

Commit 0b48e01

Browse files
committed
check noninteractive roles on interactive elements
1 parent 2de5ec2 commit 0b48e01

File tree

5 files changed

+685
-0
lines changed

5 files changed

+685
-0
lines changed

src/compiler/compile/nodes/Element.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import { INode } from './interfaces';
2020
import Component from '../Component';
2121
import compiler_warnings from '../compiler_warnings';
2222
import compiler_errors from '../compiler_errors';
23+
import { ARIARoleDefintionKey } from 'aria-query';
24+
import { noninteractive_roles } from '../utils/aria_roles';
25+
import { interactive_elements } from '../utils/elements';
2326

2427
const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|svg|switch|symbol|text|textPath|tref|tspan|unknown|use|view|vkern)$/;
2528

@@ -599,6 +602,18 @@ export default class Element extends Node {
599602
if (handlers_map.has('mouseout') && !handlers_map.has('blur')) {
600603
component.warn(this, compiler_warnings.a11y_mouse_events_have_key_events('mouseout', 'blur'));
601604
}
605+
606+
if (interactive_elements.has(this.name)) {
607+
if (attribute_map.has('role')) {
608+
const roleValue = this.attributes.find(a => a.name === 'role').get_static_value().toString() as ARIARoleDefintionKey;
609+
if (noninteractive_roles.has(roleValue)) {
610+
component.warn(this, {
611+
code: 'a11y-no-interactive-element-to-noninteractive-role',
612+
message: `A11y: <${this.name}> cannot have role ${roleValue}`
613+
});
614+
}
615+
}
616+
}
602617
}
603618

604619
validate_bindings_foreign() {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { roles as rolesMap } from 'aria-query';
2+
3+
const roles = [...rolesMap.keys()];
4+
5+
const noninteractive_roles = new Set(roles
6+
.filter((name) => !rolesMap.get(name).abstract)
7+
.filter((name) => !rolesMap.get(name).superClass.some((c) => c.includes('widget'))));
8+
9+
export { noninteractive_roles };
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const interactive_elements = new Set([
2+
'a', 'button', 'input', 'select', 'textarea'
3+
]);
4+
5+
export { interactive_elements };
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<!-- a -->
2+
<a href="test" role="article">link</a>
3+
<a href="test" role="banner">link</a>
4+
<a href="test" role="complementary">link</a>
5+
<a href="test" role="img">link</a>
6+
<a href="test" role="listitem">link</a>
7+
<a href="test" role="main">link</a>
8+
<a href="test" role="region">link</a>
9+
<a href="test" role="tooltip">link</a>
10+
<a href="test" role="button">link</a>
11+
12+
<!-- button -->
13+
<button role="article">button</button>
14+
<button role="banner">button</button>
15+
<button role="complementary">button</button>
16+
<button role="img">button</button>
17+
<button role="listitem">button</button>
18+
<button role="main">button</button>
19+
<button role="region">button</button>
20+
<button role="tooltip">button</button>
21+
<button role="button">button</button>
22+
23+
<!-- input -->
24+
<input role="article"/>
25+
<input role="banner"/>
26+
<input role="complementary"/>
27+
<input role="img"/>
28+
<input role="listitem"/>
29+
<input role="main"/>
30+
<input role="region"/>
31+
<input role="tooltip"/>
32+
<input role="button"/>
33+
34+
<!-- select -->
35+
<select role="article"/>
36+
<select role="banner"/>
37+
<select role="complementary"/>
38+
<select role="img"/>
39+
<select role="listitem"/>
40+
<select role="main"/>
41+
<select role="region"/>
42+
<select role="tooltip"/>
43+
<select role="button"/>
44+
45+
<!-- textarea -->
46+
<textarea role="article"/>
47+
<textarea role="banner"/>
48+
<textarea role="complementary"/>
49+
<textarea role="img"/>
50+
<textarea role="listitem"/>
51+
<textarea role="main"/>
52+
<textarea role="region"/>
53+
<textarea role="tooltip"/>
54+
<textarea role="button"/>

0 commit comments

Comments
 (0)