Hi!
I’m building a component library, and I’d like to figure out how to silence some warnings.
Let’s say I’m building a tooltip component - it needs a trigger that opens the tooltip when hovered over. Now, in order to make my hook work with it, it needs the data-part="trigger"
attribute set.
So, I wrote this:
attr :as, :string, default: "button"
attr :rest, :global
slot :inner_block
def trigger(assigns) do
<.dynamic_tag name={@as} data-part="trigger" {rest}><%= render_slot(@inner_block) %></.dynamic_tag>
end
This works.
Next step: the trigger might just be a button that is an existing pre-styled component. I have a ton of different button variants, sizes… and I don’t want to repeat styling of them. So, the solution would be passing my button component to be a trigger.
I came up with this beauty:
def render_as_tag_or_component(assigns, extra_assigns) do
assigns =
assigns
|> Map.get(:rest, %{})
|> Enum.reduce(assigns, fn {k, v}, acc -> assign(acc, k, v) end)
|> Map.delete(:rest)
|> Map.merge(extra_assigns)
~H"""
<%= if is_function(@as) do %>
<%= Phoenix.LiveView.TagEngine.component(
@as,
Map.delete(assigns, :as),
{__ENV__.module, __ENV__.function, __ENV__.file, __ENV__.line}
) %>
<% end %>
<%= if is_binary(@as) do %>
<.dynamic_tag name={@as} {Map.delete(assigns, :as)} />
<% end %>
"""
end
attr :as, :any, default: "button"
attr :rest, :global
slot :inner_block
def trigger(assigns) do
render_as_tag_or_component(assigns, %{"data-part" => "trigger"})
end
This can now be called with either <.trigger as="button">
or <.trigger as={&button/1}>
.
That also works.
The problem is: the button/1
component has its own set of attributes - in this case, you can add an icon prefix by passing an icon class mask with icon_left
.
<Tooltip.trigger as={&button/1} icon_left="i-ph-circle">Hover</Tooltip.trigger>
works and renders correctly without any errors, but because icon_left
is not an attribute defined on trigger/1
, I am getting an “undefined attribute” warning. Every time I call it.
I’ll never be able to list every attribute in trigger/1
, since any user-defined function outside the library can be passed, with an arbitrary amount of attributes. rest
doesn’t catch it since it’s not a catch-all.
Is there any way to make this work without getting warnings? I’d be happy to just disable attribute checks altogether for these components.