diff --git a/package.json b/package.json index 67bde194..54f531f1 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,8 @@ }, "devDependencies": { "@reach/router": "^1.2.1", - "@types/react-dom": "^16.0.9", + "@types/react": "^16.8.3", + "@types/react-dom": "^16.8.2", "axios": "^0.18.0", "eslint-import-resolver-jest": "^2.1.1", "history": "^4.7.2", diff --git a/src/__tests__/render.js b/src/__tests__/render.js index 5ee0dc6f..0ffe1322 100644 --- a/src/__tests__/render.js +++ b/src/__tests__/render.js @@ -90,3 +90,24 @@ it('supports fragments', () => { cleanup() expect(document.body.innerHTML).toBe('') }) + +test('renders options.wrapper around node', () => { + const WrapperComponent = ({children}) => ( +
{children}
+ ) + + const {container, getByTestId} = render(
, { + wrapper: WrapperComponent, + }) + + expect(getByTestId('wrapper')).toBeInTheDocument() + expect(container.firstChild).toMatchInlineSnapshot(` +
+
+
+`) +}) diff --git a/src/index.js b/src/index.js index 89a31e2c..70bf83c2 100644 --- a/src/index.js +++ b/src/index.js @@ -11,7 +11,13 @@ const mountedContainers = new Set() function render( ui, - {container, baseElement = container, queries, hydrate = false} = {}, + { + container, + baseElement = container, + queries, + hydrate = false, + wrapper: WrapperComponent, + } = {}, ) { if (!container) { // default to document.body instead of documentElement to avoid output of potentially-large @@ -25,13 +31,20 @@ function render( // they're passing us a custom container or not. mountedContainers.add(container) + + const wrapUiIfNeeded = innerElement => + WrapperComponent + ? React.createElement(WrapperComponent, null, innerElement) + : innerElement + act(() => { if (hydrate) { - ReactDOM.hydrate(ui, container) + ReactDOM.hydrate(wrapUiIfNeeded(ui), container) } else { - ReactDOM.render(ui, container) + ReactDOM.render(wrapUiIfNeeded(ui), container) } }) + return { container, baseElement, @@ -39,7 +52,7 @@ function render( debug: (el = baseElement) => console.log(prettyDOM(el)), unmount: () => ReactDOM.unmountComponentAtNode(container), rerender: rerenderUi => { - render(rerenderUi, {container, baseElement}) + render(wrapUiIfNeeded(rerenderUi), {container, baseElement}) // Intentionally do not return anything to avoid unnecessarily complicating the API. // folks can use all the same utilities we return in the first place that are bound to the container }, @@ -68,25 +81,20 @@ function testHook(callback, options = {}) { const result = { current: null, } - const toRender = () => { - const hookRender = ( - - {res => { - result.current = res - }} - - ) - if (options.wrapper) { - return React.createElement(options.wrapper, null, hookRender) - } - return hookRender - } - const {unmount, rerender: rerenderComponent} = render(toRender()) + const toRender = () => ( + + {res => { + result.current = res + }} + + ) + + const {unmount, rerender: rerenderComponent} = render(toRender(), options) return { result, unmount, rerender: () => { - rerenderComponent(toRender()) + rerenderComponent(toRender(), options) }, } } diff --git a/typings/index.d.ts b/typings/index.d.ts index 16594936..af09f778 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -25,17 +25,16 @@ export type HookResult = { unmount: () => boolean } -export type HookOptions = { - wrapper: React.FunctionComponent -} - export interface RenderOptions { container?: HTMLElement baseElement?: HTMLElement hydrate?: boolean queries?: Q + wrapper?: React.ComponentType } +export type HookOptions = RenderOptions + type Omit = Pick> /**