From 26ae9fd2e19a64e106fa0a15fab36f64fee29a3e Mon Sep 17 00:00:00 2001 From: Nguyen Tran Date: Fri, 13 Jan 2023 12:45:51 -0500 Subject: [PATCH] Implemented role-supports-aria-roles --- src/compiler/compile/compiler_warnings.ts | 11 ++++++++++ src/compiler/compile/nodes/Element.ts | 25 +++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/compiler/compile/compiler_warnings.ts b/src/compiler/compile/compiler_warnings.ts index 3f552eb8b8..cb6ddc3367 100644 --- a/src/compiler/compile/compiler_warnings.ts +++ b/src/compiler/compile/compiler_warnings.ts @@ -123,6 +123,17 @@ export default { 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_role_supports_aria_props: (attribute: string, role: string, is_implicit: boolean, name: string) => { + let message = `The attribute '${attribute}' is not supported by the role '${role}'.`; + if (is_implicit) { + message += ` This role is implicit on the element <${name}>.`; + } + + return { + code: 'a11y-role-supports-aria-props', + message: `A11y: ${message}` + }; + }, 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 06ef1ba9c1..8b0e9c3ffc 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -82,11 +82,15 @@ const a11y_nested_implicit_semantics = new Map([ const a11y_implicit_semantics = new Map([ ['a', 'link'], + ['area', 'link'], + ['article', 'article'], ['aside', 'complementary'], ['body', 'document'], + ['button', 'button'], ['datalist', 'listbox'], ['dd', 'definition'], ['dfn', 'term'], + ['dialog', 'dialog'], ['details', 'group'], ['dt', 'term'], ['fieldset', 'group'], @@ -98,10 +102,14 @@ const a11y_implicit_semantics = new Map([ ['h5', 'heading'], ['h6', 'heading'], ['hr', 'separator'], + ['img', 'img'], ['li', 'listitem'], + ['link', 'link'], ['menu', 'list'], + ['meter', 'progressbar'], ['nav', 'navigation'], ['ol', 'list'], + ['option', 'option'], ['optgroup', 'group'], ['output', 'status'], ['progress', 'progressbar'], @@ -599,6 +607,23 @@ export default class Element extends Node { component.warn(this, compiler_warnings.a11y_no_noninteractive_tabindex); } } + + // role-supports-aria-props + const role = attribute_map.get('role'); + const role_value = (role ? role.get_static_value() : a11y_implicit_semantics.get(this.name)) as ARIARoleDefintionKey; + 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))); + const is_implicit = role_value && role === undefined; + + attributes + .filter(prop => prop.type !== 'Spread') + .forEach(prop => { + if (invalid_aria_props.has(prop.name as ARIAProperty)) { + component.warn(prop, compiler_warnings.a11y_role_supports_aria_props(prop.name, role_value, is_implicit, this.name)); + } + }); + } } validate_special_cases() {