Skip to content

Commit 722ac82

Browse files
committed
feature #1690 [Icons] Set aria-hidden="true" when title/label not set (smnandre)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [Icons] Set aria-hidden="true" when title/label not set | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Issues | Fix #1629 | License | MIT Add "aria-hidden=true" when the icon has no title, aria-label or aria-labelledby attribute (option enabled per default) Commits ------- 36600e3 [Icons] Set aria-hidden="true" when title/label not set
2 parents 3ce9987 + 36600e3 commit 722ac82

File tree

4 files changed

+72
-12
lines changed

4 files changed

+72
-12
lines changed

src/Icons/src/IconRenderer.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,20 @@ public function __construct(
3838
public function renderIcon(string $name, array $attributes = []): string
3939
{
4040
return $this->registry->get($name)
41-
->withAttributes([...$this->defaultIconAttributes, ...$attributes])
41+
->withAttributes($this->getIconAttributes($name, $attributes))
4242
->toHtml()
4343
;
4444
}
45+
46+
private function getIconAttributes(string $name, array $attributes): array
47+
{
48+
$iconAttributes = $this->defaultIconAttributes;
49+
50+
// Add aria-hidden attribute
51+
if ([] === array_intersect(['aria-hidden', 'aria-label', 'aria-labelledby', 'title'], array_keys($attributes))) {
52+
$iconAttributes['aria-hidden'] = 'true';
53+
}
54+
55+
return [...$iconAttributes, ...$attributes];
56+
}
4557
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<ul class="svg">
22
<li id="first">{{ ux_icon('user', {class: 'h-8 w-8'}) }}</li>
3-
<li id="second">{{ ux_icon('user') }}</li>
3+
<li id="second">{{ ux_icon('user', {"aria-label": "AriaLabel"}) }}</li>
44
<li id="third">{{ ux_icon('sub:check', {'data-action': 'string "with" quotes'}) }}</li>
55
<li id="fifth"><twig:UX:Icon name="user" class="h-8 w-8" /></li>
6-
<li id="sixth"><twig:ux:icon name="sub:check" /></li>
6+
<li id="sixth"><twig:ux:icon name="sub:check" aria-labelledby="foo" /></li>
77
<li id="seventh"><twig:Ux:Icon :name="'sub:'~'check'" /></li>
88
<li id="eighth">{{ ux_icon('iconamoon:3d-duotone') }}</li>
99
</ul>

src/Icons/tests/Integration/RenderIconsInTwigTest.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ public function testRenderIcons(): void
2626
$this->assertSame(
2727
<<<HTML
2828
<ul class="svg">
29-
<li id="first"><svg viewBox="0 0 24 24" fill="currentColor" class="h-8 w-8"><path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z" clip-rule="evenodd"></path></svg></li>
30-
<li id="second"><svg viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6"><path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z" clip-rule="evenodd"></path></svg></li>
31-
<li id="third"><svg viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6" data-action="string &quot;with&quot; quotes"><path fill-rule="evenodd" d="M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353 8.493-12.74a.75.75 0 0 1 1.04-.207Z" clip-rule="evenodd"></path></svg></li>
32-
<li id="fifth"><svg viewBox="0 0 24 24" fill="currentColor" class="h-8 w-8"><path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z" clip-rule="evenodd"></path></svg></li>
33-
<li id="sixth"><svg viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6"><path fill-rule="evenodd" d="M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353 8.493-12.74a.75.75 0 0 1 1.04-.207Z" clip-rule="evenodd"></path></svg></li>
34-
<li id="seventh"><svg viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6"><path fill-rule="evenodd" d="M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353 8.493-12.74a.75.75 0 0 1 1.04-.207Z" clip-rule="evenodd"></path></svg></li>
35-
<li id="eighth"><svg viewBox="0 0 24 24" fill="currentColor"><g fill="none"><path fill="currentColor" d="m12 3l7.794 4.5v7.845a2 2 0 0 1-1 1.732L13 20.423a2 2 0 0 1-2 0l-5.794-3.346a2 2 0 0 1-1-1.732V7.5z" opacity=".16"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 3l7.794 4.5v7.845a2 2 0 0 1-1 1.732L13 20.423a2 2 0 0 1-2 0l-5.794-3.346a2 2 0 0 1-1-1.732V7.5z"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 7v5l-4.33 2.5M12 12l4.33 2.5"/></g></svg></li>
29+
<li id="first"><svg viewBox="0 0 24 24" fill="currentColor" class="h-8 w-8" aria-hidden="true"><path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z" clip-rule="evenodd"></path></svg></li>
30+
<li id="second"><svg viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6" aria-label="AriaLabel"><path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z" clip-rule="evenodd"></path></svg></li>
31+
<li id="third"><svg viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6" aria-hidden="true" data-action="string &quot;with&quot; quotes"><path fill-rule="evenodd" d="M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353 8.493-12.74a.75.75 0 0 1 1.04-.207Z" clip-rule="evenodd"></path></svg></li>
32+
<li id="fifth"><svg viewBox="0 0 24 24" fill="currentColor" class="h-8 w-8" aria-hidden="true"><path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z" clip-rule="evenodd"></path></svg></li>
33+
<li id="sixth"><svg viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6" aria-labelledby="foo"><path fill-rule="evenodd" d="M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353 8.493-12.74a.75.75 0 0 1 1.04-.207Z" clip-rule="evenodd"></path></svg></li>
34+
<li id="seventh"><svg viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6" aria-hidden="true"><path fill-rule="evenodd" d="M19.916 4.626a.75.75 0 0 1 .208 1.04l-9 13.5a.75.75 0 0 1-1.154.114l-6-6a.75.75 0 0 1 1.06-1.06l5.353 5.353 8.493-12.74a.75.75 0 0 1 1.04-.207Z" clip-rule="evenodd"></path></svg></li>
35+
<li id="eighth"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><g fill="none"><path fill="currentColor" d="m12 3l7.794 4.5v7.845a2 2 0 0 1-1 1.732L13 20.423a2 2 0 0 1-2 0l-5.794-3.346a2 2 0 0 1-1-1.732V7.5z" opacity=".16"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 3l7.794 4.5v7.845a2 2 0 0 1-1 1.732L13 20.423a2 2 0 0 1-2 0l-5.794-3.346a2 2 0 0 1-1-1.732V7.5z"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 7v5l-4.33 2.5M12 12l4.33 2.5"/></g></svg></li>
3636
</ul>
3737
HTML,
3838
trim($output)

src/Icons/tests/Unit/IconRendererTest.php

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public function testRenderIconWithAttributes(): void
6666

6767
$svg = $iconRenderer->renderIcon('foo', $attributes);
6868

69-
$this->assertSame('<svg viewBox="0 0 24 24" class="icon" id="FooBar"><path d="M0 0L12 12"/></svg>', $svg);
69+
$this->assertSame('<svg aria-hidden="true" viewBox="0 0 24 24" class="icon" id="FooBar"><path d="M0 0L12 12"/></svg>', $svg);
7070
}
7171

7272
public function testRenderIconWithDefaultAttributes(): void
@@ -78,7 +78,7 @@ public function testRenderIconWithDefaultAttributes(): void
7878

7979
$svg = $iconRenderer->renderIcon('foo');
8080

81-
$this->assertSame('<svg viewBox="0 0 24 24" class="icon"><path d="M0 0L12 12"/></svg>', $svg);
81+
$this->assertSame('<svg viewBox="0 0 24 24" class="icon" aria-hidden="true"><path d="M0 0L12 12"/></svg>', $svg);
8282
}
8383

8484
/**
@@ -92,6 +92,7 @@ public function testRenderIconWithAttributesAndDefaultAttributes($iconAttrs, $de
9292
$iconRenderer = new IconRenderer($registry, $defaultAttrs);
9393

9494
$svg = $iconRenderer->renderIcon('foo', $renderAttr);
95+
$svg = str_replace(' aria-hidden="true"', '', $svg);
9596
$this->assertStringStartsWith($expectedTag, $svg);
9697
}
9798

@@ -159,6 +160,53 @@ public static function provideAttributesWithDefaultAttributesCases()
159160
];
160161
}
161162

163+
/**
164+
* @dataProvider provideAriaHiddenCases
165+
*
166+
* @param array<string, string> $attributes
167+
*/
168+
public function testRenderIconWithAutoAriaHidden(array $attributes, string $expectedSvg): void
169+
{
170+
$registry = $this->createRegistry([
171+
'foo' => '<path d="M0 0L12 12"/>',
172+
]);
173+
$iconRenderer = new IconRenderer($registry);
174+
175+
$svg = $iconRenderer->renderIcon('foo', $attributes);
176+
$this->assertSame($expectedSvg, $svg);
177+
}
178+
179+
/**
180+
* @return iterable<array{array<string, string>, string}>
181+
*/
182+
public static function provideAriaHiddenCases(): iterable
183+
{
184+
yield 'no attributes' => [
185+
[],
186+
'<svg aria-hidden="true"><path d="M0 0L12 12"/></svg>',
187+
];
188+
yield 'aria-hidden attribute' => [
189+
['aria-hidden' => 'true'],
190+
'<svg aria-hidden="true"><path d="M0 0L12 12"/></svg>',
191+
];
192+
yield 'aria-hidden false + aria-label' => [
193+
['aria-hidden' => 'false', 'aria-label' => 'foo'],
194+
'<svg aria-hidden="false" aria-label="foo"><path d="M0 0L12 12"/></svg>',
195+
];
196+
yield 'title attribute' => [
197+
['title' => 'foo'],
198+
'<svg title="foo"><path d="M0 0L12 12"/></svg>',
199+
];
200+
yield 'aria-labelledby attribute' => [
201+
['aria-labelledby' => 'foo'],
202+
'<svg aria-labelledby="foo"><path d="M0 0L12 12"/></svg>',
203+
];
204+
yield 'aria-label attribute' => [
205+
['aria-label' => 'foo'],
206+
'<svg aria-label="foo"><path d="M0 0L12 12"/></svg>',
207+
];
208+
}
209+
162210
private function createRegistry(array $icons): IconRegistryInterface
163211
{
164212
$registryIcons = [];

0 commit comments

Comments
 (0)