[FIX] web: tooltip service should not cleanup when mouse is inside

Steps to reproduce
==================

- Use a laptop with touch or apply the following patch

```diff
diff --git a/addons/web/static/src/core/browser/feature_detection.js b/addons/web/static/src/core/browser/feature_detection.js
index 21c6294b1b0e..64c4bc9b899c 100644
--- a/addons/web/static/src/core/browser/feature_detection.js
+++ b/addons/web/static/src/core/browser/feature_detection.js
@@ -62,6 +62,7 @@ export function isDisplayStandalone() {
 }

 export function hasTouch() {
+    return true;
     return browser.ontouchstart !== undefined || browser.matchMedia("(pointer:coarse)").matches;
 }

```

- Enable debug mode
- Go to contacts
- Hover the mouse over a question mark next to a field

=> Nothing happens

Cause of the issue
==================

The function `shouldCleanup` returns true when the device has touch and
the popover has been triggerd with the mouse

opw-3694156

closes odoo/odoo#152629

X-original-commit: 27242a0f1070b6b16c16e479e22dc0ccad0d9659
Signed-off-by: Pierre Paridans (app) <app@odoo.com>
This commit is contained in:
Hubert Van De Walle
2024-02-04 14:39:24 +00:00
parent 3d54c3265c
commit 939395aefe
2 changed files with 47 additions and 1 deletions
@@ -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();
}
}
@@ -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`<button class="mybtn" data-tooltip="hello">Action</button>`;
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");
});
});