Skip to content

Commit 6b84639

Browse files
authored
fix(view): switch to runtime icon view component (#1165)
1 parent 619dd11 commit 6b84639

File tree

3 files changed

+46
-23
lines changed

3 files changed

+46
-23
lines changed

src/Tempest/View/src/Components/Icon.php

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@
1111
use Tempest\Core\AppConfig;
1212
use Tempest\Http\Status;
1313
use Tempest\HttpClient\HttpClient;
14+
use Tempest\Support\Html\HtmlString;
1415
use Tempest\Support\Str\ImmutableString;
15-
use Tempest\View\Elements\ViewComponentElement;
1616
use Tempest\View\IconConfig;
17-
use Tempest\View\ViewComponent;
1817

19-
final readonly class Icon implements ViewComponent
18+
final readonly class Icon
2019
{
2120
public function __construct(
2221
private AppConfig $appConfig,
@@ -25,28 +24,21 @@ public function __construct(
2524
private HttpClient $http,
2625
) {}
2726

28-
public static function getName(): string
27+
public function render(string $name, ?string $class = null): HtmlString
2928
{
30-
return 'x-icon';
31-
}
32-
33-
public function compile(ViewComponentElement $element): string
34-
{
35-
$name = $element->getAttribute('name');
36-
$class = $element->getAttribute('class');
29+
$html = $this->svg($name);
3730

38-
$svg = $this->render($name);
39-
40-
if (! $svg) {
41-
return $this->appConfig->environment->isLocal()
31+
if (! $html) {
32+
return new HtmlString($this->appConfig->environment->isLocal()
4233
? ('<!-- unknown-icon: ' . $name . ' -->')
43-
: '';
34+
: '');
35+
}
36+
37+
if ($class) {
38+
$html = $this->injectClass($html, $class);
4439
}
4540

46-
return match ($class) {
47-
null => $svg,
48-
default => $this->injectClass($svg, $class),
49-
};
41+
return new HtmlString($html);
5042
}
5143

5244
/**
@@ -79,7 +71,7 @@ private function download(string $prefix, string $name): ?string
7971
* in the cache, it will download it on the fly and cache it for future
8072
* use. If the icon is already in the cache, it will be served from there.
8173
*/
82-
private function render(string $name): ?string
74+
private function svg(string $name): ?string
8375
{
8476
try {
8577
$parts = explode(':', $name, 2);
@@ -110,8 +102,8 @@ private function injectClass(string $svg, string $class): string
110102
{
111103
return new ImmutableString($svg)
112104
->replace(
113-
search: '<svg ',
114-
replace: "<svg class=\"{$class}\" ",
105+
search: '<svg',
106+
replace: "<svg class=\"{$class}\"",
115107
)
116108
->toString();
117109
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
use Tempest\View\Components\Icon;
4+
5+
use function Tempest\get;
6+
7+
?>
8+
9+
{{ get(Icon::class)->render($name, $class ?? null) }}

tests/Integration/View/IconComponentTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,26 @@ public function test_it_forwards_the_class_attribute(): void
161161
),
162162
);
163163
}
164+
165+
public function test_with_dynamic_data(): void
166+
{
167+
$mockHttpClient = $this->createMock(HttpClient::class);
168+
$mockHttpClient
169+
->expects($this->exactly(1))
170+
->method('get')
171+
->with('https://api.iconify.design/ph/eye.svg')
172+
->willReturn(new GenericResponse(status: Status::OK, body: '<svg></svg>'));
173+
174+
$this->container->register(HttpClient::class, fn () => $mockHttpClient);
175+
176+
$rendered = $this->render(
177+
'<x-icon :name="$iconName" class="size-5" />',
178+
iconName: 'ph:eye',
179+
);
180+
181+
$this->assertSame(
182+
'<svg class="size-5"></svg>',
183+
$rendered,
184+
);
185+
}
164186
}

0 commit comments

Comments
 (0)