diff --git a/src/misc/portal.ts b/src/misc/portal.ts index 41178a5f..00cd1051 100644 --- a/src/misc/portal.ts +++ b/src/misc/portal.ts @@ -59,6 +59,16 @@ export class Portal extends Component { } }); } + /** + * Override to revert back to a classic Component's structure + * + * @override + */ + __callWillUnmount() { + super.__callWillUnmount(); + this.el!.appendChild(this.portal!.elm!); + this.doTargetLookUp = true; + } /** * At each DOM change, we must ensure that the portal contains exactly one * child @@ -126,7 +136,9 @@ export class Portal extends Component { } } this.__checkVNodeStructure(vnode); - const shouldDeploy = !this.portal && !this.doTargetLookUp; + const shouldDeploy = + (!this.portal || this.el!.contains(this.portal.elm!)) && + !this.doTargetLookUp; if (!this.doTargetLookUp && !shouldDeploy) { // Only on pure patching, provided the diff --git a/tests/misc/portal.test.ts b/tests/misc/portal.test.ts index abc14a4b..62da0031 100644 --- a/tests/misc/portal.test.ts +++ b/tests/misc/portal.test.ts @@ -548,6 +548,68 @@ describe("Portal: Basic use and DOM placement", () => { expect(error).toBeDefined(); expect(error.message).toBe("Cannot read property 'crash' of undefined"); }); + + test("portal manual unmount", async () => { + class Parent extends Component { + static components = { Portal }; + static template = xml` +
+ + gloria + +
`; + } + + const parent = new Parent(); + await parent.mount(fixture); + + expect(outside.innerHTML).toBe('gloria'); + expect(parent.el!.innerHTML).toBe(''); + + parent.unmount(); + expect(outside.innerHTML).toBe(''); + expect(parent.el!.innerHTML).toBe('gloria'); + + await parent.mount(fixture); + expect(outside.innerHTML).toBe('gloria'); + expect(parent.el!.innerHTML).toBe(''); + }); + + test("portal manual unmount with subcomponent", async () => { + expect.assertions(9); + class Child extends Component { + static template = xml`gloria`; + mounted() { + expect(outside.contains(this.el)).toBeTruthy(); + } + willUnmount() { + expect(outside.contains(this.el)).toBeTruthy(); + } + } + class Parent extends Component { + static components = { Portal , Child }; + static template = xml` +
+ + + +
`; + } + + const parent = new Parent(); + await parent.mount(fixture); + + expect(outside.innerHTML).toBe('gloria'); + expect(parent.el!.innerHTML).toBe(''); + + parent.unmount(); + expect(outside.innerHTML).toBe(''); + expect(parent.el!.innerHTML).toBe('gloria'); + + await parent.mount(fixture); + expect(outside.innerHTML).toBe('gloria'); + expect(parent.el!.innerHTML).toBe(''); + }); }); describe("Portal: Events handling", () => {