import svelte from "svelte-inline-compile"; import { render } from "@testing-library/svelte"; import Portal from "./Portal.svelte"; import PortalGroup from "./PortalGroup.svelte"; import { click } from "$lib/test-utils/interactions"; import { tick } from "svelte"; function getPortalRoot() { return document.getElementById('headlessui-portal-root')! } beforeEach(() => { document.body.innerHTML = '' }) it('should be possible to use a Portal', () => { expect(getPortalRoot()).toBe(null) render(svelte`

Contents...

`) let parent = document.getElementById('parent') let content = document.getElementById('content') expect(getPortalRoot()).not.toBe(null) // Ensure the content is not part of the parent expect(parent).not.toContainElement(content) // Ensure the content does exist expect(content).not.toBe(null) expect(content).toHaveTextContent('Contents...') }) it('should be possible to use multiple Portal elements', () => { expect(getPortalRoot()).toBe(null) render(svelte`

Contents 1 ...


Contents 2 ...

`) let parent = document.getElementById('parent') let content1 = document.getElementById('content1') let content2 = document.getElementById('content2') expect(getPortalRoot()).not.toBe(null) // Ensure the content1 is not part of the parent expect(parent).not.toContainElement(content1) // Ensure the content2 is not part of the parent expect(parent).not.toContainElement(content2) // Ensure the content does exist expect(content1).not.toBe(null) expect(content1).toHaveTextContent('Contents 1 ...') // Ensure the content does exist expect(content2).not.toBe(null) expect(content2).toHaveTextContent('Contents 2 ...') }) it('should cleanup the Portal root when the last Portal is unmounted', async () => { expect(getPortalRoot()).toBe(null) render(svelte`
{#if renderA}

Contents 1 ...

{/if} {#if renderB}

Contents 2 ...

{/if}
`) let a = document.getElementById('a') let b = document.getElementById('b') expect(getPortalRoot()).toBe(null) // Let's render the first Portal await click(a) expect(getPortalRoot()).not.toBe(null) expect(getPortalRoot().childNodes).toHaveLength(1) // Let's render the second Portal await click(b) expect(getPortalRoot()).not.toBe(null) expect(getPortalRoot().childNodes).toHaveLength(2) // Let's remove the first portal await click(a) expect(getPortalRoot()).not.toBe(null) expect(getPortalRoot().childNodes).toHaveLength(1) // Let's remove the second Portal await click(b) expect(getPortalRoot()).toBe(null) // Let's render the first Portal again await click(a) expect(getPortalRoot()).not.toBe(null) expect(getPortalRoot().childNodes).toHaveLength(1) }) it('should be possible to render multiple portals at the same time', async () => { expect(getPortalRoot()).toBe(null) render(svelte`
{#if renderA}

Contents 1 ...

{/if} {#if renderB}

Contents 2 ...

{/if} {#if renderC}

Contents 3 ...

{/if}
`) expect(getPortalRoot()).not.toBe(null) expect(getPortalRoot().childNodes).toHaveLength(3) // Remove Portal 1 await click(document.getElementById('a')) expect(getPortalRoot().childNodes).toHaveLength(2) // Remove Portal 2 await click(document.getElementById('b')) expect(getPortalRoot().childNodes).toHaveLength(1) // Re-add Portal 1 await click(document.getElementById('a')) expect(getPortalRoot().childNodes).toHaveLength(2) // Remove Portal 3 await click(document.getElementById('c')) expect(getPortalRoot().childNodes).toHaveLength(1) // Remove Portal 1 await click(document.getElementById('a')) expect(getPortalRoot()).toBe(null) // Render A and B at the same time! await click(document.getElementById('double')) expect(getPortalRoot().childNodes).toHaveLength(2) }) it('should be possible to tamper with the modal root and restore correctly', async () => { expect(getPortalRoot()).toBe(null) render(svelte`
{#if renderA}

Contents 1 ...

{/if} {#if renderB}

Contents 2 ...

{/if}
`) expect(getPortalRoot()).not.toBe(null) // Tamper tamper document.body.removeChild(document.getElementById('headlessui-portal-root')!) // Hide Portal 1 and 2 await click(document.getElementById('a')) await click(document.getElementById('b')) expect(getPortalRoot()).toBe(null) // Re-show Portal 1 and 2 await click(document.getElementById('a')) await click(document.getElementById('b')) expect(getPortalRoot()).not.toBe(null) expect(getPortalRoot().childNodes).toHaveLength(2) }) it('should be possible to force the Portal into a specific element using PortalGroup', async () => { render(svelte`
B
Next to A
I am in the portal root
`) // The random whitespace in here is a little annoying but whatever expect(document.body.innerHTML).toMatchInlineSnapshot( `"
B
I am in the portal root
"` ) }) it('should cleanup the Portal properly when Svelte would not detach it', async () => { expect(getPortalRoot()).toBe(null) render(svelte`
{#if render}

Contents 1 ...

{/if}
`) let a = document.getElementById('a') expect(getPortalRoot()).toBe(null) // Let's render the first Portal await click(a) expect(getPortalRoot()).not.toBe(null) expect(getPortalRoot().childNodes).toHaveLength(1) // Let's remove the first portal await click(a) expect(getPortalRoot()).toBe(null) // Let's render the first Portal again await click(a) expect(getPortalRoot()).not.toBe(null) expect(getPortalRoot().childNodes).toHaveLength(1) })