Fix implementation of #78

Brain fart when writing and testing the first attempt.
I can only actually reproduce the underlying issue in the REPL so I haven't confirmed that this fixes it.
This commit is contained in:
Ryan Gossiaux
2022-03-06 23:57:32 -08:00
parent b2a288c874
commit 39a8b57c36
3 changed files with 10 additions and 34 deletions

View File

@@ -2,6 +2,7 @@
import { usePortalGroupContext } from "./PortalGroup.svelte"; import { usePortalGroupContext } from "./PortalGroup.svelte";
import { usePortalRoot } from "$lib/internal/ForcePortalRootContext.svelte"; import { usePortalRoot } from "$lib/internal/ForcePortalRootContext.svelte";
import { portal } from "$lib/hooks/use-portal"; import { portal } from "$lib/hooks/use-portal";
import { tick } from "svelte";
let forceInRoot = usePortalRoot(); let forceInRoot = usePortalRoot();
let groupTarget = usePortalGroupContext(); let groupTarget = usePortalGroupContext();
$: target = (() => { $: target = (() => {
@@ -20,6 +21,15 @@
let root = document.createElement("div"); let root = document.createElement("div");
root.setAttribute("id", "headlessui-portal-root"); root.setAttribute("id", "headlessui-portal-root");
// During initial render, the "portal" might be constructed before
// the root component has even attached, causing the portal to not work.
// This is a workaround for this issue--it can't guarantee the portal is
// **always** last, but it should catch the normal cases.
tick().then(() => {
if (root !== document.body.lastChild) {
document.body.appendChild(root);
}
});
return document.body.appendChild(root); return document.body.appendChild(root);
})(); })();
</script> </script>

View File

@@ -334,26 +334,3 @@ it('should cleanup the Portal properly when Svelte would not detach it', async (
expect(getPortalRoot()).not.toBe(null) expect(getPortalRoot()).not.toBe(null)
expect(getPortalRoot().childNodes).toHaveLength(1) expect(getPortalRoot().childNodes).toHaveLength(1)
}) })
it('should move the Portal last during initial render', async () => {
expect(getPortalRoot()).toBe(null)
// We need to use a custom target because of the implementation of
// render() in the testing library
render(svelte`
<script>
let target = document.body.firstChild;
</script>
<PortalGroup {target}>
<Portal>Portal</Portal>
</PortalGroup>
<main>Main</main>
`)
await tick();
expect(document.body.innerHTML).toMatchInlineSnapshot(
`"<div> <main>Main</main><div>Portal</div></div>"`
)
})

View File

@@ -1,20 +1,9 @@
import { tick } from "svelte";
export function portal( export function portal(
element: HTMLElement, element: HTMLElement,
target: HTMLElement | null | undefined target: HTMLElement | null | undefined
) { ) {
if (target) { if (target) {
target.append(element); target.append(element);
// During initial render, the "portal" might be constructed before
// the root component has even attached, causing the portal to not work.
// This is a workaround for this issue--it can't guarantee the portal is
// **always** last, but it should catch the normal cases.
tick().then(() => {
if (target && element !== target.lastChild) {
target.appendChild(element);
}
});
} }
return { return {
update(newTarget: HTMLElement) { update(newTarget: HTMLElement) {