diff --git a/src/Tempest/View/src/Components/Icon.php b/src/Tempest/View/src/Components/Icon.php index d7f492dd1..b28757340 100644 --- a/src/Tempest/View/src/Components/Icon.php +++ b/src/Tempest/View/src/Components/Icon.php @@ -35,51 +35,39 @@ public function compile(ViewComponentElement $element): string $name = $element->getAttribute('name'); $class = $element->getAttribute('class'); - $svg = $this->render($name); - - if (! $svg) { - return $this->appConfig->environment->isLocal() - ? ('') - : ''; - } - - return match ($class) { - null => $svg, - default => $this->injectClass($svg, $class), - }; + return sprintf( + 'render(%s, \'%s\' ?: null) ?>', + self::class, + str_starts_with($name, ''], '', $name) + : "\"{$name}\"", + $class, + ); } /** - * Downloads the icon's SVG file from the Iconify API + * Renders the specified icon. If the icon is not in the cache, + * it will be downloaded it on the fly and cached for future use. + * If the icon is already in the cache, it will be served from there. */ - private function download(string $prefix, string $name): ?string + public function render(string $name, ?string $class): string { - try { - $url = new ImmutableString($this->iconConfig->iconifyApiUrl) - ->finish('/') - ->append("{$prefix}/{$name}.svg") - ->toString(); + $svg = $this->svg($name); - $response = $this->http->get($url); - - if ($response->status !== Status::OK) { - return null; - } + if (! $svg) { + return $this->appConfig->environment->isLocal() + ? ('') + : ''; + } - return $response->body; - } catch (Exception) { - return null; + if ($class !== null) { + return $this->injectClass($svg, $class); } + + return $svg; } - /** - * Renders an icon - * - * This method is responsible for rendering the icon. If the icon is not - * in the cache, it will download it on the fly and cache it for future - * use. If the icon is already in the cache, it will be served from there. - */ - private function render(string $name): ?string + private function svg(string $name): ?string { try { $parts = explode(':', $name, 2); @@ -103,6 +91,29 @@ private function render(string $name): ?string } } + /** + * Downloads the icon's SVG file from the Iconify API + */ + private function download(string $prefix, string $name): ?string + { + try { + $url = new ImmutableString($this->iconConfig->iconifyApiUrl) + ->finish('/') + ->append("{$prefix}/{$name}.svg") + ->toString(); + + $response = $this->http->get($url); + + if ($response->status !== Status::OK) { + return null; + } + + return $response->body; + } catch (Exception) { + return null; + } + } + /** * Forwards the user-provided class attribute to the SVG element */ @@ -110,8 +121,8 @@ private function injectClass(string $svg, string $class): string { return new ImmutableString($svg) ->replace( - search: 'toString(); } diff --git a/tests/Integration/View/IconComponentTest.php b/tests/Integration/View/IconComponentTest.php index 43f244261..2c95c047d 100644 --- a/tests/Integration/View/IconComponentTest.php +++ b/tests/Integration/View/IconComponentTest.php @@ -161,4 +161,26 @@ public function test_it_forwards_the_class_attribute(): void ), ); } + + public function test_with_dynamic_data(): void + { + $mockHttpClient = $this->createMock(HttpClient::class); + $mockHttpClient + ->expects($this->exactly(1)) + ->method('get') + ->with('https://api.iconify.design/ph/eye.svg') + ->willReturn(new GenericResponse(status: Status::OK, body: '')); + + $this->container->register(HttpClient::class, fn () => $mockHttpClient); + + $rendered = $this->render( + '', + iconName: 'ph:eye', + ); + + $this->assertSame( + '', + $rendered, + ); + } }