Skip to content

Commit

Permalink
fix: silence a11y attribute warnings when spread attributes present (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
dummdidumm authored Jan 30, 2025
1 parent f5406c9 commit b8607f8
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 49 deletions.
5 changes: 5 additions & 0 deletions .changeset/odd-rules-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: silence a11y attribute warnings when spread attributes present
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,8 @@ export function check_element(node, context) {
name === 'aria-activedescendant' &&
!is_dynamic_element &&
!is_interactive_element(node.name, attribute_map) &&
!attribute_map.has('tabindex')
!attribute_map.has('tabindex') &&
!has_spread
) {
w.a11y_aria_activedescendant_has_tabindex(attribute);
}
Expand Down Expand Up @@ -810,9 +811,9 @@ export function check_element(node, context) {
const role = roles_map.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)
);
const has_missing_props =
!has_spread &&
required_role_props.some((prop) => !attributes.find((a) => a.name === prop));
if (has_missing_props) {
w.a11y_role_has_required_aria_props(
attribute,
Expand All @@ -828,6 +829,7 @@ export function check_element(node, context) {

// interactive-supports-focus
if (
!has_spread &&
!has_disabled_attribute(attribute_map) &&
!is_hidden_from_screen_reader(node.name, attribute_map) &&
!is_presentation_role(current_role) &&
Expand All @@ -845,6 +847,7 @@ export function check_element(node, context) {

// no-interactive-element-to-noninteractive-role
if (
!has_spread &&
is_interactive_element(node.name, attribute_map) &&
(is_non_interactive_roles(current_role) || is_presentation_role(current_role))
) {
Expand All @@ -853,6 +856,7 @@ export function check_element(node, context) {

// no-noninteractive-element-to-interactive-role
if (
!has_spread &&
is_non_interactive_element(node.name, attribute_map) &&
is_interactive_roles(current_role) &&
!a11y_non_interactive_element_to_interactive_role_exceptions[node.name]?.includes(
Expand Down Expand Up @@ -947,6 +951,7 @@ export function check_element(node, context) {

// no-noninteractive-element-interactions
if (
!has_spread &&
!has_contenteditable_attr &&
!is_hidden_from_screen_reader(node.name, attribute_map) &&
!is_presentation_role(role_static_value) &&
Expand All @@ -964,6 +969,7 @@ export function check_element(node, context) {

// no-static-element-interactions
if (
!has_spread &&
(!role || role_static_value !== null) &&
!is_hidden_from_screen_reader(node.name, attribute_map) &&
!is_presentation_role(role_static_value) &&
Expand All @@ -981,11 +987,11 @@ export function check_element(node, context) {
}
}

if (handlers.has('mouseover') && !handlers.has('focus')) {
if (!has_spread && handlers.has('mouseover') && !handlers.has('focus')) {
w.a11y_mouse_events_have_key_events(node, 'mouseover', 'focus');
}

if (handlers.has('mouseout') && !handlers.has('blur')) {
if (!has_spread && handlers.has('mouseout') && !handlers.has('blur')) {
w.a11y_mouse_events_have_key_events(node, 'mouseout', 'blur');
}

Expand All @@ -995,7 +1001,7 @@ export function check_element(node, context) {
if (node.name === 'a' || node.name === 'button') {
const is_hidden = get_static_value(attribute_map.get('aria-hidden')) === 'true';

if (!is_hidden && !is_labelled && !has_content(node)) {
if (!has_spread && !is_hidden && !is_labelled && !has_content(node)) {
w.a11y_consider_explicit_label(node);
}
}
Expand Down Expand Up @@ -1054,7 +1060,7 @@ export function check_element(node, context) {
if (node.name === 'img') {
const alt_attribute = get_static_text_value(attribute_map.get('alt'));
const aria_hidden = get_static_value(attribute_map.get('aria-hidden'));
if (alt_attribute && !aria_hidden) {
if (alt_attribute && !aria_hidden && !has_spread) {
if (/\b(image|picture|photo)\b/i.test(alt_attribute)) {
w.a11y_img_redundant_alt(node);
}
Expand Down Expand Up @@ -1087,15 +1093,15 @@ export function check_element(node, context) {
);
return has;
};
if (!attribute_map.has('for') && !has_input_child(node)) {
if (!has_spread && !attribute_map.has('for') && !has_input_child(node)) {
w.a11y_label_has_associated_control(node);
}
}

if (node.name === 'video') {
const aria_hidden_attribute = attribute_map.get('aria-hidden');
const aria_hidden_exist = aria_hidden_attribute && get_static_value(aria_hidden_attribute);
if (attribute_map.has('muted') || aria_hidden_exist === 'true') {
if (attribute_map.has('muted') || aria_hidden_exist === 'true' || has_spread) {
return;
}
let has_caption = false;
Expand Down Expand Up @@ -1141,6 +1147,7 @@ export function check_element(node, context) {

// Check content
if (
!has_spread &&
!is_labelled &&
!has_contenteditable_binding &&
a11y_required_content.includes(node.name) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
<label>E <span></span></label>
<label>F {#if true}<input type="text" />{/if}</label>
<LabelComponent>G <input type="text" /></LabelComponent>
<label {...forMightBeInHere}>E <span></span></label>
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
<script>
// Even if otherProps contains onBlur and/or onFocus, the rule will still fail.
// Props should be passed down explicitly for rule to pass.
const otherProps = {
onBlur: () => void 0,
onFocus: () => void 0
onblur: () => {},
onfocus: () => {}
};
</script>

<!-- svelte-ignore a11y_no_static_element_interactions -->
<div on:mouseover={() => void 0}></div>
<div onmouseover={() => {}}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div on:mouseover={() => void 0} on:focus={() => void 0}></div>
<div onmouseover={() => {}} onfocus={() => {}}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div on:mouseover={() => void 0} {...otherProps}></div>
<div onmouseover={() => {}} {...otherProps}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div on:mouseout={() => void 0}></div>
<div onmouseout={() => {}}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div on:mouseout={() => void 0} on:blur={() => void 0}></div>
<div onmouseout={() => {}} onblur={() => {}}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div on:mouseout={() => void 0} {...otherProps}></div>
<div onmouseout={() => {}} {...otherProps}></div>
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,25 @@
{
"code": "a11y_mouse_events_have_key_events",
"end": {
"column": 39,
"line": 11
"column": 34,
"line": 9
},
"message": "'mouseover' event must be accompanied by 'focus' event",
"start": {
"column": 0,
"line": 11
"line": 9
}
},
{
"code": "a11y_mouse_events_have_key_events",
"end": {
"column": 55,
"column": 33,
"line": 15
},
"message": "'mouseover' event must be accompanied by 'focus' event",
"start": {
"column": 0,
"line": 15
}
},
{
"code": "a11y_mouse_events_have_key_events",
"end": {
"column": 38,
"line": 17
},
"message": "'mouseout' event must be accompanied by 'blur' event",
"start": {
"column": 0,
"line": 17
}
},
{
"code": "a11y_mouse_events_have_key_events",
"end": {
"column": 54,
"line": 21
},
"message": "'mouseout' event must be accompanied by 'blur' event",
"start": {
"column": 0,
"line": 21
"line": 15
}
}
]

0 comments on commit b8607f8

Please sign in to comment.