+ use PhoenixHTMLHelpers
@doc """
Generates tag for inlined form input errors.
diff --git a/lib/arrow_web/live/shuttle_live/shuttle_live.ex b/lib/arrow_web/live/shuttle_live/shuttle_live.ex
index a971852e6..a237d0a5d 100644
--- a/lib/arrow_web/live/shuttle_live/shuttle_live.ex
+++ b/lib/arrow_web/live/shuttle_live/shuttle_live.ex
@@ -75,12 +75,13 @@ defmodule ArrowWeb.ShuttleViewLive do
<.input field={f_route[:direction_desc]} type="text" label="Direction desc" />
- <.input
+ <.live_select
field={f_route[:shape_id]}
- type="select"
label="Shape"
- prompt="Choose a shape"
- options={Enum.map(@shapes, &{&1.name, &1.id})}
+ placeholder="Choose a shape"
+ options={options_mapper(@shapes)}
+ value_mapper={&value_mapper/1}
+ allow_clear={true}
/>
@@ -163,6 +164,22 @@ defmodule ArrowWeb.ShuttleViewLive do
|> Enum.map(&List.first(&1.shapes))
end
+ defp options_mapper(shapes) do
+ Enum.map(shapes, &option_mapper/1)
+ end
+
+ def option_mapper(%{name: name, id: id}) do
+ {name, value_mapper(id)}
+ end
+
+ def value_mapper(id) when is_integer(id) do
+ Integer.to_string(id)
+ end
+
+ def value_mapper(id) do
+ id
+ end
+
def mount(%{"id" => id} = _params, session, socket) do
logout_url = session["logout_url"]
shuttle = Shuttles.get_shuttle!(id)
@@ -275,6 +292,17 @@ defmodule ArrowWeb.ShuttleViewLive do
end
end
+ def handle_event("live_select_change", %{"text" => text, "id" => live_select_id}, socket) do
+ shapes =
+ Shuttles.list_shapes()
+ |> Enum.filter(&(String.downcase(&1.name) |> String.contains?(String.downcase(text))))
+ |> Enum.map(&option_mapper/1)
+
+ send_update(LiveSelect.Component, id: live_select_id, options: shapes)
+
+ {:noreply, socket}
+ end
+
def handle_event("add_stop", %{"value" => direction_id}, socket) do
direction_id = String.to_existing_atom(direction_id)
diff --git a/mix.exs b/mix.exs
index 63e66fa86..874b96c91 100644
--- a/mix.exs
+++ b/mix.exs
@@ -62,13 +62,15 @@ defmodule Arrow.MixProject do
{:httpoison, "~> 2.2"},
{:ja_serializer, "~> 0.18.0"},
{:jason, "~> 1.0"},
+ {:live_select, "~> 1.4"},
{:lcov_ex, "~> 0.2", only: [:dev, :test], runtime: false},
{:mox, "~> 1.2", only: :test},
{:oban, "~> 2.18"},
{:phoenix_ecto, "~> 4.0"},
{:phoenix_live_reload, "~> 1.5", only: :dev},
- {:phoenix_html, "~> 3.0"},
- {:phoenix_live_react, "~> 0.4"},
+ {:phoenix_html, "~> 4.0"},
+ {:phoenix_html_helpers, "~> 1.0"},
+ {:phoenix_live_react, "~> 0.6"},
{:phoenix_live_view, "~> 0.20.14"},
{:phoenix_live_dashboard, "~> 0.7"},
{:phoenix_pubsub, "~> 2.0"},
@@ -79,7 +81,7 @@ defmodule Arrow.MixProject do
{:telemetry_metrics, "~> 1.0"},
{:postgrex, ">= 0.0.0"},
# If react_phoenix changes, check assets/src/ReactPhoenix.js, too
- {:react_phoenix, "1.3.1"},
+ {:react_phoenix, git: "https://github.com/mbta/react-phoenix.git"},
{:tzdata, "~> 1.1"},
{:ueberauth_cognito, "0.4.0"},
{:ueberauth_oidcc, "~> 0.4.0"},
diff --git a/mix.lock b/mix.lock
index dd9fddcf3..91211fc10 100644
--- a/mix.lock
+++ b/mix.lock
@@ -34,6 +34,7 @@
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"},
"lcov_ex": {:hex, :lcov_ex, "0.3.3", "1745a88e46606c4f86408299f54878b7d0cd22ea3e9c54b0018b6ed631a9ce87", [:mix], [], "hexpm", "ea373ec4d2df213357c5a464be16ab08d1e58e61ea2de784a483780c22a1e74a"},
+ "live_select": {:hex, :live_select, "1.4.3", "ec9706952f589d8e2e6f98a0e1633c5b51ab5b807d503bd0d9622a26c999fb9a", [:mix], [{:ecto, "~> 3.8", [hex: :ecto, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.6.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_html_helpers, "~> 1.0", [hex: :phoenix_html_helpers, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "58f7d702b0f786c73d31e60a342c0a49afaf56ca5a6a078b51babf3490465220"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
"mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"},
@@ -45,9 +46,10 @@
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
"phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [: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.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.6.2", "3b83b24ab5a2eb071a20372f740d7118767c272db386831b2e77638c4dcc606d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "3f94d025f59de86be00f5f8c5dd7b5965a3298458d21ab1c328488be3b5fcd59"},
- "phoenix_html": {:hex, :phoenix_html, "3.3.4", "42a09fc443bbc1da37e372a5c8e6755d046f22b9b11343bf885067357da21cb3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0249d3abec3714aff3415e7ee3d9786cb325be3151e6c4b3021502c585bf53fb"},
+ "phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"},
+ "phoenix_html_helpers": {:hex, :phoenix_html_helpers, "1.0.1", "7eed85c52eff80a179391036931791ee5d2f713d76a81d0d2c6ebafe1e11e5ec", [:mix], [{:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cffd2385d1fa4f78b04432df69ab8da63dc5cf63e07b713a4dcf36a3740e3090"},
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.4", "4508e481f791ce62ec6a096e13b061387158cbeefacca68c6c1928e1305e23ed", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "2984aae96994fbc5c61795a73b8fb58153b41ff934019cfb522343d2d3817d59"},
- "phoenix_live_react": {:hex, :phoenix_live_react, "0.5.0", "8ef46146fcf9957b377e3f3187e85574e6f407b19d822a131501c32e6c16f4b1", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.11 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "cb64d778ca26e5cfc89153a0799a43b790cbbb5d6fe025e4e11d97ca5ef8e72d"},
+ "phoenix_live_react": {:hex, :phoenix_live_react, "0.6.0", "d6c08632e36be0149d8236953437c6fa1df98c38f0d87a35047ab64b74996b48", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_html_helpers, "~> 1.0", [hex: :phoenix_html_helpers, repo: "hexpm", optional: false]}], "hexpm", "59c0907d98a4840f33577ddb0f0642108ac22d1a896249494af7c20f437e322d"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.5.3", "f2161c207fda0e4fb55165f650f7f8db23f02b29e3bff00ff7ef161d6ac1f09d", [:mix], [{:file_system, "~> 0.3 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b4ec9cd73cb01ff1bd1cac92e045d13e7030330b74164297d1aee3907b54803c"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.20.17", "f396bbdaf4ba227b82251eb75ac0afa6b3da5e509bc0d030206374237dfc9450", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {: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 or ~> 4.0", [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]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a61d741ffb78c85fdbca0de084da6a48f8ceb5261a79165b5a0b59e5f65ce98b"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
@@ -58,7 +60,7 @@
"poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"},
"postgrex": {:hex, :postgrex, "0.19.2", "34d6884a332c7bf1e367fc8b9a849d23b43f7da5c6e263def92784d03f9da468", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "618988886ab7ae8561ebed9a3c7469034bf6a88b8995785a3378746a4b9835ec"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
- "react_phoenix": {:hex, :react_phoenix, "1.3.1", "b2abb625ce7304a3b2ac5eea3126c30ef1e1c860f9ef27290ee726ab1fa3a87a", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.11 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "a10de67f02f6c5cf04f6987cef7413400d671936b4df97e0b9ac3c227ba27e6b"},
+ "react_phoenix": {:git, "https://github.com/mbta/react-phoenix.git", "d95a3465c735783cba41b34941b91d4d4a858b37", []},
"sax_map": {:hex, :sax_map, "1.3.0", "d79ce48edf2c25720e79a2f62e3ee091ebd8b918d0e3a16f58010c7014a07d89", [:mix], [{:saxy, "~> 1.3", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "5f9086c757aae9f951a7ef0139a9ae56586a126c0da150b71d5ec6cd25309d85"},
"saxy": {:hex, :saxy, "1.6.0", "02cb4e9bd045f25ac0c70fae8164754878327ee393c338a090288210b02317ee", [:mix], [], "hexpm", "ef42eb4ac983ca77d650fbdb68368b26570f6cc5895f0faa04d34a6f384abad3"},
"sentry": {:hex, :sentry, "10.7.1", "33392222d80ccff99c503f972998d2858b4c1e5aca2219a34269b68dacba8e7d", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_ownership, "~> 0.3.0 or ~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "56291312397bf2b6afab6cf4f7aa1f27413b0eb2ceeb63b8aab2d7658aaea882"},
diff --git a/test/arrow_web/live/shuttle_live/shuttle_live_test.exs b/test/arrow_web/live/shuttle_live/shuttle_live_test.exs
index df78617b1..451d65138 100644
--- a/test/arrow_web/live/shuttle_live/shuttle_live_test.exs
+++ b/test/arrow_web/live/shuttle_live/shuttle_live_test.exs
@@ -39,7 +39,6 @@ defmodule ArrowWeb.ShuttleLiveTest do
destination: "Broadway",
direction_desc: "Southbound",
direction_id: "0",
- shape_id: "",
suffix: "",
waypoint: ""
},
@@ -48,7 +47,6 @@ defmodule ArrowWeb.ShuttleLiveTest do
destination: "Harvard",
direction_desc: "Northbound",
direction_id: "1",
- shape_id: "",
suffix: "",
waypoint: ""
}
@@ -57,6 +55,19 @@ defmodule ArrowWeb.ShuttleLiveTest do
status: "draft"
}
+ @update_hidden_attrs %{
+ routes: %{
+ "0" => %{
+ shape_id: "",
+ shape_id_text_input: ""
+ },
+ "1" => %{
+ shape_id: "",
+ shape_id_text_input: ""
+ }
+ }
+ }
+
@invalid_attrs %{
disrupted_route_id: "",
shuttle_name: nil,
@@ -106,7 +117,7 @@ defmodule ArrowWeb.ShuttleLiveTest do
{:ok, conn} =
edit_live
|> form("#shuttle-form", shuttle: @update_attrs)
- |> render_submit()
+ |> render_submit(@update_hidden_attrs)
|> follow_redirect(conn)
assert html_response(conn, 200) =~ ~r/shuttle updated successfully/i
@@ -229,6 +240,56 @@ defmodule ArrowWeb.ShuttleLiveTest do
end
end
+ describe "shape select" do
+ setup [:create_shuttle]
+
+ @tag :authenticated_admin
+ test "sets the selected shape via hidden input values", %{conn: conn, shuttle: shuttle} do
+ {:ok, edit_live, _html} = live(conn, ~p"/shuttles/#{shuttle}/edit")
+
+ first_route = List.first(shuttle.routes)
+ second_route = Enum.at(shuttle.routes, 1)
+
+ update_first_shape_attrs = %{
+ routes: %{
+ "0" => %{
+ shape_id: second_route.shape_id,
+ shape_id_text_input: second_route.shape.name
+ }
+ }
+ }
+
+ # `#` and escaping the brackets doesn't seem to work with this selector format (unlike $() in the browser)
+ live_component_selector = ~s{[id="shuttle[routes][0]_shape_id_live_select_component"]}
+ hidden_input_selector = ~s{#shuttle-form_routes_0_shape_id}
+ displayed_value_selector = ~s{#shuttle-form_routes_0_shape_id_text_input}
+
+ # initially set to original shape
+ assert edit_live
+ |> element(live_component_selector)
+ |> render() =~ ~s{value="#{first_route.shape_id}"}
+
+ assert edit_live
+ |> element(displayed_value_selector)
+ |> render() =~ ~s{value="#{first_route.shape.name}"}
+
+ assert edit_live
+ |> element(hidden_input_selector)
+ |> render() =~ ~s{value="#{first_route.shape_id}"}
+
+ rendered =
+ edit_live
+ |> element("#shuttle-form")
+ |> render_change(%{shuttle: update_first_shape_attrs})
+
+ refute rendered =~ "#{first_route.shape.name}"
+ assert rendered =~ "#{second_route.shape.name}"
+
+ assert rendered =~
+ "name=\"shuttle[routes][0][shape_id]\" type=\"hidden\" value=\"#{second_route.shape_id}\"/>"
+ end
+ end
+
defp create_shuttle(_) do
shuttle = shuttle_fixture()
%{shuttle: shuttle}