diff --git a/addons/web/static/src/core/tooltip/tooltip_service.js b/addons/web/static/src/core/tooltip/tooltip_service.js index 0210faea9c7..38e2cca939d 100644 --- a/addons/web/static/src/core/tooltip/tooltip_service.js +++ b/addons/web/static/src/core/tooltip/tooltip_service.js @@ -51,6 +51,7 @@ export const tooltipService = { let closeTooltip; let target = null; let touchPressed; + let mouseEntered; const elementsWithTooltips = new Map(); /** @@ -74,7 +75,7 @@ export const tooltipService = { if (!document.body.contains(target)) { return true; // target is no longer in the DOM } - if (hasTouch()) { + if (hasTouch() && !mouseEntered) { return !touchPressed; } return false; @@ -153,11 +154,13 @@ export const tooltipService = { * @param {MouseEvent} ev a "mouseenter" event */ function onMouseenter(ev) { + mouseEntered = true; openElementsTooltip(ev.target); } function onMouseleave(ev) { if (target === ev.target) { + mouseEntered = false; cleanup(); } } diff --git a/addons/web/static/tests/core/tooltip/tooltip_service_tests.js b/addons/web/static/tests/core/tooltip/tooltip_service_tests.js index 42dfca4e2f9..7eff5c9616c 100644 --- a/addons/web/static/tests/core/tooltip/tooltip_service_tests.js +++ b/addons/web/static/tests/core/tooltip/tooltip_service_tests.js @@ -443,4 +443,47 @@ QUnit.module("Tooltip service", (hooks) => { // tooltip did not crash and is not shown assert.containsNone(target, ".o_popover"); }); + + QUnit.test("tooltip using the mouse with a touch enabled device", async (assert) => { + // patch matchMedia to alter hasTouch value + patchWithCleanup(browser, { + matchMedia: (media) => { + if (media === "(pointer:coarse)") { + return { matches: true }; + } + return this._super(); + }, + }); + + class MyComponent extends Component {} + MyComponent.template = xml``; + let simulateTimeout; + let simulateInterval; + const mockSetTimeout = (fn) => { + simulateTimeout = fn; + }; + const mockSetInterval = (fn) => { + simulateInterval = fn; + }; + await makeParent(MyComponent, { mockSetInterval, mockSetTimeout }); + + assert.containsNone(target, ".o_popover"); + target.querySelector(".mybtn").dispatchEvent(new Event("mouseenter")); + await nextTick(); + assert.containsNone(target, ".o_popover"); + + simulateTimeout(); + await nextTick(); + assert.containsOnce(target, ".o_popover"); + assert.strictEqual(target.querySelector(".o_popover").innerText, "hello"); + + simulateInterval(); + await nextTick(); + assert.containsOnce(target, ".o_popover"); + assert.strictEqual(target.querySelector(".o_popover").innerText, "hello"); + + target.querySelector(".mybtn").dispatchEvent(new Event("mouseleave")); + await nextTick(); + assert.containsNone(target, ".o_popover"); + }); });