Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] add a11y check on abstract roles #6241

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/compiler/compile/compiler_warnings.ts
Expand Up @@ -107,6 +107,10 @@ export default {
code: 'a11y-unknown-role',
message: `A11y: Unknown role '${role}'` + (suggestion ? ` (did you mean '${suggestion}'?)` : '')
}),
a11y_no_abstract_role: (role: string | boolean) => ({
code: 'a11y-no-abstract-role',
message: `A11y: Abstract role '${role}' is forbidden`
}),
a11y_no_redundant_roles: (role: string | boolean) => ({
code: 'a11y-no-redundant-roles',
message: `A11y: Redundant role '${role}'`
Expand Down
7 changes: 5 additions & 2 deletions src/compiler/compile/nodes/Element.ts
Expand Up @@ -32,6 +32,7 @@ const aria_attribute_set = new Set(aria_attributes);

const aria_roles = 'alert alertdialog application article banner blockquote button caption cell checkbox code columnheader combobox complementary contentinfo definition deletion dialog directory document emphasis feed figure form generic graphics-document graphics-object graphics-symbol grid gridcell group heading img link list listbox listitem log main marquee math meter menu menubar menuitem menuitemcheckbox menuitemradio navigation none note option paragraph presentation progressbar radio radiogroup region row rowgroup rowheader scrollbar search searchbox separator slider spinbutton status strong subscript superscript switch tab table tablist tabpanel term textbox time timer toolbar tooltip tree treegrid treeitem'.split(' ');
const aria_role_set = new Set(aria_roles);
const aria_role_abstract_set = new Set(roles.keys().filter(role => roles.get(role).abstract));

const a11y_required_attributes = {
a: ['href'],
Expand Down Expand Up @@ -479,8 +480,10 @@ export default class Element extends Node {
}

const value = attribute.get_static_value();
// @ts-ignore
if (value && !aria_role_set.has(value)) {

if (value && aria_role_abstract_set.has(value as ARIARoleDefintionKey)) {
component.warn(attribute, compiler_warnings.a11y_no_abstract_role(value));
} else if (value && !aria_role_set.has(value as string)) {
// @ts-ignore
const match = fuzzymatch(value, aria_roles);
component.warn(attribute, compiler_warnings.a11y_unknown_role(value, match));
Expand Down
12 changes: 12 additions & 0 deletions test/validator/samples/a11y-no-abstract-roles/input.svelte
@@ -0,0 +1,12 @@
<div role="command"/>
<div role="composite"/>
<div role="input"/>
<div role="landmark"/>
<div role="range"/>
<div role="roletype"/>
<div role="section"/>
<div role="sectionhead"/>
<div role="select"/>
<div role="structure"/>
<div role="widget"/>
<div role="window"/>
182 changes: 182 additions & 0 deletions test/validator/samples/a11y-no-abstract-roles/warnings.json
@@ -0,0 +1,182 @@
[
{
"code": "a11y-no-abstract-role",
"end": {
"character": 19,
"column": 19,
"line": 1
},
"message": "A11y: Abstract role 'command' is forbidden",
"pos": 5,
"start": {
"character": 5,
"column": 5,
"line": 1
}
},
{
"code": "a11y-no-abstract-role",
"end": {
"character": 43,
"column": 21,
"line": 2
},
"message": "A11y: Abstract role 'composite' is forbidden",
"pos": 27,
"start": {
"character": 27,
"column": 5,
"line": 2
}
},
{
"code": "a11y-no-abstract-role",
"end": {
"character": 63,
"column": 17,
"line": 3
},
"message": "A11y: Abstract role 'input' is forbidden",
"pos": 51,
"start": {
"character": 51,
"column": 5,
"line": 3
}
},
{
"code": "a11y-no-abstract-role",
"end": {
"character": 86,
"column": 20,
"line": 4
},
"message": "A11y: Abstract role 'landmark' is forbidden",
"pos": 71,
"start": {
"character": 71,
"column": 5,
"line": 4
}
},
{
"code": "a11y-no-abstract-role",
"end": {
"character": 106,
"column": 17,
"line": 5
},
"message": "A11y: Abstract role 'range' is forbidden",
"pos": 94,
"start": {
"character": 94,
"column": 5,
"line": 5
}
},
{
"code": "a11y-no-abstract-role",
"end": {
"character": 129,
"column": 20,
"line": 6
},
"message": "A11y: Abstract role 'roletype' is forbidden",
"pos": 114,
"start": {
"character": 114,
"column": 5,
"line": 6
}
},
{
"code": "a11y-no-abstract-role",
"end": {
"character": 151,
"column": 19,
"line": 7
},
"message": "A11y: Abstract role 'section' is forbidden",
"pos": 137,
"start": {
"character": 137,
"column": 5,
"line": 7
}
},
{
"code": "a11y-no-abstract-role",
"end": {
"character": 177,
"column": 23,
"line": 8
},
"message": "A11y: Abstract role 'sectionhead' is forbidden",
"pos": 159,
"start": {
"character": 159,
"column": 5,
"line": 8
}
},
{
"code": "a11y-no-abstract-role",
"end": {
"character": 198,
"column": 18,
"line": 9
},
"message": "A11y: Abstract role 'select' is forbidden",
"pos": 185,
"start": {
"character": 185,
"column": 5,
"line": 9
}
},
{
"code": "a11y-no-abstract-role",
"end": {
"character": 222,
"column": 21,
"line": 10
},
"message": "A11y: Abstract role 'structure' is forbidden",
"pos": 206,
"start": {
"character": 206,
"column": 5,
"line": 10
}
},
{
"code": "a11y-no-abstract-role",
"end": {
"character": 243,
"column": 18,
"line": 11
},
"message": "A11y: Abstract role 'widget' is forbidden",
"pos": 230,
"start": {
"character": 230,
"column": 5,
"line": 11
}
},
{
"code": "a11y-no-abstract-role",
"end": {
"character": 264,
"column": 18,
"line": 12
},
"message": "A11y: Abstract role 'window' is forbidden",
"pos": 251,
"start": {
"character": 251,
"column": 5,
"line": 12
}
}
]