[FIX] web: keep new widget el in ComponentAdapter

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) <aab@odoo.com>
Signed-off-by: Michaël Mattiello <mcm-odoo@users.noreply.github.com>
This commit is contained in:
Michael Mattiello (mcm)
2020-10-22 11:06:16 +00:00
parent b2afa61ec7
commit 3f826e4eaf
2 changed files with 149 additions and 1 deletions
@@ -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;
}
/**
@@ -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`
<MyWidgetAdapter Component="MyWidget" name="state.name"/>
`;
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("<div>Click me!</div>");
},
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`
<MyWidgetAdapter Component="MyWidget" name="state.name"/>
`;
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) {