From 9b8cf7430c24cb7cc3e29c9547741c20ff590f1a Mon Sep 17 00:00:00 2001 From: Arthur Clemens Date: Sun, 9 Jul 2023 20:56:52 +0200 Subject: [PATCH] Align action_list_item label with checkbox --- lib/component.ex | 6 +- lib/helpers/attribute_helpers.ex | 1 + lib/test_helpers/todos/todo.ex | 9 +++ lib/test_helpers/todos/todos.ex | 55 ++++++++++++++ mix.exs | 6 +- mix.lock | 1 + test/components/action_list_test.exs | 105 ++++++++++++++++++++++++++ test/components/radio_button_test.exs | 1 + 8 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 lib/test_helpers/todos/todo.ex create mode 100644 lib/test_helpers/todos/todos.ex diff --git a/lib/component.ex b/lib/component.ex index 1199eb23..6e9b6605 100644 --- a/lib/component.ex +++ b/lib/component.ex @@ -836,6 +836,10 @@ defmodule PrimerLive.Component do end def action_list_item(assigns) do + %{ + input_id: input_id + } = AttributeHelpers.common_input_attrs(assigns, :checkbox) + is_selected = assigns.is_selected is_form_input = assigns[:form] || assigns[:field] # Get the first link slot, if any @@ -1043,7 +1047,7 @@ defmodule PrimerLive.Component do attributes = AttributeHelpers.append_attributes([ - [class: classes.content], + [class: classes.content, for: input_id], !is_nil(is_expanded) && ["aria-expanded": is_expanded |> Atom.to_string()] ]) diff --git a/lib/helpers/attribute_helpers.ex b/lib/helpers/attribute_helpers.ex index a35d6cec..480f3bd1 100644 --- a/lib/helpers/attribute_helpers.ex +++ b/lib/helpers/attribute_helpers.ex @@ -500,6 +500,7 @@ defmodule PrimerLive.Helpers.AttributeHelpers do def input_id(_input_id, _id, _input_type, "", _value_for_derived_label), do: nil def input_id(_input_id, _id, _input_type, input_name, _value_for_derived_label), do: input_name + @spec cleanup_id(nil | binary) :: nil | binary def cleanup_id(id) when is_nil(id), do: nil def cleanup_id(id) do diff --git a/lib/test_helpers/todos/todo.ex b/lib/test_helpers/todos/todo.ex new file mode 100644 index 00000000..4dd8f989 --- /dev/null +++ b/lib/test_helpers/todos/todo.ex @@ -0,0 +1,9 @@ +defmodule PrimerLive.TestHelpers.Repo.Todo do + use Ecto.Schema + + schema "todos" do + field(:statuses, {:array, :string}, default: []) + + timestamps() + end +end diff --git a/lib/test_helpers/todos/todos.ex b/lib/test_helpers/todos/todos.ex new file mode 100644 index 00000000..df77595c --- /dev/null +++ b/lib/test_helpers/todos/todos.ex @@ -0,0 +1,55 @@ +defmodule PrimerLive.TestHelpers.Repo.Todos do + import Ecto.Changeset, except: [change: 1] + alias PrimerLive.TestHelpers.Repo.Todo + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking to do item changes. + + ## Examples + + iex> change(to do item) + %Ecto.Changeset{data: %Todo{}} + + """ + def change(%Todo{} = todo, attrs \\ %{}) do + todo + |> changeset(attrs) + end + + def init() do + change(%Todo{}) + end + + @status_options [ + {"In progress", "in-progress"}, + {"Needs review", "needs-review"}, + {"Complete", "complete"} + ] + + def status_options, do: @status_options + + @allowed_fields [ + :statuses + ] + + @required_fields [ + :statuses + ] + + defp changeset(todo, attrs) do + todo + |> cast(attrs, @allowed_fields) + |> validate_required(@required_fields) + |> validate_empty(:statuses, "must select a status") + end + + defp validate_empty(changeset, field, message) do + values = get_field(changeset, field) + + if Enum.empty?(values) do + add_error(changeset, field, message) + else + changeset + end + end +end diff --git a/mix.exs b/mix.exs index 0f39f44d..d39cc9d0 100644 --- a/mix.exs +++ b/mix.exs @@ -30,9 +30,11 @@ defmodule PrimerLive.MixProject do defp deps do [ {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, - {:ecto_sql, "~> 3.9"}, + {:ecto, "~> 3.10", only: [:dev, :test], runtime: false}, + {:ecto_sql, "~> 3.10", only: [:dev, :test], runtime: false}, {:esbuild, "~> 0.7", only: :dev}, - {:ex_doc, "~> 0.29", only: :dev}, + {:ex_doc, "~> 0.29", only: [:dev, :test], runtime: false}, + {:phoenix_ecto, "~> 4.4", only: [:dev, :test], runtime: false}, {:phoenix_html, "~> 3.3"}, {:phoenix_live_view, ">= 0.18.3"}, {:jason, "~> 1.4"} diff --git a/mix.lock b/mix.lock index 64049537..38d95079 100644 --- a/mix.lock +++ b/mix.lock @@ -18,6 +18,7 @@ "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, "phoenix": {:hex, :phoenix, "1.7.3", "4d8eca2c020c9ed81a28e7a8c60e0a4f6f9f7f6e12eb91dfd01301eac07424c1", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.4", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "6b1bc308758f95ecf3e0d795389440a2ca88a903e0fda1f921c780918c16d640"}, + "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.2", "b21bd01fdeffcfe2fab49e4942aa938b6d3e89e93a480d4aee58085560a0bc0d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "70242edd4601d50b69273b057ecf7b684644c19ee750989fd555625ae4ce8f5d"}, "phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"}, "phoenix_live_view": {:hex, :phoenix_live_view, "0.19.0", "de5643d03e3cdf5ff19cd45b5d14543a3b1ad8551d529f6b24246e88a6c6f1b8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a650b6f814c4f386314b98f1aebf92f8652649166612f84ef2e60a20894addfa"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.2", "a4950b63ace57720b0fc1c6da083c53346b36f99021de89595cc4f026288ff51", [:mix], [], "hexpm", "45741676a94c71f9afdfed9d22d49b6856c026ff504db04e3dc03a1d86f8201c"}, diff --git a/test/components/action_list_test.exs b/test/components/action_list_test.exs index eb36e788..9fe1a910 100644 --- a/test/components/action_list_test.exs +++ b/test/components/action_list_test.exs @@ -1,11 +1,14 @@ defmodule PrimerLive.TestComponents.ActionListTest do use ExUnit.Case use PrimerLive + import PrimerLive.Helpers.TestHelpers import Phoenix.Component import Phoenix.LiveViewTest + alias PrimerLive.TestHelpers.Repo.Todos + test "Content slot" do assigns = %{} @@ -113,4 +116,106 @@ defmodule PrimerLive.TestComponents.ActionListTest do """ |> format_html() end + + test "With form" do + changeset = Todos.init() + options = Todos.status_options() + values = changeset.changes |> Map.get(:statuses) || [] + + assigns = %{ + changeset: changeset, + options: options, + values: values + } + + assert rendered_to_string(~H""" + <.form :let={f} for={@changeset} phx-change="validate" phx-submit="save"> + <.action_list is_multiple_select> + <%= for {label, value} <- @options do %> + <.action_list_item + form={f} + field={:statuses} + checked_value={value} + is_selected={value in @values} + > + <%= label %> + + <% end %> + + + """) + |> format_html() == + """ +
+ +
+ """ + |> format_html() + end + + test "With form, is_multiple_select" do + changeset = Todos.init() + options = Todos.status_options() + values = changeset.changes |> Map.get(:statuses) || [] + + assigns = %{ + changeset: changeset, + options: options, + values: values + } + + assert rendered_to_string(~H""" + <.form :let={f} for={@changeset} phx-change="validate" phx-submit="save"> + <.action_list is_multiple_select> + <%= for {label, value} <- @options do %> + <.action_list_item + form={f} + field={:statuses} + checked_value={value} + is_multiple_select + is_selected={value in @values} + > + <%= label %> + + <% end %> + + + """) + |> format_html() == + """ +
+ +
+ """ + |> format_html() + end end diff --git a/test/components/radio_button_test.exs b/test/components/radio_button_test.exs index 46c71d37..cd5f51f1 100644 --- a/test/components/radio_button_test.exs +++ b/test/components/radio_button_test.exs @@ -19,6 +19,7 @@ defmodule PrimerLive.TestComponents.RadioButtonTest do valid?: true } } + test "Called without options or inner_block: should render the component" do assigns = %{}