Skip to content

Commit

Permalink
fix: Stop restricting container option based on hydrate (#1313)
Browse files Browse the repository at this point in the history
eps1lon authored Apr 26, 2024
1 parent 48282c2 commit d143f46
Showing 3 changed files with 91 additions and 87 deletions.
149 changes: 66 additions & 83 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -25,16 +25,16 @@ export function getConfig(): Config

export type RenderResult<
Q extends Queries = typeof queries,
Container extends Element | DocumentFragment = HTMLElement,
BaseElement extends Element | DocumentFragment = Container,
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
BaseElement extends RendererableContainer | HydrateableContainer = Container,
> = {
container: Container
baseElement: BaseElement
debug: (
baseElement?:
| Element
| DocumentFragment
| Array<Element | DocumentFragment>,
| RendererableContainer
| HydrateableContainer
| Array<RendererableContainer | HydrateableContainer>,
maxLength?: number,
options?: prettyFormat.OptionsReceived,
) => void
@@ -43,10 +43,48 @@ export type RenderResult<
asFragment: () => DocumentFragment
} & {[P in keyof Q]: BoundFunction<Q[P]>}

export interface BaseRenderOptions<
/** @deprecated */
export type BaseRenderOptions<
Q extends Queries,
Container extends RendererableContainer | HydrateableContainer,
BaseElement extends Element | DocumentFragment,
BaseElement extends RendererableContainer | HydrateableContainer,
> = RenderOptions<Q, Container, BaseElement>

type RendererableContainer = ReactDOMClient.Container
type HydrateableContainer = Parameters<typeof ReactDOMClient['hydrateRoot']>[0]
/** @deprecated */
export interface ClientRenderOptions<
Q extends Queries,
Container extends RendererableContainer,
BaseElement extends RendererableContainer = Container,
> extends BaseRenderOptions<Q, Container, BaseElement> {
/**
* 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
}
/** @deprecated */
export interface HydrateOptions<
Q extends Queries,
Container extends HydrateableContainer,
BaseElement extends HydrateableContainer = Container,
> extends BaseRenderOptions<Q, Container, BaseElement> {
/**
* 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 interface RenderOptions<
Q extends Queries = typeof queries,
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
BaseElement extends RendererableContainer | HydrateableContainer = Container,
> {
/**
* 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,64 +131,18 @@ export interface BaseRenderOptions<
wrapper?: React.JSXElementConstructor<{children: React.ReactNode}>
}

type RendererableContainer = ReactDOMClient.Container
type HydrateableContainer = Parameters<typeof ReactDOMClient['hydrateRoot']>[0]
export interface ClientRenderOptions<
Q extends Queries,
Container extends Element | DocumentFragment,
BaseElement extends Element | DocumentFragment = Container,
> extends BaseRenderOptions<Q, Container, BaseElement> {
/**
* 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<Q, Container, BaseElement> {
/**
* 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<Q, Container, BaseElement>
| HydrateOptions<Q, Container, BaseElement>

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

/**
* Render into a container which is appended to document.body. It should be used with cleanup.
*/
export function render<
Q extends Queries = typeof queries,
Container extends RendererableContainer = HTMLElement,
BaseElement extends Element | DocumentFragment = Container,
>(
ui: React.ReactNode,
options: ClientRenderOptions<Q, Container, BaseElement>,
): RenderResult<Q, Container, BaseElement>
export function render<
Q extends Queries = typeof queries,
Container extends HydrateableContainer = HTMLElement,
BaseElement extends Element | DocumentFragment = Container,
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
BaseElement extends RendererableContainer | HydrateableContainer = Container,
>(
ui: React.ReactNode,
options: HydrateOptions<Q, Container, BaseElement>,
options: RenderOptions<Q, Container, BaseElement>,
): RenderResult<Q, Container, BaseElement>
export function render(
ui: React.ReactNode,
@@ -179,19 +171,15 @@ export interface RenderHookResult<Result, Props> {
unmount: () => void
}

export interface BaseRenderHookOptions<
/** @deprecated */
export type BaseRenderHookOptions<
Props,
Q extends Queries,
Container extends RendererableContainer | HydrateableContainer,
BaseElement extends Element | DocumentFragment,
> extends BaseRenderOptions<Q, Container, BaseElement> {
/**
* The argument passed to the renderHook callback. Can be useful if you plan
* to use the rerender utility to change the values passed to your hook.
*/
initialProps?: Props
}
> = RenderHookOptions<Props, Q, Container, BaseElement>

/** @deprecated */
export interface ClientRenderHookOptions<
Props,
Q extends Queries,
@@ -207,6 +195,7 @@ export interface ClientRenderHookOptions<
hydrate?: false | undefined
}

/** @deprecated */
export interface HydrateHookOptions<
Props,
Q extends Queries,
@@ -222,14 +211,18 @@ export interface HydrateHookOptions<
hydrate: true
}

export type RenderHookOptions<
export interface RenderHookOptions<
Props,
Q extends Queries = typeof queries,
Container extends Element | DocumentFragment = HTMLElement,
BaseElement extends Element | DocumentFragment = Container,
> =
| ClientRenderHookOptions<Props, Q, Container, BaseElement>
| HydrateHookOptions<Props, Q, Container, BaseElement>
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
BaseElement extends RendererableContainer | HydrateableContainer = Container,
> extends BaseRenderOptions<Q, Container, BaseElement> {
/**
* The argument passed to the renderHook callback. Can be useful if you plan
* to use the rerender utility to change the values passed to your hook.
*/
initialProps?: Props
}

/**
* Allows you to render a hook within a test React component without having to
@@ -239,21 +232,11 @@ export function renderHook<
Result,
Props,
Q extends Queries = typeof queries,
Container extends RendererableContainer = HTMLElement,
BaseElement extends Element | DocumentFragment = Container,
>(
render: (initialProps: Props) => Result,
options?: ClientRenderHookOptions<Props, Q, Container, BaseElement>,
): RenderHookResult<Result, Props>
export function renderHook<
Result,
Props,
Q extends Queries = typeof queries,
Container extends HydrateableContainer = HTMLElement,
BaseElement extends Element | DocumentFragment = Container,
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
BaseElement extends RendererableContainer | HydrateableContainer = Container,
>(
render: (initialProps: Props) => Result,
options?: HydrateHookOptions<Props, Q, Container, BaseElement>,
options?: RenderHookOptions<Props, Q, Container, BaseElement>,
): RenderHookResult<Result, Props>

/**
26 changes: 22 additions & 4 deletions types/test.tsx
Original file line number Diff line number Diff line change
@@ -166,6 +166,24 @@ export function wrappedRenderC(
return pure.render(ui, {wrapper: AppWrapperProps, ...options})
}

export function wrappedRenderHook<Props>(
hook: () => unknown,
options?: pure.RenderHookOptions<Props>,
) {
interface AppWrapperProps {
children?: React.ReactNode
userProviderProps?: {user: string}
}
const AppWrapperProps: React.FunctionComponent<AppWrapperProps> = ({
children,
userProviderProps = {user: 'TypeScript'},
}) => {
return <div data-testid={userProviderProps.user}>{children}</div>
}

return pure.renderHook(hook, {...options})
}

export function testBaseElement() {
const {baseElement: baseDefaultElement} = render(<div />)
expectType<HTMLElement, typeof baseDefaultElement>(baseDefaultElement)
@@ -213,22 +231,22 @@ export function testRenderHookProps() {
export function testContainer() {
render('a', {container: document.createElement('div')})
render('a', {container: document.createDocumentFragment()})
// @ts-expect-error Only allowed in React 19
// Only allowed in React 19
render('a', {container: document})
render('a', {container: document.createElement('div'), hydrate: true})
// @ts-expect-error Only allowed for createRoot
// Only allowed for createRoot but typing `render` appropriately makes it harder to compose.
render('a', {container: document.createDocumentFragment(), hydrate: true})
render('a', {container: document, hydrate: true})

renderHook(() => null, {container: document.createElement('div')})
renderHook(() => null, {container: document.createDocumentFragment()})
// @ts-expect-error Only allowed in React 19
// Only allowed in React 19
renderHook(() => null, {container: document})
renderHook(() => null, {
container: document.createElement('div'),
hydrate: true,
})
// @ts-expect-error Only allowed for createRoot
// Only allowed for createRoot but typing `render` appropriately makes it harder to compose.
renderHook(() => null, {
container: document.createDocumentFragment(),
hydrate: true,
3 changes: 3 additions & 0 deletions types/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"extends": "../node_modules/kcd-scripts/shared-tsconfig.json",
"compilerOptions": {
"skipLibCheck": false
},
"include": ["."]
}

0 comments on commit d143f46

Please sign in to comment.