[IMP] web: Tooltip UX on touch-enable devices

Before this commit, in a touch device the behaviour was a
"tap-hold-to-show” which let the user see the Tooltip by pressing the
element, and letting it disappear when the pressure ends.

This commit allows choosing between the previous "tap-hold-to-show"
behaviour and a new "tap-to-show" which let the user see the Tooltip by
pressing the element, and keep it open until it press something else.
This behaviour is more adapted when the tooltip is on a specific icon,
as is the case for the labels, since : f981c5f8f0

closes odoo/odoo#97680

Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com>
This commit is contained in:
Jorge Pinna Puissant
2022-08-09 09:00:02 +02:00
parent 4caa9596bc
commit 34ddcab07f
3 changed files with 55 additions and 6 deletions
@@ -23,6 +23,10 @@ const { whenReady } = owl;
* The opening delay can be modified with the "data-tooltip-delay" attribute (default: 400):
* <button data-tooltip="This is a tooltip" data-tooltip-delay="0">Do something</button>
*
* The default behaviour on touch devices to open the tooltip can be modified from "hold-to-show"
* to "tap-to-show" "with the data-tooltip-touch-tap-to-show" attribute:
* <button data-tooltip="This is a tooltip" data-tooltip-touch-tap-to-show="true">Do something</button>
*
* For advanced tooltips containing dynamic and/or html content, the
* "data-tooltip-template" and "data-tooltip-info" attributes can be used.
* For example, let's suppose the following qweb template:
@@ -188,12 +192,20 @@ export const tooltipService = {
if (hasTouch()) {
document.body.addEventListener("touchstart", onTouchStart);
document.body.addEventListener("touchend", () => {
touchPressed = false;
document.body.addEventListener("touchend", (ev) => {
if (ev.target.matches("[data-tooltip], [data-tooltip-template]")) {
if (!ev.target.dataset.tooltipTouchTapToShow) {
touchPressed = false;
}
}
});
document.body.addEventListener("touchcancel", () => {
touchPressed = false;
document.body.addEventListener("touchcancel", (ev) => {
if (ev.target.matches("[data-tooltip], [data-tooltip-template]")) {
if (!ev.target.dataset.tooltipTouchTapToShow) {
touchPressed = false;
}
}
});
return;
@@ -55,6 +55,6 @@ export class FormLabel extends Component {
}
FormLabel.template = xml`
<label class="o_form_label" t-att-for="props.id" t-att-class="className" >
<t t-esc="props.string"/><sup class="btn-link p-2" t-if="hasTooltip" t-att="{'data-tooltip-template': 'web.FieldTooltip', 'data-tooltip-info': tooltipInfo}">?</sup>
<t t-esc="props.string"/><sup class="btn-link p-2" t-if="hasTooltip" t-att="{'data-tooltip-template': 'web.FieldTooltip', 'data-tooltip-info': tooltipInfo, 'data-tooltip-touch-tap-to-show': 'true'}">?</sup>
</label>
`;
@@ -381,7 +381,7 @@ QUnit.module("Tooltip service", (hooks) => {
await nextTick();
});
QUnit.test("touch rendering", async (assert) => {
QUnit.test("touch rendering - hold-to-show", async (assert) => {
class MyComponent extends Component {}
MyComponent.template = xml`<button data-tooltip="hello">Action</button>`;
let simulateTimeout;
@@ -414,4 +414,41 @@ QUnit.module("Tooltip service", (hooks) => {
await nextTick();
assert.containsNone(target, ".o_popover_container .o_popover");
});
QUnit.test("touch rendering - tap-to-show", async (assert) => {
class MyComponent extends Component {}
MyComponent.template = xml`<button data-tooltip="hello" data-tooltip-touch-tap-to-show="true">Action</button>`;
let simulateTimeout;
const mockSetTimeout = (fn) => {
simulateTimeout = fn;
};
let simulateInterval;
const mockSetInterval = (fn) => {
simulateInterval = fn;
};
const mockOnTouchStart = () => {};
await makeParent(MyComponent, { mockSetTimeout, mockSetInterval, mockOnTouchStart });
assert.containsNone(target, ".o_popover_container .o_popover");
await triggerEvent(target, "button[data-tooltip]", "touchstart");
await nextTick();
assert.containsNone(target, ".o_popover_container .o_popover");
simulateTimeout();
await nextTick();
assert.containsOnce(target, ".o_popover_container .o_popover");
assert.strictEqual(
target.querySelector(".o_popover_container .o_popover").innerText,
"hello"
);
await triggerEvent(target, "button[data-tooltip]", "touchend");
assert.containsOnce(target, ".o_popover_container .o_popover");
simulateInterval();
await nextTick();
assert.containsOnce(target, ".o_popover_container .o_popover");
await triggerEvent(target, "button[data-tooltip]", "touchstart");
assert.containsNone(target, ".o_popover_container .o_popover");
});
});