Skip to content

Commit

Permalink
allow selection to be updated from the form
Browse files Browse the repository at this point in the history
  • Loading branch information
maxmarcon committed Dec 1, 2023
1 parent fea86c6 commit 533574d
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 6 deletions.
5 changes: 3 additions & 2 deletions lib/live_select.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ defmodule LiveSelect do
## Options
You can pass or update the list of options the user can choose from with the `options` assign.
You can set the initial list of options the user can choose from with the `options` assign.
Afterwards, you can update the options at any time using `Phoenix.LiveView.send_update/3`.
Each option will be assigned a label, which will be shown in the dropdown, and a value, which will be the value of the
LiveSelect input when the option is selected.
Expand Down Expand Up @@ -280,7 +281,7 @@ defmodule LiveSelect do

attr :options, :list,
doc:
~s(initial available options to select from. See the "Options" section for details on what you can pass here)
~s(initial available options to select from. Note that, after the initial rendering of the component, options can only be updated using `Phoenix.LiveView.send_update/3` - See the "Options" section for details)

attr :value, :any, doc: "used to manually set a selection - overrides any values from the form.
Must be a single element in `:single` mode, or a list of elements in `:tags` mode."
Expand Down
47 changes: 43 additions & 4 deletions lib/live_select/component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ defmodule LiveSelect.Component do
def update(assigns, socket) do
validate_assigns!(assigns)

initial_render = !socket.assigns[:field]

assigns =
if !initial_render && Map.has_key?(assigns, :field) do
# do not reset option when rerendering
# TODO: missing test
Map.delete(assigns, :options)
else
assigns
end

socket =
socket
|> assign(assigns)
Expand All @@ -131,10 +142,21 @@ defmodule LiveSelect.Component do
end)
|> update(:options, &normalize_options/1)
|> assign(:text_input_field, String.to_atom("#{socket.assigns.field.field}_text_input"))
|> assign_new(:selection, fn
%{field: field, options: options, mode: mode} ->
set_selection(field.value, options, mode)
end)

socket =
if assigns[:field] do
assign(
socket,
:selection,
set_selection(
decode(socket.assigns.field.value, socket.assigns.mode),
socket.assigns.options,
socket.assigns.mode
)
)
else
socket
end

socket =
if Map.has_key?(assigns, :value) do
Expand Down Expand Up @@ -586,6 +608,23 @@ defmodule LiveSelect.Component do

defp encode(value), do: Jason.encode!(value)

defp decode(nil, _), do: nil

defp decode(value, :tags) do
value
|> List.wrap()
|> Enum.map(&decode(&1, :single))
end

defp decode(value, :single) when is_binary(value) do
case Jason.decode(value) do
{:ok, decoded} -> decoded
{:error, _} -> value
end
end

defp decode(value, :single), do: value

defp already_selected?(option, selection) do
option.label in Enum.map(selection, & &1.label)
end
Expand Down
26 changes: 26 additions & 0 deletions test/live_select_tags_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -444,4 +444,30 @@ defmodule LiveSelectTagsTest do
})
end
end

test "selection can be updated from the form", %{conn: conn} do
stub_options([
%{value: 1, label: "A"},
%{value: 2, label: "B"},
%{value: 3, label: "C"}
])

{:ok, live, _html} = live(conn, "/?mode=tags")

type(live, "ABC")

select_nth_option(live, 1)

type(live, "ABC")

select_nth_option(live, 2, method: :click)

assert_selected_multiple(live, [%{value: 1, label: "A"}, %{value: 2, label: "B"}])

send_update(live,
field: Phoenix.Component.to_form(%{"city_search" => [2, 3]}, as: :my_form)[:city_search]
)

assert_selected_multiple_static(live, [%{value: 2, label: "B"}, %{value: 3, label: "C"}])
end
end
22 changes: 22 additions & 0 deletions test/live_select_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,28 @@ defmodule LiveSelectTest do
assert tag =~ "with custom slot"
end

test "selection can be updated from the form", %{conn: conn} do
stub_options(
A: 1,
B: 2,
C: 3
)

{:ok, live, _html} = live(conn, "/")

type(live, "ABC")

select_nth_option(live, 2)

assert_selected(live, :B, 2)

send_update(live,
field: Phoenix.Component.to_form(%{"city_search" => 1}, as: :my_form)[:city_search]
)

assert_selected_static(live, :A, 1)
end

for style <- [:daisyui, :tailwind, :none, nil] do
@style style

Expand Down

0 comments on commit 533574d

Please sign in to comment.