From 60dc18e6522d5a954e21ec5847e70c3e8a72d670 Mon Sep 17 00:00:00 2001 From: GlennGeelen Date: Thu, 2 May 2019 12:12:50 +0200 Subject: [PATCH] Add live view to cells --- lib/ex_cell/base.ex | 32 ++++++++-------- lib/ex_cell/live_view.ex | 63 +++++++++++++++++++++++++++++++ mix.exs | 1 + mix.lock | 1 + test/ex_cell/live_view_test.exs | 48 +++++++++++++++++++++++ test/support/conn_case.ex | 28 ++++++++++++++ test/support/mock_view_adapter.ex | 1 + 7 files changed, 159 insertions(+), 15 deletions(-) create mode 100644 lib/ex_cell/live_view.ex create mode 100644 test/ex_cell/live_view_test.exs create mode 100644 test/support/conn_case.ex diff --git a/lib/ex_cell/base.ex b/lib/ex_cell/base.ex index 04d90b2..c297c2c 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]) @@ -69,7 +70,7 @@ defmodule ExCell.Base do iex(1)> safe_to_string(AvatarCell.container) "
" """ - def container, do: container(%{}, [], [do: nil]) + def container, do: container(%{}, [], do: nil) @doc """ Returns the container of a cell as a Phoenix.Tag with it's content. @@ -78,7 +79,7 @@ defmodule ExCell.Base do iex(1)> safe_to_string(AvatarCell.container(do: "Hello")) "
Hello
" """ - def container(do: content), do: container(%{}, [], [do: content]) + def container(do: content), do: container(%{}, [], do: content) def container(callback) when is_function(callback), do: container(%{}, [], callback) @doc """ @@ -99,7 +100,7 @@ defmodule ExCell.Base do iex(1)> safe_to_string(AvatarCell.container(tag: :a, data: [foo: "bar"], class: "Moo", href: "/")) "" """ - def container(options) when is_list(options), do: container(%{}, options, [do: nil]) + def container(options) when is_list(options), do: container(%{}, options, do: nil) def container(options, content) when is_list(options), do: container(%{}, options, content) @doc """ @@ -112,18 +113,19 @@ defmodule ExCell.Base do iex(1)> safe_to_string(AvatarCell.container(%{ foo: "bar" })) "" """ - def container(%{} = params), do: - container(params, [], [do: nil]) - def container(%{} = params, [do: content]), do: - container(params, [], [do: content]) - def container(%{} = params, callback) when is_function(callback), do: - container(params, [], callback) - def container(%{} = params, options) when is_list(options), do: - container(params, options, [do: nil]) - def container(%{} = params, options, content), do: - ExCell.container(__MODULE__, UUID.uuid4(), params, options, content) - - defoverridable [class_name: 0, cell_name: 0, params: 0] + def container(%{} = params), do: container(params, [], do: nil) + def container(%{} = params, do: content), do: container(params, [], do: content) + + def container(%{} = params, callback) when is_function(callback), + do: container(params, [], callback) + + def container(%{} = params, options) when is_list(options), + do: container(params, options, do: nil) + + def container(%{} = params, options, content), + do: ExCell.container(__MODULE__, UUID.uuid4(), params, options, content) + + defoverridable class_name: 0, cell_name: 0, params: 0 end end end 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..e7201b2 --- /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