diff --git a/lib/live_select.ex b/lib/live_select.ex index 4385fcc..75d19ae 100644 --- a/lib/live_select.ex +++ b/lib/live_select.ex @@ -418,6 +418,11 @@ defmodule LiveSelect do doc: ~s(if `true`, the current list of selectable options and the content of the input text field are preserved upon selection) + attr :keep_label_on_select, :boolean, + default: Component.default_opts()[:keep_label_on_select], + doc: + ~s(if `true`, when in single mode, the input text field is preserved to the active label upon selection.) + 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." diff --git a/lib/live_select/component.ex b/lib/live_select/component.ex index c08e452..9cadd07 100644 --- a/lib/live_select/component.ex +++ b/lib/live_select/component.ex @@ -20,6 +20,7 @@ defmodule LiveSelect.Component do clear_tag_button_class: nil, clear_tag_button_extra_class: nil, keep_options_on_select: false, + keep_label_on_select: false, current_text: "", user_defined_options: false, container_class: nil, @@ -218,12 +219,25 @@ defmodule LiveSelect.Component do @impl true def handle_event(event, _params, socket) when event in ~w(focus click) do + keep_label_on_select = + socket.assigns.keep_label_on_select && + socket.assigns.mode == :single && + socket.assigns.selection != [] + + display_text = + if keep_label_on_select do + List.first(socket.assigns.selection).label + else + socket.assigns.current_text + end + socket = socket + |> assign(current_text: if(keep_label_on_select, do: "", else: socket.assigns.current_text)) |> client_select(%{ input_event: false, parent_event: socket.assigns[:"phx-focus"], - current_text: socket.assigns.current_text + current_text: display_text }) |> assign(hide_dropdown: false) @@ -542,8 +556,8 @@ defmodule LiveSelect.Component do defp clear(socket, params) do socket - |> assign(selection: []) - |> client_select(params) + |> assign(selection: [], current_text: "") + |> client_select(Map.put(params, :current_text, "")) end defp clear_options(socket) do diff --git a/lib/support/live_select_web/live/showcase_live.ex b/lib/support/live_select_web/live/showcase_live.ex index cf49fa7..c5571e0 100644 --- a/lib/support/live_select_web/live/showcase_live.ex +++ b/lib/support/live_select_web/live/showcase_live.ex @@ -76,6 +76,10 @@ defmodule LiveSelectWeb.ShowcaseLive do default: Component.default_opts()[:keep_options_on_select] ) + field(:keep_label_on_select, :boolean, + default: Component.default_opts()[:keep_label_on_select] + ) + field(:mode, Ecto.Enum, values: [:single, :tags, :quick_tags], default: Component.default_opts()[:mode] @@ -106,6 +110,7 @@ defmodule LiveSelectWeb.ShowcaseLive do :disabled, :options_styled_as_checkboxes, :keep_options_on_select, + :keep_label_on_select, :max_selectable, :user_defined_options, :mode, @@ -145,7 +150,8 @@ defmodule LiveSelectWeb.ShowcaseLive do (remove_defaults && value == Keyword.get(default_opts, option)) || (settings.mode == :single && option == :max_selectable) || (settings.mode != :single && option == :allow_clear) || - (settings.mode == :quick_tags && option == :keep_options_on_select) + (settings.mode == :quick_tags && option == :keep_options_on_select) || + (settings.mode != :single && option == :keep_label_on_select) end) |> Keyword.new() end diff --git a/lib/support/live_select_web/live/showcase_live.html.heex b/lib/support/live_select_web/live/showcase_live.html.heex index d1f9ef7..25a9581 100644 --- a/lib/support/live_select_web/live/showcase_live.html.heex +++ b/lib/support/live_select_web/live/showcase_live.html.heex @@ -74,6 +74,13 @@ disabled: to_string(@settings_form[:mode].value) == "quick_tags" )} + <%= label class: "label cursor-pointer" do %> Disabled:  {checkbox(@settings_form, :disabled, class: "toggle")} diff --git a/test/live_select_test.exs b/test/live_select_test.exs index ab55024..d508d2d 100644 --- a/test/live_select_test.exs +++ b/test/live_select_test.exs @@ -1213,4 +1213,92 @@ defmodule LiveSelectTest do refute_selected(live) end + + describe "when keep_label_on_select = true" do + setup %{conn: conn} do + stub_options(A: 1, B: 2, C: 3) + + {:ok, live, _html} = live(conn, "/?keep_label_on_select=true") + + type(live, "ABC") + select_nth_option(live, 2) + assert_selected(live, :B, 2) + + %{live: live} + end + + test "on focus, the text input displays the selected label", %{live: live} do + element(live, selectors()[:text_input]) + |> render_focus() + + assert_set_text_field(live, :B) + end + + test "on click, the text input displays the selected label", %{live: live} do + element(live, selectors()[:text_input]) + |> render_click() + + assert_set_text_field(live, :B) + end + + test "on blur, the text input is restored to the selected label", %{live: live} do + element(live, selectors()[:text_input]) + |> render_focus() + + element(live, selectors()[:text_input]) + |> render_blur() + + assert_set_text_field(live, :B) + end + + test "blur then focus cycle preserves the selected label", %{live: live} do + element(live, selectors()[:text_input]) + |> render_blur() + + assert_set_text_field(live, :B) + + element(live, selectors()[:text_input]) + |> render_focus() + + assert_set_text_field(live, :B) + + element(live, selectors()[:text_input]) + |> render_blur() + + assert_selected_static(live, :B, 2) + end + + test "without a selection, focus behaves as default", %{conn: conn} do + stub_options(A: 1, B: 2, C: 3) + + {:ok, live, _html} = live(conn, "/?keep_label_on_select=true") + + type(live, "ABC") + + element(live, selectors()[:text_input]) + |> render_focus() + + assert_set_text_field(live, "ABC") + end + + test "phx-focus parent_event fires normally on focus", %{conn: conn} do + stub_options(A: 1, B: 2, C: 3) + + {:ok, live, _html} = + live(conn, "/?keep_label_on_select=true&phx-focus=focus-event-for-parent") + + type(live, "ABC") + select_nth_option(live, 2) + assert_selected(live, :B, 2) + + element(live, selectors()[:text_input]) + |> render_focus() + + assert_push_event(live, "select", %{ + id: "my_form_city_search_live_select_component", + current_text: :B, + parent_event: "focus-event-for-parent" + }) + end + end end