@griffel/react 1.7.1 → 1.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main.js +20 -0
- package/.storybook/preview.js +1 -0
- package/CHANGELOG.json +1281 -0
- package/CHANGELOG.md +501 -0
- package/README.md +4 -0
- package/bundle-size/__css.fixture.js +7 -0
- package/bundle-size/__styles.fixture.js +7 -0
- package/bundle-size/makeResetStyles.fixture.js +7 -0
- package/bundle-size/makeStaticStyles.fixture.js +7 -0
- package/bundle-size/makeStyles.fixture.js +7 -0
- package/eslint.config.mjs +31 -0
- package/package.json +3 -3
- package/project.json +96 -0
- package/src/RendererContext.tsx +52 -0
- package/src/TextDirectionContext.tsx +34 -0
- package/src/__css.ts +21 -0
- package/src/__resetCSS.ts +19 -0
- package/src/__resetStyles.ts +28 -0
- package/src/{__staticCSS.js → __staticCSS.ts} +7 -6
- package/src/__staticStyles.ts +22 -0
- package/src/__styles.ts +27 -0
- package/src/createDOMRenderer.test.tsx +133 -0
- package/src/{index.d.ts → index.ts} +6 -0
- package/src/insertionFactory-node.test.ts +31 -0
- package/src/insertionFactory.test.ts +29 -0
- package/src/insertionFactory.ts +27 -0
- package/src/makeResetStyles.test.tsx +27 -0
- package/src/makeResetStyles.ts +31 -0
- package/src/makeStaticStyles.ts +23 -0
- package/src/makeStyles.test.tsx +27 -0
- package/src/makeStyles.ts +31 -0
- package/src/renderToStyleElements-node.test.tsx +418 -0
- package/src/renderToStyleElements.test.tsx +103 -0
- package/src/renderToStyleElements.ts +61 -0
- package/src/stories/ComponentStyles.stories.tsx +55 -0
- package/src/stories/DOMRendererFilter.stories.tsx +76 -0
- package/src/stories/FallbackValues.stories.tsx +50 -0
- package/src/stories/makeStyles.stories.tsx +17 -0
- package/src/useInsertionEffect.ts +11 -0
- package/src/utils/canUseDOM-node.test.ts +14 -0
- package/src/utils/canUseDOM.test.tsx +8 -0
- package/src/utils/canUseDOM.ts +8 -0
- package/src/utils/isInsideComponent.ts +41 -0
- package/tsconfig.json +20 -0
- package/tsconfig.lib.json +28 -0
- package/tsconfig.spec.json +21 -0
- package/tsconfig.storybook.json +12 -0
- package/vitest.config.ts +21 -0
- package/LICENSE.md +0 -21
- package/lib/RendererContext.cjs +0 -45
- package/lib/TextDirectionContext.cjs +0 -33
- package/lib/__css.cjs +0 -22
- package/lib/__resetCSS.cjs +0 -22
- package/lib/__resetStyles.cjs +0 -26
- package/lib/__staticCSS.cjs +0 -18
- package/lib/__staticStyles.cjs +0 -23
- package/lib/__styles.cjs +0 -26
- package/lib/index.cjs +0 -78
- package/lib/insertionFactory.cjs +0 -33
- package/lib/makeResetStyles.cjs +0 -35
- package/lib/makeStaticStyles.cjs +0 -28
- package/lib/makeStyles.cjs +0 -35
- package/lib/renderToStyleElements.cjs +0 -50
- package/lib/useInsertionEffect.cjs +0 -20
- package/lib/utils/canUseDOM.cjs +0 -17
- package/lib/utils/isInsideComponent.cjs +0 -46
- package/src/RendererContext.d.ts +0 -24
- package/src/RendererContext.js +0 -31
- package/src/RendererContext.js.map +0 -1
- package/src/TextDirectionContext.d.ts +0 -19
- package/src/TextDirectionContext.js +0 -22
- package/src/TextDirectionContext.js.map +0 -1
- package/src/__css.d.ts +0 -7
- package/src/__css.js +0 -17
- package/src/__css.js.map +0 -1
- package/src/__resetCSS.d.ts +0 -6
- package/src/__resetCSS.js +0 -17
- package/src/__resetCSS.js.map +0 -1
- package/src/__resetStyles.d.ts +0 -7
- package/src/__resetStyles.js +0 -20
- package/src/__resetStyles.js.map +0 -1
- package/src/__staticCSS.d.ts +0 -6
- package/src/__staticCSS.js.map +0 -1
- package/src/__staticStyles.d.ts +0 -7
- package/src/__staticStyles.js +0 -18
- package/src/__staticStyles.js.map +0 -1
- package/src/__styles.d.ts +0 -7
- package/src/__styles.js +0 -20
- package/src/__styles.js.map +0 -1
- package/src/index.js +0 -16
- package/src/index.js.map +0 -1
- package/src/insertionFactory.d.ts +0 -2
- package/src/insertionFactory.js +0 -21
- package/src/insertionFactory.js.map +0 -1
- package/src/makeResetStyles.d.ts +0 -2
- package/src/makeResetStyles.js +0 -23
- package/src/makeResetStyles.js.map +0 -1
- package/src/makeStaticStyles.d.ts +0 -2
- package/src/makeStaticStyles.js +0 -17
- package/src/makeStaticStyles.js.map +0 -1
- package/src/makeStyles.d.ts +0 -2
- package/src/makeStyles.js +0 -23
- package/src/makeStyles.js.map +0 -1
- package/src/renderToStyleElements.d.ts +0 -8
- package/src/renderToStyleElements.js +0 -51
- package/src/renderToStyleElements.js.map +0 -1
- package/src/useInsertionEffect.d.ts +0 -1
- package/src/useInsertionEffect.js +0 -10
- package/src/useInsertionEffect.js.map +0 -1
- package/src/utils/canUseDOM.d.ts +0 -1
- package/src/utils/canUseDOM.js +0 -9
- package/src/utils/canUseDOM.js.map +0 -1
- package/src/utils/isInsideComponent.d.ts +0 -1
- package/src/utils/isInsideComponent.js +0 -39
- package/src/utils/isInsideComponent.js.map +0 -1
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @vitest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// 👆 this is intentionally to test in SSR like environment
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect } from 'vitest';
|
|
8
|
+
import { createDOMRenderer } from '@griffel/core';
|
|
9
|
+
import * as prettier from 'prettier';
|
|
10
|
+
import type * as React from 'react';
|
|
11
|
+
import * as ReactDOM from 'react-dom/server';
|
|
12
|
+
|
|
13
|
+
import { makeStyles } from './makeStyles.js';
|
|
14
|
+
import { makeResetStyles } from './makeResetStyles.js';
|
|
15
|
+
import { RendererProvider } from './RendererContext.js';
|
|
16
|
+
import { renderToStyleElements } from './renderToStyleElements.js';
|
|
17
|
+
|
|
18
|
+
async function formatHtml(value: string) {
|
|
19
|
+
return (await prettier.format(value, { parser: 'html' })).trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('renderToStyleElements (node)', () => {
|
|
23
|
+
describe('makeStyles', () => {
|
|
24
|
+
it('supports overrides', async () => {
|
|
25
|
+
const useExampleStyles = makeStyles({
|
|
26
|
+
root: {
|
|
27
|
+
color: 'red',
|
|
28
|
+
':hover': { color: 'pink' },
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
const ExampleComponent: React.FC = () => {
|
|
32
|
+
const classes = useExampleStyles();
|
|
33
|
+
|
|
34
|
+
return <div className={classes.root} />;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const renderer = createDOMRenderer();
|
|
38
|
+
|
|
39
|
+
ReactDOM.renderToStaticMarkup(
|
|
40
|
+
<RendererProvider renderer={renderer}>
|
|
41
|
+
<ExampleComponent />
|
|
42
|
+
</RendererProvider>,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(await formatHtml(ReactDOM.renderToStaticMarkup(<>{renderToStyleElements(renderer)}</>)))
|
|
46
|
+
.toMatchInlineSnapshot(`
|
|
47
|
+
"<style
|
|
48
|
+
data-make-styles-bucket="d"
|
|
49
|
+
data-priority="0"
|
|
50
|
+
data-make-styles-rehydration="true"
|
|
51
|
+
>
|
|
52
|
+
.fe3e8s9 {
|
|
53
|
+
color: red;
|
|
54
|
+
}</style
|
|
55
|
+
><style
|
|
56
|
+
data-make-styles-bucket="h"
|
|
57
|
+
data-priority="0"
|
|
58
|
+
data-make-styles-rehydration="true"
|
|
59
|
+
>
|
|
60
|
+
.f14hep94:hover {
|
|
61
|
+
color: pink;
|
|
62
|
+
}
|
|
63
|
+
</style>"
|
|
64
|
+
`);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('supports overrides', async () => {
|
|
68
|
+
const useExampleStylesA = makeStyles({
|
|
69
|
+
root: {
|
|
70
|
+
paddingLeft: '10px',
|
|
71
|
+
margin: '10px',
|
|
72
|
+
':hover': { paddingRight: '20px' },
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
const useExampleStylesB = makeStyles({
|
|
76
|
+
root: { padding: '10px', ':hover': { padding: '20px' } },
|
|
77
|
+
});
|
|
78
|
+
const useExampleStylesC = makeStyles({
|
|
79
|
+
root: { marginLeft: '10px' },
|
|
80
|
+
});
|
|
81
|
+
const ExampleComponent: React.FC = () => {
|
|
82
|
+
useExampleStylesA();
|
|
83
|
+
useExampleStylesB();
|
|
84
|
+
useExampleStylesC();
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const renderer = createDOMRenderer();
|
|
90
|
+
|
|
91
|
+
ReactDOM.renderToStaticMarkup(
|
|
92
|
+
<RendererProvider renderer={renderer}>
|
|
93
|
+
<ExampleComponent />
|
|
94
|
+
</RendererProvider>,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(await formatHtml(ReactDOM.renderToStaticMarkup(<>{renderToStyleElements(renderer)}</>)))
|
|
98
|
+
.toMatchInlineSnapshot(`
|
|
99
|
+
"<style
|
|
100
|
+
data-make-styles-bucket="d"
|
|
101
|
+
data-priority="-1"
|
|
102
|
+
data-make-styles-rehydration="true"
|
|
103
|
+
>
|
|
104
|
+
.femlv54 {
|
|
105
|
+
margin: 10px;
|
|
106
|
+
}
|
|
107
|
+
.fbhmu18 {
|
|
108
|
+
padding: 10px;
|
|
109
|
+
}</style
|
|
110
|
+
><style
|
|
111
|
+
data-make-styles-bucket="d"
|
|
112
|
+
data-priority="0"
|
|
113
|
+
data-make-styles-rehydration="true"
|
|
114
|
+
>
|
|
115
|
+
.frdkuqy {
|
|
116
|
+
padding-left: 10px;
|
|
117
|
+
}
|
|
118
|
+
.f81rol6 {
|
|
119
|
+
padding-right: 10px;
|
|
120
|
+
}
|
|
121
|
+
.f1oou7ox {
|
|
122
|
+
margin-left: 10px;
|
|
123
|
+
}
|
|
124
|
+
.f1pxv85q {
|
|
125
|
+
margin-right: 10px;
|
|
126
|
+
}</style
|
|
127
|
+
><style
|
|
128
|
+
data-make-styles-bucket="h"
|
|
129
|
+
data-priority="-1"
|
|
130
|
+
data-make-styles-rehydration="true"
|
|
131
|
+
>
|
|
132
|
+
.fp9hkdp:hover {
|
|
133
|
+
padding: 20px;
|
|
134
|
+
}</style
|
|
135
|
+
><style
|
|
136
|
+
data-make-styles-bucket="h"
|
|
137
|
+
data-priority="0"
|
|
138
|
+
data-make-styles-rehydration="true"
|
|
139
|
+
>
|
|
140
|
+
.f19vcps:hover {
|
|
141
|
+
padding-right: 20px;
|
|
142
|
+
}
|
|
143
|
+
.f1mr755h:hover {
|
|
144
|
+
padding-left: 20px;
|
|
145
|
+
}
|
|
146
|
+
</style>"
|
|
147
|
+
`);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('handles @at rules', async () => {
|
|
151
|
+
const useExampleStyles = makeStyles({
|
|
152
|
+
media: {
|
|
153
|
+
'@media screen and (max-width: 992px)': {
|
|
154
|
+
':hover': { color: 'blue' },
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
support: {
|
|
159
|
+
'@supports (display: grid)': {
|
|
160
|
+
color: 'red',
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
const ExampleComponent: React.FC = () => {
|
|
165
|
+
const classes = useExampleStyles();
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<>
|
|
169
|
+
<div className={classes.media} />
|
|
170
|
+
<div className={classes.support} />
|
|
171
|
+
</>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const renderer = createDOMRenderer();
|
|
176
|
+
|
|
177
|
+
ReactDOM.renderToStaticMarkup(
|
|
178
|
+
<RendererProvider renderer={renderer}>
|
|
179
|
+
<ExampleComponent />
|
|
180
|
+
</RendererProvider>,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
expect(await formatHtml(ReactDOM.renderToStaticMarkup(<>{renderToStyleElements(renderer)}</>)))
|
|
184
|
+
.toMatchInlineSnapshot(`
|
|
185
|
+
"<style
|
|
186
|
+
data-make-styles-bucket="t"
|
|
187
|
+
data-priority="0"
|
|
188
|
+
data-make-styles-rehydration="true"
|
|
189
|
+
>
|
|
190
|
+
@supports (display: grid) {
|
|
191
|
+
.fo1gfrc {
|
|
192
|
+
color: red;
|
|
193
|
+
}
|
|
194
|
+
}</style
|
|
195
|
+
><style
|
|
196
|
+
media="screen and (max-width: 992px)"
|
|
197
|
+
data-make-styles-bucket="m"
|
|
198
|
+
data-priority="0"
|
|
199
|
+
data-make-styles-rehydration="true"
|
|
200
|
+
>
|
|
201
|
+
@media screen and (max-width: 992px) {
|
|
202
|
+
.fzd6x39:hover {
|
|
203
|
+
color: blue;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
</style>"
|
|
207
|
+
`);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('handles media query order', async () => {
|
|
211
|
+
const useExampleStyles = makeStyles({
|
|
212
|
+
media: {
|
|
213
|
+
color: 'red',
|
|
214
|
+
'@media (max-width: 4px)': {
|
|
215
|
+
':hover': { color: 'blue' },
|
|
216
|
+
},
|
|
217
|
+
'@media (max-width: 2px)': {
|
|
218
|
+
':hover': { color: 'blue' },
|
|
219
|
+
},
|
|
220
|
+
'@supports (display: grid)': {
|
|
221
|
+
color: 'green',
|
|
222
|
+
},
|
|
223
|
+
'@media (max-width: 3px)': {
|
|
224
|
+
':hover': { color: 'blue' },
|
|
225
|
+
},
|
|
226
|
+
'@media (max-width: 1px)': {
|
|
227
|
+
':hover': { color: 'blue' },
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
const ExampleComponent: React.FC = () => {
|
|
232
|
+
const classes = useExampleStyles();
|
|
233
|
+
|
|
234
|
+
return <div className={classes.media} />;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const mediaQueryOrder = ['(max-width: 1px)', '(max-width: 2px)', '(max-width: 3px)', '(max-width: 4px)'];
|
|
238
|
+
const renderer = createDOMRenderer(undefined, {
|
|
239
|
+
compareMediaQueries(a, b) {
|
|
240
|
+
return mediaQueryOrder.indexOf(a) - mediaQueryOrder.indexOf(b);
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
ReactDOM.renderToStaticMarkup(
|
|
245
|
+
<RendererProvider renderer={renderer}>
|
|
246
|
+
<ExampleComponent />
|
|
247
|
+
</RendererProvider>,
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
expect(await formatHtml(ReactDOM.renderToStaticMarkup(<>{renderToStyleElements(renderer)}</>)))
|
|
251
|
+
.toMatchInlineSnapshot(`
|
|
252
|
+
"<style
|
|
253
|
+
data-make-styles-bucket="d"
|
|
254
|
+
data-priority="0"
|
|
255
|
+
data-make-styles-rehydration="true"
|
|
256
|
+
>
|
|
257
|
+
.fe3e8s9 {
|
|
258
|
+
color: red;
|
|
259
|
+
}</style
|
|
260
|
+
><style
|
|
261
|
+
data-make-styles-bucket="t"
|
|
262
|
+
data-priority="0"
|
|
263
|
+
data-make-styles-rehydration="true"
|
|
264
|
+
>
|
|
265
|
+
@supports (display: grid) {
|
|
266
|
+
.fui0tgz {
|
|
267
|
+
color: green;
|
|
268
|
+
}
|
|
269
|
+
}</style
|
|
270
|
+
><style
|
|
271
|
+
media="(max-width: 1px)"
|
|
272
|
+
data-make-styles-bucket="m"
|
|
273
|
+
data-priority="0"
|
|
274
|
+
data-make-styles-rehydration="true"
|
|
275
|
+
>
|
|
276
|
+
@media (max-width: 1px) {
|
|
277
|
+
.f13d6lhy:hover {
|
|
278
|
+
color: blue;
|
|
279
|
+
}
|
|
280
|
+
}</style
|
|
281
|
+
><style
|
|
282
|
+
media="(max-width: 2px)"
|
|
283
|
+
data-make-styles-bucket="m"
|
|
284
|
+
data-priority="0"
|
|
285
|
+
data-make-styles-rehydration="true"
|
|
286
|
+
>
|
|
287
|
+
@media (max-width: 2px) {
|
|
288
|
+
.f1b07yzi:hover {
|
|
289
|
+
color: blue;
|
|
290
|
+
}
|
|
291
|
+
}</style
|
|
292
|
+
><style
|
|
293
|
+
media="(max-width: 3px)"
|
|
294
|
+
data-make-styles-bucket="m"
|
|
295
|
+
data-priority="0"
|
|
296
|
+
data-make-styles-rehydration="true"
|
|
297
|
+
>
|
|
298
|
+
@media (max-width: 3px) {
|
|
299
|
+
.f1cy3850:hover {
|
|
300
|
+
color: blue;
|
|
301
|
+
}
|
|
302
|
+
}</style
|
|
303
|
+
><style
|
|
304
|
+
media="(max-width: 4px)"
|
|
305
|
+
data-make-styles-bucket="m"
|
|
306
|
+
data-priority="0"
|
|
307
|
+
data-make-styles-rehydration="true"
|
|
308
|
+
>
|
|
309
|
+
@media (max-width: 4px) {
|
|
310
|
+
.fyvg8w:hover {
|
|
311
|
+
color: blue;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
</style>"
|
|
315
|
+
`);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('handles keyframes', async () => {
|
|
319
|
+
const useExampleStyles = makeStyles({
|
|
320
|
+
keyframe: {
|
|
321
|
+
animationName: {
|
|
322
|
+
from: {
|
|
323
|
+
transform: 'rotate(0deg)',
|
|
324
|
+
},
|
|
325
|
+
to: {
|
|
326
|
+
transform: 'rotate(360deg)',
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
const ExampleComponent: React.FC = () => {
|
|
332
|
+
const classes = useExampleStyles();
|
|
333
|
+
|
|
334
|
+
return <div className={classes.keyframe} />;
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const renderer = createDOMRenderer();
|
|
338
|
+
|
|
339
|
+
ReactDOM.renderToStaticMarkup(
|
|
340
|
+
<RendererProvider renderer={renderer}>
|
|
341
|
+
<ExampleComponent />
|
|
342
|
+
</RendererProvider>,
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
expect(await formatHtml(ReactDOM.renderToStaticMarkup(<>{renderToStyleElements(renderer)}</>)))
|
|
346
|
+
.toMatchInlineSnapshot(`
|
|
347
|
+
"<style
|
|
348
|
+
data-make-styles-bucket="d"
|
|
349
|
+
data-priority="0"
|
|
350
|
+
data-make-styles-rehydration="true"
|
|
351
|
+
>
|
|
352
|
+
.f1g6ul6r {
|
|
353
|
+
animation-name: f1q8eu9e;
|
|
354
|
+
}
|
|
355
|
+
.f1fp4ujf {
|
|
356
|
+
animation-name: f55c0se;
|
|
357
|
+
}</style
|
|
358
|
+
><style
|
|
359
|
+
data-make-styles-bucket="k"
|
|
360
|
+
data-priority="0"
|
|
361
|
+
data-make-styles-rehydration="true"
|
|
362
|
+
>
|
|
363
|
+
@keyframes f1q8eu9e {
|
|
364
|
+
from {
|
|
365
|
+
transform: rotate(0deg);
|
|
366
|
+
}
|
|
367
|
+
to {
|
|
368
|
+
transform: rotate(360deg);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
@keyframes f55c0se {
|
|
372
|
+
from {
|
|
373
|
+
transform: rotate(0deg);
|
|
374
|
+
}
|
|
375
|
+
to {
|
|
376
|
+
transform: rotate(-360deg);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
</style>"
|
|
380
|
+
`);
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
describe('makeResetStyles', () => {
|
|
385
|
+
it('renders styles', async () => {
|
|
386
|
+
const useClassName = makeResetStyles({
|
|
387
|
+
color: 'red',
|
|
388
|
+
':hover': { color: 'pink' },
|
|
389
|
+
});
|
|
390
|
+
const ExampleComponent: React.FC = () => {
|
|
391
|
+
return <div className={useClassName()} />;
|
|
392
|
+
};
|
|
393
|
+
const renderer = createDOMRenderer();
|
|
394
|
+
|
|
395
|
+
ReactDOM.renderToStaticMarkup(
|
|
396
|
+
<RendererProvider renderer={renderer}>
|
|
397
|
+
<ExampleComponent />
|
|
398
|
+
</RendererProvider>,
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
expect(await formatHtml(ReactDOM.renderToStaticMarkup(<>{renderToStyleElements(renderer)}</>)))
|
|
402
|
+
.toMatchInlineSnapshot(`
|
|
403
|
+
"<style
|
|
404
|
+
data-make-styles-bucket="r"
|
|
405
|
+
data-priority="0"
|
|
406
|
+
data-make-styles-rehydration="true"
|
|
407
|
+
>
|
|
408
|
+
.r1tsu58y {
|
|
409
|
+
color: red;
|
|
410
|
+
}
|
|
411
|
+
.r1tsu58y:hover {
|
|
412
|
+
color: pink;
|
|
413
|
+
}
|
|
414
|
+
</style>"
|
|
415
|
+
`);
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import type { GriffelRenderer } from '@griffel/core';
|
|
3
|
+
import { createDOMRenderer } from '@griffel/core';
|
|
4
|
+
import * as prettier from 'prettier';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { createRoot } from 'react-dom/client';
|
|
7
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
8
|
+
|
|
9
|
+
import { makeStyles } from './makeStyles.js';
|
|
10
|
+
import { makeResetStyles } from './makeResetStyles.js';
|
|
11
|
+
import { RendererProvider } from './RendererContext.js';
|
|
12
|
+
import { renderToStyleElements } from './renderToStyleElements.js';
|
|
13
|
+
|
|
14
|
+
async function formatHtml(value: string) {
|
|
15
|
+
return (await prettier.format(value, { parser: 'html' })).trim();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('renderToStyleElements (DOM)', () => {
|
|
19
|
+
let renderer: GriffelRenderer;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
process.env.NODE_ENV = 'production';
|
|
23
|
+
renderer = createDOMRenderer(document);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
document.head.innerHTML = '';
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('makeStyles', async () => {
|
|
31
|
+
const useExampleStyles = makeStyles({
|
|
32
|
+
root: { color: 'red', ':hover': { color: 'green' } },
|
|
33
|
+
});
|
|
34
|
+
const ExampleComponent: React.FC = () => {
|
|
35
|
+
const classes = useExampleStyles();
|
|
36
|
+
|
|
37
|
+
return <div className={classes.root} />;
|
|
38
|
+
};
|
|
39
|
+
const root = createRoot(document.createElement('div'));
|
|
40
|
+
|
|
41
|
+
React.act(() => {
|
|
42
|
+
root.render(
|
|
43
|
+
<RendererProvider renderer={renderer}>
|
|
44
|
+
<ExampleComponent />
|
|
45
|
+
</RendererProvider>,
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(await formatHtml(renderToStaticMarkup(<>{renderToStyleElements(renderer)}</>))).toMatchInlineSnapshot(`
|
|
50
|
+
"<style
|
|
51
|
+
data-make-styles-bucket="d"
|
|
52
|
+
data-priority="0"
|
|
53
|
+
data-make-styles-rehydration="true"
|
|
54
|
+
>
|
|
55
|
+
.fe3e8s9 {
|
|
56
|
+
color: red;
|
|
57
|
+
}</style
|
|
58
|
+
><style
|
|
59
|
+
data-make-styles-bucket="h"
|
|
60
|
+
data-priority="0"
|
|
61
|
+
data-make-styles-rehydration="true"
|
|
62
|
+
>
|
|
63
|
+
.f1ej289o:hover {
|
|
64
|
+
color: green;
|
|
65
|
+
}
|
|
66
|
+
</style>"
|
|
67
|
+
`);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('makeResetStyles', async () => {
|
|
71
|
+
const useClassName = makeResetStyles({
|
|
72
|
+
color: 'red',
|
|
73
|
+
':hover': { color: 'pink' },
|
|
74
|
+
});
|
|
75
|
+
const ExampleComponent: React.FC = () => {
|
|
76
|
+
return <div className={useClassName()} />;
|
|
77
|
+
};
|
|
78
|
+
const root = createRoot(document.createElement('div'));
|
|
79
|
+
|
|
80
|
+
React.act(() => {
|
|
81
|
+
root.render(
|
|
82
|
+
<RendererProvider renderer={renderer}>
|
|
83
|
+
<ExampleComponent />
|
|
84
|
+
</RendererProvider>,
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(await formatHtml(renderToStaticMarkup(<>{renderToStyleElements(renderer)}</>))).toMatchInlineSnapshot(`
|
|
89
|
+
"<style
|
|
90
|
+
data-make-styles-bucket="r"
|
|
91
|
+
data-priority="0"
|
|
92
|
+
data-make-styles-rehydration="true"
|
|
93
|
+
>
|
|
94
|
+
.r1tsu58y {
|
|
95
|
+
color: red;
|
|
96
|
+
}
|
|
97
|
+
.r1tsu58y:hover {
|
|
98
|
+
color: pink;
|
|
99
|
+
}
|
|
100
|
+
</style>"
|
|
101
|
+
`);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { styleBucketOrdering } from '@griffel/core';
|
|
4
|
+
import { createElement } from 'react';
|
|
5
|
+
import type { ReactElement } from 'react';
|
|
6
|
+
import type { GriffelRenderer } from '@griffel/core';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* This method returns a list of <style> React elements with the rendered CSS. This is useful for Server-Side rendering.
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export function renderToStyleElements(renderer: GriffelRenderer): ReactElement[] {
|
|
14
|
+
const stylesheets = Object.values(renderer.stylesheets)
|
|
15
|
+
// first sort: bucket by order [data-priority]
|
|
16
|
+
.sort((a, b) => {
|
|
17
|
+
return Number(a.elementAttributes['data-priority']) - Number(b.elementAttributes['data-priority']);
|
|
18
|
+
})
|
|
19
|
+
// second sort: bucket by bucket name
|
|
20
|
+
.sort((a, b) => {
|
|
21
|
+
return styleBucketOrdering.indexOf(a.bucketName) - styleBucketOrdering.indexOf(b.bucketName);
|
|
22
|
+
})
|
|
23
|
+
// third sort: media queries
|
|
24
|
+
.sort((a, b) => {
|
|
25
|
+
const mediaA = a.elementAttributes['media'];
|
|
26
|
+
const mediaB = b.elementAttributes['media'];
|
|
27
|
+
|
|
28
|
+
if (mediaA && mediaB) {
|
|
29
|
+
return renderer.compareMediaQueries(mediaA, mediaB);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (mediaA || mediaB) {
|
|
33
|
+
return mediaA ? 1 : -1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return 0;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return stylesheets
|
|
40
|
+
.map(stylesheet => {
|
|
41
|
+
const cssRules = stylesheet.cssRules();
|
|
42
|
+
// don't want to create any empty style elements
|
|
43
|
+
if (!cssRules.length) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return createElement('style', {
|
|
48
|
+
key: stylesheet.bucketName,
|
|
49
|
+
|
|
50
|
+
// TODO: support "nonce"
|
|
51
|
+
// ...renderer.styleNodeAttributes,
|
|
52
|
+
...stylesheet.elementAttributes,
|
|
53
|
+
'data-make-styles-rehydration': 'true',
|
|
54
|
+
|
|
55
|
+
dangerouslySetInnerHTML: {
|
|
56
|
+
__html: cssRules.join(''),
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
})
|
|
60
|
+
.filter(Boolean) as ReactElement[];
|
|
61
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
import type { StoryObj } from '@storybook/react-vite';
|
|
3
|
+
|
|
4
|
+
import { makeStyles, mergeClasses, shorthands } from '../';
|
|
5
|
+
|
|
6
|
+
const tokens = {
|
|
7
|
+
brandBackground: '#106ebe',
|
|
8
|
+
brandBackgroundHover: '#2899f5',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const useButtonStyles = makeStyles({
|
|
12
|
+
root: {
|
|
13
|
+
backgroundColor: 'transparent',
|
|
14
|
+
outlineStyle: 'none',
|
|
15
|
+
...shorthands.borderRadius('.3em'),
|
|
16
|
+
...shorthands.border('1px', 'solid', '#333'),
|
|
17
|
+
...shorthands.padding('.3em', '1em'),
|
|
18
|
+
':hover': {
|
|
19
|
+
cursor: 'pointer',
|
|
20
|
+
backgroundColor: '#ddd',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
primary: {
|
|
25
|
+
backgroundColor: tokens.brandBackground,
|
|
26
|
+
color: '#eff6fc',
|
|
27
|
+
':hover': {
|
|
28
|
+
backgroundColor: tokens.brandBackgroundHover,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const Button: React.FunctionComponent<{ className?: string; primary?: boolean }> = ({
|
|
34
|
+
className,
|
|
35
|
+
primary = false,
|
|
36
|
+
...props
|
|
37
|
+
}) => {
|
|
38
|
+
const classes = useButtonStyles();
|
|
39
|
+
const mergedClasses = mergeClasses(classes.root, primary && classes.primary, className);
|
|
40
|
+
return <button {...props} className={mergedClasses} />;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const ComponentStyles: StoryObj<{ primary: boolean }> = {
|
|
44
|
+
render: args => {
|
|
45
|
+
return <Button {...args}>button</Button>;
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
args: {
|
|
49
|
+
primary: false,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default {
|
|
54
|
+
title: 'Component styles',
|
|
55
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { StoryObj } from '@storybook/react-vite';
|
|
3
|
+
|
|
4
|
+
import { createDOMRenderer } from '@griffel/core';
|
|
5
|
+
import { makeStyles, RendererProvider, shorthands } from '../';
|
|
6
|
+
|
|
7
|
+
const useStyles = makeStyles({
|
|
8
|
+
root: {
|
|
9
|
+
width: '200px',
|
|
10
|
+
height: '100px',
|
|
11
|
+
display: 'flex',
|
|
12
|
+
justifyContent: 'center',
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
...shorthands.border('1px', 'solid', '#333'),
|
|
15
|
+
backgroundColor: 'cornflowerblue',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const ColorBox: React.FC = () => {
|
|
20
|
+
const classes = useStyles();
|
|
21
|
+
|
|
22
|
+
return <div className={classes.root}>blue box</div>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const CustomRendererProvider: React.FC<{ children: React.ReactNode; filterEnabled: boolean }> = ({
|
|
26
|
+
children,
|
|
27
|
+
filterEnabled,
|
|
28
|
+
}) => {
|
|
29
|
+
const customDOMRenderer = React.useMemo(() => {
|
|
30
|
+
return createDOMRenderer(undefined, {
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
32
|
+
unstable_filterCSSRule: cssRule => {
|
|
33
|
+
// Filter out background-color
|
|
34
|
+
return !filterEnabled || cssRule.indexOf('{background-color:') === -1;
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}, [filterEnabled]);
|
|
38
|
+
|
|
39
|
+
return <RendererProvider renderer={customDOMRenderer}>{children}</RendererProvider>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const DOMRendererFilter: StoryObj<{ filterEnabled: boolean }> = {
|
|
43
|
+
render: ({ filterEnabled }) => {
|
|
44
|
+
return (
|
|
45
|
+
<CustomRendererProvider filterEnabled={filterEnabled}>
|
|
46
|
+
<p>
|
|
47
|
+
It is possible to define a filter function in <code>DOMRenderer</code> to filter out CSS rules before adding
|
|
48
|
+
them to DOM. The classes are still applied to an element.
|
|
49
|
+
</p>
|
|
50
|
+
<p>Once the CSS rule is added, there is no way to remove it.</p>
|
|
51
|
+
<ol>
|
|
52
|
+
<li>Render this story with filter enabled → it renders the box without a background.</li>
|
|
53
|
+
<li>Disable the filter → story is re-rendered, blue background is added to the box.</li>
|
|
54
|
+
<li>
|
|
55
|
+
Enable the filter again → there is still the blue background, you need to refresh the page to get rid
|
|
56
|
+
of the background
|
|
57
|
+
</li>
|
|
58
|
+
</ol>
|
|
59
|
+
<p style={{ color: 'red' }}>
|
|
60
|
+
Filter is currently marked as unstable. This functionality can be changed or removed without considering it a
|
|
61
|
+
breaking change!
|
|
62
|
+
</p>
|
|
63
|
+
|
|
64
|
+
<ColorBox />
|
|
65
|
+
</CustomRendererProvider>
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
args: {
|
|
70
|
+
filterEnabled: true,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default {
|
|
75
|
+
title: 'unstable_filterCSSRule',
|
|
76
|
+
};
|