Skip to content

Commit d810fe1

Browse files
committed
[Icons] Allow to add custom pre-renderers
1 parent 781f66c commit d810fe1

File tree

7 files changed

+83
-26
lines changed

7 files changed

+83
-26
lines changed

src/Icons/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Add `aliases` configuration option to define icon alternative names.
66
- Add support for `int` and `float` attribute values in `<twig:ux:icon />`.
7+
- Allow to add custom pre-renderers
78

89
## 2.19.0
910

src/Icons/config/services.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1313

14+
use Symfony\UX\Icons\AriaHiddenPreRenderer;
1415
use Symfony\UX\Icons\Command\WarmCacheCommand;
1516
use Symfony\UX\Icons\IconCacheWarmer;
1617
use Symfony\UX\Icons\IconRenderer;
@@ -58,6 +59,7 @@
5859
service('.ux_icons.icon_registry'),
5960
abstract_arg('default_icon_attributes'),
6061
abstract_arg('icon_aliases'),
62+
tagged_iterator('ux_icons.icon_pre_renderer'),
6163
])
6264

6365
->alias('Symfony\UX\Icons\IconRendererInterface', '.ux_icons.icon_renderer')
@@ -79,5 +81,8 @@
7981
service('.ux_icons.cache_warmer'),
8082
])
8183
->tag('console.command')
84+
85+
->set('.ux_icons.aria_hidden_pre_renderer', AriaHiddenPreRenderer::class)
86+
->tag('ux_icons.icon_pre_renderer')
8287
;
8388
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Icons;
13+
14+
final class AriaHiddenPreRenderer implements IconPreRendererInterface
15+
{
16+
public function __invoke(string $name, Icon $icon): Icon
17+
{
18+
if ([] === array_intersect(['aria-hidden', 'aria-label', 'aria-labelledby', 'title'], array_keys($icon->getAttributes()))) {
19+
return $icon->withAttributes(['aria-hidden' => 'true']);
20+
}
21+
22+
return $icon;
23+
}
24+
}

src/Icons/src/DependencyInjection/UXIconsExtension.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
2121
use Symfony\Contracts\HttpClient\HttpClientInterface;
2222
use Symfony\UX\Icons\Iconify;
23+
use Symfony\UX\Icons\IconPreRendererInterface;
2324

2425
/**
2526
* @author Kevin Bond <kevinbond@gmail.com>
@@ -128,5 +129,9 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container
128129
if (!$container->getParameter('kernel.debug')) {
129130
$container->removeDefinition('.ux_icons.command.import');
130131
}
132+
133+
$container
134+
->registerForAutoconfiguration(IconPreRendererInterface::class)
135+
->addTag('ux_icons.icon_pre_renderer');
131136
}
132137
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Icons;
13+
14+
interface IconPreRendererInterface
15+
{
16+
public function __invoke(string $name, Icon $icon): Icon;
17+
}

src/Icons/src/IconRenderer.php

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@
1818
*/
1919
final class IconRenderer implements IconRendererInterface
2020
{
21+
/**
22+
* @param iterable<IconPreRendererInterface> $preRenderers
23+
*/
2124
public function __construct(
2225
private readonly IconRegistryInterface $registry,
2326
private readonly array $defaultIconAttributes = [],
2427
private readonly ?array $iconAliases = [],
28+
private readonly iterable $preRenderers = [],
2529
) {
2630
}
2731

@@ -42,30 +46,10 @@ public function renderIcon(string $name, array $attributes = []): string
4246
->withAttributes($this->defaultIconAttributes)
4347
->withAttributes($attributes);
4448

45-
foreach ($this->getPreRenderers() as $preRenderer) {
46-
$icon = $preRenderer($icon);
49+
foreach ($this->preRenderers as $preRenderer) {
50+
$icon = $preRenderer($name, $icon);
4751
}
4852

4953
return $icon->toHtml();
5054
}
51-
52-
/**
53-
* @return iterable<callable(Icon): Icon>
54-
*/
55-
private function getPreRenderers(): iterable
56-
{
57-
yield self::setAriaHidden(...);
58-
}
59-
60-
/**
61-
* Set `aria-hidden=true` if not defined & no textual alternative provided.
62-
*/
63-
private static function setAriaHidden(Icon $icon): Icon
64-
{
65-
if ([] === array_intersect(['aria-hidden', 'aria-label', 'aria-labelledby', 'title'], array_keys($icon->getAttributes()))) {
66-
return $icon->withAttributes(['aria-hidden' => 'true']);
67-
}
68-
69-
return $icon;
70-
}
7155
}

src/Icons/tests/Unit/IconRendererTest.php

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
namespace Symfony\UX\Icons\Tests\Unit;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\UX\Icons\AriaHiddenPreRenderer;
1516
use Symfony\UX\Icons\Exception\IconNotFoundException;
1617
use Symfony\UX\Icons\Icon;
18+
use Symfony\UX\Icons\IconPreRendererInterface;
1719
use Symfony\UX\Icons\IconRegistryInterface;
1820
use Symfony\UX\Icons\IconRenderer;
1921
use Symfony\UX\Icons\Tests\Util\InMemoryIconRegistry;
@@ -61,7 +63,7 @@ public function testRenderIconWithAttributes(): void
6163
$registry = $this->createRegistry([
6264
'foo' => '<path d="M0 0L12 12"/>',
6365
]);
64-
$iconRenderer = new IconRenderer($registry);
66+
$iconRenderer = new IconRenderer($registry, [], [], [new AriaHiddenPreRenderer()]);
6567
$attributes = ['viewBox' => '0 0 24 24', 'class' => 'icon', 'id' => 'FooBar'];
6668

6769
$svg = $iconRenderer->renderIcon('foo', $attributes);
@@ -74,7 +76,7 @@ public function testRenderIconWithDefaultAttributes(): void
7476
$registry = $this->createRegistry([
7577
'foo' => '<path d="M0 0L12 12"/>',
7678
]);
77-
$iconRenderer = new IconRenderer($registry, ['viewBox' => '0 0 24 24', 'class' => 'icon']);
79+
$iconRenderer = new IconRenderer($registry, ['viewBox' => '0 0 24 24', 'class' => 'icon'], [], [new AriaHiddenPreRenderer()]);
7880

7981
$svg = $iconRenderer->renderIcon('foo');
8082

@@ -172,7 +174,7 @@ public function testRenderIconWithAutoAriaHidden(string|array $icon, array $attr
172174
$registry = $this->createRegistry([
173175
'foo' => $icon,
174176
]);
175-
$iconRenderer = new IconRenderer($registry);
177+
$iconRenderer = new IconRenderer($registry, [], [], [new AriaHiddenPreRenderer()]);
176178

177179
$svg = $iconRenderer->renderIcon('foo', $attributes);
178180
$this->assertSame($expectedSvg, $svg);
@@ -237,7 +239,7 @@ public function testRenderIconWithAliases(): void
237239
'bar' => '<path d="M0 BAR"/>',
238240
'baz' => '<path d="M0 BAZ"/>',
239241
]);
240-
$iconRenderer = new IconRenderer($registry, [], ['foo' => 'bar']);
242+
$iconRenderer = new IconRenderer($registry, [], ['foo' => 'bar'], [new AriaHiddenPreRenderer()]);
241243

242244
$svg = $iconRenderer->renderIcon('bar');
243245
$this->assertSame('<svg aria-hidden="true"><path d="M0 BAR"/></svg>', $svg);
@@ -249,6 +251,25 @@ public function testRenderIconWithAliases(): void
249251
$this->assertSame('<svg aria-hidden="true"><path d="M0 BAZ"/></svg>', $svg);
250252
}
251253

254+
public function testRenderWithMultiplePreRenders(): void
255+
{
256+
$registry = $this->createRegistry([
257+
'foo' => '<path d="M0 0L12 12"/>',
258+
]);
259+
$customPreRenderer = new class implements IconPreRendererInterface {
260+
public function __invoke(string $name, Icon $icon): Icon
261+
{
262+
return $icon->withAttributes(['data-test-id' => $name]);
263+
}
264+
};
265+
$iconRenderer = new IconRenderer($registry, [], [], [new AriaHiddenPreRenderer(), $customPreRenderer]);
266+
$attributes = ['viewBox' => '0 0 24 24', 'class' => 'icon', 'id' => 'FooBar'];
267+
268+
$svg = $iconRenderer->renderIcon('foo', $attributes);
269+
270+
$this->assertSame('<svg viewBox="0 0 24 24" class="icon" id="FooBar" aria-hidden="true" data-test-id="foo"><path d="M0 0L12 12"/></svg>', $svg);
271+
}
272+
252273
private function createRegistry(array $icons): IconRegistryInterface
253274
{
254275
$registryIcons = [];

0 commit comments

Comments
 (0)