-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
) * Add SSR check on window call * Handle client render after server using state * Add changeset * Lint * Add testing renderHookServer * Remove unused * Update changeset * Lint * useIsSsr to useSsrCheck * Lint * Update viewport hook * Add R17 version * Revert R17 package changes * CR changes * Lint * Is it that simple? * Semi shot in the dark * Revert "Semi shot in the dark" This reverts commit d8b5fed. * Bump changesets to minor
- Loading branch information
Showing
11 changed files
with
224 additions
and
8 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
--- | ||
'@leafygreen-ui/testing-lib': minor | ||
--- | ||
|
||
Adds `renderHookServer` method | ||
|
||
@testing-library/react-hooks/server exposed a `renderHook` method | ||
that allowed for one to render hooks as if SSR, and control | ||
hydration. This is no longer supported in versions >=18. | ||
|
||
This code was extracted from @testing-library/react-hooks/server and | ||
updated to be compatible with React version >= 18 using `hydrateRoot`. | ||
|
||
More context found here: | ||
https://github.com/testing-library/react-testing-library/issues/1120 |
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,7 @@ | ||
--- | ||
'@leafygreen-ui/hooks': minor | ||
--- | ||
|
||
Adds `useSsrCheck` and adds it to viewport check in `useViewportSize`. | ||
|
||
When server side rendering is used, `window` is not defined. This is causing build issues on the server where we access `window` in `useViewportSize`. To fix this, this change adds a hook, `useSsrCheck`, that checks the rendering environment and can be used before attempting to access `window`. It adds a check of this to `useViewportSize` to fix the current build issue. |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { useEffect, useState } from 'react'; | ||
|
||
export default function useSsrCheck() { | ||
const [isSsr, setIsSsr] = useState(typeof window === 'undefined'); | ||
|
||
useEffect(() => { | ||
// When rendered on server, this won't run until we're on the client. Therefore, | ||
// isSsr should be true when server rendered, and only be set to false on subsequent client render. | ||
// When rendered directly on the client, isSsr should already be false, so | ||
// this update shouldn't trigger a re-render. | ||
setIsSsr(false); | ||
}, []); | ||
|
||
return isSsr; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { esmConfig, umdConfig } from '@lg-tools/build/config/rollup.config.mjs'; | ||
|
||
export default [ | ||
esmConfig, | ||
umdConfig, | ||
{ | ||
...esmConfig, | ||
input: ['./src/renderHookServer.tsx', './src/renderHookServerV17.tsx'], | ||
output: { | ||
// cjs is fully supported in node.js | ||
format: 'cjs', // overrides esm format from esmConfig.output | ||
entryFileNames: '[name].js', | ||
dir: 'dist', | ||
preserveModules: true, | ||
exports: 'auto', | ||
}, | ||
}, | ||
]; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import type { ReactNode } from 'react'; | ||
import React from 'react'; | ||
//@ts-ignore Cannot find module 'react-dom/client' or its corresponding type declarations | ||
import { hydrateRoot } from 'react-dom/client'; | ||
import { renderToString } from 'react-dom/server'; | ||
import { act } from 'react-dom/test-utils'; | ||
|
||
export interface RenderHookServerOptions { | ||
wrapper?: ({ children }: { children: ReactNode }) => JSX.Element; | ||
} | ||
|
||
export interface RenderHookServerResult<Hook extends () => any> { | ||
result: { current: ReturnType<Hook> }; | ||
hydrate: () => void; | ||
} | ||
|
||
/** | ||
* Allows you to mock the server side rendering of a hook. | ||
* | ||
* @testing-library/react-hooks/server exposed a `renderHook` method | ||
* that allowed for one to render hooks as if SSR, and control | ||
* hydration. This is no longer supported in versions >=18. | ||
* | ||
* This code was extracted from @testing-library/react-hooks/server and | ||
* updated to be compatible with React version >= 18 using `hydrateRoot`. | ||
* | ||
* More context found here: | ||
* https://github.com/testing-library/react-testing-library/issues/1120 | ||
* | ||
* e.g. | ||
* ```typescript | ||
* it('should return true when server-side rendered and false after hydration', () => { | ||
* const { result, hydrate } = renderHookServer(useMyHook); | ||
* expect(result.current).toBe(true); | ||
* hydrate(); | ||
* expect(result.current).toBe(false); | ||
* }); | ||
* ``` | ||
} | ||
*/ | ||
export function renderHookServer<Hook extends () => any>( | ||
useHook: Hook, | ||
{ wrapper: Wrapper }: RenderHookServerOptions = {}, | ||
): RenderHookServerResult<Hook> { | ||
// Store hook return value | ||
const results: Array<ReturnType<Hook>> = []; | ||
const result = { | ||
get current() { | ||
return results.slice(-1)[0]; | ||
}, | ||
}; | ||
|
||
// Test component to render hook in | ||
const Component = ({ useHook }: { useHook: Hook }) => { | ||
results.push(useHook()); | ||
return null; | ||
}; | ||
|
||
// Add wrapper if necessary | ||
const component = Wrapper ? ( | ||
<Wrapper> | ||
<Component useHook={useHook} /> | ||
</Wrapper> | ||
) : ( | ||
<Component useHook={useHook} /> | ||
); | ||
|
||
// Running tests in an environment that simulates a browser (like Jest with jsdom), | ||
// the window object will still be available even when server rendered. To ensure | ||
// that window is not available during SSR we need to explicitly mock or remove the | ||
// window object. | ||
// @ts-ignore Type 'undefined' is not assignable to type 'Window'. | ||
jest.spyOn(global, 'window', 'get').mockImplementation(() => undefined); | ||
|
||
// Render hook on server | ||
const serverOutput = renderToString(component); | ||
|
||
// Restore window | ||
jest.spyOn(global, 'window', 'get').mockRestore(); | ||
|
||
// Render hook on client | ||
const hydrate = () => { | ||
const root = document.createElement('div'); | ||
root.innerHTML = serverOutput; | ||
act(() => { | ||
hydrateRoot(root, component); | ||
}); | ||
}; | ||
|
||
return { | ||
result, | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { renderHook } from '@testing-library/react-hooks/server'; | ||
|
||
import { | ||
RenderHookServerOptions, | ||
RenderHookServerResult, | ||
} from './renderHookServer'; | ||
|
||
/** | ||
* Allows you to mock the server side rendering of a hook in pre React 18 versions. | ||
* For versions >=18, use `@testing-lib/renderHookServer`. | ||
* | ||
* e.g. | ||
* ```typescript | ||
* it('should return true when server-side rendered and false after hydration', () => { | ||
* const { result, hydrate } = renderHookServer(useMyHook); | ||
* expect(result.current).toBe(true); | ||
* hydrate(); | ||
* expect(result.current).toBe(false); | ||
* }); | ||
* ``` | ||
} | ||
*/ | ||
export function renderHookServer<Hook extends () => any>( | ||
useHook: Hook, | ||
{ wrapper }: RenderHookServerOptions = {}, | ||
): RenderHookServerResult<Hook> { | ||
// @ts-ignore Type 'undefined' is not assignable to type 'Window'. | ||
jest.spyOn(global, 'window', 'get').mockImplementation(() => undefined); | ||
const response = renderHook(useHook, { wrapper }); | ||
jest.spyOn(global, 'window', 'get').mockRestore(); | ||
return response; | ||
} |