From 067d0c6d2e87092f6ecaa8c9fcf505e4576055cf Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 23 Apr 2024 12:21:18 +0200 Subject: [PATCH] fix: Don't raise TypeScript errors when hydating `document` (#1304) --- types/index.d.ts | 60 ++++++++++++++++++++++++++++++++++++++++++------ types/test.tsx | 11 +++++++++ 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index e7cf02bc..566e3d05 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,5 +1,5 @@ // TypeScript Version: 3.8 - +import * as ReactDOMClient from 'react-dom/client' import { queries, Queries, @@ -43,10 +43,10 @@ export type RenderResult< asFragment: () => DocumentFragment } & {[P in keyof Q]: BoundFunction} -export interface RenderOptions< - Q extends Queries = typeof queries, - Container extends Element | DocumentFragment = HTMLElement, - BaseElement extends Element | DocumentFragment = Container, +export interface BaseRenderOptions< + Q extends Queries, + Container extends RendererableContainer | HydrateableContainer, + BaseElement extends Element | DocumentFragment, > { /** * By default, React Testing Library will create a div and append that div to the document.body. Your React component will be rendered in the created div. If you provide your own HTMLElement container via this option, @@ -93,6 +93,44 @@ export interface RenderOptions< wrapper?: React.JSXElementConstructor<{children: React.ReactNode}> } +type RendererableContainer = ReactDOMClient.Container +type HydrateableContainer = Parameters[0] +export interface ClientRenderOptions< + Q extends Queries, + Container extends Element | DocumentFragment, + BaseElement extends Element | DocumentFragment = Container, +> extends BaseRenderOptions { + /** + * If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side + * rendering and use ReactDOM.hydrate to mount your components. + * + * @see https://testing-library.com/docs/react-testing-library/api/#hydrate) + */ + hydrate?: false | undefined +} + +export interface HydrateOptions< + Q extends Queries, + Container extends Element | DocumentFragment, + BaseElement extends Element | DocumentFragment = Container, +> extends BaseRenderOptions { + /** + * If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side + * rendering and use ReactDOM.hydrate to mount your components. + * + * @see https://testing-library.com/docs/react-testing-library/api/#hydrate) + */ + hydrate: true +} + +export type RenderOptions< + Q extends Queries = typeof queries, + Container extends RendererableContainer | HydrateableContainer = HTMLElement, + BaseElement extends Element | DocumentFragment = Container, +> = + | ClientRenderOptions + | HydrateOptions + type Omit = Pick> /** @@ -100,11 +138,19 @@ type Omit = Pick> */ export function render< Q extends Queries = typeof queries, - Container extends Element | DocumentFragment = HTMLElement, + Container extends RendererableContainer = HTMLElement, + BaseElement extends Element | DocumentFragment = Container, +>( + ui: React.ReactNode, + options: ClientRenderOptions, +): RenderResult +export function render< + Q extends Queries = typeof queries, + Container extends HydrateableContainer = HTMLElement, BaseElement extends Element | DocumentFragment = Container, >( ui: React.ReactNode, - options: RenderOptions, + options: HydrateOptions, ): RenderResult export function render( ui: React.ReactNode, diff --git a/types/test.tsx b/types/test.tsx index 6ff899de..da0bda06 100644 --- a/types/test.tsx +++ b/types/test.tsx @@ -206,6 +206,17 @@ export function testRenderHookProps() { unmount() } +export function testContainer() { + render('a', {container: document.createElement('div')}) + render('a', {container: document.createDocumentFragment()}) + // @ts-expect-error Only allowed in React 19 + render('a', {container: document}) + render('a', {container: document.createElement('div'), hydrate: true}) + // @ts-expect-error Only allowed for createRoot + render('a', {container: document.createDocumentFragment(), hydrate: true}) + render('a', {container: document, hydrate: true}) +} + /* eslint testing-library/prefer-explicit-assert: "off",