From f4682999b0c4a959ee743aa43a071c61c063eda0 Mon Sep 17 00:00:00 2001 From: Nguyen Tran Date: Thu, 16 Mar 2023 20:42:15 -0400 Subject: [PATCH 01/11] Implement a11y no-noninteractive-element-interaction --- src/compiler/compile/compiler_warnings.ts | 4 ++++ src/compiler/compile/nodes/Element.ts | 26 ++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/compiler/compile/compiler_warnings.ts b/src/compiler/compile/compiler_warnings.ts index 710377374f0e..d41348779a09 100644 --- a/src/compiler/compile/compiler_warnings.ts +++ b/src/compiler/compile/compiler_warnings.ts @@ -119,6 +119,10 @@ export default { code: 'a11y-no-interactive-element-to-noninteractive-role', message: `A11y: <${element}> cannot have role '${role}'` }), + a11y_no_noninteractive_element_interactions: (element: string) => ({ + code: 'a11y-no-noninteractive-element-interactions', + message: `A11y: Non-interactive element <${element}> should not be assigned mouse or keyboard event listeners.` + }), a11y_no_noninteractive_element_to_interactive_role: (role: string | boolean, element: string) => ({ code: 'a11y-no-noninteractive-element-to-interactive-role', message: `A11y: Non-interactive element <${element}> cannot have interactive role '${role}'` diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 2fd91ac49599..ba21985691b7 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -75,6 +75,15 @@ const a11y_labelable = new Set([ 'textarea' ]); +const a11y_interactive_handlers = new Set([ + 'click', + 'mousedown', + 'mouseup', + 'keypress', + 'keydown', + 'keyup' +]); + const a11y_nested_implicit_semantics = new Map([ ['header', 'banner'], ['footer', 'contentinfo'] @@ -677,7 +686,8 @@ export default class Element extends Node { // role-supports-aria-props const role = attribute_map.get('role'); - const role_value = (role ? role.get_static_value() : get_implicit_role(this.name, attribute_map)) as ARIARoleDefinitionKey; + const role_static_value = role?.get_static_value() as ARIARoleDefinitionKey; + const role_value = (role ? role_static_value : get_implicit_role(this.name, attribute_map)) as ARIARoleDefinitionKey; if (typeof role_value === 'string' && roles.has(role_value)) { const { props } = roles.get(role_value); const invalid_aria_props = new Set(aria.keys().filter(attribute => !(attribute in props))); @@ -691,6 +701,20 @@ export default class Element extends Node { } }); } + + // no-noninteractive-element-interactions + if ( + !is_hidden_from_screen_reader(this.name, attribute_map) && + !is_presentation_role(role_static_value) && + ((!is_interactive_element(this.name, attribute_map) && + is_non_interactive_roles(role_static_value)) || + (is_non_interactive_element(this.name, attribute_map) && !role)) + ) { + const has_interactive_handlers = handlers.some((handler) => a11y_interactive_handlers.has(handler.name)); + if (has_interactive_handlers) { + component.warn(this, compiler_warnings.a11y_no_noninteractive_element_interactions(this.name)); + } + } } validate_special_cases() { From 8fc0aceb864a425d0a656925b42ee200e2893d93 Mon Sep 17 00:00:00 2001 From: Nguyen Tran Date: Thu, 16 Mar 2023 20:43:38 -0400 Subject: [PATCH 02/11] Add some basic tests for the this warning --- .../input.svelte | 11 ++++++ .../warnings.json | 38 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 test/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte create mode 100644 test/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json diff --git a/test/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte b/test/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte new file mode 100644 index 000000000000..59fd45c86955 --- /dev/null +++ b/test/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte @@ -0,0 +1,11 @@ + +
{}} /> +
{}} on:keypress={() => {}} /> +
{}} on:keypress={() => {}} /> +