diff --git a/addons/web/static/src/core/effects/effect_service.js b/addons/web/static/src/core/effects/effect_service.js index 5f2401b7884..e789e77db1c 100644 --- a/addons/web/static/src/core/effects/effect_service.js +++ b/addons/web/static/src/core/effects/effect_service.js @@ -19,21 +19,21 @@ const effectRegistry = registry.category("effects"); * * @param {Object} env * @param {Object} [params={}] - * @param {string} [params.message="Well Done"] + * @param {string} [params.message="Well Done!"] * The message in the notice the rainbowman holds or the content of the notification if effects are disabled * Can be a simple a string * Can be a string representation of html (prefer component if you want interactions in the DOM) - * @param {boolean} [params.img_url="/web/static/img/smile.svg"] + * @param {string} [params.img_url="/web/static/img/smile.svg"] * The url of the image to display inside the rainbow - * @param {boolean} [params.messageIsHtml] + * @param {boolean} [params.messageIsHtml=false] * Set to true if the message encodes html, s.t. it will be correctly inserted into the DOM. * @param {"slow"|"medium"|"fast"|"no"} [params.fadeout="medium"] * Delay for rainbowman to disappear * 'fast' will make rainbowman dissapear quickly * 'medium' and 'slow' will wait little longer before disappearing (can be used when options.message is longer) * 'no' will keep rainbowman on screen until user clicks anywhere outside rainbowman - * @param {owl.Component} [params.Component=RainbowMan] - * Component class to instantiate (if effects aren't disabled) + * @param {owl.Component} [params.Component] + * Custom Component class to instantiate inside the Rainbow Man * @param {Object} [params.props] * If params.Component is given, its props can be passed with this argument */ @@ -50,19 +50,19 @@ function rainbowMan(env, params = {}) { ); message = message.outerHTML; } else if (!message) { - message = env._t("well Done!"); + message = env._t("Well Done!"); } if (env.services.user.showEffect) { - return { - Component: params.Component || RainbowMan, - props: { - imgUrl: params.img_url || "/web/static/img/smile.svg", - fadeout: params.fadeout, - message, - messageIsHtml: params.messageIsHtml || false, - ...params.props, - }, + /** @type {import("./rainbow_man").RainbowManProps} */ + const props = { + imgUrl: params.img_url || "/web/static/img/smile.svg", + fadeout: params.fadeout || "medium", + message, + messageIsHtml: params.messageIsHtml || false, + Component: params.Component, + props: params.props, }; + return { Component: RainbowMan, props }; } env.services.notification.add(message); } @@ -82,10 +82,10 @@ export const effectService = { let effectId = 0; /** - * @param {Object} params various params depending on the type of effect + * @param {Object} [params] various params depending on the type of effect * @param {string} [params.type="rainbow_man"] the effect to display */ - function add(params) { + function add(params = {}) { const type = params.type || "rainbow_man"; const effect = effectRegistry.get(type); const { Component, props } = effect(env, params) || {}; diff --git a/addons/web/static/src/core/effects/rainbow_man.js b/addons/web/static/src/core/effects/rainbow_man.js index a8aae22ac09..f3243edb6da 100644 --- a/addons/web/static/src/core/effects/rainbow_man.js +++ b/addons/web/static/src/core/effects/rainbow_man.js @@ -5,6 +5,26 @@ import { useEffect } from "@web/core/utils/hooks"; const { Component, hooks } = owl; +/** + * @typedef Common + * @property {string} [fadeout='medium'] Delay for rainbowman to disappear. + * - 'fast' will make rainbowman dissapear quickly, + * - 'medium' and 'slow' will wait little longer before disappearing + * (can be used when props.message is longer), + * - 'no' will keep rainbowman on screen until user clicks anywhere outside rainbowman + * @property {string} [imgUrl] URL of the image to be displayed + * + * @typedef Simple + * @property {string} message Message to be displayed on rainbowman card + * @property {boolean} [messageIsHtml=false] + * + * @typedef Custom + * @property {Component} Component + * @property {any} [props] + * + * @typedef {Common & (Simple | Custom)} RainbowManProps + */ + /** * The RainbowMan widget is the widget displayed by default as a 'fun/rewarding' * effect in some cases. For example, when the user marked a large deal as won, @@ -16,20 +36,10 @@ const { Component, hooks } = owl; * service (by triggering the 'show_effect' event) */ export class RainbowMan extends Component { - /** - * @override - * @constructor - * @param {Object} [options] - * @param {string} [options.message] Message to be displayed on rainbowman card - * @param {string} [options.fadeout='medium'] Delay for rainbowman to disappear. 'fast' will make rainbowman dissapear quickly, 'medium' and 'slow' will wait little longer before disappearing (can be used when options.message is longer), 'no' will keep rainbowman on screen until user clicks anywhere outside rainbowman - * @param {string} [options.img_url] URL of the image to be displayed - */ setup() { hooks.useExternalListener(document.body, "click", this.closeRainbowMan); - const fadeout = "fadeout" in this.props ? this.props.fadeout : "medium"; - const delay = fadeout ? RainbowMan.rainbowFadeouts[fadeout] : false; - this.delay = typeof delay === "number" ? delay : false; - if (this.delay !== false) { + this.delay = RainbowMan.rainbowFadeouts[this.props.fadeout]; + if (this.delay) { useEffect( () => { const timeout = browser.setTimeout(() => { @@ -43,7 +53,7 @@ export class RainbowMan extends Component { } onAnimationEnd(ev) { - if (this.delay !== false && ev.animationName === "reward-fading-reverse") { + if (this.delay && ev.animationName === "reward-fading-reverse") { ev.stopPropagation(); this.closeRainbowMan(); } diff --git a/addons/web/static/tests/core/effects/effect_service_tests.js b/addons/web/static/tests/core/effects/effect_service_tests.js index ae673191e85..c8a473e037b 100644 --- a/addons/web/static/tests/core/effects/effect_service_tests.js +++ b/addons/web/static/tests/core/effects/effect_service_tests.js @@ -3,12 +3,11 @@ import { notificationService } from "@web/core/notifications/notification_service"; import { registry } from "@web/core/registry"; import { effectService } from "@web/core/effects/effect_service"; -import { RainbowMan } from "@web/core/effects/rainbow_man"; import { userService } from "@web/core/user_service"; import { session } from "@web/session"; import { makeTestEnv } from "../../helpers/mock_env"; import { makeFakeLocalizationService } from "../../helpers/mock_services"; -import { click, getFixture, nextTick, patchWithCleanup } from "../../helpers/utils"; +import { click, getFixture, mockTimeout, nextTick, patchWithCleanup } from "../../helpers/utils"; import { registerCleanup } from "../../helpers/cleanup"; const { Component, mount, tags } = owl; @@ -38,13 +37,14 @@ async function makeParent() { QUnit.module("Effect Service", (hooks) => { let effectParams; + let execRegisteredTimeouts; hooks.beforeEach(() => { effectParams = { message: "