Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds :append option to add values to the selection in :tags mode. #63

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions lib/live_select.ex
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,22 @@ defmodule LiveSelect do
send_update(LiveSelect.Component, id: live_select_id, value: new_selection)
```

`new_selection` must be a single element in `:single` mode, a list in `:tags` mode. If it's `nil`, the selection will be cleared.
After updating the selection, `LiveSelect` will trigger a change event in the form.
`new_selection` must be a single element in `:single` mode, a list in `:tags` mode. If it's `nil`, the selection will be cleared.
After updating the selection, `LiveSelect` will trigger a change event in the form.

To set a custom id for the component to use with `Phoenix.LiveView.send_update/3`, you can pass the `id` assign to `live_select/1`.

### Appending to the selection in `:tags` mode

When you want to append to the current selection, you can use the `:append` key in the `send_update` call:

```
send_update(LiveSelect.Component, id: live_select_id, append: values_to_append)
```

`values_to_append` can be a single element or a list to append to the current selection. If the value to append already exists in the selection, it will be ignored.

Note: This does not work in `:single` mode. Use `:value` instead to replace the selection.

## Examples

Expand Down
48 changes: 39 additions & 9 deletions lib/live_select/component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,24 @@ defmodule LiveSelect.Component do
end

socket =
if Map.has_key?(assigns, :value) do
update(socket, :selection, fn
selection, %{options: options, value: value, mode: mode, value_mapper: value_mapper} ->
update_selection(value, selection, options, mode, value_mapper)
end)
|> client_select(%{input_event: true})
else
socket
cond do
Map.has_key?(assigns, :value) ->
update(socket, :selection, fn
selection,
%{options: options, value: value, mode: mode, value_mapper: value_mapper} ->
update_selection(value, selection, options, mode, value_mapper)
end)
|> client_select(%{input_event: true})

Map.has_key?(assigns, :append) ->
update(socket, :selection, fn
selection, %{append: value, mode: mode, value_mapper: value_mapper} ->
append_selection(value, selection, mode, value_mapper)
end)
|> client_select(%{input_event: true})

true ->
socket
end

{:ok, socket}
Expand Down Expand Up @@ -365,6 +375,7 @@ defmodule LiveSelect.Component do
:clear_button,
:hide_dropdown,
:value_mapper,
:append,
# for backwards compatibility
:form
]
Expand All @@ -376,7 +387,7 @@ defmodule LiveSelect.Component do
|> Enum.sort_by(&String.jaro_distance(to_string(&1), to_string(assign)))
|> List.last()

raise ~s(Invalid assign: "#{assign}". Did you mean "#{most_similar}" ?)
raise ~s(Invalid assign: "#{assign}". Did you mean "#{most_similar}"?)
end
end
end
Expand Down Expand Up @@ -527,6 +538,25 @@ defmodule LiveSelect.Component do
|> Enum.reject(&is_nil/1)
end

defp append_selection(_value, _current_selection, :single, _value_mapper) do
raise """
Appending values only works in `:tags` mode
"""
end

defp append_selection(nil, current_selection, _mode, _value_mapper), do: current_selection

defp append_selection(value, current_selection, :tags, value_mapper) do
value = if Enumerable.impl_for(value), do: value, else: [value]

normalized_value =
Enum.map(value, &normalize_selection_value(&1, current_selection, value_mapper))

(current_selection ++ normalized_value)
|> Enum.reject(&is_nil/1)
|> Enum.uniq()
end

defp normalize_selection_value(%Ecto.Changeset{action: :replace}, _options, _value_mapper),
do: nil

Expand Down
52 changes: 52 additions & 0 deletions test/live_select_tags_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,58 @@ defmodule LiveSelectTagsTest do
assert_selected_multiple(live, [%{label: "C", value: 3}, %{label: "E", value: 5}])
end

test "can append values to the selection", %{conn: conn} do
{:ok, live, _html} = live(conn, "/?mode=tags")

stub_options(~w(A B C))

type(live, "ABC")

select_nth_option(live, 1)

assert_selected_multiple(live, ~w(A))

send_update(live, append: ["B"])

assert_selected_multiple(live, ~w(A B))

send_update(live, append: "C")

assert_selected_multiple(live, ~w(A B C))
end

test "does not duplicate selection when appending values", %{conn: conn} do
{:ok, live, _html} = live(conn, "/?mode=tags")

stub_options(~w(A B C))

type(live, "ABC")

select_nth_option(live, 1)

assert_selected_multiple(live, ~w(A))

send_update(live, append: ~w(A))

assert_selected_multiple(live, ~w(A))
end

test "does not change the selection when appending nil values", %{conn: conn} do
{:ok, live, _html} = live(conn, "/?mode=tags")

stub_options(~w(A B C))

type(live, "ABC")

select_nth_option(live, 1)

assert_selected_multiple(live, ~w(A))

send_update(live, append: nil)

assert_selected_multiple(live, ~w(A))
end

test "can render custom clear button", %{conn: conn} do
{:ok, live, _html} = live(conn, "/live_component_test")

Expand Down
Loading