-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Don’t overwrite classes during SSR when rendering fragments (#2173)
* Refactor SSR test helpers * Add SSR tests for transition * Don’t overwrite classes during SSR when rendering fragments * Update changelog
- Loading branch information
1 parent
08c0837
commit aac78d5
Showing
10 changed files
with
205 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
packages/@headlessui-react/src/components/transitions/transition.ssr.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import React, { Fragment } from 'react' | ||
import { Transition } from './transition' | ||
import { renderSSR } from '../../test-utils/ssr' | ||
|
||
beforeAll(() => { | ||
jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any) | ||
jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any) | ||
}) | ||
|
||
describe('Rendering', () => { | ||
describe('SSR', () => { | ||
it('should not overwrite className of children when as=Fragment', async () => { | ||
await renderSSR( | ||
<Transition | ||
as={Fragment} | ||
show={true} | ||
appear={true} | ||
enter="enter" | ||
enterFrom="enter-from" | ||
enterTo="enter-to" | ||
> | ||
<div className="inner"></div> | ||
</Transition> | ||
) | ||
|
||
let div = document.querySelector('.inner') | ||
|
||
expect(div).not.toBeNull() | ||
expect(div?.className).toBe('inner enter enter-from') | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { RenderResult } from '@testing-library/react' | ||
import { render, RenderOptions } from '@testing-library/react' | ||
import React, { ReactElement } from 'react' | ||
import { renderToString } from 'react-dom/server' | ||
import { env } from '../utils/env' | ||
|
||
type ServerRenderOptions = Omit<RenderOptions, 'queries'> & { | ||
strict?: boolean | ||
} | ||
|
||
interface ServerRenderResult { | ||
type: 'ssr' | 'hydrate' | ||
contents: string | ||
result: RenderResult | ||
hydrate: () => Promise<ServerRenderResult> | ||
} | ||
|
||
export async function renderSSR( | ||
ui: ReactElement, | ||
options: ServerRenderOptions = {} | ||
): Promise<ServerRenderResult> { | ||
let container = document.createElement('div') | ||
document.body.appendChild(container) | ||
options = { ...options, container } | ||
|
||
if (options.strict) { | ||
options = { | ||
...options, | ||
wrapper({ children }) { | ||
return <React.StrictMode>{children}</React.StrictMode> | ||
}, | ||
} | ||
} | ||
|
||
env.set('server') | ||
let contents = renderToString(ui) | ||
let result = render(<div dangerouslySetInnerHTML={{ __html: contents }} />, options) | ||
|
||
async function hydrate(): Promise<ServerRenderResult> { | ||
// This hack-ish way of unmounting the server rendered content is necessary | ||
// otherwise we won't actually end up testing the hydration code path properly. | ||
// Probably because React hangs on to internal references on the DOM nodes | ||
result.unmount() | ||
container.innerHTML = contents | ||
|
||
env.set('client') | ||
let newResult = render(ui, { | ||
...options, | ||
hydrate: true, | ||
}) | ||
|
||
return { | ||
type: 'hydrate', | ||
contents: container.innerHTML, | ||
result: newResult, | ||
hydrate, | ||
} | ||
} | ||
|
||
return { | ||
type: 'ssr', | ||
contents, | ||
result, | ||
hydrate, | ||
} | ||
} | ||
|
||
export async function renderHydrate(el: ReactElement, options: ServerRenderOptions = {}) { | ||
return renderSSR(el, options).then((r) => r.hydrate()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
packages/@headlessui-vue/src/components/transitions/transition.ssr.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import * as Transition from './transition' | ||
import { renderSSR } from '../../test-utils/ssr' | ||
import { defineComponent } from 'vue' | ||
import { html } from '../../test-utils/html' | ||
|
||
beforeAll(() => { | ||
jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any) | ||
jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any) | ||
}) | ||
|
||
describe('Rendering', () => { | ||
describe('SSR', () => { | ||
it('should not overwrite className of children when as=Fragment', async () => { | ||
await renderSSR( | ||
defineComponent({ | ||
components: Transition, | ||
template: html` | ||
<TransitionRoot | ||
as="template" | ||
:show="true" | ||
:appear="true" | ||
enter="enter" | ||
enterFrom="enter-from" | ||
enterTo="enter-to" | ||
> | ||
<div class="inner"></div> | ||
</TransitionRoot> | ||
`, | ||
}) | ||
) | ||
|
||
let div = document.querySelector('.inner') | ||
|
||
expect(div).not.toBeNull() | ||
expect(div?.className).toBe('inner enter enter-from') | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.