[FIX] portal: manually unmounting works

Before this commit, when manually unmounting the portal
or one of its parents, the teleported elements were not cleaned
and stayed teleported forever

After this commit, the teleported content is brought back and
unmounted much like any component

closes #531
This commit is contained in:
Lucas Perais (lpe)
2019-12-03 15:10:23 +01:00
committed by Géry Debongnie
parent f12d3373c9
commit ecfccf2448
2 changed files with 75 additions and 1 deletions
+13 -1
View File
@@ -59,6 +59,16 @@ export class Portal extends Component<any, any> {
}
});
}
/**
* 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<any, any> {
}
}
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
+62
View File
@@ -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<any, any> {
static components = { Portal };
static template = xml`
<div>
<Portal target="'#outside'">
<span>gloria</span>
</Portal>
</div>`;
}
const parent = new Parent();
await parent.mount(fixture);
expect(outside.innerHTML).toBe('<span>gloria</span>');
expect(parent.el!.innerHTML).toBe('<portal></portal>');
parent.unmount();
expect(outside.innerHTML).toBe('');
expect(parent.el!.innerHTML).toBe('<portal><span>gloria</span></portal>');
await parent.mount(fixture);
expect(outside.innerHTML).toBe('<span>gloria</span>');
expect(parent.el!.innerHTML).toBe('<portal></portal>');
});
test("portal manual unmount with subcomponent", async () => {
expect.assertions(9);
class Child extends Component<any, any> {
static template = xml`<span>gloria</span>`;
mounted() {
expect(outside.contains(this.el)).toBeTruthy();
}
willUnmount() {
expect(outside.contains(this.el)).toBeTruthy();
}
}
class Parent extends Component<any, any> {
static components = { Portal , Child };
static template = xml`
<div>
<Portal target="'#outside'">
<Child />
</Portal>
</div>`;
}
const parent = new Parent();
await parent.mount(fixture);
expect(outside.innerHTML).toBe('<span>gloria</span>');
expect(parent.el!.innerHTML).toBe('<portal></portal>');
parent.unmount();
expect(outside.innerHTML).toBe('');
expect(parent.el!.innerHTML).toBe('<portal><span>gloria</span></portal>');
await parent.mount(fixture);
expect(outside.innerHTML).toBe('<span>gloria</span>');
expect(parent.el!.innerHTML).toBe('<portal></portal>');
});
});
describe("Portal: Events handling", () => {