Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,25 @@ msgstr "Aguanta mientras volvemos a la normalidad"
component-specific presentation or behavior without adding library-level options. LiveToast does not interpret
metadata.

### Default custom toast component

Pass `toast_component_fn` to `LiveToast.toast_group` when all programmatic toasts in that host should use the same
custom component. A `component:` passed to an individual `send_toast/3` call takes precedence. This host default does
not change Phoenix flash or connection-notice rendering.

```heex
<LiveToast.toast_group
flash={@flash}
connected={assigns[:socket] != nil}
toasts_sync={assigns[:toasts_sync]}
toast_component_fn={&MyAppWeb.Components.NotificationToast.render/1}
/>
```

```elixir
LiveToast.send_toast(:info, "The message was sent.", title: "Sent")
```

### Custom component metadata

Custom component functions receive `metadata` alongside the standard toast assigns such as `kind`, `title`, `body`,
Expand Down
11 changes: 11 additions & 0 deletions demo/lib/demo_web/live/tabs/demo.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,17 @@
<p class="mt-2 mb-4">
It will totally take over rendering for that call, so that you can render totally custom toasts. It receives all the assigns of the built in toast function. See the default implementation in GitHub for an example.
</p>

<p class="mt-2 mb-4">
If you want every call to
<code class="text-rose-600 bg-zinc-50 rounded px-1">send_toast</code>
to use a custom function, you can also define it up in the call to <.link
class="text-blue-700 hover:text-blue-500 underline"
href="https://hexdocs.pm/live_toast/LiveToast.html#toast_group/1"
>
<code class="text-rose-600 bg-zinc-50 rounded px-1">toast_group</code>
</.link>.
</p>
<div class="flex flex-wrap gap-3">
<.button phx-click={
JS.push("toast",
Expand Down
71 changes: 71 additions & 0 deletions demo/test/demo_web/live/send_toast_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,54 @@ defmodule DemoWeb.SendToastTest do
defp centered_corner(_corner), do: :top_center
end

defmodule DefaultComponentLive do
@moduledoc false

use Phoenix.LiveView

def mount(_params, _session, socket), do: {:ok, socket}

def handle_event("send_default", _, socket) do
LiveToast.send_toast(:info, "Rendered by the host default.", uuid: "component-precedence")

{:noreply, socket}
end

def handle_event("send_override", _, socket) do
LiveToast.send_toast(:info, "Rendered by the per-toast override.",
component: &override_toast/1,
uuid: "component-precedence"
)

{:noreply, socket}
end

def render(assigns) do
~H"""
<LiveToast.toast_group
flash={@flash}
toasts_sync={assigns[:toasts_sync]}
connected={assigns[:socket] != nil}
toast_component_fn={&default_toast/1}
/>
<button phx-click="send_default">Send Default Component Toast</button>
<button phx-click="send_override">Send Override Component Toast</button>
"""
end

defp default_toast(assigns) do
~H"""
<p data-toast-renderer="default">{@body}</p>
"""
end

defp override_toast(assigns) do
~H"""
<p data-toast-renderer="override">{@body}</p>
"""
end
end

defmodule ConnectionNotificationLive do
@moduledoc false

Expand Down Expand Up @@ -216,6 +264,29 @@ defmodule DemoWeb.SendToastTest do
refute html =~ ~s(data-metadata-icon)
assert html =~ ~s(data-reference="receipt-123")
end

test "uses the host default component unless a toast supplies an override", %{conn: conn} do
{:ok, view, _html} = live_isolated(conn, DefaultComponentLive)

view
|> element("button", "Send Default Component Toast")
|> render_click()

html = render(view)

assert html =~ ~s(data-toast-renderer="default")
assert html =~ "Rendered by the host default."

view
|> element("button", "Send Override Component Toast")
|> render_click()

html = render(view)

assert html =~ ~s(data-toast-renderer="override")
assert html =~ "Rendered by the per-toast override."
refute html =~ ~s(data-toast-renderer="default")
end
end

describe "centered positioning" do
Expand Down
6 changes: 6 additions & 0 deletions lib/live_toast.ex
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,11 @@ defmodule LiveToast do
doc: "function to override the toast classes"
)

attr(:toast_component_fn, :any,
default: nil,
doc: "default component function used to render programmatic toasts"
)

attr :toasts_sync, :list, required: true, doc: "toasts that get synchronized when calling `put_toast`"

attr :flash_duration, :integer, default: 0, doc: "if provided clears flash after provided milliseconds"
Expand Down Expand Up @@ -325,6 +330,7 @@ defmodule LiveToast do
flash_duration={@flash_duration}
client_error_delay={@client_error_delay}
toast_class_fn={@toast_class_fn}
toast_component_fn={@toast_component_fn}
group_class_fn={@group_class_fn}
connection_notifications={@connection_notifications}
client_error={@client_error}
Expand Down
2 changes: 1 addition & 1 deletion lib/live_toast/live_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ defmodule LiveToast.LiveComponent do
duration={duration}
kind={k}
toast_class_fn={@toast_class_fn}
component={component}
component={component || @toast_component_fn}
icon={icon}
action={action}
metadata={metadata}
Expand Down
Loading