diff --git a/web/static/src/ts/core/Widget.ts b/web/static/src/ts/core/Widget.ts index 081f701f..9608f95f 100644 --- a/web/static/src/ts/core/Widget.ts +++ b/web/static/src/ts/core/Widget.ts @@ -56,7 +56,7 @@ export class Widget { if (document.body.contains(target)) { this.visitSubTree(w => { - if (!w.isMounted) { + if (!w.isMounted && this.el!.contains(w.el)) { w.isMounted = true; w.mounted(); } diff --git a/web/static/tests/widget.test.ts b/web/static/tests/widget.test.ts index 0e274a66..7a0fdfaa 100644 --- a/web/static/tests/widget.test.ts +++ b/web/static/tests/widget.test.ts @@ -157,7 +157,7 @@ describe("lifecycle hooks", () => { let childMounted = false; class ParentWidget extends Widget { name = "a"; - template = `
Hello
`; + template = `
`; widgets = { child: ChildWidget }; mounted() { expect(childMounted).toBe(false); @@ -216,6 +216,35 @@ describe("lifecycle hooks", () => { expect(hookCounter).toBe(2); target.remove(); }); + + test("mounted hook is correctly called on subwidgets created in mounted hook", async done => { + // the issue here is that the parent widget creates in the + // mounted hook a new widget, which means that it modifies + // in place its list of children. But this list of children is currently + // being visited, so the mount action of the parent could cause a mount + // action of the new child widget, even though it is not ready yet. + expect.assertions(1); + + const target = document.createElement("div"); + document.body.appendChild(target); + class ParentWidget extends Widget { + name = "a"; + mounted() { + const child = new ChildWidget(this); + child.mount(this.el!); + } + } + class ChildWidget extends Widget { + mounted() { + expect(this.el).toBeTruthy(); + target.remove(); + done(); + } + } + + const widget = makeWidget(ParentWidget); + await widget.mount(target); + }); }); describe("destroy method", () => {