diff --git a/addons/web/static/src/core/tooltip/tooltip_hook.js b/addons/web/static/src/core/tooltip/tooltip_hook.js
index 96c6d0065cb..1ac73eff3d8 100644
--- a/addons/web/static/src/core/tooltip/tooltip_hook.js
+++ b/addons/web/static/src/core/tooltip/tooltip_hook.js
@@ -4,7 +4,6 @@ import { browser } from "@web/core/browser/browser";
import { useListener } from "@web/core/utils/hooks";
import { usePopover } from "@web/core/popover/popover_hook";
import { Tooltip } from "./tooltip";
-import { registry } from "@web/core/registry";
/**
* The useTooltip hook allows to display custom tooltips on every elements with
@@ -18,6 +17,19 @@ import { registry } from "@web/core/registry";
* The ideal position of the tooltip can be specified thanks to the attribute
* "data-tooltip-position":
*
+ *
+ * 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:
+ *
+ *
+ *
info.x
+ *
info.y
+ *
+ *
+ * This template can then be used in a tooltip as follows:
+ *
+ * with "info" being a stringified object with two keys "x" and "y".
*/
const OPEN_DELAY = 400;
const CLOSE_DELAY = 200;
@@ -46,13 +58,12 @@ export function useTooltip() {
function onMouseEnter(ev) {
const dataset = ev.target.dataset;
const tooltip = dataset.tooltip;
-
- let Component, props;
- if ("tooltipComponent" in dataset) {
- ({ Component, props } = JSON.parse(dataset["tooltipComponent"]));
- Component = registry.category("tooltips").get(Component, null);
+ let template, info;
+ if ("tooltipTemplate" in dataset) {
+ template = dataset.tooltipTemplate;
+ info = dataset.tooltipInfo ? JSON.parse(dataset.tooltipInfo) : null;
}
- if (tooltip || Component) {
+ if (tooltip || template) {
cleanup();
target = ev.target;
openTooltipTimeout = browser.setTimeout(() => {
@@ -60,7 +71,7 @@ export function useTooltip() {
closeTooltip = popover.add(
target,
Tooltip,
- { tooltip, Component, props },
+ { tooltip, template, info },
{ position }
);
}, OPEN_DELAY);
@@ -97,6 +108,7 @@ export function useTooltip() {
}, CLOSE_DELAY);
// Listen (using event delegation) to "mouseenter" events on all nodes with
- // the "data-tooltip" attribute, to open the tooltip.
- useListener("mouseenter", "[data-tooltip]", onMouseEnter, { capture: true });
+ // the "data-tooltip" or "data-tooltip-template" attribute, to open the tooltip.
+ const selector = "[data-tooltip], [data-tooltip-template]";
+ useListener("mouseenter", selector, onMouseEnter, { capture: true });
}
diff --git a/addons/web/static/tests/core/tooltip/tooltip_hook_tests.js b/addons/web/static/tests/core/tooltip/tooltip_hook_tests.js
index 252fae3e06d..0e099f7d3f8 100644
--- a/addons/web/static/tests/core/tooltip/tooltip_hook_tests.js
+++ b/addons/web/static/tests/core/tooltip/tooltip_hook_tests.js
@@ -203,4 +203,53 @@ QUnit.module("Tooltip hook", () => {
assert.strictEqual(parent.el.querySelector(".o_popover").innerText, "left");
assert.hasClass(parent.el.querySelector(".o_popover"), "o-popper-position--lm");
});
+
+ QUnit.test("tooltip with a template, no info", async (assert) => {
+ class MyComponent extends Component {}
+ MyComponent.template = xml`
+
+ `;
+ const parent = await makeParent(MyComponent);
+ parent.env.qweb.addTemplate("my_tooltip_template", "tooltip");
+
+ assert.containsNone(parent, ".o_popover_container .o-tooltip");
+ parent.el.querySelector("button").dispatchEvent(new Event("mouseenter"));
+ await nextTick();
+ assert.containsOnce(parent, ".o_popover_container .o-tooltip");
+ assert.strictEqual(parent.el.querySelector(".o-tooltip").innerHTML, "tooltip");
+ });
+
+ QUnit.test("tooltip with a template and info", async (assert) => {
+ class MyComponent extends Component {
+ get info() {
+ return JSON.stringify({ x: 3, y: "abc" });
+ }
+ }
+ MyComponent.template = xml`
+
+ `;
+ const parent = await makeParent(MyComponent);
+ parent.env.qweb.addTemplate(
+ "my_tooltip_template",
+ `
+