From 2081aa89274b90812b0380bda90c20d03145a1ca Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 20 Jul 2026 22:17:27 -0400 Subject: [PATCH 1/2] Allow disabling connection notifications --- README.md | 14 +- demo/lib/demo_web/live/home_live.html.heex | 6 + demo/lib/demo_web/live/tabs/recipes.html.heex | 25 ++++ demo/test/demo_web/live/home_live_test.exs | 9 ++ demo/test/demo_web/live/send_toast_test.exs | 52 ++++++++ lib/live_toast.ex | 4 +- lib/live_toast/components.ex | 124 +++++++++--------- 7 files changed, 171 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 835123d..e6a5133 100644 --- a/README.md +++ b/README.md @@ -343,7 +343,19 @@ LiveToast.dismiss_toast(uuid) ### Connection-state notifications -Customize the built-in client and server connection notices with `connection_notifications`. Each entry accepts +Set `connection_notifications={false}` when your application supplies its own connection-state UI. This removes both +built-in notices, including their connection-event wiring. + +```heex + +``` + +Otherwise, customize the built-in client and server connection notices with `connection_notifications`. Each entry accepts `kind`, `title`, and `body`; unspecified values retain their defaults. Notices are persistent, non-dismissible, and excluded from the ordinary visible-toast limit. diff --git a/demo/lib/demo_web/live/home_live.html.heex b/demo/lib/demo_web/live/home_live.html.heex index 58d7f22..0598abe 100644 --- a/demo/lib/demo_web/live/home_live.html.heex +++ b/demo/lib/demo_web/live/home_live.html.heex @@ -174,6 +174,12 @@ > Connection Notices + <.link + href={~p"/recipes#disable-connection-notifications"} + class="block mb-1 text-sm text-zinc-600 px-2 py-0.5 hover:text-zinc-900 hover:bg-zinc-100 rounded-md transition-colors" + > + Disable Connection Notices + <.link patch={~p"/customization"} diff --git a/demo/lib/demo_web/live/tabs/recipes.html.heex b/demo/lib/demo_web/live/tabs/recipes.html.heex index 710c1e5..fae20f2 100644 --- a/demo/lib/demo_web/live/tabs/recipes.html.heex +++ b/demo/lib/demo_web/live/tabs/recipes.html.heex @@ -243,3 +243,28 @@ LiveToast.dismiss_toast(uuid)

+ +
+

+ Disable Connection Notices +

+

+ When your application has its own connection-state UI, set + connection_notifications={false} + to omit LiveToast's built-in disconnected and reconnecting notices. +

+
<LiveToast.toast_group
+  flash={@flash}
+  connected={assigns[:socket] != nil}
+  toasts_sync={assigns[:toasts_sync]}
+  connection_notifications={false}
+/>
+

+ <.link + class="text-blue-700 hover:text-blue-500 underline" + href="https://github.com/srcrip/live_toast/blob/main/demo/lib/demo_web/live/tabs/recipes.html.heex" + > + View the recipe source + +

+
diff --git a/demo/test/demo_web/live/home_live_test.exs b/demo/test/demo_web/live/home_live_test.exs index 196adc4..e4e9467 100644 --- a/demo/test/demo_web/live/home_live_test.exs +++ b/demo/test/demo_web/live/home_live_test.exs @@ -137,6 +137,14 @@ defmodule DemoWeb.HomeLiveTest do refute has_element?(view, "#client-error button[aria-label=close]") end + test "renders the disabled connection notice recipe", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/recipes") + + assert html =~ "Disable Connection Notices" + assert html =~ "connection_notifications={false}" + assert html =~ "View the recipe source" + end + test "supports both persistent duration forms", %{conn: conn} do {:ok, view, html} = live(conn, ~p"/recipes") @@ -183,6 +191,7 @@ defmodule DemoWeb.HomeLiveTest do assert html =~ ~s(href="/recipes#centered-positions") assert html =~ ~s(href="/recipes#persistent-toasts") assert html =~ ~s(href="/recipes#connection-notifications") + assert html =~ ~s(href="/recipes#disable-connection-notifications") end end end diff --git a/demo/test/demo_web/live/send_toast_test.exs b/demo/test/demo_web/live/send_toast_test.exs index bf1a152..31ba8e4 100644 --- a/demo/test/demo_web/live/send_toast_test.exs +++ b/demo/test/demo_web/live/send_toast_test.exs @@ -186,6 +186,25 @@ defmodule DemoWeb.SendToastTest do end end + defmodule ConnectionNotificationsDisabledLive do + @moduledoc false + + use Phoenix.LiveView + + def mount(_params, _session, socket), do: {:ok, socket} + + def render(assigns) do + ~H""" + + """ + end + end + attr :flash, :map, required: true attr :toasts_sync, :list, required: true @@ -207,6 +226,17 @@ defmodule DemoWeb.SendToastTest do """ end + defp disabled_connection_notification_host(assigns) do + ~H""" + + """ + end + describe "LiveToast.send_toast/3" do test "renders info toast", %{conn: conn} do {:ok, view, _html} = live_isolated(conn, TestLive) @@ -344,4 +374,26 @@ defmodule DemoWeb.SendToastTest do refute html =~ ~s(aria-label="close") end end + + describe "disabled connection notifications" do + test "omits connection notices in a live host", %{conn: conn} do + {:ok, _view, html} = live_isolated(conn, ConnectionNotificationsDisabledLive) + + refute html =~ ~s(id="client-error") + refute html =~ ~s(id="server-error") + refute html =~ "data-live-toast-connection" + end + + test "omits connection notices in a dead host" do + html = + render_component(&disabled_connection_notification_host/1, + flash: %{}, + toasts_sync: [] + ) + + refute html =~ ~s(id="client-error") + refute html =~ ~s(id="server-error") + refute html =~ "data-live-toast-connection" + end + end end diff --git a/lib/live_toast.ex b/lib/live_toast.ex index f1ccfed..054befc 100644 --- a/lib/live_toast.ex +++ b/lib/live_toast.ex @@ -307,9 +307,9 @@ defmodule LiveToast do attr :client_error_delay, :integer, default: 3000, doc: "adds a delay before the disconnected client error is shown" - attr(:connection_notifications, :map, + attr(:connection_notifications, :any, default: %{}, - doc: "copy and kind overrides for connection-state notifications" + doc: "false disables connection-state notifications; a map overrides their copy and kind" ) slot(:client_error, doc: "optional custom content for the client connection notice") diff --git a/lib/live_toast/components.ex b/lib/live_toast/components.ex index 4bdb193..7454618 100644 --- a/lib/live_toast/components.ex +++ b/lib/live_toast/components.ex @@ -133,9 +133,9 @@ defmodule LiveToast.Components do doc: "function to override the toast classes" ) - attr(:connection_notifications, :map, + attr(:connection_notifications, :any, default: %{}, - doc: "copy and kind overrides for connection-state notifications" + doc: "false disables connection-state notifications; a map overrides their copy and kind" ) attr(:client_error, :list, default: [], doc: "optional custom content for the client connection notice") @@ -162,63 +162,65 @@ defmodule LiveToast.Components do phx-update="ignore" flash={@f} /> - <.toast - data-component="flash" - corner={@corner} - toast_class_fn={@toast_class_fn} - id="client-error" - kind={@connection_notifications.client_error.kind} - title={Utility.translate(@connection_notifications.client_error.title)} - duration={0} - dismissible={false} - data-live-toast-connection="client_error" - delay={@client_error_delay} - phx-update="ignore" - phx-disconnected={Utility.show_error(".phx-client-error #client-error")} - phx-connected={Utility.hide_error("#client-error")} - data-disconnected={Utility.show(".phx-client-error #client-error")} - data-connected={Utility.hide("#client-error")} - hidden - > - <%= if @client_error == [] do %> - {Utility.translate(@connection_notifications.client_error.body)} - - <% else %> - {render_slot( - @client_error, - connection_notice_assigns(@connection_notifications.client_error, "client-error", @corner) - )} - <% end %> - + <%= if @connection_notifications do %> + <.toast + data-component="flash" + corner={@corner} + toast_class_fn={@toast_class_fn} + id="client-error" + kind={@connection_notifications.client_error.kind} + title={Utility.translate(@connection_notifications.client_error.title)} + duration={0} + dismissible={false} + data-live-toast-connection="client_error" + delay={@client_error_delay} + phx-update="ignore" + phx-disconnected={Utility.show_error(".phx-client-error #client-error")} + phx-connected={Utility.hide_error("#client-error")} + data-disconnected={Utility.show(".phx-client-error #client-error")} + data-connected={Utility.hide("#client-error")} + hidden + > + <%= if @client_error == [] do %> + {Utility.translate(@connection_notifications.client_error.body)} + + <% else %> + {render_slot( + @client_error, + connection_notice_assigns(@connection_notifications.client_error, "client-error", @corner) + )} + <% end %> + - <.toast - data-component="flash" - corner={@corner} - toast_class_fn={@toast_class_fn} - id="server-error" - kind={@connection_notifications.server_error.kind} - title={Utility.translate(@connection_notifications.server_error.title)} - duration={0} - dismissible={false} - data-live-toast-connection="server_error" - phx-update="ignore" - phx-disconnected={Utility.show_error(".phx-server-error #server-error")} - phx-connected={Utility.hide_error("#server-error")} - data-disconnected={Utility.show(".phx-server-error #server-error")} - data-connected={Utility.hide("#server-error")} - delay={@client_error_delay} - hidden - > - <%= if @server_error == [] do %> - {Utility.translate(@connection_notifications.server_error.body)} - - <% else %> - {render_slot( - @server_error, - connection_notice_assigns(@connection_notifications.server_error, "server-error", @corner) - )} - <% end %> - + <.toast + data-component="flash" + corner={@corner} + toast_class_fn={@toast_class_fn} + id="server-error" + kind={@connection_notifications.server_error.kind} + title={Utility.translate(@connection_notifications.server_error.title)} + duration={0} + dismissible={false} + data-live-toast-connection="server_error" + phx-update="ignore" + phx-disconnected={Utility.show_error(".phx-server-error #server-error")} + phx-connected={Utility.hide_error("#server-error")} + data-disconnected={Utility.show(".phx-server-error #server-error")} + data-connected={Utility.hide("#server-error")} + delay={@client_error_delay} + hidden + > + <%= if @server_error == [] do %> + {Utility.translate(@connection_notifications.server_error.body)} + + <% else %> + {render_slot( + @server_error, + connection_notice_assigns(@connection_notifications.server_error, "server-error", @corner) + )} + <% end %> + + <% end %> """ end @@ -249,9 +251,9 @@ defmodule LiveToast.Components do attr(:flash_duration, :integer, default: 0, doc: "if provided clears flash after provided milliseconds") - attr(:connection_notifications, :map, + attr(:connection_notifications, :any, default: %{}, - doc: "copy and kind overrides for connection-state notifications" + doc: "false disables connection-state notifications; a map overrides their copy and kind" ) attr(:client_error, :list, default: [], doc: "optional custom content for the client connection notice") @@ -277,6 +279,8 @@ defmodule LiveToast.Components do """ end + defp connection_notifications(false), do: false + defp connection_notifications(overrides) do defaults = %{ client_error: %{ From 60d074bebaabc759fcfb7a4d1766541b13a659f1 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 20 Jul 2026 22:31:50 -0400 Subject: [PATCH 2/2] Clarify connection notice opt-out --- README.md | 4 ++-- demo/lib/demo_web/live/tabs/recipes.html.heex | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e6a5133..1602426 100644 --- a/README.md +++ b/README.md @@ -343,8 +343,8 @@ LiveToast.dismiss_toast(uuid) ### Connection-state notifications -Set `connection_notifications={false}` when your application supplies its own connection-state UI. This removes both -built-in notices, including their connection-event wiring. +Set `connection_notifications={false}` if you prefer not to show LiveToast's built-in connection failure notices. This +removes both client and server notices, including their connection-event wiring. ```heex Disable Connection Notices

- When your application has its own connection-state UI, set - connection_notifications={false} - to omit LiveToast's built-in disconnected and reconnecting notices. + Set connection_notifications={false} + if you prefer not to show LiveToast's built-in disconnected and reconnecting notices.

<LiveToast.toast_group
   flash={@flash}