-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Admin select component performance #6213
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
base: main
Are you sure you want to change the base?
Admin select component performance #6213
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #6213 +/- ##
==========================================
+ Coverage 86.52% 88.79% +2.27%
==========================================
Files 518 840 +322
Lines 11934 18227 +6293
==========================================
+ Hits 10326 16185 +5859
- Misses 1608 2042 +434 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This will be very healthy when trying to deal with zipcode zones and I really appreciate this change. |
@chaimann nice! Which select(s) do we need this for? |
This is very useful for Product-, Variant- and Taxon-Selects. |
Right, we were doing something similar with select2 on the legacy admin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing. Thanks. Can we make the src
argument a bit more approachable? It's too important to "hide" it in a Hash :)
@@ -25,22 +25,40 @@ class SolidusAdmin::UI::Forms::Select::Component < SolidusAdmin::BaseComponent | |||
}, | |||
}.freeze | |||
|
|||
def initialize(label:, name:, choices:, size: :m, hint: nil, tip: nil, error: nil, **attributes) | |||
def initialize(label:, name:, choices: nil, size: :m, hint: nil, tip: nil, error: nil, **attributes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to have src
as named argument instead of "hiding" it in the attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense! 😄
admin/app/javascript/solidus_admin/web_components/solidus_select.js
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we please not use data-
attributes on our custom element? Sorry, missed that during previous PR review
async fetchOptions() { | ||
const response = await fetch(this.getAttribute("data-src")); | ||
return await response.json(); | ||
const [url, dataPath] = this.getAttribute("data-src").split("::"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add a dedicated attribute for that? something like json-result-path
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tvdeyen are you strongly against this one? 😄 I did it this way because it's more "convention over configuration" and saves us from specifying one more attribute on the select element. Also it felt natural in a way that src
is not just a URL but a full "waypoint" to where the relevant data can be accessed. Or do you think it's way too implicit/inconvenient?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. But it seems a bit weird. I think another attribute is nothing bad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure. I had another thought and guess being more explicit about this feature might be a better choice
@kennyadsl also initially this was a spin-off from PR for zones add/edit where when a zone is state based there is a select with all the states. TomSelect handles dynamically loaded options pretty good, without any noticeable impact in the browsers; there is also a setting |
@chaimann it's more complicated than that. You can expect on Zip based zones up to thousands of values. The issue is outlined in #6187, |
@fthobe amazing! I'm super glad that it's going to be useful :) |
9de8feb
to
f9e3557
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like that! I also worry about the 500 results we pull. Can we utilize the pagination for that? Does TomSelect support it, like select2 does?
controlClass: "control", | ||
dropdownClass: "dropdown", | ||
dropdownContentClass: "dropdown-content", | ||
optionClass: "option", | ||
wrapperClass: "wrapper", | ||
maxOptions: null, | ||
maxOptions: 500, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to utilize the pagination feature from our API, like we do in the productPicker and variantAutoComplete?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tvdeyen The results are actually preloaded in full, and this maxOptions
does not control how many results we pull, but how many results we display on dropdown open. It's like a small "hack" that allows to reduce the lag on TomSelect side (when displaying more than 1k options the search and select/unselect starts to have a visible delay). As I mentioned previously, having it in place helps to build a visual of a "full" dropdown, while expecting the user to use search instead of selecting options by manually scroll-finding them.
We also should be able to make pagination work with TomSelect, there is a plugin for that which I haven't tested yet but I believe it should work. I agree that it would be better in the long term to have paginated results instead of full preload, as the larger a dataset is the longer the request would be processed. The only downside I can see is that the search function would now need to hit the server as well, but I think I can configure it to search the "cached" fetched data first.
This change covers the problem that was discovered while working on zone creation and modification update (states select). The select box performance dropped significantly when there were thousands of options rendered for native select. So instead of rendering regular options, it's now possible to load them from the server and let TomSelect construct available options. For convenience, it also leverages TomSelect "loading" template option to show feedback to the user while request is being processed.
When json response contains other data than just array of option data, allow to access given array by specifying its path in "src" attribute.
Uses "virtual_scroll" plugin to implement loading more options on scroll. Retrieves next page URL from response header "Link". Also patches an existing bug with dropdown scroll: orchidjs/tom-select#556 orchidjs/tom-select#867
Separates existing functionality from "solidus_select" into plugins: - patch_scroll.js - patches mentioned bug with dropdown scroll; - stash_on_search.js - hide and store selected item in a single select when typing, restore if nothing selected; - remote_with_pagination.js - core functionality to load options from remote source;
808bcdc
to
0e9fb3b
Compare
44c726d
to
2f4eae1
Compare
Screen.Recording.2025-04-23.at.09.26.17.movAdded infinite scroll for remote select, also patched the bug with dropdown scroll, and redesigned single select search to show search term when typing in the field, restoring selected option if user does not select anything after search. |
For preselected options to work correctly these two settings need to be initialized along with TomSelect rather than inside the remote_with_pagination plugin.
Summary
Allows to fetch options from remote resource, rather than rendering all options in place.
Usage
:src
attributeWhen initialising select component pass
:src
attribute with a url to JSON resource, omit:choices
or pass them asnil
.Mapping fields
Maps fields "id" and "name" from JSON data to be value and text of an option accordingly. If the fields have different names, map them with
:"data-option-value-field"
and:"data-option-label-field"
when initialising select component, e.g.:Nested JSON data
If data in response JSON is nested, you can specify
:"data-json-path"
parameter to retrieve the data by given path signature, e.g.:Loading template
A nice little "Loading" template placeholder is there to be displayed while the options are being loaded.
Screen.Recording.2025-04-10.at.16.14.46.mov
Checklist
Check out our PR guidelines for more details.
The following are mandatory for all PRs: