From 6eca7a2715e49bc4744e7d3d658fa201921c340a Mon Sep 17 00:00:00 2001 From: Jose Farias <31393016+josefarias@users.noreply.github.com> Date: Tue, 19 Nov 2024 18:55:30 -0600 Subject: [PATCH] Optionally preload async comboboxes (#224) --- app/presenters/hotwire_combobox/component.rb | 7 ++- .../hotwire_combobox/component/paginated.rb | 11 +++- .../hotwire_combobox/_pagination.html.erb | 4 +- lib/hotwire_combobox/helper.rb | 8 ++- .../app/controllers/comboboxes_controller.rb | 3 + .../views/comboboxes/async_preload.html.erb | 1 + test/dummy/config/routes.rb | 55 ++++++++++--------- test/system/async_test.rb | 5 ++ 8 files changed, 59 insertions(+), 35 deletions(-) create mode 100644 test/dummy/app/views/comboboxes/async_preload.html.erb diff --git a/app/presenters/hotwire_combobox/component.rb b/app/presenters/hotwire_combobox/component.rb index 1a4d614..d1e511b 100644 --- a/app/presenters/hotwire_combobox/component.rb +++ b/app/presenters/hotwire_combobox/component.rb @@ -26,12 +26,13 @@ def initialize( name_when_new: nil, open: false, options: [], + preload: false, request: nil, value: nil, **rest) @view, @autocomplete, @id, @name, @value, @form, @async_src, @label, @free_text, @request, - @name_when_new, @open, @data, @mobile_at, @multiselect_chip_src, @options, @dialog_label = + @preload, @name_when_new, @open, @data, @mobile_at, @multiselect_chip_src, @options, @dialog_label = view, autocomplete, id, name.to_s, value, form, async_src, label, free_text, request, - name_when_new, open, data, mobile_at, multiselect_chip_src, options, dialog_label + preload, name_when_new, open, data, mobile_at, multiselect_chip_src, options, dialog_label @combobox_attrs = input.reverse_merge(rest).deep_symbolize_keys @association_name = association_name || infer_association_name @@ -46,7 +47,7 @@ def render_in(view_context, &block) private attr_reader :view, :autocomplete, :id, :name, :value, :form, :free_text, :open, :request, - :data, :combobox_attrs, :mobile_at, :association_name, :multiselect_chip_src + :data, :combobox_attrs, :mobile_at, :association_name, :multiselect_chip_src, :preload def canonical_id @canonical_id ||= id || form&.field_id(name) || SecureRandom.uuid diff --git a/app/presenters/hotwire_combobox/component/paginated.rb b/app/presenters/hotwire_combobox/component/paginated.rb index cf50254..80cccf9 100644 --- a/app/presenters/hotwire_combobox/component/paginated.rb +++ b/app/presenters/hotwire_combobox/component/paginated.rb @@ -4,6 +4,15 @@ def paginated? end def pagination_attrs - { for_id: canonical_id, src: async_src } + { for_id: canonical_id, src: async_src, loading: preload_next_page? ? :eager : :lazy } end + + private + def preload_next_page? + view.hw_first_page? && preload? + end + + def preload? + preload.present? + end end diff --git a/app/views/hotwire_combobox/_pagination.html.erb b/app/views/hotwire_combobox/_pagination.html.erb index fb84b5f..cbdcdc4 100644 --- a/app/views/hotwire_combobox/_pagination.html.erb +++ b/app/views/hotwire_combobox/_pagination.html.erb @@ -1,7 +1,7 @@ -<%# locals: (for_id:, src:) -%> +<%# locals: (for_id:, src:, loading: :lazy) -%> <%= tag.li id: hw_pagination_frame_wrapper_id(for_id), class: "hw_combobox__pagination__wrapper", data: { hw_combobox_target: "endOfOptionsStream", input_type: params[:input_type], callback_id: params[:callback_id] }, aria: { hidden: true } do %> - <%= turbo_frame_tag hw_pagination_frame_id(for_id), src: src, loading: :lazy %> + <%= turbo_frame_tag hw_pagination_frame_id(for_id), src: src, loading: loading %> <% end %> diff --git a/lib/hotwire_combobox/helper.rb b/lib/hotwire_combobox/helper.rb index 592fec1..6eb7d9f 100644 --- a/lib/hotwire_combobox/helper.rb +++ b/lib/hotwire_combobox/helper.rb @@ -25,7 +25,7 @@ def hw_combobox_options(options, render_in: {}, include_blank: nil, display: :to end def hw_paginated_combobox_options(options, for_id: params[:for_id], src: hw_fullpath_for_pagination, next_page: nil, render_in: {}, include_blank: {}, **custom_methods) - include_blank = params[:page].to_i > 0 ? nil : include_blank + include_blank = hw_first_page? ? include_blank : nil options = hw_combobox_options options, render_in: render_in, include_blank: include_blank, **custom_methods this_page = render "hotwire_combobox/paginated_options", for_id: for_id, options: options next_page = render "hotwire_combobox/next_page", for_id: for_id, src: hw_combobox_next_page_uri(src, next_page, for_id) @@ -106,7 +106,11 @@ def hw_pagination_frame_id(id) end def hw_combobox_page_stream_action - params[:page].to_i > 0 ? :append : :update + hw_first_page? ? :update : :append + end + + def hw_first_page? + params[:page].to_i.zero? end def hw_uri_with_params(url_or_path, **params) diff --git a/test/dummy/app/controllers/comboboxes_controller.rb b/test/dummy/app/controllers/comboboxes_controller.rb index 3502094..b711999 100644 --- a/test/dummy/app/controllers/comboboxes_controller.rb +++ b/test/dummy/app/controllers/comboboxes_controller.rb @@ -34,6 +34,9 @@ def list_autocomplete def async end + def async_preload + end + def freetext_async end diff --git a/test/dummy/app/views/comboboxes/async_preload.html.erb b/test/dummy/app/views/comboboxes/async_preload.html.erb new file mode 100644 index 0000000..d76911b --- /dev/null +++ b/test/dummy/app/views/comboboxes/async_preload.html.erb @@ -0,0 +1 @@ +<%= combobox_tag "movie", movies_path, id: "movie-field", label: "Choose your movie!", preload: true %> diff --git a/test/dummy/config/routes.rb b/test/dummy/config/routes.rb index 0341f98..57bf4bc 100644 --- a/test/dummy/config/routes.rb +++ b/test/dummy/config/routes.rb @@ -1,39 +1,40 @@ Rails.application.routes.draw do - get "plain", to: "comboboxes#plain" - get "padded", to: "comboboxes#padded" - get "open", to: "comboboxes#open" - get "html_options", to: "comboboxes#html_options" - get "prefilled", to: "comboboxes#prefilled" - get "prefilled_html", to: "comboboxes#prefilled_html" - get "required", to: "comboboxes#required" - get "formbuilder", to: "comboboxes#formbuilder" - get "new_options", to: "comboboxes#new_options" - get "inline_autocomplete", to: "comboboxes#inline_autocomplete" - get "list_autocomplete", to: "comboboxes#list_autocomplete" get "async", to: "comboboxes#async" - get "freetext_async", to: "comboboxes#freetext_async" - get "prefilled_async", to: "comboboxes#prefilled_async" - get "prefilled_form", to: "comboboxes#prefilled_form" - get "prefilled_free_text", to: "comboboxes#prefilled_free_text" get "async_html", to: "comboboxes#async_html" - get "render_in", to: "comboboxes#render_in" + get "async_preload", to: "comboboxes#async_preload" + get "conflicting_order", to: "comboboxes#conflicting_order" + get "custom_attrs", to: "comboboxes#custom_attrs" + get "custom_events", to: "comboboxes#custom_events" + get "dialog", to: "comboboxes#dialog" get "enum", to: "comboboxes#enum" + get "external_clear", to: "comboboxes#external_clear" + get "form_object", to: "comboboxes#form_object" + get "formbuilder", to: "comboboxes#formbuilder" + get "freetext_async", to: "comboboxes#freetext_async" + get "grouped_options", to: "comboboxes#grouped_options" + get "html_options", to: "comboboxes#html_options" get "include_blank", to: "comboboxes#include_blank" - get "custom_events", to: "comboboxes#custom_events" - get "custom_attrs", to: "comboboxes#custom_attrs" - get "conflicting_order", to: "comboboxes#conflicting_order" - get "render_in_locals", to: "comboboxes#render_in_locals" + get "inline_autocomplete", to: "comboboxes#inline_autocomplete" + get "list_autocomplete", to: "comboboxes#list_autocomplete" + get "morph", to: "comboboxes#morph" get "multiselect", to: "comboboxes#multiselect" - get "multiselect_dismissing", to: "comboboxes#multiselect_dismissing" get "multiselect_async_html", to: "comboboxes#multiselect_async_html" - get "multiselect_prefilled_form", to: "comboboxes#multiselect_prefilled_form" get "multiselect_custom_events", to: "comboboxes#multiselect_custom_events" + get "multiselect_dismissing", to: "comboboxes#multiselect_dismissing" get "multiselect_new_values", to: "comboboxes#multiselect_new_values" - get "grouped_options", to: "comboboxes#grouped_options" - get "morph", to: "comboboxes#morph" - get "form_object", to: "comboboxes#form_object" - get "external_clear", to: "comboboxes#external_clear" - get "dialog", to: "comboboxes#dialog" + get "multiselect_prefilled_form", to: "comboboxes#multiselect_prefilled_form" + get "new_options", to: "comboboxes#new_options" + get "open", to: "comboboxes#open" + get "padded", to: "comboboxes#padded" + get "plain", to: "comboboxes#plain" + get "prefilled", to: "comboboxes#prefilled" + get "prefilled_async", to: "comboboxes#prefilled_async" + get "prefilled_form", to: "comboboxes#prefilled_form" + get "prefilled_free_text", to: "comboboxes#prefilled_free_text" + get "prefilled_html", to: "comboboxes#prefilled_html" + get "render_in", to: "comboboxes#render_in" + get "render_in_locals", to: "comboboxes#render_in_locals" + get "required", to: "comboboxes#required" resources :movies, only: %i[ index update ] get "movies_html", to: "movies#index_html" diff --git a/test/system/async_test.rb b/test/system/async_test.rb index d74dd6f..2df09fd 100644 --- a/test/system/async_test.rb +++ b/test/system/async_test.rb @@ -54,4 +54,9 @@ class AsyncTest < ApplicationSystemTestCase click_on_option "A Few Good Men" assert_combobox_display_and_value "#movie-field", "A Few Good Men", movies(:a_few_good_men).id end + + test "preload" do + visit async_preload_path + assert_options_with count: 5, visible: :hidden + end end