diff --git a/lib/ex_cell/base.ex b/lib/ex_cell/base.ex
index 04d90b2..b7f68e2 100644
--- a/lib/ex_cell/base.ex
+++ b/lib/ex_cell/base.ex
@@ -4,6 +4,7 @@ defmodule ExCell.Base do
defmacro __using__(opts \\ []) do
quote do
import ExCell.View
+ import ExCell.LiveView
@adapter unquote(opts[:adapter])
@namespace unquote(opts[:namespace])
diff --git a/lib/ex_cell/live_view.ex b/lib/ex_cell/live_view.ex
new file mode 100644
index 0000000..2cc3e24
--- /dev/null
+++ b/lib/ex_cell/live_view.ex
@@ -0,0 +1,63 @@
+defmodule ExCell.LiveView do
+ @moduledoc """
+ Cell helpers used to render the live view cells in both Views and Cells
+ """
+ @view_adapter ExCell.config(:view_adapter, Phoenix.LiveView)
+
+ @doc """
+ Renders a cell in the view.
+
+ ### Examples
+ iex(0)> safe_to_string(AppWeb.AvatarView.live_cell(AvatarLiveCell, socket))
+ "
"
+ """
+ def live_cell(cell, conn_or_socket) do
+ render_cell(cell, conn_or_socket, [])
+ end
+
+ @doc """
+ Renders a cell in the view with children.
+
+ ### Examples
+ iex(0)> safe_to_string(AppWeb.AvatarView.live_cell(AvatarLiveCell, do: "Foo"))
+ "
Foo
"
+ """
+ def live_cell(cell, conn_or_socket, do: children) do
+ render_cell(cell, conn_or_socket, children: children)
+ end
+
+ @doc """
+ Renders a cell in the view with assigns.
+
+ ### Examples
+ iex(0)> safe_to_string(AppWeb.AvatarView.live_cell(AvatarLiveCell, user: %User{name: "Bar"}))
+ "
Bar
"
+ """
+ def live_cell(cell, conn_or_socket, assigns) when is_list(assigns) do
+ render_cell(cell, conn_or_socket, assigns)
+ end
+
+ @doc """
+ Renders a cell in the view with children without a block.
+
+ ### Examples
+ iex(0)> safe_to_string(AppWeb.AvatarView.live_cell(AvatarLiveCell, "Hello"))
+ "
Hello
"
+ """
+ def live_cell(cell, conn_or_socket, children) do
+ render_cell(cell, conn_or_socket, children: children)
+ end
+
+ def live_cell(cell, conn_or_socket, assigns, do: children) when is_list(assigns) do
+ render_cell(cell, conn_or_socket, [children: children] ++ assigns)
+ end
+
+ def live_cell(cell, conn_or_socket, children, assigns) when is_list(assigns) do
+ render_cell(cell, conn_or_socket, [children: children] ++ assigns)
+ end
+
+ defp render_cell(cell, conn_or_socket, assigns) do
+ assigns = Map.new(assigns)
+ @view_adapter.live_render(conn_or_socket, cell, session: %{assigns: assigns})
+ end
+end
diff --git a/mix.exs b/mix.exs
index 9306ae1..c73968c 100644
--- a/mix.exs
+++ b/mix.exs
@@ -52,6 +52,7 @@ defmodule ExCell.Mixfile do
{:excoveralls, "~> 0.7", only: :test},
{:mix_test_watch, "~> 0.3", only: :dev, runtime: false},
{:phoenix_html, "~> 2.10"},
+ {:phoenix_live_view, github: "phoenixframework/phoenix_live_view"},
{:phoenix, "~> 1.4.0", optional: true},
{:jason, "~> 1.1"},
{:elixir_uuid, "~> 1.2"}
diff --git a/mix.lock b/mix.lock
index 44c5a69..348a3e4 100644
--- a/mix.lock
+++ b/mix.lock
@@ -21,6 +21,7 @@
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
"phoenix": {:hex, :phoenix, "1.4.3", "8eed4a64ff1e12372cd634724bddd69185938f52c18e1396ebac76375d85677d", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"phoenix_html": {:hex, :phoenix_html, "2.13.2", "f5d27c9b10ce881a60177d2b5227314fc60881e6b66b41dfe3349db6ed06cf57", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
+ "phoenix_live_view": {:git, "https://github.com/phoenixframework/phoenix_live_view.git", "da34e4886885f1f0f0dd7692b72663e4e94d9c98", []},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"},
"plug": {:hex, :plug, "1.8.0", "9d2685cb007fe5e28ed9ac27af2815bc262b7817a00929ac10f56f169f43b977", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
diff --git a/test/ex_cell/live_view_test.exs b/test/ex_cell/live_view_test.exs
new file mode 100644
index 0000000..055e339
--- /dev/null
+++ b/test/ex_cell/live_view_test.exs
@@ -0,0 +1,48 @@
+defmodule ExCell.LiveViewTest do
+ use ExCell.ConnCase
+ alias ExCell.LiveView
+
+ test "live_cell/2 with ExCell", %{conn: conn} do
+ assert LiveView.live_cell(:mock_cell, conn) === [conn, :mock_cell, [session: %{assigns: %{}}]]
+ end
+
+ test "live_cell/3 with assigns", %{conn: conn} do
+ assert LiveView.live_cell(:mock_cell, conn, foo: "bar") === [
+ conn,
+ :mock_cell,
+ [session: %{assigns: %{foo: "bar"}}]
+ ]
+ end
+
+ test "live_cell/3 with do block", %{conn: conn} do
+ assert LiveView.live_cell(:mock_cell, conn, do: "yes") === [
+ conn,
+ :mock_cell,
+ [session: %{assigns: %{children: "yes"}}]
+ ]
+ end
+
+ test "live_cell/3 with children", %{conn: conn} do
+ assert LiveView.live_cell(:mock_cell, conn, "yes") === [
+ conn,
+ :mock_cell,
+ [session: %{assigns: %{children: "yes"}}]
+ ]
+ end
+
+ test "live_cell/3 with assign and do block", %{conn: conn} do
+ assert LiveView.live_cell(:mock_cell, conn, [foo: "bar"], do: "yes") === [
+ conn,
+ :mock_cell,
+ [session: %{assigns: %{children: "yes", foo: "bar"}}]
+ ]
+ end
+
+ test "live_cell/3 with children and assign", %{conn: conn} do
+ assert LiveView.live_cell(:mock_cell, conn, "yes", foo: "bar") === [
+ conn,
+ :mock_cell,
+ [session: %{assigns: %{children: "yes", foo: "bar"}}]
+ ]
+ end
+end
diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex
new file mode 100644
index 0000000..062bb2e
--- /dev/null
+++ b/test/support/conn_case.ex
@@ -0,0 +1,28 @@
+defmodule ExCell.ConnCase do
+ @moduledoc """
+ This module defines the test case to be used by
+ tests that require setting up a connection.
+
+ Such tests rely on `Phoenix.ConnTest` and also
+ import other functionality to make it easier
+ to build common data structures and query the data layer.
+
+ Finally, if the test case interacts with the database,
+ it cannot be async. For this reason, every test runs
+ inside a transaction which is reset at the beginning
+ of the test unless the test case is marked as async.
+ """
+
+ use ExUnit.CaseTemplate
+
+ using do
+ quote do
+ # Import conveniences for testing with connections
+ use Phoenix.ConnTest
+ end
+ end
+
+ setup _tags do
+ {:ok, conn: Phoenix.ConnTest.build_conn()}
+ end
+end
diff --git a/test/support/mock_view_adapter.ex b/test/support/mock_view_adapter.ex
index 4a718a7..6690f3f 100644
--- a/test/support/mock_view_adapter.ex
+++ b/test/support/mock_view_adapter.ex
@@ -2,4 +2,5 @@ defmodule ExCell.MockViewAdapter do
@moduledoc false
def render(cell, template, args), do: [cell, template, args]
def render_to_string(cell, template, args), do: [cell, template, args]
+ def live_render(conn, cell, args), do: [conn, cell, args]
end