From dee764b830acdc3c5bf180270ddf552e3e2df9e9 Mon Sep 17 00:00:00 2001 From: Anthony Le Goas Date: Sun, 25 Apr 2021 12:46:01 +0200 Subject: [PATCH 1/3] add a11y check on abstract roles --- src/compiler/compile/nodes/Element.ts | 13 +- .../a11y-no-abstract-roles/input.svelte | 12 ++ .../a11y-no-abstract-roles/warnings.json | 182 ++++++++++++++++++ 3 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 test/validator/samples/a11y-no-abstract-roles/input.svelte create mode 100644 test/validator/samples/a11y-no-abstract-roles/warnings.json diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index b7b2572986f..ecaa8bf0924 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -33,6 +33,9 @@ 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_roles_abstract = 'command composite input landmark range roletype section sectionhead select structure widget window'.split(' '); +const aria_roles_abstract_set = new Set(aria_roles_abstract); + const a11y_required_attributes = { a: ['href'], area: ['alt', 'aria-label', 'aria-labelledby'], @@ -479,8 +482,14 @@ export default class Element extends Node { } const value = attribute.get_static_value(); - // @ts-ignore - if (value && !aria_role_set.has(value)) { + + if (value && aria_roles_abstract_set.has(value as string)) { + const message = `A11y: abstract role '${value}' is forbidden`; + component.warn(attribute, { + code: 'a11y-no-abstract-role', + message + }); + } 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)); diff --git a/test/validator/samples/a11y-no-abstract-roles/input.svelte b/test/validator/samples/a11y-no-abstract-roles/input.svelte new file mode 100644 index 00000000000..dbe6bd85811 --- /dev/null +++ b/test/validator/samples/a11y-no-abstract-roles/input.svelte @@ -0,0 +1,12 @@ +
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/validator/samples/a11y-no-abstract-roles/warnings.json b/test/validator/samples/a11y-no-abstract-roles/warnings.json new file mode 100644 index 00000000000..2f1aada3715 --- /dev/null +++ b/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 + } + } +] From 6fd4aae6e7ee67156397279d42b38565400fc913 Mon Sep 17 00:00:00 2001 From: tanhauhau Date: Mon, 11 Jul 2022 13:29:51 +0800 Subject: [PATCH 2/3] use aria-query and move warnings to compiler_warnings file --- src/compiler/compile/compiler_warnings.ts | 4 ++++ src/compiler/compile/nodes/Element.ts | 12 +++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/compiler/compile/compiler_warnings.ts b/src/compiler/compile/compiler_warnings.ts index 267a97afcc3..727f620cb9d 100644 --- a/src/compiler/compile/compiler_warnings.ts +++ b/src/compiler/compile/compiler_warnings.ts @@ -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}'` diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index ecaa8bf0924..760e229b0fd 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -32,9 +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_roles_abstract = 'command composite input landmark range roletype section sectionhead select structure widget window'.split(' '); -const aria_roles_abstract_set = new Set(aria_roles_abstract); +const aria_role_abstract_set = new Set(roles.keys().filter(role => roles.get(role).abstract)); const a11y_required_attributes = { a: ['href'], @@ -483,12 +481,8 @@ export default class Element extends Node { const value = attribute.get_static_value(); - if (value && aria_roles_abstract_set.has(value as string)) { - const message = `A11y: abstract role '${value}' is forbidden`; - component.warn(attribute, { - code: 'a11y-no-abstract-role', - message - }); + 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); From cc371ec8f6e329381f8b30990e216956cbd97e9a Mon Sep 17 00:00:00 2001 From: tanhauhau Date: Mon, 11 Jul 2022 13:30:49 +0800 Subject: [PATCH 3/3] uppercase warning message --- src/compiler/compile/compiler_warnings.ts | 2 +- .../a11y-no-abstract-roles/warnings.json | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/compiler/compile/compiler_warnings.ts b/src/compiler/compile/compiler_warnings.ts index 727f620cb9d..0d703e0d8d9 100644 --- a/src/compiler/compile/compiler_warnings.ts +++ b/src/compiler/compile/compiler_warnings.ts @@ -109,7 +109,7 @@ export default { }), a11y_no_abstract_role: (role: string | boolean) => ({ code: 'a11y-no-abstract-role', - message: `A11y: abstract role '${role}' is forbidden` + message: `A11y: Abstract role '${role}' is forbidden` }), a11y_no_redundant_roles: (role: string | boolean) => ({ code: 'a11y-no-redundant-roles', diff --git a/test/validator/samples/a11y-no-abstract-roles/warnings.json b/test/validator/samples/a11y-no-abstract-roles/warnings.json index 2f1aada3715..917522d7cab 100644 --- a/test/validator/samples/a11y-no-abstract-roles/warnings.json +++ b/test/validator/samples/a11y-no-abstract-roles/warnings.json @@ -6,7 +6,7 @@ "column": 19, "line": 1 }, - "message": "A11y: abstract role 'command' is forbidden", + "message": "A11y: Abstract role 'command' is forbidden", "pos": 5, "start": { "character": 5, @@ -21,7 +21,7 @@ "column": 21, "line": 2 }, - "message": "A11y: abstract role 'composite' is forbidden", + "message": "A11y: Abstract role 'composite' is forbidden", "pos": 27, "start": { "character": 27, @@ -36,7 +36,7 @@ "column": 17, "line": 3 }, - "message": "A11y: abstract role 'input' is forbidden", + "message": "A11y: Abstract role 'input' is forbidden", "pos": 51, "start": { "character": 51, @@ -51,7 +51,7 @@ "column": 20, "line": 4 }, - "message": "A11y: abstract role 'landmark' is forbidden", + "message": "A11y: Abstract role 'landmark' is forbidden", "pos": 71, "start": { "character": 71, @@ -66,7 +66,7 @@ "column": 17, "line": 5 }, - "message": "A11y: abstract role 'range' is forbidden", + "message": "A11y: Abstract role 'range' is forbidden", "pos": 94, "start": { "character": 94, @@ -81,7 +81,7 @@ "column": 20, "line": 6 }, - "message": "A11y: abstract role 'roletype' is forbidden", + "message": "A11y: Abstract role 'roletype' is forbidden", "pos": 114, "start": { "character": 114, @@ -96,7 +96,7 @@ "column": 19, "line": 7 }, - "message": "A11y: abstract role 'section' is forbidden", + "message": "A11y: Abstract role 'section' is forbidden", "pos": 137, "start": { "character": 137, @@ -111,7 +111,7 @@ "column": 23, "line": 8 }, - "message": "A11y: abstract role 'sectionhead' is forbidden", + "message": "A11y: Abstract role 'sectionhead' is forbidden", "pos": 159, "start": { "character": 159, @@ -126,7 +126,7 @@ "column": 18, "line": 9 }, - "message": "A11y: abstract role 'select' is forbidden", + "message": "A11y: Abstract role 'select' is forbidden", "pos": 185, "start": { "character": 185, @@ -141,7 +141,7 @@ "column": 21, "line": 10 }, - "message": "A11y: abstract role 'structure' is forbidden", + "message": "A11y: Abstract role 'structure' is forbidden", "pos": 206, "start": { "character": 206, @@ -156,7 +156,7 @@ "column": 18, "line": 11 }, - "message": "A11y: abstract role 'widget' is forbidden", + "message": "A11y: Abstract role 'widget' is forbidden", "pos": 230, "start": { "character": 230, @@ -171,7 +171,7 @@ "column": 18, "line": 12 }, - "message": "A11y: abstract role 'window' is forbidden", + "message": "A11y: Abstract role 'window' is forbidden", "pos": 251, "start": { "character": 251,