diff --git a/.changeset/odd-rules-hear.md b/.changeset/odd-rules-hear.md new file mode 100644 index 000000000000..325b8ddf9628 --- /dev/null +++ b/.changeset/odd-rules-hear.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: silence a11y attribute warnings when spread attributes present diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js index a5ca8463a4f7..24a8e5122d11 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js @@ -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); } @@ -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, @@ -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) && @@ -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)) ) { @@ -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( @@ -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) && @@ -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) && @@ -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'); } @@ -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); } } @@ -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); } @@ -1087,7 +1093,7 @@ 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); } } @@ -1095,7 +1101,7 @@ export function check_element(node, context) { 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; @@ -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) && diff --git a/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte b/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte index 124888c089ad..f47743b33b26 100644 --- a/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte +++ b/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte @@ -10,3 +10,4 @@ G + \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte index 613b80e6d940..f9fe4f15c1c3 100644 --- a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte +++ b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte @@ -1,21 +1,19 @@ -
void 0}>
+
{}}>
-
void 0} on:focus={() => void 0}>
+
{}} onfocus={() => {}}>
-
void 0} {...otherProps}>
+
{}} {...otherProps}>
-
void 0}>
+
{}}>
-
void 0} on:blur={() => void 0}>
+
{}} onblur={() => {}}>
-
void 0} {...otherProps}>
+
{}} {...otherProps}>
diff --git a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json index 574b019e0f61..3dee4e967341 100644 --- a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json +++ b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json @@ -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 } } ]