Files
zulip/static/js/dropdown_list_widget.js
aryanshridhar 2c50c67d07 dropdown_list_widget: Properly bind focus event to input field.
Previously, the focus event was triggering on a hidden
dropdown input field, which caused it to not gain focus when
clicked on dropdown button.

This is so because focus events cannot be triggered to
hidden elements or elements that aren't visible in DOM.

Added a fix by explicilty triggering the focus event to
dropdown input field only if the input field is visible in the DOM.
2021-05-09 19:26:40 -07:00

164 lines
5.2 KiB
JavaScript

import $ from "jquery";
import render_dropdown_list from "../templates/settings/dropdown_list.hbs";
import * as blueslip from "./blueslip";
import * as ListWidget from "./list_widget";
export const DropdownListWidget = function ({
widget_name,
data,
default_text,
render_text = (item_name) => item_name,
null_value = null,
include_current_item = true,
value,
on_update = () => {},
}) {
const container_id = `${widget_name}_widget`;
const value_id = `id_${widget_name}`;
if (value === undefined) {
value = null_value;
blueslip.warn("dropdown-list-widget: Called without a default value; using null value");
}
const render_default_text = (elem) => {
elem.text(default_text);
elem.addClass("text-warning");
elem.closest(".input-group").find(".dropdown_list_reset_button:enabled").hide();
};
const render = (value) => {
$(`#${CSS.escape(container_id)} #${CSS.escape(value_id)}`).data("value", value);
const elem = $(`#${CSS.escape(container_id)} #${CSS.escape(widget_name)}_name`);
if (!value || value === null_value) {
render_default_text(elem);
return;
}
// Happy path
const item = data.find((x) => x.value === value.toString());
if (item === undefined) {
render_default_text(elem);
return;
}
const text = render_text(item.name);
elem.text(text);
elem.removeClass("text-warning");
elem.closest(".input-group").find(".dropdown_list_reset_button:enabled").show();
};
const update = (value) => {
render(value);
on_update(value);
};
const register_event_handlers = () => {
$(`#${CSS.escape(container_id)} .dropdown-list-body`).on(
"click keypress",
".list_item",
function (e) {
const setting_elem = $(this).closest(`.${CSS.escape(widget_name)}_setting`);
if (e.type === "keypress") {
if (e.which === 13) {
setting_elem.find(".dropdown-menu").dropdown("toggle");
} else {
return;
}
}
const value = $(this).attr("data-value");
update(value);
},
);
$(`#${CSS.escape(container_id)} .dropdown_list_reset_button`).on("click", (e) => {
update(null_value);
e.preventDefault();
});
};
const setup = () => {
// populate the dropdown
const dropdown_list_body = $(
`#${CSS.escape(container_id)} .dropdown-list-body`,
).expectOne();
const search_input = $(`#${CSS.escape(container_id)} .dropdown-search > input[type=text]`);
const dropdown_toggle = $(`#${CSS.escape(container_id)} .dropdown-toggle`);
const get_data = (data) => {
if (include_current_item) {
return data;
}
return data.filter((x) => x.value !== value.toString());
};
ListWidget.create(dropdown_list_body, get_data(data), {
name: `${CSS.escape(widget_name)}_list`,
modifier(item) {
return render_dropdown_list({item});
},
filter: {
element: search_input,
predicate(item, value) {
return item.name.toLowerCase().includes(value);
},
},
simplebar_container: $(`#${CSS.escape(container_id)} .dropdown-list-wrapper`),
});
$(`#${CSS.escape(container_id)} .dropdown-search`).on("click", (e) => {
e.stopPropagation();
});
dropdown_toggle.on("click", () => {
search_input.val("").trigger("input");
});
dropdown_toggle.on("focus", (e) => {
// On opening a Bootstrap Dropdown, the parent element receives focus.
// Here, we want our search input to have focus instead.
e.preventDefault();
// This function gets called twice when focusing the
// dropdown, and only in the second call is the input
// field visible in the DOM; so the following visibility
// check ensures we wait for the second call to focus.
if (dropdown_list_body.is(":visible")) {
search_input.trigger("focus");
}
});
search_input.on("keydown", (e) => {
if (!/(38|40|27)/.test(e.keyCode)) {
return;
}
e.preventDefault();
const custom_event = new $.Event("keydown.dropdown.data-api", {
keyCode: e.keyCode,
which: e.keyCode,
});
dropdown_toggle.trigger(custom_event);
});
render(value);
register_event_handlers();
};
const get_value = () => {
let val = $(`#${CSS.escape(container_id)} #${CSS.escape(value_id)}`).data("value");
if (val === null) {
val = "";
}
return val;
};
// Run setup() automatically on initialization.
setup();
return {
render,
value: get_value,
update,
};
};