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
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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}` 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
<LiveToast.toast_group
flash={@flash}
connected={assigns[:socket] != nil}
toasts_sync={assigns[:toasts_sync]}
connection_notifications={false}
/>
```

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.

Expand Down
6 changes: 6 additions & 0 deletions demo/lib/demo_web/live/home_live.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,12 @@
>
Connection Notices
</.link>
<.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>
</div>
<.link
patch={~p"/customization"}
Expand Down
24 changes: 24 additions & 0 deletions demo/lib/demo_web/live/tabs/recipes.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,27 @@ LiveToast.dismiss_toast(uuid)</code></pre>
</.link>
</p>
</div>

<div class="mb-12 space-y-4">
<h3 id="disable-connection-notifications" class="scroll-mt-20 text-zinc-900 font-medium">
Disable Connection Notices
</h3>
<p>
Set <code>connection_notifications=&#123;false&#125;</code>
if you prefer not to show LiveToast's built-in disconnected and reconnecting notices.
</p>
<pre class="overflow-x-auto rounded-md bg-zinc-950 p-4 text-sm text-zinc-100"><code>&lt;LiveToast.toast_group
flash=&#123;@flash&#125;
connected=&#123;assigns[:socket] != nil&#125;
toasts_sync=&#123;assigns[:toasts_sync]&#125;
connection_notifications=&#123;false&#125;
/&gt;</code></pre>
<p>
<.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
</.link>
</p>
</div>
9 changes: 9 additions & 0 deletions demo/test/demo_web/live/home_live_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down Expand Up @@ -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
52 changes: 52 additions & 0 deletions demo/test/demo_web/live/send_toast_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
<LiveToast.toast_group
flash={@flash}
connected={assigns[:socket] != nil}
toasts_sync={assigns[:toasts_sync]}
connection_notifications={false}
/>
"""
end
end

attr :flash, :map, required: true
attr :toasts_sync, :list, required: true

Expand All @@ -207,6 +226,17 @@ defmodule DemoWeb.SendToastTest do
"""
end

defp disabled_connection_notification_host(assigns) do
~H"""
<LiveToast.toast_group
flash={@flash}
connected={false}
toasts_sync={@toasts_sync}
connection_notifications={false}
/>
"""
end

describe "LiveToast.send_toast/3" do
test "renders info toast", %{conn: conn} do
{:ok, view, _html} = live_isolated(conn, TestLive)
Expand Down Expand Up @@ -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
4 changes: 2 additions & 2 deletions lib/live_toast.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
124 changes: 64 additions & 60 deletions lib/live_toast/components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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)}
<Utility.svg name="hero-arrow-path" class="inline-block ml-1 h-3 w-3 animate-spin" />
<% else %>
{render_slot(
@client_error,
connection_notice_assigns(@connection_notifications.client_error, "client-error", @corner)
)}
<% end %>
</.toast>
<%= 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)}
<Utility.svg name="hero-arrow-path" class="inline-block ml-1 h-3 w-3 animate-spin" />
<% else %>
{render_slot(
@client_error,
connection_notice_assigns(@connection_notifications.client_error, "client-error", @corner)
)}
<% end %>
</.toast>

<.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)}
<Utility.svg name="hero-arrow-path" class="inline-block ml-1 h-3 w-3 animate-spin" />
<% else %>
{render_slot(
@server_error,
connection_notice_assigns(@connection_notifications.server_error, "server-error", @corner)
)}
<% end %>
</.toast>
<.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)}
<Utility.svg name="hero-arrow-path" class="inline-block ml-1 h-3 w-3 animate-spin" />
<% else %>
{render_slot(
@server_error,
connection_notice_assigns(@connection_notifications.server_error, "server-error", @corner)
)}
<% end %>
</.toast>
<% end %>
"""
end

Expand Down Expand Up @@ -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")
Expand All @@ -277,6 +279,8 @@ defmodule LiveToast.Components do
"""
end

defp connection_notifications(false), do: false

defp connection_notifications(overrides) do
defaults = %{
client_error: %{
Expand Down
Loading