From c0dac7e09c5df6f25890a40c950e6f8291eb13e4 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Tue, 18 Jul 2023 13:24:08 +0200 Subject: [PATCH] rework --- packages/core/src/index.ts | 1 + packages/core/src/makeResetStyles.ts | 17 ++---- packages/core/src/makeStaticStyles.ts | 27 ++++---- packages/core/src/makeStyles.ts | 26 +++++--- .../runtime/resolveStaticStyleRules.test.ts | 60 +++++++++--------- .../src/runtime/resolveStaticStyleRules.ts | 38 ++++++------ packages/core/src/types.ts | 6 ++ packages/react/jest.config.ts | 3 + packages/react/src/RendererContext.tsx | 11 +--- packages/react/src/__styles.ts | 30 ++------- packages/react/src/createDOMRenderer.test.tsx | 42 ++++++++++--- packages/react/src/makeResetStyles.test.tsx | 27 ++++++++ packages/react/src/makeResetStyles.ts | 5 +- packages/react/src/makeStyles.test.tsx | 7 ++- packages/react/src/makeStyles.ts | 61 +++++++++---------- .../react/src/renderToStyleElements.test.tsx | 34 +++++++---- packages/react/src/useInsertionEffect.ts | 4 +- packages/react/src/utils/canUseDOM.ts | 6 ++ 18 files changed, 227 insertions(+), 178 deletions(-) create mode 100644 packages/react/src/makeResetStyles.test.tsx create mode 100644 packages/react/src/utils/canUseDOM.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 0e2f2932d..1db8aa218 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -102,6 +102,7 @@ export { StyleBucketName, // Util GriffelRenderer, + GriffelInsertionFactory, } from './types'; // Private exports, are used by devtools diff --git a/packages/core/src/makeResetStyles.ts b/packages/core/src/makeResetStyles.ts index b05604067..f4d388743 100644 --- a/packages/core/src/makeResetStyles.ts +++ b/packages/core/src/makeResetStyles.ts @@ -3,14 +3,16 @@ import type { GriffelResetStyle } from '@griffel/style-types'; import { DEBUG_RESET_CLASSES } from './constants'; import { resolveResetStyleRules } from './runtime/resolveResetStyleRules'; import type { GriffelRenderer } from './types'; +import type { GriffelInsertionFactory } from './types'; +import { insertionFactory } from './makeStyles'; export interface MakeResetStylesOptions { dir: 'ltr' | 'rtl'; renderer: GriffelRenderer; } -export function makeResetStyles(styles: GriffelResetStyle) { - const insertionCache: Record = {}; +export function makeResetStyles(styles: GriffelResetStyle, factory: GriffelInsertionFactory = insertionFactory) { + const insertStyles = factory(); let ltrClassName: string | null = null; let rtlClassName: string | null = null; @@ -24,16 +26,9 @@ export function makeResetStyles(styles: GriffelResetStyle) { [ltrClassName, rtlClassName, cssRules] = resolveResetStyleRules(styles); } - const isLTR = dir === 'ltr'; - // As RTL classes are different they should have a different cache key for insertion - const rendererId = isLTR ? renderer.id : renderer.id + 'r'; + insertStyles(renderer, dir, { r: cssRules! }); - if (insertionCache[rendererId] === undefined) { - renderer.insertCSSRules({ r: cssRules! }); - insertionCache[rendererId] = true; - } - - const className = isLTR ? ltrClassName : rtlClassName || ltrClassName; + const className = dir === 'ltr' ? ltrClassName : rtlClassName || ltrClassName; if (process.env.NODE_ENV !== 'production') { DEBUG_RESET_CLASSES[className] = 1; diff --git a/packages/core/src/makeStaticStyles.ts b/packages/core/src/makeStaticStyles.ts index 09276a626..21027571a 100644 --- a/packages/core/src/makeStaticStyles.ts +++ b/packages/core/src/makeStaticStyles.ts @@ -2,30 +2,25 @@ import type { GriffelStaticStyles } from '@griffel/style-types'; import { resolveStaticStyleRules } from './runtime/resolveStaticStyleRules'; import type { GriffelRenderer } from './types'; +import type { GriffelInsertionFactory } from './types'; +import { insertionFactory } from './makeStyles'; export interface MakeStaticStylesOptions { renderer: GriffelRenderer; } -/** - * Register static css. - * @param styles - styles object or string. - */ -export function makeStaticStyles(styles: GriffelStaticStyles | GriffelStaticStyles[]) { - const styleCache: Record = {}; +export function makeStaticStyles( + styles: GriffelStaticStyles | GriffelStaticStyles[], + factory: GriffelInsertionFactory = insertionFactory, +) { + const insertStyles = factory(); const stylesSet: GriffelStaticStyles[] = Array.isArray(styles) ? styles : [styles]; function useStaticStyles(options: MakeStaticStylesOptions): void { - const cacheKey = options.renderer.id; - if (styleCache[cacheKey]) { - return; - } - - for (const styleRules of stylesSet) { - options.renderer.insertCSSRules(resolveStaticStyleRules(styleRules)); - } - - styleCache[cacheKey] = true; + insertStyles(options.renderer, 'ltr', { + // 👇 static rules should be inserted into default bucket + d: resolveStaticStyleRules(stylesSet), + }); } return useStaticStyles; diff --git a/packages/core/src/makeStyles.ts b/packages/core/src/makeStyles.ts index 136c80052..bc2294232 100644 --- a/packages/core/src/makeStyles.ts +++ b/packages/core/src/makeStyles.ts @@ -2,15 +2,31 @@ import { debugData, isDevToolsEnabled, getSourceURLfromError } from './devtools' import { resolveStyleRulesForSlots } from './resolveStyleRulesForSlots'; import { reduceToClassNameForSlots } from './runtime/reduceToClassNameForSlots'; import type { CSSClassesMapBySlot, CSSRulesByBucket, GriffelRenderer, StylesBySlots } from './types'; +import type { GriffelInsertionFactory } from './types'; export interface MakeStylesOptions { dir: 'ltr' | 'rtl'; renderer: GriffelRenderer; } -export function makeStyles(stylesBySlots: StylesBySlots) { +export const insertionFactory: GriffelInsertionFactory = () => { const insertionCache: Record = {}; + return function insert(renderer: GriffelRenderer, dir: string, cssRules: CSSRulesByBucket) { + console.log('insert', renderer.id, insertionCache[renderer.id], cssRules); + if (insertionCache[renderer.id] === undefined) { + renderer.insertCSSRules(cssRules!); + insertionCache[renderer.id] = true; + } + }; +}; + +export function makeStyles( + stylesBySlots: StylesBySlots, + factory: GriffelInsertionFactory = insertionFactory, +) { + const insertStyles = factory(); + let classesMapBySlot: CSSClassesMapBySlot | null = null; let cssRules: CSSRulesByBucket | null = null; @@ -30,8 +46,6 @@ export function makeStyles(stylesBySlots: StylesB } const isLTR = dir === 'ltr'; - // As RTL classes are different they should have a different cache key for insertion - const rendererId = isLTR ? renderer.id : renderer.id + 'r'; if (isLTR) { if (ltrClassNamesForSlots === null) { @@ -43,10 +57,8 @@ export function makeStyles(stylesBySlots: StylesB } } - if (insertionCache[rendererId] === undefined) { - renderer.insertCSSRules(cssRules!); - insertionCache[rendererId] = true; - } + insertStyles(renderer, dir, cssRules!); + const classNamesForSlots = isLTR ? (ltrClassNamesForSlots as Record) : (rtlClassNamesForSlots as Record); diff --git a/packages/core/src/runtime/resolveStaticStyleRules.test.ts b/packages/core/src/runtime/resolveStaticStyleRules.test.ts index bdcc80559..965ac7926 100644 --- a/packages/core/src/runtime/resolveStaticStyleRules.test.ts +++ b/packages/core/src/runtime/resolveStaticStyleRules.test.ts @@ -3,50 +3,48 @@ import { resolveStaticStyleRules } from './resolveStaticStyleRules'; describe('resolveStaticStyleRules', () => { it('handles font-face', () => { expect( - resolveStaticStyleRules({ - '@font-face': { - fontFamily: 'Open Sans', - src: `url("webfont.woff2") format("woff2")`, + resolveStaticStyleRules([ + { + '@font-face': { + fontFamily: 'Open Sans', + src: `url("webfont.woff2") format("woff2")`, + }, }, - }), + ]), ).toMatchInlineSnapshot(` - Object { - "d": Array [ - "@font-face{font-family:Open Sans;src:url(\\"webfont.woff2\\") format(\\"woff2\\");}", - ], - } + Array [ + "@font-face{font-family:Open Sans;src:url(\\"webfont.woff2\\") format(\\"woff2\\");}", + ] `); }); it('handles static css', () => { expect( - resolveStaticStyleRules({ - body: { - background: 'blue', + resolveStaticStyleRules([ + { + body: { + background: 'blue', + }, + '.foo': { + background: 'yellow', + marginLeft: '5px', + }, }, - '.foo': { - background: 'yellow', - marginLeft: '5px', - }, - }), + ]), ).toMatchInlineSnapshot(` - Object { - "d": Array [ - "body{background:blue;}", - ".foo{background:yellow;margin-left:5px;}", - ], - } + Array [ + "body{background:blue;}", + ".foo{background:yellow;margin-left:5px;}", + ] `); }); it('handles css string', () => { - expect(resolveStaticStyleRules('body {background: red;} div {color: green;}')).toMatchInlineSnapshot(` - Object { - "d": Array [ - "body{background:red;}", - "div{color:green;}", - ], - } + expect(resolveStaticStyleRules(['body {background: red;} div {color: green;}'])).toMatchInlineSnapshot(` + Array [ + "body{background:red;}", + "div{color:green;}", + ] `); }); }); diff --git a/packages/core/src/runtime/resolveStaticStyleRules.ts b/packages/core/src/runtime/resolveStaticStyleRules.ts index ba51f24fd..25fde1116 100644 --- a/packages/core/src/runtime/resolveStaticStyleRules.ts +++ b/packages/core/src/runtime/resolveStaticStyleRules.ts @@ -1,30 +1,30 @@ import type { GriffelStaticStyles } from '@griffel/style-types'; -import type { CSSRulesByBucket } from '../types'; + +import type { CSSBucketEntry } from '../types'; import { compileCSSRules } from './compileCSSRules'; import { compileStaticCSS } from './compileStaticCSS'; -export function resolveStaticStyleRules(styles: GriffelStaticStyles, result: CSSRulesByBucket = {}): CSSRulesByBucket { - if (typeof styles === 'string') { - const cssRules = compileCSSRules(styles, false); +export function resolveStaticStyleRules( + stylesSet: GriffelStaticStyles[], + result: CSSBucketEntry[] = [], +): CSSBucketEntry[] { + for (const styles of stylesSet) { + if (typeof styles === 'string') { + const cssRules = compileCSSRules(styles, false); - for (const rule of cssRules) { - addResolvedStyles(rule, result); - } - } else { - // eslint-disable-next-line guard-for-in - for (const property in styles) { - const value = styles[property]; - const staticCSS = compileStaticCSS(property, value); + for (const rule of cssRules) { + result.push(rule); + } + } else { + // eslint-disable-next-line guard-for-in + for (const property in styles) { + const value = styles[property]; + const staticCSS = compileStaticCSS(property, value); - addResolvedStyles(staticCSS, result); + result.push(staticCSS); + } } } return result; } - -function addResolvedStyles(cssRule: string, result: CSSRulesByBucket = {}): void { - // 👇 static rules should be inserted into default bucket - result.d = result.d || []; - result.d.push(cssRule); -} diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 408378b9f..83e60d287 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -87,6 +87,12 @@ export type CSSRulesByBucket = { c?: CSSBucketEntry[]; }; +export type GriffelInsertionFactory = () => ( + renderer: GriffelRenderer, + dir: string, + cssRules: CSSRulesByBucket, +) => void; + /** @internal */ export type CSSBucketEntry = string | [string, Record]; diff --git a/packages/react/jest.config.ts b/packages/react/jest.config.ts index b7b517ed2..0a5d03f44 100644 --- a/packages/react/jest.config.ts +++ b/packages/react/jest.config.ts @@ -5,6 +5,9 @@ export default { transform: { '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/react/babel'] }], }, + globals: { + IS_REACT_ACT_ENVIRONMENT: true, + }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], coverageDirectory: '../../coverage/packages/react', }; diff --git a/packages/react/src/RendererContext.tsx b/packages/react/src/RendererContext.tsx index 62e470e8a..dcf53d563 100644 --- a/packages/react/src/RendererContext.tsx +++ b/packages/react/src/RendererContext.tsx @@ -1,6 +1,8 @@ import { createDOMRenderer, rehydrateRendererCache } from '@griffel/core'; -import * as React from 'react'; import type { GriffelRenderer } from '@griffel/core'; +import * as React from 'react'; + +import { canUseDOM } from './utils/canUseDOM'; export interface RendererProviderProps { /** An instance of Griffel renderer. */ @@ -17,13 +19,6 @@ export interface RendererProviderProps { children: React.ReactNode; } -/** - * Verifies if an application can use DOM. - */ -function canUseDOM(): boolean { - return typeof window !== 'undefined' && !!(window.document && window.document.createElement); -} - /** * @private */ diff --git a/packages/react/src/__styles.ts b/packages/react/src/__styles.ts index b81f0bec5..345627ec6 100644 --- a/packages/react/src/__styles.ts +++ b/packages/react/src/__styles.ts @@ -1,9 +1,8 @@ -import { reduceToClassNameForSlots } from '@griffel/core'; +import { __styles as vanillaStyles } from '@griffel/core'; import type { CSSClassesMapBySlot, CSSRulesByBucket } from '@griffel/core'; import { useRenderer } from './RendererContext'; import { useTextDirection } from './TextDirectionContext'; -import { useInsertionEffect } from './useInsertionEffect'; /** * A version of makeStyles() that accepts build output as an input and skips all runtime transforms. @@ -15,31 +14,12 @@ export function __styles( classesMapBySlot: CSSClassesMapBySlot, cssRules: CSSRulesByBucket, ) { - let ltrClassNamesForSlots: Record | null = null; - let rtlClassNamesForSlots: Record | null = null; + const getStyles = vanillaStyles(classesMapBySlot, cssRules); - function computeClasses(): Record { + return function useClasses(): Record { const dir = useTextDirection(); const renderer = useRenderer(); - const isLTR = dir === 'ltr'; - - if (isLTR) { - if (ltrClassNamesForSlots === null) { - ltrClassNamesForSlots = reduceToClassNameForSlots(classesMapBySlot, dir); - } - } else { - if (rtlClassNamesForSlots === null) { - rtlClassNamesForSlots = reduceToClassNameForSlots(classesMapBySlot, dir); - } - } - - useInsertionEffect(() => { - renderer.insertCSSRules(cssRules!); - }, [isLTR, renderer]); - - return isLTR ? (ltrClassNamesForSlots as Record) : (rtlClassNamesForSlots as Record); - } - - return computeClasses; + return getStyles({ dir, renderer }); + }; } diff --git a/packages/react/src/createDOMRenderer.test.tsx b/packages/react/src/createDOMRenderer.test.tsx index 670e29ef1..bcf83ec9b 100644 --- a/packages/react/src/createDOMRenderer.test.tsx +++ b/packages/react/src/createDOMRenderer.test.tsx @@ -1,12 +1,20 @@ import { createDOMRenderer, mergeClasses } from '@griffel/core'; import * as React from 'react'; -import { hydrate } from 'react-dom'; +import { hydrateRoot } from 'react-dom/client'; import { renderToStaticMarkup } from 'react-dom/server'; +import { act } from 'react-dom/test-utils'; import { makeStyles } from './makeStyles'; import { makeResetStyles } from './makeResetStyles'; import { RendererProvider } from './RendererContext'; import { renderToStyleElements } from './renderToStyleElements'; +import { useInsertionEffect as _useInsertionEffect } from './useInsertionEffect'; + +jest.mock('./useInsertionEffect', () => ({ + useInsertionEffect: jest.fn(), +})); + +const useInsertionEffect = _useInsertionEffect as jest.MockedFunction; describe('createDOMRenderer', () => { it('rehydrateCache() avoids double insertion', () => { @@ -45,11 +53,26 @@ describe('createDOMRenderer', () => { // A "server" renders components to static HTML that will be transferred to a client // + useInsertionEffect.mockImplementation(fn => fn()); + const componentHTML = renderToStaticMarkup( , ); + + useInsertionEffect.mockImplementation(React.useInsertionEffect); + expect(serverRenderer.insertionCache).toMatchInlineSnapshot(` + Object { + ".f1p9cr64{animation-name:f1kgwxhb;}": "d", + ".fe3e8s9{color:red;}": "d", + ".rp2atum:hover{color:blue;}": "r", + ".rp2atum{color:red;}": "r", + "@keyframes f1kgwxhb{from{height:10px;}to{height:20px;}}": "k", + "@media screen and (max-width: 992px){.fnao3vb:hover{color:blue;}}": "m", + } + `); + const stylesHTML = renderToStaticMarkup(<>{renderToStyleElements(serverRenderer)}); // There is no DOM on a server, style nodes should not be present @@ -61,6 +84,7 @@ describe('createDOMRenderer', () => { // const container = document.createElement('div'); + document.body.appendChild(container); container.innerHTML = componentHTML; @@ -73,13 +97,15 @@ describe('createDOMRenderer', () => { jest.spyOn(styleEl.sheet!, 'insertRule'), ); - hydrate( - // "RendererProvider" is not required there, we need it only for Jest spies - - - , - container, - ); + act(() => { + hydrateRoot( + container, + // "RendererProvider" is not required there, we need it only for Jest spies + + + , + ); + }); const styleElementsAfterHydration = document.querySelectorAll('style'); diff --git a/packages/react/src/makeResetStyles.test.tsx b/packages/react/src/makeResetStyles.test.tsx new file mode 100644 index 000000000..b8dde1008 --- /dev/null +++ b/packages/react/src/makeResetStyles.test.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { createRoot } from 'react-dom/client'; +import { act } from 'react-dom/test-utils'; + +import { makeResetStyles } from './makeResetStyles'; + +describe('makeResetStyles', () => { + it('works outside of React components', () => { + expect(() => makeResetStyles({ root: { color: 'red' } })).not.toThrow(); + }); + + it('throws inside React components', () => { + // eslint-disable-next-line @typescript-eslint/no-empty-function + jest.spyOn(console, 'error').mockImplementation(() => {}); + + const Example: React.FC = () => { + makeResetStyles({ color: 'red' }); + return null; + }; + const root = createRoot(document.createElement('div')); + + expect(() => act(() => root.render())).toThrow(/All makeResetStyles\(\) calls should be top level/); + + // Should not throw outside React components after rendering + expect(() => makeResetStyles({ color: 'red' })).not.toThrow(); + }); +}); diff --git a/packages/react/src/makeResetStyles.ts b/packages/react/src/makeResetStyles.ts index a42b98014..e72c19a74 100644 --- a/packages/react/src/makeResetStyles.ts +++ b/packages/react/src/makeResetStyles.ts @@ -1,12 +1,13 @@ import { makeResetStyles as vanillaMakeResetStyles } from '@griffel/core'; import type { GriffelResetStyle } from '@griffel/core'; -import { isInsideComponent } from './utils/isInsideComponent'; +import { insertionFactory } from './makeStyles'; import { useRenderer } from './RendererContext'; import { useTextDirection } from './TextDirectionContext'; +import { isInsideComponent } from './utils/isInsideComponent'; export function makeResetStyles(styles: GriffelResetStyle) { - const getStyles = vanillaMakeResetStyles(styles); + const getStyles = vanillaMakeResetStyles(styles, insertionFactory); if (process.env.NODE_ENV !== 'production') { if (isInsideComponent()) { diff --git a/packages/react/src/makeStyles.test.tsx b/packages/react/src/makeStyles.test.tsx index fa9e4bb30..dfa40ef9b 100644 --- a/packages/react/src/makeStyles.test.tsx +++ b/packages/react/src/makeStyles.test.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import * as ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; +import { act } from 'react-dom/test-utils'; import { makeStyles } from './makeStyles'; @@ -16,9 +17,9 @@ describe('makeStyles', () => { makeStyles({ root: { color: 'red' } }); return null; }; - const container = document.createElement('div'); + const root = createRoot(document.createElement('div')); - expect(() => ReactDOM.render(, container)).toThrow(/All makeStyles\(\) calls should be top level/); + expect(() => act(() => root.render())).toThrow(/All makeStyles\(\) calls should be top level/); // Should not throw outside React components after rendering expect(() => makeStyles({ root: { color: 'red' } })).not.toThrow(); diff --git a/packages/react/src/makeStyles.ts b/packages/react/src/makeStyles.ts index b5185921d..6c9e36f2e 100644 --- a/packages/react/src/makeStyles.ts +++ b/packages/react/src/makeStyles.ts @@ -1,12 +1,35 @@ -import { reduceToClassNameForSlots, resolveStyleRulesForSlots } from '@griffel/core'; -import type { CSSClassesMapBySlot, CSSRulesByBucket, GriffelStyle } from '@griffel/core'; +import { makeStyles as vanillaMakeStyles } from '@griffel/core'; +import type { CSSRulesByBucket, GriffelInsertionFactory, GriffelRenderer, GriffelStyle } from '@griffel/core'; import { isInsideComponent } from './utils/isInsideComponent'; import { useRenderer } from './RendererContext'; import { useTextDirection } from './TextDirectionContext'; import { useInsertionEffect } from './useInsertionEffect'; +export const insertionFactory: GriffelInsertionFactory = () => { + const insertionCache: Record = {}; + + return function insert(renderer: GriffelRenderer, dir: string, cssRules: CSSRulesByBucket) { + if (typeof document !== 'undefined' && useInsertionEffect) { + useInsertionEffect(() => { + renderer.insertCSSRules(cssRules!); + }, [renderer, cssRules]); + return; + } + + // As RTL classes are different they should have a different cache key for insertion + const rendererId = dir === 'ltr' ? renderer.id : renderer.id + 'r'; + + if (insertionCache[rendererId] === undefined) { + renderer.insertCSSRules(cssRules!); + insertionCache[rendererId] = true; + } + }; +}; + export function makeStyles(stylesBySlots: Record) { + const getStyles = vanillaMakeStyles(stylesBySlots, insertionFactory); + if (process.env.NODE_ENV !== 'production') { if (isInsideComponent()) { throw new Error( @@ -18,38 +41,10 @@ export function makeStyles(stylesBySlots: Record< } } - let classesMapBySlot: CSSClassesMapBySlot | null = null; - let cssRules: CSSRulesByBucket | null = null; - - let ltrClassNamesForSlots: Record | null = null; - let rtlClassNamesForSlots: Record | null = null; - - function computeClasses(): Record { + return function useClasses(): Record { const dir = useTextDirection(); const renderer = useRenderer(); - const isLTR = dir === 'ltr'; - - if (classesMapBySlot === null) { - [classesMapBySlot, cssRules] = resolveStyleRulesForSlots(stylesBySlots); - } - - if (isLTR) { - if (ltrClassNamesForSlots === null) { - ltrClassNamesForSlots = reduceToClassNameForSlots(classesMapBySlot, dir); - } - } else { - if (rtlClassNamesForSlots === null) { - rtlClassNamesForSlots = reduceToClassNameForSlots(classesMapBySlot, dir); - } - } - - useInsertionEffect(() => { - renderer.insertCSSRules(cssRules!); - }, [isLTR, renderer]); - - return isLTR ? (ltrClassNamesForSlots as Record) : (rtlClassNamesForSlots as Record); - } - - return computeClasses; + return getStyles({ dir, renderer }); + }; } diff --git a/packages/react/src/renderToStyleElements.test.tsx b/packages/react/src/renderToStyleElements.test.tsx index 82572a687..42aff0566 100644 --- a/packages/react/src/renderToStyleElements.test.tsx +++ b/packages/react/src/renderToStyleElements.test.tsx @@ -1,7 +1,9 @@ import { createDOMRenderer, GriffelRenderer } from '@griffel/core'; import * as prettier from 'prettier'; import * as React from 'react'; -import * as ReactDOM from 'react-dom/server'; +import { createRoot } from 'react-dom/client'; +import { renderToStaticMarkup } from 'react-dom/server'; +import { act } from 'react-dom/test-utils'; import { makeStyles } from './makeStyles'; import { makeResetStyles } from './makeResetStyles'; @@ -43,14 +45,17 @@ describe('renderToStyleElements (DOM)', () => { return
; }; + const root = createRoot(document.createElement('div')); - ReactDOM.renderToStaticMarkup( - - - , - ); + act(() => { + root.render( + + + , + ); + }); - expect(ReactDOM.renderToStaticMarkup(<>{renderToStyleElements(renderer)})).toMatchInlineSnapshot(` + expect(renderToStaticMarkup(<>{renderToStyleElements(renderer)})).toMatchInlineSnapshot(`