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:
@@ -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>
|
||||||
|
|||||||
23
src/lib/components/portal/portal.test.ts
vendored
23
src/lib/components/portal/portal.test.ts
vendored
@@ -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>"`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user