mirror of
https://github.com/sveltejs/svelte.git
synced 2024-12-01 17:30:59 +01:00
[fix] a11y - allow fallback roles (#8045)
This commit is contained in:
parent
6330bfc052
commit
676716979a
@ -194,10 +194,10 @@ function is_valid_aria_attribute_value(schema: ARIAPropertyDefinition, value: st
|
||||
.indexOf(typeof value === 'string' ? value.toLowerCase() : value) > -1;
|
||||
case 'idlist': // if list of ids, split each
|
||||
return typeof value === 'string'
|
||||
&& value.split(' ').every((id) => typeof id === 'string');
|
||||
&& value.split(regex_any_repeated_whitespaces).every((id) => typeof id === 'string');
|
||||
case 'tokenlist': // if list of tokens, split each
|
||||
return typeof value === 'string'
|
||||
&& value.split(' ').every((token) => (schema.values || []).indexOf(token.toLowerCase()) > -1);
|
||||
&& value.split(regex_any_repeated_whitespaces).every((token) => (schema.values || []).indexOf(token.toLowerCase()) > -1);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -492,47 +492,52 @@ export default class Element extends Node {
|
||||
component.warn(attribute, compiler_warnings.a11y_misplaced_role(this.name));
|
||||
}
|
||||
|
||||
const value = attribute.get_static_value() as ARIARoleDefintionKey;
|
||||
const value = attribute.get_static_value();
|
||||
|
||||
if (value && aria_role_abstract_set.has(value)) {
|
||||
component.warn(attribute, compiler_warnings.a11y_no_abstract_role(value));
|
||||
} else if (value && !aria_role_set.has(value)) {
|
||||
const match = fuzzymatch(value, aria_roles);
|
||||
component.warn(attribute, compiler_warnings.a11y_unknown_role(value, match));
|
||||
}
|
||||
|
||||
// no-redundant-roles
|
||||
const has_redundant_role = value === a11y_implicit_semantics.get(this.name);
|
||||
|
||||
if (this.name === value || has_redundant_role) {
|
||||
component.warn(attribute, compiler_warnings.a11y_no_redundant_roles(value));
|
||||
}
|
||||
|
||||
// Footers and headers are special cases, and should not have redundant roles unless they are the children of sections or articles.
|
||||
const is_parent_section_or_article = is_parent(this.parent, ['section', 'article']);
|
||||
if (!is_parent_section_or_article) {
|
||||
const has_nested_redundant_role = value === a11y_nested_implicit_semantics.get(this.name);
|
||||
if (has_nested_redundant_role) {
|
||||
component.warn(attribute, compiler_warnings.a11y_no_redundant_roles(value));
|
||||
}
|
||||
}
|
||||
|
||||
// role-has-required-aria-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));
|
||||
if (typeof value === 'string') {
|
||||
value.split(regex_any_repeated_whitespaces).forEach((current_role: ARIARoleDefintionKey) => {
|
||||
if (current_role && aria_role_abstract_set.has(current_role)) {
|
||||
component.warn(attribute, compiler_warnings.a11y_no_abstract_role(current_role));
|
||||
} else if (current_role && !aria_role_set.has(current_role)) {
|
||||
const match = fuzzymatch(current_role, aria_roles);
|
||||
component.warn(attribute, compiler_warnings.a11y_unknown_role(current_role, match));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no-interactive-element-to-noninteractive-role
|
||||
if (is_interactive_element(this.name, attribute_map) && (is_non_interactive_roles(value) || is_presentation_role(value))) {
|
||||
component.warn(this, compiler_warnings.a11y_no_interactive_element_to_noninteractive_role(value, this.name));
|
||||
// no-redundant-roles
|
||||
const has_redundant_role = current_role === a11y_implicit_semantics.get(this.name);
|
||||
|
||||
if (this.name === current_role || has_redundant_role) {
|
||||
component.warn(attribute, compiler_warnings.a11y_no_redundant_roles(current_role));
|
||||
}
|
||||
|
||||
// Footers and headers are special cases, and should not have redundant roles unless they are the children of sections or articles.
|
||||
const is_parent_section_or_article = is_parent(this.parent, ['section', 'article']);
|
||||
if (!is_parent_section_or_article) {
|
||||
const has_nested_redundant_role = current_role === a11y_nested_implicit_semantics.get(this.name);
|
||||
if (has_nested_redundant_role) {
|
||||
component.warn(attribute, compiler_warnings.a11y_no_redundant_roles(current_role));
|
||||
}
|
||||
}
|
||||
|
||||
// role-has-required-aria-props
|
||||
if (!is_semantic_role_element(current_role, this.name, attribute_map)) {
|
||||
const role = roles.get(current_role);
|
||||
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(current_role, required_role_props));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no-interactive-element-to-noninteractive-role
|
||||
if (is_interactive_element(this.name, attribute_map) && (is_non_interactive_roles(current_role) || is_presentation_role(current_role))) {
|
||||
component.warn(this, compiler_warnings.a11y_no_interactive_element_to_noninteractive_role(current_role, this.name));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -621,7 +626,7 @@ export default class Element extends Node {
|
||||
if (href_static_value === null || href_static_value.match(/^(https?:)?\/\//i)) {
|
||||
const rel = attribute_map.get('rel');
|
||||
if (rel == null || rel.is_static) {
|
||||
const rel_values = rel ? rel.get_static_value().split(' ') : [];
|
||||
const rel_values = rel ? rel.get_static_value().split(regex_any_repeated_whitespaces) : [];
|
||||
const expected_values = ['noreferrer'];
|
||||
expected_values.forEach(expected_value => {
|
||||
if (!rel || rel && rel_values.indexOf(expected_value) < 0) {
|
||||
|
@ -1 +1,7 @@
|
||||
<div role="toooltip"></div>
|
||||
<!-- valid -->
|
||||
<div role="tooltip"></div>
|
||||
<div role="button tooltip"></div>
|
||||
|
||||
<!-- not valid -->
|
||||
<div role="toooltip"></div>
|
||||
<div role="button toooltip"></div>
|
||||
|
@ -3,15 +3,30 @@
|
||||
"code": "a11y-unknown-role",
|
||||
"message": "A11y: Unknown role 'toooltip' (did you mean 'tooltip'?)",
|
||||
"start": {
|
||||
"line": 1,
|
||||
"line": 6,
|
||||
"column": 5,
|
||||
"character": 5
|
||||
"character": 101
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"line": 6,
|
||||
"column": 20,
|
||||
"character": 20
|
||||
"character": 116
|
||||
},
|
||||
"pos": 5
|
||||
"pos": 101
|
||||
},
|
||||
{
|
||||
"code": "a11y-unknown-role",
|
||||
"message": "A11y: Unknown role 'toooltip' (did you mean 'tooltip'?)",
|
||||
"start": {
|
||||
"line": 7,
|
||||
"column": 5,
|
||||
"character": 129
|
||||
},
|
||||
"end": {
|
||||
"line": 7,
|
||||
"column": 27,
|
||||
"character": 151
|
||||
},
|
||||
"pos": 129
|
||||
}
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user