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`
BNext to AI 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)
})