popover_menus: Stop propagating click events when opening a popover.

This could result in things like compose box being opened by the click
that opens the popover.
This commit is contained in:
Aman Agrawal 2022-11-29 08:04:27 +00:00 committed by Tim Abbott
parent 223dca65b2
commit 8b6dc955f1
1 changed files with 41 additions and 12 deletions

View File

@ -3,7 +3,7 @@
popovers system in popovers.js. */ popovers system in popovers.js. */
import $ from "jquery"; import $ from "jquery";
import {delegate} from "tippy.js"; import tippy, {delegate} from "tippy.js";
import render_compose_control_buttons_popover from "../templates/compose_control_buttons_popover.hbs"; import render_compose_control_buttons_popover from "../templates/compose_control_buttons_popover.hbs";
import render_compose_select_enter_behaviour_popover from "../templates/compose_select_enter_behaviour_popover.hbs"; import render_compose_select_enter_behaviour_popover from "../templates/compose_select_enter_behaviour_popover.hbs";
@ -67,10 +67,39 @@ function on_show_prep(instance) {
popovers.hide_all_except_sidebars(instance); popovers.hide_all_except_sidebars(instance);
} }
function tippy_no_propagation(target, popover_props) {
// For some elements, such as the click target to open the message
// actions menu, we want to avoid propagating the click event to
// parent elements. Tippy's built-in `delegate` method does not
// have an option to do stopPropagation, so we use this method to
// open the Tippy popovers associated with such elements.
//
// A click on the click target will close the menu; for this to
// work correctly without leaking, all callers need call
// `instance.destroy()` inside their `onHidden` handler.
//
// TODO: Should we instead we wrap the caller's `onHidden` hook,
// if any, to add `instance.destroy()`?
$("body").on("click", target, (e) => {
e.preventDefault();
e.stopPropagation();
const instance = e.currentTarget._tippy;
if (instance) {
instance.hide();
return;
}
tippy(e.currentTarget, {
...default_popover_props,
showOnCreate: true,
...popover_props,
});
});
}
export function initialize() { export function initialize() {
delegate("body", { tippy_no_propagation("#streams_inline_icon", {
...default_popover_props,
target: "#streams_inline_icon",
onShow(instance) { onShow(instance) {
const can_create_streams = const can_create_streams =
settings_data.user_can_create_private_streams() || settings_data.user_can_create_private_streams() ||
@ -90,12 +119,15 @@ export function initialize() {
left_sidebar_stream_setting_popover_displayed = true; left_sidebar_stream_setting_popover_displayed = true;
return true; return true;
}, },
onHidden() { onHidden(instance) {
instance.destroy();
left_sidebar_stream_setting_popover_displayed = false; left_sidebar_stream_setting_popover_displayed = false;
}, },
}); });
// compose box buttons popover shown on mobile widths. // compose box buttons popover shown on mobile widths.
// We want this click event to propagate and hide other popovers
// that could possibly obstruct user from using this popover.
delegate("body", { delegate("body", {
...default_popover_props, ...default_popover_props,
target: ".compose_mobile_button", target: ".compose_mobile_button",
@ -135,9 +167,7 @@ export function initialize() {
// Click event handlers for it are handled in `compose_ui` and // Click event handlers for it are handled in `compose_ui` and
// we don't want to close this popover on click inside it but // we don't want to close this popover on click inside it but
// only if user clicked outside it. // only if user clicked outside it.
delegate("body", { tippy_no_propagation(".compose_control_menu_wrapper", {
...default_popover_props,
target: ".compose_control_menu_wrapper",
placement: "top", placement: "top",
onShow(instance) { onShow(instance) {
instance.setContent( instance.setContent(
@ -150,14 +180,13 @@ export function initialize() {
compose_control_buttons_popover_instance = instance; compose_control_buttons_popover_instance = instance;
popovers.hide_all_except_sidebars(instance); popovers.hide_all_except_sidebars(instance);
}, },
onHidden() { onHidden(instance) {
instance.destroy();
compose_control_buttons_popover_instance = undefined; compose_control_buttons_popover_instance = undefined;
}, },
}); });
delegate("body", { tippy_no_propagation(".enter_sends", {
...default_popover_props,
target: ".enter_sends",
placement: "top", placement: "top",
onShow(instance) { onShow(instance) {
on_show_prep(instance); on_show_prep(instance);