Don’t throw when SSR rendering internal portals in Vue (#1459)

* Don’t throw when SSR rendering portals

* Update changelog
This commit is contained in:
Jordan Pittman
2022-05-16 12:10:39 -04:00
committed by GitHub
parent 6fd5f58c2c
commit 8ca73eb67a
3 changed files with 81 additions and 1 deletions
+1
View File
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Simplify `Popover` Tab logic by using sentinel nodes instead of keydown event interception ([#1440](https://github.com/tailwindlabs/headlessui/pull/1440))
- Ensure the `PopoverPanel` is clickable without closing the `Popover` ([#1443](https://github.com/tailwindlabs/headlessui/pull/1443))
- Improve "Scroll lock" scrollbar width for `Dialog` component ([#1457](https://github.com/tailwindlabs/headlessui/pull/1457))
- Dont throw when SSR rendering internal portals in Vue ([#1459](https://github.com/tailwindlabs/headlessui/pull/1459))
## [Unreleased - @headlessui/react]
@@ -1,6 +1,7 @@
import { defineComponent, ref, nextTick, ComponentOptionsWithoutProps } from 'vue'
import { h, defineComponent, ref, nextTick, ComponentOptionsWithoutProps, createSSRApp } from 'vue'
import { render } from '../../test-utils/vue-testing-library'
import { renderToString } from 'vue/server-renderer'
import { Portal, PortalGroup } from './portal'
import { click } from '../../test-utils/interactions'
import { html } from '../../test-utils/html'
@@ -38,6 +39,80 @@ function renderTemplate(input: string | ComponentOptionsWithoutProps) {
)
}
async function ssrRenderTemplate(input: string | ComponentOptionsWithoutProps) {
let defaultComponents = { Portal, PortalGroup }
if (typeof input === 'string') {
let app = createSSRApp({
render: () => h(defineComponent({ template: input, components: defaultComponents })),
})
return await renderToString(app)
}
let app = createSSRApp({
render: () =>
h(
defineComponent(
Object.assign({}, input, {
components: { ...defaultComponents, ...input.components },
}) as Parameters<typeof defineComponent>[0]
)
),
})
return await renderToString(app)
}
async function withoutBrowserGlobals<T>(fn: () => Promise<T>) {
let oldWindow = globalThis.window
let oldDocument = globalThis.document
Object.defineProperty(globalThis, '_document', {
value: undefined,
configurable: true,
})
Object.defineProperty(globalThis, '_globalProxy', {
value: undefined,
configurable: true,
})
try {
return await fn()
} finally {
Object.defineProperty(globalThis, '_globalProxy', {
value: oldWindow,
configurable: true,
})
Object.defineProperty(globalThis, '_document', {
value: oldDocument,
configurable: true,
})
}
}
it('SSR-rendering a Portal should not error', async () => {
expect(getPortalRoot()).toBe(null)
let result = await withoutBrowserGlobals(() =>
ssrRenderTemplate(
html`
<main id="parent">
<Portal>
<p id="content">Contents...</p>
</Portal>
</main>
`
)
)
expect(getPortalRoot()).toBe(null)
expect(result).toBe(html`<main id="parent"><!----></main>`)
})
it('should be possible to use a Portal', () => {
expect(getPortalRoot()).toBe(null)
@@ -23,6 +23,10 @@ import { getOwnerDocument } from '../../utils/owner'
function getPortalRoot(contextElement?: Element | null) {
let ownerDocument = getOwnerDocument(contextElement)
if (!ownerDocument) {
if (contextElement === null) {
return null
}
throw new Error(
`[Headless UI]: Cannot find ownerDocument for contextElement: ${contextElement}`
)