From fa9845ddbf51283257ae6e67df48e13508eb6dd2 Mon Sep 17 00:00:00 2001
From: LB <mail@lb.ee>
Date: Wed, 23 Oct 2024 14:25:44 +1000
Subject: [PATCH] [patch] `no-redundandant-roles`: allow `<img src="*.svg"
 role="img" />`

Setting role="img" is a valid use case to work around a Safari bug for better accessibility when the source image is an SVG file.
This improvement does not account for variables in the `src` attribute but adds a valid exception for when we can parse the string.
Fixes #936
---
 __tests__/src/rules/no-redundant-roles-test.js |  4 ++++
 docs/rules/no-redundant-roles.md               |  1 +
 src/util/implicitRoles/img.js                  | 10 ++++++++++
 3 files changed, 15 insertions(+)

diff --git a/__tests__/src/rules/no-redundant-roles-test.js b/__tests__/src/rules/no-redundant-roles-test.js
index 60cc2185e..068e54049 100644
--- a/__tests__/src/rules/no-redundant-roles-test.js
+++ b/__tests__/src/rules/no-redundant-roles-test.js
@@ -83,12 +83,16 @@ ruleTester.run(`${ruleName}:recommended (valid list role override)`, rule, {
     { code: '<ul role="list" />' },
     { code: '<ol role="list" />' },
     { code: '<dl role="list" />' },
+    { code: '<img src="example.svg" role="img" />' },
+    { code: '<svg role="img" />' },
   ))
     .map(ruleOptionsMapperFactory(listException))
     .map(parserOptionsMapper),
   invalid: parsers.all([].concat(
     { code: '<ul role="list" />', errors: [expectedError('ul', 'list')] },
     { code: '<ol role="list" />', errors: [expectedError('ol', 'list')] },
+    { code: '<img role="img" />', errors: [expectedError('img', 'img')] },
+    { code: '<img src={someVariable} role="img" />', errors: [expectedError('img', 'img')] },
   ))
     .map(parserOptionsMapper),
 });
diff --git a/docs/rules/no-redundant-roles.md b/docs/rules/no-redundant-roles.md
index 0506b6066..6b20240cf 100644
--- a/docs/rules/no-redundant-roles.md
+++ b/docs/rules/no-redundant-roles.md
@@ -43,3 +43,4 @@ General best practice (reference resources)
 ### Resources
 
 - [ARIA Spec, ARIA Adds Nothing to Default Semantics of Most HTML Elements](https://www.w3.org/TR/using-aria/#aria-does-nothing)
+- [Identifying SVG as an image](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#identifying_svg_as_an_image)
diff --git a/src/util/implicitRoles/img.js b/src/util/implicitRoles/img.js
index a03217ffd..e62bbc128 100644
--- a/src/util/implicitRoles/img.js
+++ b/src/util/implicitRoles/img.js
@@ -10,5 +10,15 @@ export default function getImplicitRoleForImg(attributes) {
     return '';
   }
 
+  /**
+   * If the src attribute can be determined to be an svg, allow the role to be set to 'img'
+   * so that VoiceOver on Safari can be better supported.
+   *
+   * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#identifying_svg_as_an_image
+   * @see https://bugs.webkit.org/show_bug.cgi?id=216364
+   */
+  const src = getProp(attributes, 'src');
+  if (src && getLiteralPropValue(src)?.includes('.svg')) { return ''; }
+
   return 'img';
 }