F
Filament7mo ago
Thomas

FilamentIcon::register view() wrongly (?) rendered as image source

I am trying to replace an icon. For example: tables::actions.filter In the docs it says:
Alternatively, you may use HTML instead of an icon name to render an icon from a Blade view for example:
Source: https://filamentphp.com/docs/3.x/support/icons#replacing-the-default-icons When I pass a view() it will load the content as the src of the img-element. What I expect to happen here is to replace the entire img-element by what's in my Blade file. Is this intended behaviour or is this an issue with Filament?
38 Replies
Dennis Koch
Dennis Koch7mo ago
When I pass a view() it will load the content as the src of the img-element.
Not sure what that means. Icons probably should be SVG and not <img> tags. Can you share any code? What does your Blade file look like? Also maybe share a screenshot of what is happening
Thomas
ThomasOP7mo ago
I use this in my service provider.
FilamentIcon::register([
'tables::actions.filter' => view('icons.filter'),
]);
FilamentIcon::register([
'tables::actions.filter' => view('icons.filter'),
]);
The result is:
<img class="fi-btn-icon transition duration-75 h-4 w-4 text-gray-400 dark:text-gray-500" src="<div>contents of the icons.filter file</div>">
<img class="fi-btn-icon transition duration-75 h-4 w-4 text-gray-400 dark:text-gray-500" src="<div>contents of the icons.filter file</div>">
I thought it would replace the entire image element like so:
<div>contents of the icons.filter file</div>
<div>contents of the icons.filter file</div>
Thomas
ThomasOP7mo ago
The result is it can't find the image because I didn't pass it a url.
No description
Dennis Koch
Dennis Koch7mo ago
I haven’t looked into the code but I guess this is the fallback when it’s not an svg? 🤔
Thomas
ThomasOP7mo ago
Same issue.
<img class="fi-btn-icon transition duration-75 h-4 w-4 text-gray-400 dark:text-gray-500" src="<svg xmlns=&quot;http://www.w3.org/2000/svg&quot; fill=&quot;none&quot; viewBox=&quot;0 0 24 24&quot; stroke-width=&quot;1.5&quot; stroke=&quot;currentColor&quot; class=&quot;size-6&quot;>
<path stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; d=&quot;M10.5 6h9.75M10.5 6a1.5 1.5 0 1 1-3 0m3 0a1.5 1.5 0 1 0-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-9.75 0h9.75&quot; />
</svg>">
<img class="fi-btn-icon transition duration-75 h-4 w-4 text-gray-400 dark:text-gray-500" src="<svg xmlns=&quot;http://www.w3.org/2000/svg&quot; fill=&quot;none&quot; viewBox=&quot;0 0 24 24&quot; stroke-width=&quot;1.5&quot; stroke=&quot;currentColor&quot; class=&quot;size-6&quot;>
<path stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; d=&quot;M10.5 6h9.75M10.5 6a1.5 1.5 0 1 1-3 0m3 0a1.5 1.5 0 1 0-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-9.75 0h9.75&quot; />
</svg>">
I tried to find the code that does this but couldn't really find it.
LeandroFerreira
LeandroFerreira7mo ago
would you like to use another heroicon? I mean
->filtersTriggerAction(fn (Tables\Actions\Action $action) => $action
->icon('heroicon-s-adjustments-vertical')
)
->filtersTriggerAction(fn (Tables\Actions\Action $action) => $action
->icon('heroicon-s-adjustments-vertical')
)
Thomas
ThomasOP7mo ago
No, I want to specify html
Thomas
ThomasOP7mo ago
Which should be possible according to https://github.com/filamentphp/filament/blob/3.x/packages/support/resources/views/components/icon.blade.php but when I debug the $icon it's already rendered and is a string at that point so the if-statement that checks if its Htmlable is always false
GitHub
filament/packages/support/resources/views/components/icon.blade.php...
A collection of beautiful full-stack components for Laravel. The perfect starting point for your next app. Using Livewire, Alpine.js and Tailwind CSS. - filamentphp/filament
Dennis Koch
Dennis Koch7mo ago
Trying to debug this. It's already returned as a string from the Action Seems like you are the first person trying this. The docs were added in December 2023 😅 @Zep Fietje Since you added the docs, do you have any idea how this was supposed to work? We have HtmlString in a lot of places but it's missing from Support\Concerns\HasIcon. Even if we add this: Views aren't HtmlStrings.
Thomas
ThomasOP7mo ago
It's really weird. Because FilamentIcon::resolve returns the Htmlable view in HasFilters but for some reason it's converted to a string in HasIcon even though there's nothing in between. I tried to add Htmlable to the icon method but that didn't change anything.
Dennis Koch
Dennis Koch7mo ago
The $icon property and all the methods don't accept HtmlString so it's casted to a string
Thomas
ThomasOP7mo ago
I forgot to import the Htmlable class, so yea that's the fix Never knew that, interesting
Dennis Koch
Dennis Koch7mo ago
But does it work with plain view()? 🤔 I wrapped it like this: new HtmlString(view('icons.chevron-up')->render()),
Thomas
ThomasOP7mo ago
It should once Htmlable is added to all the methods I've only tested it in HasIcon's icon() method for now.
Dennis Koch
Dennis Koch7mo ago
Doesn't work for me. Since view()gets casted to string, too, I think
Dennis Koch
Dennis Koch7mo ago
GitHub
HasIcon: Support HtmlString and update docs by pxlrbt · Pull Reques...
Description According to the docs you should be able to pass a view to register an icon alias. This gets casted to a string though and is then outputted as an image. Also view() doesn't cast to...
Thomas
ThomasOP7mo ago
Maybe it gets converted at some point because a view does return an Htmlable element
Dennis Koch
Dennis Koch7mo ago
When I test this, I get: test(): Return value must be of type Illuminate\Support\HtmlString, Illuminate\View\View returned It's Htmlable but doesn't return HtmlString.
Thomas
ThomasOP7mo ago
HtmlString is just a wrapper to create an Htmlable from a string though? As HtmlString implements Htmlable
Dennis Koch
Dennis Koch7mo ago
Htmlable is an interface. Not a class
Thomas
ThomasOP7mo ago
That doesn't matter, Laravel checks if it's an Htmlable element and calls toHtml(), if you check Laravel's source it never checks if something is an HtmlString, it always checks if its Htmlable
Dennis Koch
Dennis Koch7mo ago
But we are in Filament codebase here, not Laravel. And the code you shared above icon.blade.php checks for HtmlString not Htmlable. Not sure why we don't use Htmlable everywhere.
Thomas
ThomasOP7mo ago
Huh, maybe I'm not checking the right file but it's checking for Htmlable
Thomas
ThomasOP7mo ago
GitHub
filament/packages/support/resources/views/components/icon.blade.php...
A collection of beautiful full-stack components for Laravel. The perfect starting point for your next app. Using Livewire, Alpine.js and Tailwind CSS. - filamentphp/filament
Dennis Koch
Dennis Koch7mo ago
Oh you are right. Okay, we use Htmlable there. But all the methods and properties usually use HtmlString 🙈
Thomas
ThomasOP7mo ago
I've done some digging but I think that's unrelated. It seems like passing an Htmlable as a prop to a component renders it. When you use an HtmlString it works. Feels like a bug in Laravel but I can't find how that works.
Dennis Koch
Dennis Koch7mo ago
Yes. That's where I ended up too. The issue is passing it from action.blade.php to icon-button.blade.php. I think we had a workaround for this before, but I am not sure how it works. I CCed Zep in that PR
Thomas
ThomasOP7mo ago
That's the reason it gets converted. A view has __toString() and HtmlString doesn't. I'll add it to the PR Hm, no, that's not it. It's still returned as a string somehow 🤦‍♂️
Dennis Koch
Dennis Koch7mo ago
What do you mean? What was your change?
Thomas
ThomasOP7mo ago
In sanitizeComponentAttribute I've added a conditional for Htmlable, that doesn't sanitize the view, but further down the line it's still rendered somehow
Dennis Koch
Dennis Koch7mo ago
That probably won't be a solution for us anyway. It would only work with newer version and might be a breaking change for Laravel
Thomas
ThomasOP7mo ago
At this point I want to know why this is happening. The workaround of using an html string is fine for me.
Thomas
ThomasOP7mo ago
GitHub
framework/src/Illuminate/View/View.php at 11.x · laravel/framework
The Laravel Framework. Contribute to laravel/framework development by creating an account on GitHub.
Thomas
ThomasOP7mo ago
That's the reason it happens. It renders Renderables. Views are renderable while Htmlable's are not.
Zep Fietje
Zep Fietje7mo ago
Views are Htmlable, right? Not too sure, but maybe the original functionality broke when we fixed another bug the original PR introduced? 😅 Yeah this was one of the bugs: the html icon needs to be passed as slot instead of prop
Dennis Koch
Dennis Koch7mo ago
Doubt this ever worked because actions only return a string no HtmlString.
Thomas
ThomasOP7mo ago
It never worked for two reasons. A view() is stringable. As the HasIcon trait didn't have the Htmlable type, PHP would convert it to string and render the HTML. After the type was added it didn't work either and that's because a view is also Renderable. If you pass a view to another view as a prop it renders all Renderables. As a workaround I added the Htmlable types and included this in getIcon inside the HasIcons trait:
$icon = $this->icon instanceof Renderable
? new HtmlString($this->icon->render())
: $this->icon;
$icon = $this->icon instanceof Renderable
? new HtmlString($this->icon->render())
: $this->icon;
This looks a bit weird because you're not accepting a renderable, but because a view is both this works.

Did you find this page helpful?