From a41110bc7f9ba0f35b77dd1bc4fa6c2d00097fe6 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 28 Aug 2026 16:42:07 -0400 Subject: [PATCH 1/3] Update CHANGELOG.md --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3eb0ec..80f7563 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. Note that the versions prior to `0.6.0` are very unstable. +## [v0.10.0] (unreleased) + +Note: this release includes breaking changes involving translation. The library no longer calls `gettext` functions on +dynamic strings passed to `put_toast` and `send_toast`. + +### Changed + +- [Translation behavior](https://github.com/srcrip/live_toast/pull/78): translation of LiveToast's built-in connection notices is now opt-in; configure `gettext_backend` to enable it. `send_toast/3`, `put_toast/4`, and custom connection-notification copy now render the supplied message and title unchanged, so translate application-owned copy before passing it to LiveToast. +- [TypeScript compatibility](https://github.com/srcrip/live_toast/pull/77): LiveToast no longer augments the global `HTMLElement` interface, and the package now ships and exposes its TypeScript declarations. + ## [v0.1.0] (2024-03-06) 🚀 Initial release. From 2c30f02cf9ba3bbae8f91e7afaf10778c94f48e8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 28 Aug 2026 16:47:04 -0400 Subject: [PATCH 2/3] Render flashes with host toast component --- README.md | 10 ++- demo/lib/demo_web/components/layouts.ex | 13 ++++ .../demo_web/components/layouts/app.html.heex | 3 + .../live/tabs/customization.html.heex | 16 ++++- demo/test/demo_web/live/send_toast_test.exs | 62 +++++++++++++++++++ lib/live_toast.ex | 10 ++- lib/live_toast/components.ex | 50 +++++++++++---- lib/live_toast/live_component.ex | 2 + 8 files changed, 149 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index cbba910..ff8ead0 100644 --- a/README.md +++ b/README.md @@ -316,9 +316,9 @@ classes; use a custom component function for the icon, title, body, action, and ### 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. +Pass `toast_component_fn` to `LiveToast.toast_group` when programmatic toasts and Phoenix flashes in that host should +use the same custom component. A `component:` passed to an individual `send_toast/3` call takes precedence for that +toast. Connection notices continue to use their default content or the corresponding slot. ```heex ``` +`flash_group_id` optionally adds an addressable container around only the Phoenix flashes. Programmatic toasts remain +outside that container. + ```elixir LiveToast.send_toast(:info, "The message was sent.", title: "Sent") ``` diff --git a/demo/lib/demo_web/components/layouts.ex b/demo/lib/demo_web/components/layouts.ex index 27a66b5..1070eba 100644 --- a/demo/lib/demo_web/components/layouts.ex +++ b/demo/lib/demo_web/components/layouts.ex @@ -35,6 +35,19 @@ defmodule DemoWeb.Layouts do ] end + attr(:kind, :atom, required: true) + attr(:title, :string, default: nil) + attr(:body, :any, required: true) + + def demo_toast_component_fn(assigns) do + ~H""" +
+

{@title}

+

{@body}

+
+ """ + end + attr(:notice, :map, required: true) def connection_notice(assigns) do diff --git a/demo/lib/demo_web/components/layouts/app.html.heex b/demo/lib/demo_web/components/layouts/app.html.heex index e69fe7d..c93675f 100644 --- a/demo/lib/demo_web/components/layouts/app.html.heex +++ b/demo/lib/demo_web/components/layouts/app.html.heex @@ -2,6 +2,9 @@ toast_component_fn on toast_group - to use it for every programmatic toast in that host. + to use it for every programmatic toast and Phoenix flash in that host. +

+
<LiveToast.toast_group
+  flash={@flash}
+  connected={assigns[:socket] != nil}
+  toasts_sync={assigns[:toasts_sync]}
+  toast_component_fn={&notification/1}
+/>
+

+ <.link + class="text-blue-700 hover:text-blue-500 underline" + href="https://github.com/srcrip/live_toast/blob/main/demo/lib/demo_web/components/layouts.ex" + > + View the demo component +

diff --git a/demo/test/demo_web/live/send_toast_test.exs b/demo/test/demo_web/live/send_toast_test.exs index ff1d8ad..5790247 100644 --- a/demo/test/demo_web/live/send_toast_test.exs +++ b/demo/test/demo_web/live/send_toast_test.exs @@ -156,6 +156,28 @@ defmodule DemoWeb.SendToastTest do end end + defmodule FlashComponentLive do + @moduledoc false + + use Phoenix.LiveView + + def mount(_params, _session, socket) do + {:ok, Phoenix.LiveView.put_flash(socket, :info, "Rendered from a live flash.")} + end + + def render(assigns) do + ~H""" + + """ + end + end + defmodule ClientToastLive do @moduledoc false @@ -262,6 +284,24 @@ defmodule DemoWeb.SendToastTest do """ end + defp dead_flash_component_host(assigns) do + ~H""" + + """ + end + + def flash_component(assigns) do + ~H""" +

{@body}

+ """ + end + describe "LiveToast.send_toast/3" do test "renders info toast", %{conn: conn} do {:ok, view, _html} = live_isolated(conn, TestLive) @@ -344,6 +384,28 @@ defmodule DemoWeb.SendToastTest do end end + describe "Phoenix flashes" do + test "uses the host component in a live host", %{conn: conn} do + {:ok, view, html} = live_isolated(conn, FlashComponentLive) + + assert html =~ ~s(id="live-flashes") + assert html =~ ~s(data-flash-renderer="custom") + assert html =~ "Rendered from a live flash." + assert has_element?(view, "#live-flashes [data-kind=info]") + end + + test "uses the host component in a dead host" do + html = + render_component(&dead_flash_component_host/1, + flash: %{"error" => "Rendered from a dead flash."} + ) + + assert html =~ ~s(id="dead-flashes") + assert html =~ ~s(data-flash-renderer="custom") + assert html =~ "Rendered from a dead flash." + end + end + describe "client toast API" do test "renders a client toast with supported options", %{conn: conn} do {:ok, view, _html} = live_isolated(conn, ClientToastLive) diff --git a/lib/live_toast.ex b/lib/live_toast.ex index 054befc..3fb62ff 100644 --- a/lib/live_toast.ex +++ b/lib/live_toast.ex @@ -298,7 +298,12 @@ defmodule LiveToast do attr(:toast_component_fn, :any, default: nil, - doc: "default component function used to render programmatic toasts" + doc: "default component function used to render programmatic toasts and Phoenix flashes" + ) + + attr(:flash_group_id, :string, + default: nil, + doc: "optional id for a container around Phoenix flashes" ) attr :toasts_sync, :list, required: true, doc: "toasts that get synchronized when calling `put_toast`" @@ -332,6 +337,7 @@ defmodule LiveToast do client_error_delay={@client_error_delay} toast_class_fn={@toast_class_fn} toast_component_fn={@toast_component_fn} + flash_group_id={@flash_group_id} group_class_fn={@group_class_fn} connection_notifications={@connection_notifications} client_error={@client_error} @@ -345,7 +351,9 @@ defmodule LiveToast do corner={@corner} flash_duration={@flash_duration} toast_class_fn={@toast_class_fn} + toast_component_fn={@toast_component_fn} group_class_fn={@group_class_fn} + flash_group_id={@flash_group_id} client_error_delay={@client_error_delay} connection_notifications={@connection_notifications} client_error={@client_error} diff --git a/lib/live_toast/components.ex b/lib/live_toast/components.ex index b50dfc0..b19d009 100644 --- a/lib/live_toast/components.ex +++ b/lib/live_toast/components.ex @@ -133,6 +133,13 @@ defmodule LiveToast.Components do doc: "function to override the toast classes" ) + attr(:toast_component_fn, :any, + default: nil, + doc: "component function used to render Phoenix flash content" + ) + + attr(:id, :string, default: nil, doc: "optional id for the flash-only container") + attr(:connection_notifications, :any, default: %{}, doc: "false disables connection-state notifications; a map overrides their copy and kind" @@ -150,18 +157,10 @@ defmodule LiveToast.Components do assigns = assign(assigns, :connection_notifications, connection_notifications(assigns.connection_notifications)) ~H""" - <.toast - :for={level <- @kinds} - data-component="flash" - corner={@corner} - toast_class_fn={@toast_class_fn} - flash_duration={@flash_duration} - duration={0} - kind={level} - title={String.capitalize(to_string(level))} - phx-update="ignore" - flash={@f} - /> +
+ <.flash_toasts {assigns} /> +
+ <.flash_toasts :if={!@id} {assigns} /> <%= if @connection_notifications do %> <.toast data-component="flash" @@ -224,6 +223,24 @@ defmodule LiveToast.Components do """ end + defp flash_toasts(assigns) do + ~H""" + <.toast + :for={level <- @kinds} + data-component="flash" + corner={@corner} + toast_class_fn={@toast_class_fn} + component={@toast_component_fn} + flash_duration={@flash_duration} + duration={0} + kind={level} + title={String.capitalize(to_string(level))} + phx-update="ignore" + flash={@f} + /> + """ + end + defp duration_value(:infinity), do: "Infinity" defp duration_value(duration), do: duration @@ -247,6 +264,13 @@ defmodule LiveToast.Components do doc: "function to override the toast classes" ) + attr(:toast_component_fn, :any, + default: nil, + doc: "component function used to render Phoenix flash content" + ) + + attr(:flash_group_id, :string, default: nil, doc: "optional id for the flash-only container") + attr(:client_error_delay, :integer, default: 3000, doc: "adds a delay before the disconnected client error is shown") attr(:flash_duration, :integer, default: 0, doc: "if provided clears flash after provided milliseconds") @@ -269,6 +293,8 @@ defmodule LiveToast.Components do corner={@corner} flash_duration={@flash_duration} toast_class_fn={@toast_class_fn} + toast_component_fn={@toast_component_fn} + id={@flash_group_id} client_error_delay={@client_error_delay} connection_notifications={@connection_notifications} client_error={@client_error} diff --git a/lib/live_toast/live_component.ex b/lib/live_toast/live_component.ex index 5f9e52c..171f12c 100644 --- a/lib/live_toast/live_component.ex +++ b/lib/live_toast/live_component.ex @@ -141,6 +141,8 @@ defmodule LiveToast.LiveComponent do corner={@corner} flash_duration={@flash_duration} toast_class_fn={@toast_class_fn} + toast_component_fn={@toast_component_fn} + id={@flash_group_id} client_error_delay={@client_error_delay} connection_notifications={@connection_notifications} client_error={@client_error} From 61c6e7b2eb439a09ade98396ab168945bed9ed3d Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 28 Aug 2026 16:51:23 -0400 Subject: [PATCH 3/3] Allow live flash updates --- demo/test/demo_web/live/send_toast_test.exs | 16 ++++++++++++---- lib/live_toast/components.ex | 1 - 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/demo/test/demo_web/live/send_toast_test.exs b/demo/test/demo_web/live/send_toast_test.exs index 5790247..f7c91f3 100644 --- a/demo/test/demo_web/live/send_toast_test.exs +++ b/demo/test/demo_web/live/send_toast_test.exs @@ -161,8 +161,10 @@ defmodule DemoWeb.SendToastTest do use Phoenix.LiveView - def mount(_params, _session, socket) do - {:ok, Phoenix.LiveView.put_flash(socket, :info, "Rendered from a live flash.")} + def mount(_params, _session, socket), do: {:ok, socket} + + def handle_event("show_flash", _params, socket) do + {:noreply, Phoenix.LiveView.put_flash(socket, :info, "Rendered from a live flash.")} end def render(assigns) do @@ -174,6 +176,7 @@ defmodule DemoWeb.SendToastTest do toast_component_fn={&DemoWeb.SendToastTest.flash_component/1} flash_group_id="live-flashes" /> + """ end end @@ -389,8 +392,13 @@ defmodule DemoWeb.SendToastTest do {:ok, view, html} = live_isolated(conn, FlashComponentLive) assert html =~ ~s(id="live-flashes") - assert html =~ ~s(data-flash-renderer="custom") - assert html =~ "Rendered from a live flash." + refute html =~ ~s(data-flash-renderer="custom") + + view + |> element("button", "Show Flash") + |> render_click() + + assert render(view) =~ "Rendered from a live flash." assert has_element?(view, "#live-flashes [data-kind=info]") end diff --git a/lib/live_toast/components.ex b/lib/live_toast/components.ex index b19d009..fb9e18c 100644 --- a/lib/live_toast/components.ex +++ b/lib/live_toast/components.ex @@ -235,7 +235,6 @@ defmodule LiveToast.Components do duration={0} kind={level} title={String.capitalize(to_string(level))} - phx-update="ignore" flash={@f} /> """