Skip to content

Commit d143f46

Browse files
authored
fix: Stop restricting container option based on hydrate (#1313)
1 parent 48282c2 commit d143f46

File tree

3 files changed

+91
-87
lines changed

3 files changed

+91
-87
lines changed

types/index.d.ts

+66-83
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@ export function getConfig(): Config
2525

2626
export type RenderResult<
2727
Q extends Queries = typeof queries,
28-
Container extends Element | DocumentFragment = HTMLElement,
29-
BaseElement extends Element | DocumentFragment = Container,
28+
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
29+
BaseElement extends RendererableContainer | HydrateableContainer = Container,
3030
> = {
3131
container: Container
3232
baseElement: BaseElement
3333
debug: (
3434
baseElement?:
35-
| Element
36-
| DocumentFragment
37-
| Array<Element | DocumentFragment>,
35+
| RendererableContainer
36+
| HydrateableContainer
37+
| Array<RendererableContainer | HydrateableContainer>,
3838
maxLength?: number,
3939
options?: prettyFormat.OptionsReceived,
4040
) => void
@@ -43,10 +43,48 @@ export type RenderResult<
4343
asFragment: () => DocumentFragment
4444
} & {[P in keyof Q]: BoundFunction<Q[P]>}
4545

46-
export interface BaseRenderOptions<
46+
/** @deprecated */
47+
export type BaseRenderOptions<
4748
Q extends Queries,
4849
Container extends RendererableContainer | HydrateableContainer,
49-
BaseElement extends Element | DocumentFragment,
50+
BaseElement extends RendererableContainer | HydrateableContainer,
51+
> = RenderOptions<Q, Container, BaseElement>
52+
53+
type RendererableContainer = ReactDOMClient.Container
54+
type HydrateableContainer = Parameters<typeof ReactDOMClient['hydrateRoot']>[0]
55+
/** @deprecated */
56+
export interface ClientRenderOptions<
57+
Q extends Queries,
58+
Container extends RendererableContainer,
59+
BaseElement extends RendererableContainer = Container,
60+
> extends BaseRenderOptions<Q, Container, BaseElement> {
61+
/**
62+
* If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side
63+
* rendering and use ReactDOM.hydrate to mount your components.
64+
*
65+
* @see https://testing-library.com/docs/react-testing-library/api/#hydrate)
66+
*/
67+
hydrate?: false | undefined
68+
}
69+
/** @deprecated */
70+
export interface HydrateOptions<
71+
Q extends Queries,
72+
Container extends HydrateableContainer,
73+
BaseElement extends HydrateableContainer = Container,
74+
> extends BaseRenderOptions<Q, Container, BaseElement> {
75+
/**
76+
* If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side
77+
* rendering and use ReactDOM.hydrate to mount your components.
78+
*
79+
* @see https://testing-library.com/docs/react-testing-library/api/#hydrate)
80+
*/
81+
hydrate: true
82+
}
83+
84+
export interface RenderOptions<
85+
Q extends Queries = typeof queries,
86+
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
87+
BaseElement extends RendererableContainer | HydrateableContainer = Container,
5088
> {
5189
/**
5290
* 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<
93131
wrapper?: React.JSXElementConstructor<{children: React.ReactNode}>
94132
}
95133

96-
type RendererableContainer = ReactDOMClient.Container
97-
type HydrateableContainer = Parameters<typeof ReactDOMClient['hydrateRoot']>[0]
98-
export interface ClientRenderOptions<
99-
Q extends Queries,
100-
Container extends Element | DocumentFragment,
101-
BaseElement extends Element | DocumentFragment = Container,
102-
> extends BaseRenderOptions<Q, Container, BaseElement> {
103-
/**
104-
* If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side
105-
* rendering and use ReactDOM.hydrate to mount your components.
106-
*
107-
* @see https://testing-library.com/docs/react-testing-library/api/#hydrate)
108-
*/
109-
hydrate?: false | undefined
110-
}
111-
112-
export interface HydrateOptions<
113-
Q extends Queries,
114-
Container extends Element | DocumentFragment,
115-
BaseElement extends Element | DocumentFragment = Container,
116-
> extends BaseRenderOptions<Q, Container, BaseElement> {
117-
/**
118-
* If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side
119-
* rendering and use ReactDOM.hydrate to mount your components.
120-
*
121-
* @see https://testing-library.com/docs/react-testing-library/api/#hydrate)
122-
*/
123-
hydrate: true
124-
}
125-
126-
export type RenderOptions<
127-
Q extends Queries = typeof queries,
128-
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
129-
BaseElement extends Element | DocumentFragment = Container,
130-
> =
131-
| ClientRenderOptions<Q, Container, BaseElement>
132-
| HydrateOptions<Q, Container, BaseElement>
133-
134134
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
135135

136136
/**
137137
* Render into a container which is appended to document.body. It should be used with cleanup.
138138
*/
139139
export function render<
140140
Q extends Queries = typeof queries,
141-
Container extends RendererableContainer = HTMLElement,
142-
BaseElement extends Element | DocumentFragment = Container,
143-
>(
144-
ui: React.ReactNode,
145-
options: ClientRenderOptions<Q, Container, BaseElement>,
146-
): RenderResult<Q, Container, BaseElement>
147-
export function render<
148-
Q extends Queries = typeof queries,
149-
Container extends HydrateableContainer = HTMLElement,
150-
BaseElement extends Element | DocumentFragment = Container,
141+
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
142+
BaseElement extends RendererableContainer | HydrateableContainer = Container,
151143
>(
152144
ui: React.ReactNode,
153-
options: HydrateOptions<Q, Container, BaseElement>,
145+
options: RenderOptions<Q, Container, BaseElement>,
154146
): RenderResult<Q, Container, BaseElement>
155147
export function render(
156148
ui: React.ReactNode,
@@ -179,19 +171,15 @@ export interface RenderHookResult<Result, Props> {
179171
unmount: () => void
180172
}
181173

182-
export interface BaseRenderHookOptions<
174+
/** @deprecated */
175+
export type BaseRenderHookOptions<
183176
Props,
184177
Q extends Queries,
185178
Container extends RendererableContainer | HydrateableContainer,
186179
BaseElement extends Element | DocumentFragment,
187-
> extends BaseRenderOptions<Q, Container, BaseElement> {
188-
/**
189-
* The argument passed to the renderHook callback. Can be useful if you plan
190-
* to use the rerender utility to change the values passed to your hook.
191-
*/
192-
initialProps?: Props
193-
}
180+
> = RenderHookOptions<Props, Q, Container, BaseElement>
194181

182+
/** @deprecated */
195183
export interface ClientRenderHookOptions<
196184
Props,
197185
Q extends Queries,
@@ -207,6 +195,7 @@ export interface ClientRenderHookOptions<
207195
hydrate?: false | undefined
208196
}
209197

198+
/** @deprecated */
210199
export interface HydrateHookOptions<
211200
Props,
212201
Q extends Queries,
@@ -222,14 +211,18 @@ export interface HydrateHookOptions<
222211
hydrate: true
223212
}
224213

225-
export type RenderHookOptions<
214+
export interface RenderHookOptions<
226215
Props,
227216
Q extends Queries = typeof queries,
228-
Container extends Element | DocumentFragment = HTMLElement,
229-
BaseElement extends Element | DocumentFragment = Container,
230-
> =
231-
| ClientRenderHookOptions<Props, Q, Container, BaseElement>
232-
| HydrateHookOptions<Props, Q, Container, BaseElement>
217+
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
218+
BaseElement extends RendererableContainer | HydrateableContainer = Container,
219+
> extends BaseRenderOptions<Q, Container, BaseElement> {
220+
/**
221+
* The argument passed to the renderHook callback. Can be useful if you plan
222+
* to use the rerender utility to change the values passed to your hook.
223+
*/
224+
initialProps?: Props
225+
}
233226

234227
/**
235228
* Allows you to render a hook within a test React component without having to
@@ -239,21 +232,11 @@ export function renderHook<
239232
Result,
240233
Props,
241234
Q extends Queries = typeof queries,
242-
Container extends RendererableContainer = HTMLElement,
243-
BaseElement extends Element | DocumentFragment = Container,
244-
>(
245-
render: (initialProps: Props) => Result,
246-
options?: ClientRenderHookOptions<Props, Q, Container, BaseElement>,
247-
): RenderHookResult<Result, Props>
248-
export function renderHook<
249-
Result,
250-
Props,
251-
Q extends Queries = typeof queries,
252-
Container extends HydrateableContainer = HTMLElement,
253-
BaseElement extends Element | DocumentFragment = Container,
235+
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
236+
BaseElement extends RendererableContainer | HydrateableContainer = Container,
254237
>(
255238
render: (initialProps: Props) => Result,
256-
options?: HydrateHookOptions<Props, Q, Container, BaseElement>,
239+
options?: RenderHookOptions<Props, Q, Container, BaseElement>,
257240
): RenderHookResult<Result, Props>
258241

259242
/**

types/test.tsx

+22-4
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,24 @@ export function wrappedRenderC(
166166
return pure.render(ui, {wrapper: AppWrapperProps, ...options})
167167
}
168168

169+
export function wrappedRenderHook<Props>(
170+
hook: () => unknown,
171+
options?: pure.RenderHookOptions<Props>,
172+
) {
173+
interface AppWrapperProps {
174+
children?: React.ReactNode
175+
userProviderProps?: {user: string}
176+
}
177+
const AppWrapperProps: React.FunctionComponent<AppWrapperProps> = ({
178+
children,
179+
userProviderProps = {user: 'TypeScript'},
180+
}) => {
181+
return <div data-testid={userProviderProps.user}>{children}</div>
182+
}
183+
184+
return pure.renderHook(hook, {...options})
185+
}
186+
169187
export function testBaseElement() {
170188
const {baseElement: baseDefaultElement} = render(<div />)
171189
expectType<HTMLElement, typeof baseDefaultElement>(baseDefaultElement)
@@ -213,22 +231,22 @@ export function testRenderHookProps() {
213231
export function testContainer() {
214232
render('a', {container: document.createElement('div')})
215233
render('a', {container: document.createDocumentFragment()})
216-
// @ts-expect-error Only allowed in React 19
234+
// Only allowed in React 19
217235
render('a', {container: document})
218236
render('a', {container: document.createElement('div'), hydrate: true})
219-
// @ts-expect-error Only allowed for createRoot
237+
// Only allowed for createRoot but typing `render` appropriately makes it harder to compose.
220238
render('a', {container: document.createDocumentFragment(), hydrate: true})
221239
render('a', {container: document, hydrate: true})
222240

223241
renderHook(() => null, {container: document.createElement('div')})
224242
renderHook(() => null, {container: document.createDocumentFragment()})
225-
// @ts-expect-error Only allowed in React 19
243+
// Only allowed in React 19
226244
renderHook(() => null, {container: document})
227245
renderHook(() => null, {
228246
container: document.createElement('div'),
229247
hydrate: true,
230248
})
231-
// @ts-expect-error Only allowed for createRoot
249+
// Only allowed for createRoot but typing `render` appropriately makes it harder to compose.
232250
renderHook(() => null, {
233251
container: document.createDocumentFragment(),
234252
hydrate: true,

types/tsconfig.json

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
22
"extends": "../node_modules/kcd-scripts/shared-tsconfig.json",
3+
"compilerOptions": {
4+
"skipLibCheck": false
5+
},
36
"include": ["."]
47
}

0 commit comments

Comments
 (0)