Repository navigation
Conversation
Backpex.HTML.Combobox.combobox/1 is a select with search, multiple selection, groups, custom option markup and server search, driven by the new BackpexCombobox hook. It is a form control like a native select: the server renders all markup and state, the hook only toggles attributes, so it can be used in custom fields, filters and LiveViews outside of a LiveResource. Existing fields and filters keep their current inputs.
A page at /admin/combobox with one section per capability of the combobox, each showing the value the server holds.
Floating UI replaces the hook's own geometry and its scroll and resize listeners. Besides scrolling and resizing, it repositions the popup when the control or the popup change their size or place, which the hook did not notice before. @floating-ui/dom (MIT) is bundled into priv/static/js like imask.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
The formatter of Elixir 1.16 removes the parentheses around the condition in describedby/2, the one of later versions keeps them, so the formatting check failed on 1.16. Name the condition instead.
The rendering tests left the badge and clear button classes of the
sizes, a group inside a group, the {:hr, nil} separator and a combobox
without a name unchecked.
With select_all, the popup of a multiple combobox without a maximum shows the number of selected values and buttons to select and deselect all options the search shows. Values the search hides stay as they are.
The forms of Select, BelongsTo, MultiSelect and HasMany, the select of HasManyThrough and the Select and MultiSelect filters render Backpex.HTML.Combobox.combobox/1 instead of a native select or a dropdown with checkboxes. An admin changes the selection in the browser and it reaches the server with the phx-change of the form, so the multi select needs no event per option. HasMany searches its options on the server through on_search and loads further pages with "Show more". Select, BelongsTo and HasManyThrough take the new option searchable_options, which adds a search input. A select without a prompt still selects its first option, like a native select. Backpex.HTML.Form.multi_select/1 is removed. Backpex.Testing sends the values of fields with multiple values with the event, as their inputs only exist in the browser. The combobox passes phx-debounce and phx-throttle to its anchor input and blurs it when focus leaves, keeps the focus when it sits in a focusable element like the filter dropdown, and isolates its stacking context, so its clear button stays below overlays.
develop made the multi select's toggle-option event ignore an id that matches no option. The field no longer has that event: its combobox submits the selection with the form. It now leaves values that match no option out of the selection it renders, so they are not submitted again.
Backpex.Testing parsed the form with LazyHTML to find the fields with multiple values. LazyHTML is a test-only dependency, so compiling Backpex in dev failed with warnings as errors, and an app that uses Backpex got warnings for the undefined module. Ask LiveViewTest for the anchor input of each param instead.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
closes #984
Adds
Backpex.HTML.Combobox.combobox/1, a select with search, multiple selection, groups, custom option markup and server search, and theBackpexComboboxhook that drives it. It is built to be reusable outside of a LiveResource (custom fields, filters, any LiveView) and with few ties to Backpex: one function component and one hook. The only new dependency is@floating-ui/dom(MIT), bundled intopriv/static/jslikeimask.The built-in selects and multi selects of fields and filters render the combobox (commit
6de672fe), andBackpex.HTML.Form.multi_select/1is removed. That part is breaking, see "Fields and filters" below andguides/upgrading/v0.25.md.What it does
field(orid,name,value) and the options formats ofoptions_for_select/2, submits through the form'sphx-changeand submit, and needs no event handlers for local options.multiplewithmax,clearable, groups, disabled options, sizesxstoxl,disabledandreadonly.on_search(debounced, honorsphx-target, loading state), withselected_optionsfor the labels of values the search results do not contain.select_all: a line in the popup with the number of selected values and buttons to select and deselect all options the search shows (multiplewithoutmax).:option,:header,:footer,:empty.JS.dispatch/2:combobox:open,combobox:close,combobox:clear,combobox:select-all. The hook emitscombobox:change.Fields and filters
Fields.SelectandFields.BelongsTo(form andindex_editable),Fields.MultiSelect,Fields.HasMany, the select in the modal ofFields.HasManyThrough,Filters.SelectandFilters.MultiSelectrender the combobox. Their options and callbacks are unchanged and the forms submit the same params.phx-change. The eventstoggle-optionandtoggle-select-allare gone.Fields.MultiSelectusesselect_all.Fields.HasManysearches on the server (on_search), loads further pages with "Show more" in the:footerslot, and keeps its own "Select all" in the:headerslot: it selects every option of theoptions_queryon the server, also the ones on pages that are not loaded.searchable_options(defaultfalse) forSelect,BelongsToandHasManyThroughadds a search input. The demo turns it on for the author of a post and the country of an address.SelectorBelongsTowithout apromptstill selects its first option while the item has no value, like the native select did. With apromptit shows the prompt and a clear button.Fields.MultiSelectleaves values of the form params that match no option out of the selection it renders, so they are not submitted again.developmade the removedtoggle-optionevent ignore such ids; this keeps that guarantee after the merge.c:Backpex.Filter.render_form/1receives the label of the filter as@label; the select filters use it as the accessible name of their combobox.Backpex.Testing.submit_form/3andconfirm_item_action/2send the values of fields with multiple values with the event, as their inputs only exist in the browser.phx-debounce/phx-throttlego to the anchor input (with"blur"the change is sent when focus leaves the combobox), a click on an option keeps the focus when the combobox sits in a focusable element (the menu of the filter dropdown closed the popup before the click), and the root isolates its stacking context (the clear button of an index editable select showed through the filter dropdown).Breaking:
Form.multi_select/1and its events are removed, the markup and ids of these fields and filters changed (has-many-dropdown-<name>,multi-select-<id>), the"search"event ofHasManytakes%{"query" => query},PhoenixTest.select/3andcheck/2find no native control for them, and a filter that overridesrender_form/1and calls the one ofFilters.SelectorFilters.MultiSelectwith assigns of its own has to passlabelon.Not covered:
BelongsTostill loads all options (no server search), and aSelectas a child field ofInlineCRUDwas not looked at in a browser.Screenshots
Multiple selection at
max, light theme::optionslot, dark theme:Select all (
select_all):combobox-select-all.mp4
Fields.HasManyin the form of a post (server search, "Select all" for all options of the query):Fields.BelongsTowithsearchable_options:Filters.MultiSelectinside the filter dropdown:Design decisions
hidden,aria-*anddata-*, sets input values and clones a server-rendered badge template; it contains no class names and no texts.backpex.esm.js(6.8 kB minified and gzipped).type="hidden"as used from the start, so its errors would show before the admin touched it. The combobox therefore renders one hidden text input with the plain name (the value for a single select, the empty fallback formultiple) and dispatchesinput/changeon it.on_searchthey act on the last search result. The button that has nothing to do isaria-disabled, notdisabled, so it does not drop the focus when it is used with the keyboard.phx-changehandler that renders another value shows that value. If the server renders the value it had before, the patch does not touch the field and the admin's choice stays, as with a native select.Tests
test/js/combobox_test.js: matching, navigation and typeahead as pure functions; debounced search push,phx-target, stale replies and cleanup with a fake root.test/html/combobox_test.exs: rendering (options formats, selection, form controls and submitted params, field errors, disabled/readonly, ARIA wiring, slots, texts).integration_test/e2e/combobox_test.exs: mouse and keyboard operation, local and server search, multiple, commands, server patches while open, server overrule, Escape in a<dialog>and in the Backpex modal, flipping, following the control on a layout shift, select all with mouse and keyboard, control height per size, a mobile viewport.integration_test/e2e/a11y_test.exs: axe on the fixture closed and open, in light and dark.integration_test/e2e/select_fields_test.exs: the fields and filters in the browser (select all, search, "Show more", saving, index edit, the modal ofHasManyThrough, the filters inside the filter dropdown). Unit and integration tests of the fields and filters drive the new markup.Backpex.Test.ComboboxLiveat/admin/combobox;harness_test.exsnow only treatsIndex,ShowandFormviews as LiveResource views.Docs
Moduledoc of
Backpex.HTML.Combobox, a section in the custom fields guide,usage-rules.mdand thecreate-fieldskill. For the fields and filters: their moduledocs, the readonly guide, thecreate-filterskill and the upgrade guidev0.25.Demo
/admin/comboboxshows one section per capability with the value the server holds (second commit; drop it if the showcase should not be part of the demo).Known limits
resetdoes not update the display.priv/static/jscarries no license notice for its bundled MIT dependencies (imaskalready, now also@floating-ui/dom).labelclass, whichForm.input/1uses as well.