From ced5b4568d82bbc1da01f03e8165bb353aa80930 Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Tue, 7 Sep 2021 12:31:10 +0000 Subject: [PATCH] [FIX] web: View component support banner_route The View component supports banner_route, fetches the HTML and passes on the prop "bannerHTML" to the concrete component class. The feature has been a little forgotten in 0134495ba55bea16c2d11d5eb5d832edad575694. This commit just revives it. closes odoo/odoo#76370 X-original-commit: 99d1da58392a66dea1187ef4f00e41e924382d61 Related: odoo/enterprise#20779 Signed-off-by: Aaron Bohy (aab) --- .../src/views/helpers/standard_view_props.js | 1 + .../web/static/src/views/onboarding_banner.js | 50 +++ addons/web/static/src/views/view.js | 8 +- addons/web/static/tests/views/view_tests.js | 325 +++++++++++++++++- 4 files changed, 378 insertions(+), 6 deletions(-) create mode 100644 addons/web/static/src/views/onboarding_banner.js diff --git a/addons/web/static/src/views/helpers/standard_view_props.js b/addons/web/static/src/views/helpers/standard_view_props.js index f610ef51ad4..ae2ae2063ae 100644 --- a/addons/web/static/src/views/helpers/standard_view_props.js +++ b/addons/web/static/src/views/helpers/standard_view_props.js @@ -31,4 +31,5 @@ export const standardViewProps = { state: { type: Object, optional: 1 }, resId: { type: Number, optional: 1 }, resIds: { type: Array, optional: 1 }, + bannerRoute: { type: String, optional: 1 }, }; diff --git a/addons/web/static/src/views/onboarding_banner.js b/addons/web/static/src/views/onboarding_banner.js new file mode 100644 index 00000000000..301a94b6b38 --- /dev/null +++ b/addons/web/static/src/views/onboarding_banner.js @@ -0,0 +1,50 @@ +/**@odoo-module */ + +import { loadAssets } from "@web/core/assets"; +import { useService } from "@web/core/utils/hooks"; +import { useActionLinks } from "@web/views/helpers/view_hook"; + +export class OnboardingBanner extends owl.Component { + setup() { + this.rpc = useService("rpc"); + this.user = useService("user"); + const resModel = "searchModel" in this.env ? this.env.searchModel.resModel : undefined; + useActionLinks({ + resModel, + reload: async () => { + this.bannerHTML = await this.loadBanner(this.props.bannerRoute); + this.render(); + }, + }); + } + + async willStart() { + this.bannerHTML = await this.loadBanner(this.props.bannerRoute); + } + + async loadBanner(bannerRoute) { + const response = await this.rpc(bannerRoute, { context: this.user.context }); + if (!response.html) { + return; + } + + const banner = new DOMParser().parseFromString(response.html, "text/html"); + const assets = { + jsLibs: [], + cssLibs: [], + }; + banner + .querySelectorAll(`link[rel="stylesheet"] , script[type="text/javascript"]`) + .forEach((elem) => { + if (elem.tagName === "SCRIPT") { + assets.jsLibs.push(elem.src); + } else if (elem.tagName === "LINK") { + assets.cssLibs.push(elem.href); + } + elem.remove(); + }); + await loadAssets(assets); + return new XMLSerializer().serializeToString(banner); + } +} +OnboardingBanner.template = owl.tags.xml`
`; diff --git a/addons/web/static/src/views/view.js b/addons/web/static/src/views/view.js index 8b2137e5773..6bcaffcd5fb 100644 --- a/addons/web/static/src/views/view.js +++ b/addons/web/static/src/views/view.js @@ -201,16 +201,13 @@ export class View extends Component { rootAttrs[attrName] = rootNode.getAttribute(attrName); } - ////////////////////////////////////////////////////////////////// - /** @todo take care of banner_route rootAttribute*/ - ////////////////////////////////////////////////////////////////// - // determine ViewClass to instantiate (if not already done) - if (rootAttrs.js_class) { ViewClass = viewRegistry.get(rootAttrs.js_class); } + const bannerRoute = rootAttrs.banner_route; + // prepare the view props let viewProps = { info: { @@ -228,6 +225,7 @@ export class View extends Component { fields, resModel, useSampleModel: false, + bannerRoute, }; if ("useSampleModel" in this.props) { diff --git a/addons/web/static/tests/views/view_tests.js b/addons/web/static/tests/views/view_tests.js index aa1ba8e0ab2..911f7845625 100644 --- a/addons/web/static/tests/views/view_tests.js +++ b/addons/web/static/tests/views/view_tests.js @@ -6,8 +6,10 @@ import { ormService } from "@web/core/orm_service"; import { registerCleanup } from "@web/../tests/helpers/cleanup"; import { registry } from "@web/core/registry"; import { View } from "@web/views/view"; +import { OnboardingBanner } from "@web/views/onboarding_banner"; import { viewService } from "@web/views/view_service"; -import { click } from "../helpers/utils"; +import { click, makeDeferred } from "../helpers/utils"; +import { actionService } from "@web/webclient/actions/action_service"; const { Component, mount, hooks, tags } = owl; const { useState } = hooks; @@ -95,6 +97,7 @@ QUnit.module("Views", (hooks) => { } ToyView.template = xml`
`; ToyView.type = "toy"; + ToyView.components = { Banner: OnboardingBanner }; class ToyViewImp extends ToyView { setup() { @@ -811,6 +814,326 @@ QUnit.module("Views", (hooks) => { await click(toy.el.querySelector("a")); }); + QUnit.test("renders banner_route", async (assert) => { + assert.expect(3); + serverData.views["animal,1,toy"] = ` + + + `; + + const mockRPC = (route) => { + if (route === "/mybody/isacage") { + assert.step(route); + return { html: `
myBanner
` }; + } + }; + + const toy = await makeView({ + serverData, + mockRPC, + resModel: "animal", + type: "toy", + views: [[1, "toy"]], + }); + + assert.verifySteps(["/mybody/isacage"]); + assert.containsOnce(toy, ".setmybodyfree"); + }); + + QUnit.test("renders banner_route with js and css assets", async (assert) => { + assert.expect(7); + serverData.views["animal,1,toy"] = ` + + + `; + + const bannerArch = ` +
+ +