From 3f826e4eafa2d120e8e770e855c42430d51f0f70 Mon Sep 17 00:00:00 2001 From: "Michael Mattiello (mcm)" Date: Mon, 19 Oct 2020 10:58:34 +0000 Subject: [PATCH] [FIX] web: keep new widget el in ComponentAdapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this commit, when sub widget replaced its el, the ComponentAdapter parent kept the previous el. This caused some issues as in the ActivityRecordAdapter which lost its event listeners when re-rendered. This commit forces the ComponentAdapter to keep the new sub widget el after being patched. task id: 2360021 closes odoo/odoo#60521 X-original-commit: 393b5fd4cfba254ad054a526996e82922f9057e2 Signed-off-by: Aaron Bohy (aab) Signed-off-by: Michaƫl Mattiello --- addons/web/static/src/js/owl_compatibility.js | 6 +- .../static/tests/owl_compatibility_tests.js | 144 ++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/owl_compatibility.js b/addons/web/static/src/js/owl_compatibility.js index b5ff2a026d1..a33f5ebed58 100644 --- a/addons/web/static/src/js/owl_compatibility.js +++ b/addons/web/static/src/js/owl_compatibility.js @@ -142,7 +142,11 @@ odoo.define('web.OwlCompatibility', function () { } vnode.elm = this.widget.el; } - return super.__patch(...arguments); + const result = super.__patch(...arguments); + if (this.widget && this.el !== this.widget.el) { + this.__owl__.vnode.elm = this.widget.el; + } + return result; } /** diff --git a/addons/web/static/tests/owl_compatibility_tests.js b/addons/web/static/tests/owl_compatibility_tests.js index 1cc463d0eeb..a4f453867cf 100644 --- a/addons/web/static/tests/owl_compatibility_tests.js +++ b/addons/web/static/tests/owl_compatibility_tests.js @@ -680,6 +680,150 @@ odoo.define('web.OwlCompatibilityTests', function (require) { parent.destroy(); }); + QUnit.test("adapter keeps same el as sub widget (modify)", async function (assert) { + assert.expect(7); + + let myWidget; + const MyWidget = Widget.extend({ + events: { + click: "_onClick", + }, + init: function (parent, name) { + myWidget = this; + this._super.apply(this, arguments); + this.name = name; + }, + start: function () { + this.render(); + }, + render: function () { + this.$el.text("Click me!"); + }, + update: function (name) { + this.name = name; + }, + _onClick: function () { + assert.step(this.name); + }, + }); + class MyWidgetAdapter extends ComponentAdapter { + updateWidget(nextProps) { + return this.widget.update(nextProps.name); + } + renderWidget() { + this.widget.render(); + } + } + class Parent extends Component { + constructor() { + super(...arguments); + this.MyWidget = MyWidget; + this.state = useState({ + name: "GED", + }); + } + } + Parent.template = xml` + + `; + Parent.components = { MyWidgetAdapter }; + + const target = testUtils.prepareTarget(); + const parent = new Parent(); + await parent.mount(target); + + assert.strictEqual(parent.el, myWidget.el); + await testUtils.dom.click(parent.el); + + parent.state.name = "AAB"; + await nextTick(); + + assert.strictEqual(parent.el, myWidget.el); + await testUtils.dom.click(parent.el); + + parent.state.name = "MCM"; + await nextTick(); + + assert.strictEqual(parent.el, myWidget.el); + await testUtils.dom.click(parent.el); + + assert.verifySteps(["GED", "AAB", "MCM"]); + + parent.destroy(); + }); + + QUnit.test("adapter keeps same el as sub widget (replace)", async function (assert) { + assert.expect(7); + + let myWidget; + const MyWidget = Widget.extend({ + events: { + click: "_onClick", + }, + init: function (parent, name) { + myWidget = this; + this._super.apply(this, arguments); + this.name = name; + }, + start: function () { + this.render(); + }, + render: function () { + this._replaceElement("
Click me!
"); + }, + update: function (name) { + this.name = name; + }, + _onClick: function () { + assert.step(this.name); + }, + }); + class MyWidgetAdapter extends ComponentAdapter { + updateWidget(nextProps) { + return this.widget.update(nextProps.name); + } + renderWidget() { + this.widget.render(); + } + } + class Parent extends Component { + constructor() { + super(...arguments); + this.MyWidget = MyWidget; + this.state = useState({ + name: "GED", + }); + } + } + Parent.template = xml` + + `; + Parent.components = { MyWidgetAdapter }; + + const target = testUtils.prepareTarget(); + const parent = new Parent(); + await parent.mount(target); + + assert.strictEqual(parent.el, myWidget.el); + await testUtils.dom.click(parent.el); + + parent.state.name = "AAB"; + await nextTick(); + + assert.strictEqual(parent.el, myWidget.el); + await testUtils.dom.click(parent.el); + + parent.state.name = "MCM"; + await nextTick(); + + assert.strictEqual(parent.el, myWidget.el); + await testUtils.dom.click(parent.el); + + assert.verifySteps(["GED", "AAB", "MCM"]); + + parent.destroy(); + }); + QUnit.module('WidgetAdapterMixin and ComponentWrapper'); QUnit.test("widget with sub component", async function (assert) {