From 980aff8a4aaf91677f045e757949ecc3be6605b2 Mon Sep 17 00:00:00 2001 From: metonym Date: Tue, 13 Sep 2022 08:44:20 -0700 Subject: [PATCH] [fix] omit a11y warning for native checkbox/radio inputs (#7838) * [fix] omit a11y warning for native checkbox/radio inputs Fixes #7837 * align implementation with eslint-plugin-jsx-a11y Co-authored-by: tanhauhau --- src/compiler/compile/nodes/Element.ts | 18 +-- src/compiler/compile/utils/a11y.ts | 26 ++++- .../a11y-no-redundant-roles/warnings.json | 105 ------------------ .../input.svelte | 2 + 4 files changed, 35 insertions(+), 116 deletions(-) diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 20381ee4f6..79f2800437 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -24,7 +24,7 @@ import { Literal } from 'estree'; import compiler_warnings from '../compiler_warnings'; import compiler_errors from '../compiler_errors'; import { ARIARoleDefintionKey, roles, aria, ARIAPropertyDefinition, ARIAProperty } from 'aria-query'; -import { is_interactive_element, is_non_interactive_roles, is_presentation_role, is_interactive_roles, is_hidden_from_screen_reader } from '../utils/a11y'; +import { is_interactive_element, is_non_interactive_roles, is_presentation_role, is_interactive_roles, is_hidden_from_screen_reader, is_semantic_role_element } from '../utils/a11y'; const aria_attributes = 'activedescendant atomic autocomplete busy checked colcount colindex colspan controls current describedby description details disabled dropeffect errormessage expanded flowto grabbed haspopup hidden invalid keyshortcuts label labelledby level live modal multiline multiselectable orientation owns placeholder posinset pressed readonly relevant required roledescription rowcount rowindex rowspan selected setsize sort valuemax valuemin valuenow valuetext'.split(' '); const aria_attribute_set = new Set(aria_attributes); @@ -514,13 +514,15 @@ export default class Element extends Node { } // role-has-required-aria-props - const role = roles.get(value); - 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)); + if (!is_semantic_role_element(value, this.name, attribute_map)) { + const role = roles.get(value); + 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)); + } } } diff --git a/src/compiler/compile/utils/a11y.ts b/src/compiler/compile/utils/a11y.ts index 3f35327e7c..b96386a3b0 100644 --- a/src/compiler/compile/utils/a11y.ts +++ b/src/compiler/compile/utils/a11y.ts @@ -4,7 +4,7 @@ import { elementRoles, ARIARoleRelationConcept } from 'aria-query'; -import { AXObjects, elementAXObjects } from 'axobject-query'; +import { AXObjects, AXObjectRoles, elementAXObjects } from 'axobject-query'; import Attribute from '../nodes/Attribute'; const roles = [...roles_map.keys()]; @@ -119,8 +119,8 @@ function match_schema( schema_attribute.value && schema_attribute.value !== attribute.get_static_value() ) { -return false; -} + return false; + } return true; }); } @@ -155,3 +155,23 @@ export function is_interactive_element( return false; } + +export function is_semantic_role_element(role: ARIARoleDefintionKey, tag_name: string, attribute_map: Map) { + for (const [schema, ax_object] of elementAXObjects.entries()) { + if (schema.name === tag_name && (!schema.attributes || schema.attributes.every( + (attr) => attribute_map.has(attr.name) && attribute_map.get(attr.name).get_static_value() === attr.value + ))) { + for (const name of ax_object) { + const roles = AXObjectRoles.get(name); + if (roles) { + for (const { name } of roles) { + if (name === role) { + return true; + } + } + } + } + } + } + return false; +} diff --git a/test/validator/samples/a11y-no-redundant-roles/warnings.json b/test/validator/samples/a11y-no-redundant-roles/warnings.json index 0947a467af..cb346f87b2 100644 --- a/test/validator/samples/a11y-no-redundant-roles/warnings.json +++ b/test/validator/samples/a11y-no-redundant-roles/warnings.json @@ -224,21 +224,6 @@ "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": { @@ -254,21 +239,6 @@ "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": { @@ -284,21 +254,6 @@ "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": { @@ -314,21 +269,6 @@ "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": { @@ -344,21 +284,6 @@ "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": { @@ -374,21 +299,6 @@ "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": { @@ -524,21 +434,6 @@ "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": { 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 index 8e88b08908..113ed65316 100644 --- a/test/validator/samples/a11y-role-has-required-aria-props/input.svelte +++ b/test/validator/samples/a11y-role-has-required-aria-props/input.svelte @@ -7,3 +7,5 @@
+ + \ No newline at end of file