settings: Improve user interaction in DropdownListWidget.

This is a finicky change; we need to adapt around bootstrap internals
to first steal focus from the list, and then if the user uses arrow
keys, send that key to the list letting bootstrap focus on the list
elements.

The reverse: stealing abck focus to the input from the list, is not
possible without changing third/bootstrap.js because it's concept of
currently selected item depends on the item being focused. We retain
the pre-commit behavior for this, where the user can SHIFT+TAB to get
back to the input and type.

Ideally, a user will now interact with this widget like this:

1. Click the button to open the widget. The input is in focus.
2. Type a query to filter the results.
3. Seemlessly start using arrow keys to select an option.
4. Press "enter" to select the option.
This commit is contained in:
Rohitt Vashishtha 2020-04-16 02:25:05 +05:30 committed by Tim Abbott
parent 8bf407878d
commit 9d1f9e8a75
1 changed files with 21 additions and 1 deletions

View File

@ -55,6 +55,7 @@ const DropdownListWidget = function (opts) {
// populate the dropdown
const dropdown_list_body = $(`#${opts.container_id} .dropdown-list-body`).expectOne();
const search_input = $(`#${opts.container_id} .dropdown-search > input[type=text]`);
const dropdown_toggle = $(`#${opts.container_id} .dropdown-toggle`);
list_render.create(dropdown_list_body, opts.data, {
name: `${opts.setting_name}_list`,
@ -72,10 +73,29 @@ const DropdownListWidget = function (opts) {
e.stopPropagation();
});
$(`#${opts.container_id} .dropdown-toggle`).click(function () {
dropdown_toggle.click(function () {
search_input.val("").trigger("input");
});
dropdown_toggle.focus(function (e) {
// On opening a Bootstrap Dropdown, the parent element recieves focus.
// Here, we want our search input to have focus instead.
e.preventDefault();
search_input.focus();
});
search_input.keydown(function (e) {
if (!/(38|40|27)/.test(e.keyCode)) {
return;
}
e.preventDefault();
const custom_event = jQuery.Event("keydown.dropdown.data-api", {
keyCode: e.keyCode,
which: e.keyCode,
});
dropdown_toggle.trigger(custom_event);
});
render(page_params[opts.setting_name]);
register_event_handlers();
};