diff --git a/package-lock.json b/package-lock.json index 38abdfc3306..ab4b20ed594 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,12 +18,14 @@ "@rollup/plugin-typescript": "^2.0.1", "@rollup/plugin-virtual": "^2.0.0", "@sveltejs/eslint-config": "github:sveltejs/eslint-config#v5.8.0", + "@types/aria-query": "^5.0.0", "@types/mocha": "^7.0.0", "@types/node": "^8.10.53", "@typescript-eslint/eslint-plugin": "^5.22.0", "@typescript-eslint/parser": "^5.22.0", "acorn": "^8.4.1", "agadoo": "^1.1.0", + "aria-query": "^5.0.0", "code-red": "^0.2.5", "css-tree": "^1.1.2", "eslint": "^8.0.0", @@ -285,6 +287,12 @@ "resolved": "git+ssh://git@github.com/sveltejs/eslint-config.git#31fd4faeea88990069502460b023698b1c9c2d13", "dev": true }, + "node_modules/@types/aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-P+dkdFu0n08PDIvw+9nT9ByQnd+Udc8DaWPb9HKfaPwCvWvQpC5XaMRx2xLWECm9x1VKNps6vEAlirjA6+uNrQ==", + "dev": true + }, "node_modules/@types/estree": { "version": "0.0.50", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", @@ -698,6 +706,15 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, "node_modules/array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", @@ -4927,6 +4944,12 @@ "dev": true, "from": "@sveltejs/eslint-config@github:sveltejs/eslint-config#v5.8.0" }, + "@types/aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-P+dkdFu0n08PDIvw+9nT9ByQnd+Udc8DaWPb9HKfaPwCvWvQpC5XaMRx2xLWECm9x1VKNps6vEAlirjA6+uNrQ==", + "dev": true + }, "@types/estree": { "version": "0.0.50", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", @@ -5208,6 +5231,12 @@ "sprintf-js": "~1.0.2" } }, + "aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "dev": true + }, "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", diff --git a/package.json b/package.json index de712b9bab9..ade713f4e3f 100644 --- a/package.json +++ b/package.json @@ -124,12 +124,14 @@ "@rollup/plugin-typescript": "^2.0.1", "@rollup/plugin-virtual": "^2.0.0", "@sveltejs/eslint-config": "github:sveltejs/eslint-config#v5.8.0", + "@types/aria-query": "^5.0.0", "@types/mocha": "^7.0.0", "@types/node": "^8.10.53", "@typescript-eslint/eslint-plugin": "^5.22.0", "@typescript-eslint/parser": "^5.22.0", "acorn": "^8.4.1", "agadoo": "^1.1.0", + "aria-query": "^5.0.0", "code-red": "^0.2.5", "css-tree": "^1.1.2", "eslint": "^8.0.0", diff --git a/site/content/docs/05-accessibility-warnings.md b/site/content/docs/05-accessibility-warnings.md index b39b06f9ffb..9d0835b4432 100644 --- a/site/content/docs/05-accessibility-warnings.md +++ b/site/content/docs/05-accessibility-warnings.md @@ -54,6 +54,17 @@ The following elements are visually distracting: `` and ``. --- +### `role-has-required-aria-props` + +Elements with ARIA roles must have all required attributes for that role. + +```sv + + +``` + +--- + ### `a11y-hidden` Certain DOM elements are useful for screen reader navigation and should not be hidden. diff --git a/src/compiler/compile/compiler_warnings.ts b/src/compiler/compile/compiler_warnings.ts index 86ec98cb8b1..833722c3b95 100644 --- a/src/compiler/compile/compiler_warnings.ts +++ b/src/compiler/compile/compiler_warnings.ts @@ -80,6 +80,10 @@ export default { code: 'a11y-no-redundant-roles', message: `A11y: Redundant role '${role}'` }), + a11y_role_has_required_aria_props: (role: string, props: string[]) => ({ + code: 'a11y-role-has-required-aria-props', + message: `A11y: Elements with the ARIA role "${role}" must have the following attributes defined: ${props.map(name => `"${name}"`).join(', ')}` + }), a11y_accesskey: { code: 'a11y-accesskey', message: 'A11y: Avoid using accesskey' diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 735562d6274..35131cefcfd 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -23,6 +23,7 @@ import { string_literal } from '../utils/stringify'; import { Literal } from 'estree'; import compiler_warnings from '../compiler_warnings'; import compiler_errors from '../compiler_errors'; +import { ARIARoleDefintionKey, roles } from 'aria-query'; 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)$/; @@ -407,9 +408,9 @@ export default class Element extends Node { } validate_attributes_a11y() { - const { component } = this; + const { component, attributes } = this; - this.attributes.forEach(attribute => { + attributes.forEach(attribute => { if (attribute.is_spread) return; const name = attribute.name.toLowerCase(); @@ -462,6 +463,17 @@ export default class Element extends Node { component.warn(attribute, compiler_warnings.a11y_no_redundant_roles(value)); } } + + // role-has-required-aria-props + const role = roles.get(value as ARIARoleDefintionKey); + if (role) { + const required_role_props = Object.keys(role.requiredProps); + const has_missing_props = required_role_props.some(prop => !attributes.find(a => a.name === prop)); + + if (has_missing_props) { + component.warn(attribute, compiler_warnings.a11y_role_has_required_aria_props(value as string, required_role_props)); + } + } } // no-access-key diff --git a/test/validator/samples/a11y-no-redundant-roles/warnings.json b/test/validator/samples/a11y-no-redundant-roles/warnings.json index 8aa3520b323..0947a467af0 100644 --- a/test/validator/samples/a11y-no-redundant-roles/warnings.json +++ b/test/validator/samples/a11y-no-redundant-roles/warnings.json @@ -1,632 +1,737 @@ [ - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 23, - "column": 23, - "line": 1 - }, - "message": "A11y: Redundant role 'link'", - "pos": 12, - "start": { - "character": 12, - "column": 12, - "line": 1 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 58, - "column": 23, - "line": 2 - }, - "message": "A11y: Redundant role 'article'", - "pos": 44, - "start": { - "character": 44, - "column": 9, - "line": 2 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 89, - "column": 27, - "line": 3 - }, - "message": "A11y: Redundant role 'complementary'", - "pos": 69, - "start": { - "character": 69, - "column": 7, - "line": 3 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 114, - "column": 21, - "line": 4 - }, - "message": "A11y: Redundant role 'document'", - "pos": 99, - "start": { - "character": 99, - "column": 6, - "line": 4 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 139, - "column": 21, - "line": 5 - }, - "message": "A11y: Redundant role 'button'", - "pos": 126, - "start": { - "character": 126, - "column": 8, - "line": 5 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 167, - "column": 24, - "line": 6 - }, - "message": "A11y: Redundant role 'listbox'", - "pos": 153, - "start": { - "character": 153, - "column": 10, - "line": 6 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 192, - "column": 21, - "line": 7 - }, - "message": "A11y: Redundant role 'definition'", - "pos": 175, - "start": { - "character": 175, - "column": 4, - "line": 7 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 212, - "column": 16, - "line": 8 - }, - "message": "A11y: Redundant role 'term'", - "pos": 201, - "start": { - "character": 201, - "column": 5, - "line": 8 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 237, - "column": 21, - "line": 9 - }, - "message": "A11y: Redundant role 'group'", - "pos": 225, - "start": { - "character": 225, - "column": 9, - "line": 9 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 262, - "column": 21, - "line": 10 - }, - "message": "A11y: Redundant role 'dialog'", - "pos": 249, - "start": { - "character": 249, - "column": 8, - "line": 10 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 281, - "column": 15, - "line": 11 - }, - "message": "A11y: Redundant role 'term'", - "pos": 270, - "start": { - "character": 270, - "column": 4, - "line": 11 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 307, - "column": 22, - "line": 12 - }, - "message": "A11y: Redundant role 'group'", - "pos": 295, - "start": { - "character": 295, - "column": 10, - "line": 12 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 332, - "column": 21, - "line": 13 - }, - "message": "A11y: Redundant role 'figure'", - "pos": 319, - "start": { - "character": 319, - "column": 8, - "line": 13 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 353, - "column": 17, - "line": 14 - }, - "message": "A11y: Redundant role 'form'", - "pos": 342, - "start": { - "character": 342, - "column": 6, - "line": 14 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 383, - "column": 18, - "line": 15 - }, - "message": "A11y: Redundant role 'heading'", - "pos": 369, - "start": { - "character": 369, - "column": 4, - "line": 15 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 415, - "column": 18, - "line": 16 - }, - "message": "A11y: Redundant role 'heading'", - "pos": 401, - "start": { - "character": 401, - "column": 4, - "line": 16 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 447, - "column": 18, - "line": 17 - }, - "message": "A11y: Redundant role 'heading'", - "pos": 433, - "start": { - "character": 433, - "column": 4, - "line": 17 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 479, - "column": 18, - "line": 18 - }, - "message": "A11y: Redundant role 'heading'", - "pos": 465, - "start": { - "character": 465, - "column": 4, - "line": 18 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 511, - "column": 18, - "line": 19 - }, - "message": "A11y: Redundant role 'heading'", - "pos": 497, - "start": { - "character": 497, - "column": 4, - "line": 19 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 543, - "column": 18, - "line": 20 - }, - "message": "A11y: Redundant role 'heading'", - "pos": 529, - "start": { - "character": 529, - "column": 4, - "line": 20 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 577, - "column": 20, - "line": 21 - }, - "message": "A11y: Redundant role 'separator'", - "pos": 561, - "start": { - "character": 561, - "column": 4, - "line": 21 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 600, - "column": 19, - "line": 22 - }, - "message": "A11y: Redundant role 'listitem'", - "pos": 585, - "start": { - "character": 585, - "column": 4, - "line": 22 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 621, - "column": 17, - "line": 23 - }, - "message": "A11y: Redundant role 'link'", - "pos": 610, - "start": { - "character": 610, - "column": 6, - "line": 23 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 642, - "column": 17, - "line": 24 - }, - "message": "A11y: Redundant role 'main'", - "pos": 631, - "start": { - "character": 631, - "column": 6, - "line": 24 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 668, - "column": 17, - "line": 25 - }, - "message": "A11y: Redundant role 'list'", - "pos": 657, - "start": { - "character": 657, - "column": 6, - "line": 25 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 694, - "column": 22, - "line": 26 - }, - "message": "A11y: Redundant role 'navigation'", - "pos": 677, - "start": { - "character": 677, - "column": 5, - "line": 26 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 713, - "column": 15, - "line": 27 - }, - "message": "A11y: Redundant role 'list'", - "pos": 702, - "start": { - "character": 702, - "column": 4, - "line": 27 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 739, - "column": 22, - "line": 28 - }, - "message": "A11y: Redundant role 'group'", - "pos": 727, - "start": { - "character": 727, - "column": 10, - "line": 28 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 764, - "column": 21, - "line": 29 - }, - "message": "A11y: Redundant role 'option'", - "pos": 751, - "start": { - "character": 751, - "column": 8, - "line": 29 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 789, - "column": 21, - "line": 30 - }, - "message": "A11y: Redundant role 'status'", - "pos": 776, - "start": { - "character": 776, - "column": 8, - "line": 30 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 821, - "column": 28, - "line": 31 - }, - "message": "A11y: Redundant role 'progressbar'", - "pos": 803, - "start": { - "character": 803, - "column": 10, - "line": 31 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 847, - "column": 22, - "line": 32 - }, - "message": "A11y: Redundant role 'region'", - "pos": 834, - "start": { - "character": 834, - "column": 9, - "line": 32 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 873, - "column": 22, - "line": 33 - }, - "message": "A11y: Redundant role 'button'", - "pos": 860, - "start": { - "character": 860, - "column": 9, - "line": 33 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 896, - "column": 19, - "line": 34 - }, - "message": "A11y: Redundant role 'table'", - "pos": 884, - "start": { - "character": 884, - "column": 7, - "line": 34 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 922, - "column": 22, - "line": 35 - }, - "message": "A11y: Redundant role 'rowgroup'", - "pos": 907, - "start": { - "character": 907, - "column": 7, - "line": 35 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 950, - "column": 24, - "line": 36 - }, - "message": "A11y: Redundant role 'textbox'", - "pos": 936, - "start": { - "character": 936, - "column": 10, - "line": 36 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 976, - "column": 22, - "line": 37 - }, - "message": "A11y: Redundant role 'rowgroup'", - "pos": 961, - "start": { - "character": 961, - "column": 7, - "line": 37 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 1002, - "column": 22, - "line": 38 - }, - "message": "A11y: Redundant role 'rowgroup'", - "pos": 987, - "start": { - "character": 987, - "column": 7, - "line": 38 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 1020, - "column": 14, - "line": 39 - }, - "message": "A11y: Redundant role 'row'", - "pos": 1010, - "start": { - "character": 1010, - "column": 4, - "line": 39 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 1039, - "column": 15, - "line": 40 - }, - "message": "A11y: Redundant role 'list'", - "pos": 1028, - "start": { - "character": 1028, - "column": 4, - "line": 40 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 1125, - "column": 21, - "line": 43 - }, - "message": "A11y: Redundant role 'banner'", - "pos": 1112, - "start": { - "character": 1112, - "column": 8, - "line": 43 - } - }, - { - "code": "a11y-no-redundant-roles", - "end": { - "character": 1162, - "column": 26, - "line": 44 - }, - "message": "A11y: Redundant role 'contentinfo'", - "pos": 1144, - "start": { - "character": 1144, - "column": 8, - "line": 44 - } - } -] \ No newline at end of file + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 23, + "column": 23, + "line": 1 + }, + "message": "A11y: Redundant role 'link'", + "pos": 12, + "start": { + "character": 12, + "column": 12, + "line": 1 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 58, + "column": 23, + "line": 2 + }, + "message": "A11y: Redundant role 'article'", + "pos": 44, + "start": { + "character": 44, + "column": 9, + "line": 2 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 89, + "column": 27, + "line": 3 + }, + "message": "A11y: Redundant role 'complementary'", + "pos": 69, + "start": { + "character": 69, + "column": 7, + "line": 3 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 114, + "column": 21, + "line": 4 + }, + "message": "A11y: Redundant role 'document'", + "pos": 99, + "start": { + "character": 99, + "column": 6, + "line": 4 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 139, + "column": 21, + "line": 5 + }, + "message": "A11y: Redundant role 'button'", + "pos": 126, + "start": { + "character": 126, + "column": 8, + "line": 5 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 167, + "column": 24, + "line": 6 + }, + "message": "A11y: Redundant role 'listbox'", + "pos": 153, + "start": { + "character": 153, + "column": 10, + "line": 6 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 192, + "column": 21, + "line": 7 + }, + "message": "A11y: Redundant role 'definition'", + "pos": 175, + "start": { + "character": 175, + "column": 4, + "line": 7 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 212, + "column": 16, + "line": 8 + }, + "message": "A11y: Redundant role 'term'", + "pos": 201, + "start": { + "character": 201, + "column": 5, + "line": 8 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 237, + "column": 21, + "line": 9 + }, + "message": "A11y: Redundant role 'group'", + "pos": 225, + "start": { + "character": 225, + "column": 9, + "line": 9 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 262, + "column": 21, + "line": 10 + }, + "message": "A11y: Redundant role 'dialog'", + "pos": 249, + "start": { + "character": 249, + "column": 8, + "line": 10 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 281, + "column": 15, + "line": 11 + }, + "message": "A11y: Redundant role 'term'", + "pos": 270, + "start": { + "character": 270, + "column": 4, + "line": 11 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 307, + "column": 22, + "line": 12 + }, + "message": "A11y: Redundant role 'group'", + "pos": 295, + "start": { + "character": 295, + "column": 10, + "line": 12 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 332, + "column": 21, + "line": 13 + }, + "message": "A11y: Redundant role 'figure'", + "pos": 319, + "start": { + "character": 319, + "column": 8, + "line": 13 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 353, + "column": 17, + "line": 14 + }, + "message": "A11y: Redundant role 'form'", + "pos": 342, + "start": { + "character": 342, + "column": 6, + "line": 14 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 383, + "column": 18, + "line": 15 + }, + "message": "A11y: Redundant role 'heading'", + "pos": 369, + "start": { + "character": 369, + "column": 4, + "line": 15 + } + }, + { + "code": "a11y-role-has-required-aria-props", + "end": { + "character": 383, + "column": 18, + "line": 15 + }, + "message": "A11y: Elements with the ARIA role \"heading\" must have the following attributes defined: \"aria-level\"", + "pos": 369, + "start": { + "character": 369, + "column": 4, + "line": 15 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 415, + "column": 18, + "line": 16 + }, + "message": "A11y: Redundant role 'heading'", + "pos": 401, + "start": { + "character": 401, + "column": 4, + "line": 16 + } + }, + { + "code": "a11y-role-has-required-aria-props", + "message": "A11y: Elements with the ARIA role \"heading\" must have the following attributes defined: \"aria-level\"", + "pos": 401, + "start": { + "character": 401, + "column": 4, + "line": 16 + }, + "end": { + "character": 415, + "column": 18, + "line": 16 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 447, + "column": 18, + "line": 17 + }, + "message": "A11y: Redundant role 'heading'", + "pos": 433, + "start": { + "character": 433, + "column": 4, + "line": 17 + } + }, + { + "code": "a11y-role-has-required-aria-props", + "end": { + "character": 447, + "column": 18, + "line": 17 + }, + "message": "A11y: Elements with the ARIA role \"heading\" must have the following attributes defined: \"aria-level\"", + "pos": 433, + "start": { + "character": 433, + "column": 4, + "line": 17 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 479, + "column": 18, + "line": 18 + }, + "message": "A11y: Redundant role 'heading'", + "pos": 465, + "start": { + "character": 465, + "column": 4, + "line": 18 + } + }, + { + "code": "a11y-role-has-required-aria-props", + "message": "A11y: Elements with the ARIA role \"heading\" must have the following attributes defined: \"aria-level\"", + "pos": 465, + "start": { + "character": 465, + "column": 4, + "line": 18 + }, + "end": { + "character": 479, + "column": 18, + "line": 18 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 511, + "column": 18, + "line": 19 + }, + "message": "A11y: Redundant role 'heading'", + "pos": 497, + "start": { + "character": 497, + "column": 4, + "line": 19 + } + }, + { + "code": "a11y-role-has-required-aria-props", + "message": "A11y: Elements with the ARIA role \"heading\" must have the following attributes defined: \"aria-level\"", + "pos": 497, + "start": { + "character": 497, + "column": 4, + "line": 19 + }, + "end": { + "character": 511, + "column": 18, + "line": 19 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 543, + "column": 18, + "line": 20 + }, + "message": "A11y: Redundant role 'heading'", + "pos": 529, + "start": { + "character": 529, + "column": 4, + "line": 20 + } + }, + { + "code": "a11y-role-has-required-aria-props", + "message": "A11y: Elements with the ARIA role \"heading\" must have the following attributes defined: \"aria-level\"", + "pos": 529, + "start": { + "character": 529, + "column": 4, + "line": 20 + }, + "end": { + "character": 543, + "column": 18, + "line": 20 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 577, + "column": 20, + "line": 21 + }, + "message": "A11y: Redundant role 'separator'", + "pos": 561, + "start": { + "character": 561, + "column": 4, + "line": 21 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 600, + "column": 19, + "line": 22 + }, + "message": "A11y: Redundant role 'listitem'", + "pos": 585, + "start": { + "character": 585, + "column": 4, + "line": 22 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 621, + "column": 17, + "line": 23 + }, + "message": "A11y: Redundant role 'link'", + "pos": 610, + "start": { + "character": 610, + "column": 6, + "line": 23 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 642, + "column": 17, + "line": 24 + }, + "message": "A11y: Redundant role 'main'", + "pos": 631, + "start": { + "character": 631, + "column": 6, + "line": 24 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 668, + "column": 17, + "line": 25 + }, + "message": "A11y: Redundant role 'list'", + "pos": 657, + "start": { + "character": 657, + "column": 6, + "line": 25 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 694, + "column": 22, + "line": 26 + }, + "message": "A11y: Redundant role 'navigation'", + "pos": 677, + "start": { + "character": 677, + "column": 5, + "line": 26 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 713, + "column": 15, + "line": 27 + }, + "message": "A11y: Redundant role 'list'", + "pos": 702, + "start": { + "character": 702, + "column": 4, + "line": 27 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 739, + "column": 22, + "line": 28 + }, + "message": "A11y: Redundant role 'group'", + "pos": 727, + "start": { + "character": 727, + "column": 10, + "line": 28 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 764, + "column": 21, + "line": 29 + }, + "message": "A11y: Redundant role 'option'", + "pos": 751, + "start": { + "character": 751, + "column": 8, + "line": 29 + } + }, + { + "code": "a11y-role-has-required-aria-props", + "message": "A11y: Elements with the ARIA role \"option\" must have the following attributes defined: \"aria-selected\"", + "pos": 751, + "start": { + "character": 751, + "column": 8, + "line": 29 + }, + "end": { + "character": 764, + "column": 21, + "line": 29 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 789, + "column": 21, + "line": 30 + }, + "message": "A11y: Redundant role 'status'", + "pos": 776, + "start": { + "character": 776, + "column": 8, + "line": 30 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 821, + "column": 28, + "line": 31 + }, + "message": "A11y: Redundant role 'progressbar'", + "pos": 803, + "start": { + "character": 803, + "column": 10, + "line": 31 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 847, + "column": 22, + "line": 32 + }, + "message": "A11y: Redundant role 'region'", + "pos": 834, + "start": { + "character": 834, + "column": 9, + "line": 32 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 873, + "column": 22, + "line": 33 + }, + "message": "A11y: Redundant role 'button'", + "pos": 860, + "start": { + "character": 860, + "column": 9, + "line": 33 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 896, + "column": 19, + "line": 34 + }, + "message": "A11y: Redundant role 'table'", + "pos": 884, + "start": { + "character": 884, + "column": 7, + "line": 34 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 922, + "column": 22, + "line": 35 + }, + "message": "A11y: Redundant role 'rowgroup'", + "pos": 907, + "start": { + "character": 907, + "column": 7, + "line": 35 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 950, + "column": 24, + "line": 36 + }, + "message": "A11y: Redundant role 'textbox'", + "pos": 936, + "start": { + "character": 936, + "column": 10, + "line": 36 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 976, + "column": 22, + "line": 37 + }, + "message": "A11y: Redundant role 'rowgroup'", + "pos": 961, + "start": { + "character": 961, + "column": 7, + "line": 37 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 1002, + "column": 22, + "line": 38 + }, + "message": "A11y: Redundant role 'rowgroup'", + "pos": 987, + "start": { + "character": 987, + "column": 7, + "line": 38 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 1020, + "column": 14, + "line": 39 + }, + "message": "A11y: Redundant role 'row'", + "pos": 1010, + "start": { + "character": 1010, + "column": 4, + "line": 39 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 1039, + "column": 15, + "line": 40 + }, + "message": "A11y: Redundant role 'list'", + "pos": 1028, + "start": { + "character": 1028, + "column": 4, + "line": 40 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 1125, + "column": 21, + "line": 43 + }, + "message": "A11y: Redundant role 'banner'", + "pos": 1112, + "start": { + "character": 1112, + "column": 8, + "line": 43 + } + }, + { + "code": "a11y-no-redundant-roles", + "end": { + "character": 1162, + "column": 26, + "line": 44 + }, + "message": "A11y: Redundant role 'contentinfo'", + "pos": 1144, + "start": { + "character": 1144, + "column": 8, + "line": 44 + } + } +] diff --git a/test/validator/samples/a11y-role-has-required-aria-props/input.svelte b/test/validator/samples/a11y-role-has-required-aria-props/input.svelte new file mode 100644 index 00000000000..8e88b089080 --- /dev/null +++ b/test/validator/samples/a11y-role-has-required-aria-props/input.svelte @@ -0,0 +1,9 @@ +
+ +
+
+ +
+ +
+
diff --git a/test/validator/samples/a11y-role-has-required-aria-props/warnings.json b/test/validator/samples/a11y-role-has-required-aria-props/warnings.json new file mode 100644 index 00000000000..4de7c840016 --- /dev/null +++ b/test/validator/samples/a11y-role-has-required-aria-props/warnings.json @@ -0,0 +1,62 @@ +[ + { + "code": "a11y-role-has-required-aria-props", + "message": "A11y: Elements with the ARIA role \"heading\" must have the following attributes defined: \"aria-level\"", + "start": { + "line": 1, + "column": 5, + "character": 5 + }, + "end": { + "line": 1, + "column": 19, + "character": 19 + }, + "pos": 5 + }, + { + "code": "a11y-role-has-required-aria-props", + "message": "A11y: Elements with the ARIA role \"checkbox\" must have the following attributes defined: \"aria-checked\"", + "start": { + "line": 2, + "column": 6, + "character": 33 + }, + "end": { + "line": 2, + "column": 21, + "character": 48 + }, + "pos": 33 + }, + { + "code": "a11y-role-has-required-aria-props", + "message": "A11y: Elements with the ARIA role \"meter\" must have the following attributes defined: \"aria-valuenow\"", + "start": { + "line": 3, + "column": 5, + "character": 62 + }, + "end": { + "line": 3, + "column": 17, + "character": 74 + }, + "pos": 62 + }, + { + "code": "a11y-role-has-required-aria-props", + "message": "A11y: Elements with the ARIA role \"scrollbar\" must have the following attributes defined: \"aria-controls\", \"aria-valuenow\"", + "start": { + "character": 87, + "column": 5, + "line": 4 + }, + "end": { + "character": 103, + "column": 21, + "line": 4 + }, + "pos": 87 + } +]