@@ -5,6 +5,23 @@ import { CacheProvider } from '@emotion/react';
5
5
import createCache from '@emotion/cache' ;
6
6
import { StyleSheet } from '@emotion/sheet' ;
7
7
8
+ // Need to add a private variable to test the generated CSS from Emotion, this is the simplest way to do it.
9
+ // We can't test the CSS from `style` tag easily because the `speedy: true` (produce empty text content) is enabled by Emotion.
10
+ // Even if we disable it, JSDOM needs extra configuration to be able to parse `@layer` CSS.
11
+ export const TEST_INTERNALS_DO_NOT_USE = {
12
+ /**
13
+ * to intercept the generated CSS before inserting to the style tag, so that we can check the generated CSS.
14
+ *
15
+ * let rule;
16
+ * TEST_INTERNALS_DO_NOT_USE.insert = (...args) => {
17
+ * rule = args[0];
18
+ * };
19
+ *
20
+ * expect(rule).to.equal(...);
21
+ */
22
+ insert : undefined ,
23
+ } ;
24
+
8
25
// We might be able to remove this when this issue is fixed:
9
26
// https://github.com/emotion-js/emotion/issues/2790
10
27
const createEmotionCache = ( options , CustomSheet ) => {
@@ -23,11 +40,11 @@ const createEmotionCache = (options, CustomSheet) => {
23
40
return cache ;
24
41
} ;
25
42
26
- let cache ;
43
+ let insertionPoint ;
27
44
if ( typeof document === 'object' ) {
28
45
// Use `insertionPoint` over `prepend`(deprecated) because it can be controlled for GlobalStyles injection order
29
46
// For more information, see https://github.com/mui/material-ui/issues/44597
30
- let insertionPoint = document . querySelector ( '[name="emotion-insertion-point"]' ) ;
47
+ insertionPoint = document . querySelector ( '[name="emotion-insertion-point"]' ) ;
31
48
if ( ! insertionPoint ) {
32
49
insertionPoint = document . createElement ( 'meta' ) ;
33
50
insertionPoint . setAttribute ( 'name' , 'emotion-insertion-point' ) ;
@@ -37,32 +54,67 @@ if (typeof document === 'object') {
37
54
head . prepend ( insertionPoint ) ;
38
55
}
39
56
}
40
- /**
41
- * This is for client-side apps only.
42
- * A custom sheet is required to make the GlobalStyles API injected above the insertion point.
43
- * This is because the [sheet](https://github.com/emotion-js/emotion/blob/main/packages/react/src/global.js#L94-L99) does not consume the options.
44
- */
45
- class MyStyleSheet extends StyleSheet {
46
- insert ( rule , options ) {
47
- if ( this . key && this . key . endsWith ( 'global' ) ) {
48
- this . before = insertionPoint ;
57
+ }
58
+
59
+ function getCache ( injectFirst , enableCssLayer ) {
60
+ if ( injectFirst || enableCssLayer ) {
61
+ /**
62
+ * This is for client-side apps only.
63
+ * A custom sheet is required to make the GlobalStyles API injected above the insertion point.
64
+ * This is because the [sheet](https://github.com/emotion-js/emotion/blob/main/packages/react/src/global.js#L94-L99) does not consume the options.
65
+ */
66
+ class MyStyleSheet extends StyleSheet {
67
+ insert ( rule , options ) {
68
+ if ( TEST_INTERNALS_DO_NOT_USE . insert ) {
69
+ return TEST_INTERNALS_DO_NOT_USE . insert ( rule , options ) ;
70
+ }
71
+ if ( this . key && this . key . endsWith ( 'global' ) ) {
72
+ this . before = insertionPoint ;
73
+ }
74
+ return super . insert ( rule , options ) ;
49
75
}
50
- return super . insert ( rule , options ) ;
51
76
}
77
+ const emotionCache = createEmotionCache (
78
+ {
79
+ key : 'css' ,
80
+ insertionPoint : injectFirst ? insertionPoint : undefined ,
81
+ } ,
82
+ MyStyleSheet ,
83
+ ) ;
84
+ if ( enableCssLayer ) {
85
+ const prevInsert = emotionCache . insert ;
86
+ emotionCache . insert = ( ...args ) => {
87
+ if ( ! args [ 1 ] . styles . startsWith ( '@layer' ) ) {
88
+ // avoid nested @layer
89
+ args [ 1 ] . styles = `@layer mui {${ args [ 1 ] . styles } }` ;
90
+ }
91
+ return prevInsert ( ...args ) ;
92
+ } ;
93
+ }
94
+ return emotionCache ;
52
95
}
53
- cache = createEmotionCache ( { key : 'css' , insertionPoint } , MyStyleSheet ) ;
96
+ return undefined ;
54
97
}
55
98
56
99
export default function StyledEngineProvider ( props ) {
57
- const { injectFirst, children } = props ;
58
- return injectFirst && cache ? < CacheProvider value = { cache } > { children } </ CacheProvider > : children ;
100
+ const { injectFirst, enableCssLayer, children } = props ;
101
+ const cache = React . useMemo (
102
+ ( ) => getCache ( injectFirst , enableCssLayer ) ,
103
+ [ injectFirst , enableCssLayer ] ,
104
+ ) ;
105
+ return cache ? < CacheProvider value = { cache } > { children } </ CacheProvider > : children ;
59
106
}
60
107
61
108
StyledEngineProvider . propTypes = {
62
109
/**
63
110
* Your component tree.
64
111
*/
65
112
children : PropTypes . node ,
113
+ /**
114
+ * If `true`, the styles are wrapped in `@layer mui`.
115
+ * Learn more about [Cascade layers](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Cascade_layers).
116
+ */
117
+ enableCssLayer : PropTypes . bool ,
66
118
/**
67
119
* By default, the styles are injected last in the <head> element of the page.
68
120
* As a result, they gain more specificity than any other style sheet.
0 commit comments