@pyreon/styler 0.11.4 → 0.11.6
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/README.md +27 -23
- package/lib/index.d.ts +9 -2
- package/lib/index.js +47 -4
- package/package.json +22 -22
- package/src/ThemeProvider.ts +10 -3
- package/src/__tests__/ThemeProvider.test.ts +21 -21
- package/src/__tests__/benchmark.bench.ts +56 -45
- package/src/__tests__/composition-chain.test.ts +200 -151
- package/src/__tests__/forward.test.ts +122 -122
- package/src/__tests__/globalStyle.test.ts +18 -18
- package/src/__tests__/hash.test.ts +27 -27
- package/src/__tests__/hybrid-injection.test.ts +83 -59
- package/src/__tests__/index.ts +10 -10
- package/src/__tests__/insertion-effect.test.ts +45 -32
- package/src/__tests__/integration.test.ts +81 -51
- package/src/__tests__/keyframes.test.ts +13 -13
- package/src/__tests__/memory-growth.test.ts +21 -21
- package/src/__tests__/p3-features.test.ts +162 -104
- package/src/__tests__/shared.test.ts +51 -33
- package/src/__tests__/sheet-advanced.test.ts +227 -227
- package/src/__tests__/sheet-split-atrules.test.ts +85 -85
- package/src/__tests__/sheet.test.ts +69 -69
- package/src/__tests__/styled-ssr.test.ts +36 -28
- package/src/__tests__/styled.test.ts +214 -145
- package/src/__tests__/theme.test.ts +11 -11
- package/src/__tests__/useCSS.test.ts +89 -59
- package/src/css.ts +1 -1
- package/src/forward.ts +187 -187
- package/src/globalStyle.ts +5 -5
- package/src/index.ts +15 -15
- package/src/keyframes.ts +3 -3
- package/src/resolve.ts +14 -14
- package/src/shared.ts +2 -2
- package/src/sheet.ts +26 -26
- package/src/styled.tsx +145 -100
- package/src/useCSS.ts +4 -4
|
@@ -6,100 +6,131 @@
|
|
|
6
6
|
* CSS properties like `position: absolute` might get lost.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { VNode } from
|
|
10
|
-
import { h } from
|
|
11
|
-
import { describe, expect, it } from
|
|
12
|
-
import { css } from
|
|
13
|
-
import { normalizeCSS, resolve, resolveValue } from
|
|
14
|
-
import { createSheet } from
|
|
15
|
-
import { styled } from
|
|
9
|
+
import type { VNode } from '@pyreon/core'
|
|
10
|
+
import { h } from '@pyreon/core'
|
|
11
|
+
import { describe, expect, it } from 'vitest'
|
|
12
|
+
import { css } from '../css'
|
|
13
|
+
import { normalizeCSS, resolve, resolveValue } from '../resolve'
|
|
14
|
+
import { createSheet } from '../sheet'
|
|
15
|
+
import { styled } from '../styled'
|
|
16
16
|
|
|
17
17
|
// =====================================================================
|
|
18
18
|
// LAYER 1: resolve() with nested CSSResults — the raw resolution chain
|
|
19
19
|
// =====================================================================
|
|
20
20
|
|
|
21
|
-
describe(
|
|
22
|
-
describe(
|
|
23
|
-
it(
|
|
24
|
-
const inner = css`
|
|
25
|
-
|
|
21
|
+
describe('resolve composition chain', () => {
|
|
22
|
+
describe('CSSResult nesting (css-in-css)', () => {
|
|
23
|
+
it('resolves nested css`...` calls', () => {
|
|
24
|
+
const inner = css`
|
|
25
|
+
color: red;
|
|
26
|
+
`
|
|
27
|
+
const outer = css`
|
|
28
|
+
${inner} font-size: 16px;
|
|
29
|
+
`
|
|
26
30
|
const result = normalizeCSS(resolve(outer.strings, outer.values, {}))
|
|
27
|
-
expect(result).toContain(
|
|
28
|
-
expect(result).toContain(
|
|
31
|
+
expect(result).toContain('color: red;')
|
|
32
|
+
expect(result).toContain('font-size: 16px;')
|
|
29
33
|
})
|
|
30
34
|
|
|
31
|
-
it(
|
|
32
|
-
const level3 = css`
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
it('resolves deeply nested css calls (3 levels)', () => {
|
|
36
|
+
const level3 = css`
|
|
37
|
+
position: absolute;
|
|
38
|
+
`
|
|
39
|
+
const level2 = css`
|
|
40
|
+
${level3} display: flex;
|
|
41
|
+
`
|
|
42
|
+
const level1 = css`
|
|
43
|
+
${level2} color: blue;
|
|
44
|
+
`
|
|
35
45
|
const result = normalizeCSS(resolve(level1.strings, level1.values, {}))
|
|
36
|
-
expect(result).toContain(
|
|
37
|
-
expect(result).toContain(
|
|
38
|
-
expect(result).toContain(
|
|
46
|
+
expect(result).toContain('position: absolute;')
|
|
47
|
+
expect(result).toContain('display: flex;')
|
|
48
|
+
expect(result).toContain('color: blue;')
|
|
39
49
|
})
|
|
40
50
|
|
|
41
|
-
it(
|
|
51
|
+
it('resolves array of CSS strings (processDescriptor fragments)', () => {
|
|
42
52
|
// This mimics unistyle/styles/index.ts: fragments array from propertyMap
|
|
43
53
|
const fragments = [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
'',
|
|
55
|
+
'',
|
|
56
|
+
'position: absolute;',
|
|
57
|
+
'',
|
|
58
|
+
'display: flex;',
|
|
59
|
+
'',
|
|
60
|
+
'height: 2.5rem;',
|
|
61
|
+
'',
|
|
52
62
|
]
|
|
53
|
-
const result = css
|
|
63
|
+
const result = css`
|
|
64
|
+
${fragments}
|
|
65
|
+
`
|
|
54
66
|
const resolved = normalizeCSS(resolve(result.strings, result.values, {}))
|
|
55
|
-
expect(resolved).toContain(
|
|
56
|
-
expect(resolved).toContain(
|
|
57
|
-
expect(resolved).toContain(
|
|
67
|
+
expect(resolved).toContain('position: absolute;')
|
|
68
|
+
expect(resolved).toContain('display: flex;')
|
|
69
|
+
expect(resolved).toContain('height: 2.5rem;')
|
|
58
70
|
})
|
|
59
71
|
|
|
60
|
-
it(
|
|
72
|
+
it('resolves CSSResult wrapping an array of fragments', () => {
|
|
61
73
|
// styles() returns css`${fragments}` where fragments is an array
|
|
62
|
-
const fragments = [
|
|
63
|
-
const stylesResult = css
|
|
74
|
+
const fragments = ['position: absolute;', '', 'color: red;']
|
|
75
|
+
const stylesResult = css`
|
|
76
|
+
${fragments}
|
|
77
|
+
`
|
|
64
78
|
// makeItResponsive wraps: css`${renderStyles(theme)}`
|
|
65
|
-
const mirResult = css
|
|
79
|
+
const mirResult = css`
|
|
80
|
+
${stylesResult}
|
|
81
|
+
`
|
|
66
82
|
const resolved = normalizeCSS(resolve(mirResult.strings, mirResult.values, {}))
|
|
67
|
-
expect(resolved).toContain(
|
|
68
|
-
expect(resolved).toContain(
|
|
83
|
+
expect(resolved).toContain('position: absolute;')
|
|
84
|
+
expect(resolved).toContain('color: red;')
|
|
69
85
|
})
|
|
70
86
|
})
|
|
71
87
|
|
|
72
|
-
describe(
|
|
73
|
-
it(
|
|
74
|
-
const fn = (props: any) =>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
88
|
+
describe('function interpolations (styled component render path)', () => {
|
|
89
|
+
it('resolves function that returns a CSSResult', () => {
|
|
90
|
+
const fn = (props: any) =>
|
|
91
|
+
css`
|
|
92
|
+
color: ${props.color};
|
|
93
|
+
`
|
|
94
|
+
const template = css`
|
|
95
|
+
${fn}
|
|
96
|
+
`
|
|
97
|
+
const result = normalizeCSS(resolve(template.strings, template.values, { color: 'red' }))
|
|
98
|
+
expect(result).toContain('color: red;')
|
|
78
99
|
})
|
|
79
100
|
|
|
80
|
-
it(
|
|
101
|
+
it('resolves function that returns an array (makeItResponsive responsive path)', () => {
|
|
81
102
|
// makeItResponsive returns an array when breakpoints exist
|
|
82
103
|
const fn = () => [
|
|
83
|
-
css`
|
|
84
|
-
|
|
104
|
+
css`
|
|
105
|
+
position: absolute;
|
|
106
|
+
`,
|
|
107
|
+
css`
|
|
108
|
+
@media (min-width: 36em) {
|
|
109
|
+
font-size: 2rem;
|
|
110
|
+
}
|
|
111
|
+
`,
|
|
85
112
|
]
|
|
86
|
-
const template = css
|
|
113
|
+
const template = css`
|
|
114
|
+
${fn}
|
|
115
|
+
`
|
|
87
116
|
const result = normalizeCSS(resolve(template.strings, template.values, {}))
|
|
88
|
-
expect(result).toContain(
|
|
89
|
-
expect(result).toContain(
|
|
90
|
-
expect(result).toContain(
|
|
117
|
+
expect(result).toContain('position: absolute;')
|
|
118
|
+
expect(result).toContain('@media')
|
|
119
|
+
expect(result).toContain('font-size: 2rem;')
|
|
91
120
|
})
|
|
92
121
|
|
|
93
|
-
it(
|
|
122
|
+
it('resolves function returning CSSResult containing another function', () => {
|
|
94
123
|
// This is the exact rocketstyle pattern:
|
|
95
124
|
// .styles((css) => css`${({$rocketstyle}) => { ... return css`${baseTheme};` }}`)
|
|
96
125
|
const innerFn = (props: any) => {
|
|
97
126
|
const theme = props.$rocketstyle
|
|
98
127
|
const fragments = [
|
|
99
|
-
theme.position ? `position: ${theme.position};` :
|
|
100
|
-
theme.display ? `display: ${theme.display};` :
|
|
128
|
+
theme.position ? `position: ${theme.position};` : '',
|
|
129
|
+
theme.display ? `display: ${theme.display};` : '',
|
|
101
130
|
]
|
|
102
|
-
return css
|
|
131
|
+
return css`
|
|
132
|
+
${fragments}
|
|
133
|
+
`
|
|
103
134
|
}
|
|
104
135
|
|
|
105
136
|
const outerResult = css`
|
|
@@ -109,15 +140,15 @@ describe("resolve composition chain", () => {
|
|
|
109
140
|
|
|
110
141
|
const resolved = normalizeCSS(
|
|
111
142
|
resolve(outerResult.strings, outerResult.values, {
|
|
112
|
-
$rocketstyle: { position:
|
|
143
|
+
$rocketstyle: { position: 'absolute', display: 'flex' },
|
|
113
144
|
}),
|
|
114
145
|
)
|
|
115
|
-
expect(resolved).toContain(
|
|
116
|
-
expect(resolved).toContain(
|
|
117
|
-
expect(resolved).toContain(
|
|
146
|
+
expect(resolved).toContain('font-weight: 500;')
|
|
147
|
+
expect(resolved).toContain('position: absolute;')
|
|
148
|
+
expect(resolved).toContain('display: flex;')
|
|
118
149
|
})
|
|
119
150
|
|
|
120
|
-
it(
|
|
151
|
+
it('resolves the full rocketstyle+unistyle chain pattern', () => {
|
|
121
152
|
// Simulates the full chain:
|
|
122
153
|
// 1. processDescriptor generates CSS string fragments
|
|
123
154
|
// 2. styles() wraps them in css`${fragments}`
|
|
@@ -135,12 +166,12 @@ describe("resolve composition chain", () => {
|
|
|
135
166
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: complex logic is inherent to this function
|
|
136
167
|
}) => {
|
|
137
168
|
const fragments = [
|
|
138
|
-
t.position ? `position: ${t.position};` :
|
|
139
|
-
t.display ? `display: ${t.display};` :
|
|
140
|
-
t.height ? `height: ${t.height}rem;` :
|
|
141
|
-
t.fontSize ? `font-size: ${t.fontSize}rem;` :
|
|
142
|
-
t.backgroundColor ? `background-color: ${t.backgroundColor};` :
|
|
143
|
-
t.color ? `color: ${t.color};` :
|
|
169
|
+
t.position ? `position: ${t.position};` : '',
|
|
170
|
+
t.display ? `display: ${t.display};` : '',
|
|
171
|
+
t.height ? `height: ${t.height}rem;` : '',
|
|
172
|
+
t.fontSize ? `font-size: ${t.fontSize}rem;` : '',
|
|
173
|
+
t.backgroundColor ? `background-color: ${t.backgroundColor};` : '',
|
|
174
|
+
t.color ? `color: ${t.color};` : '',
|
|
144
175
|
]
|
|
145
176
|
return cssFn`${fragments}`
|
|
146
177
|
}
|
|
@@ -180,31 +211,31 @@ describe("resolve composition chain", () => {
|
|
|
180
211
|
const stylesArray = [stylesCb(css)]
|
|
181
212
|
|
|
182
213
|
// This is the styled component template resolution
|
|
183
|
-
const templateStrings = Object.assign([
|
|
184
|
-
raw: [
|
|
214
|
+
const templateStrings = Object.assign(['\n ', ';\n'], {
|
|
215
|
+
raw: ['\n ', ';\n'],
|
|
185
216
|
}) as unknown as TemplateStringsArray
|
|
186
217
|
|
|
187
218
|
const resolved = normalizeCSS(
|
|
188
219
|
resolve(templateStrings, [stylesArray], {
|
|
189
220
|
$rocketstyle: {
|
|
190
|
-
position:
|
|
191
|
-
display:
|
|
221
|
+
position: 'absolute',
|
|
222
|
+
display: 'flex',
|
|
192
223
|
height: 2.5,
|
|
193
|
-
backgroundColor:
|
|
194
|
-
color:
|
|
224
|
+
backgroundColor: '#0070f3',
|
|
225
|
+
color: '#fff',
|
|
195
226
|
},
|
|
196
227
|
}),
|
|
197
228
|
)
|
|
198
229
|
|
|
199
|
-
expect(resolved).toContain(
|
|
200
|
-
expect(resolved).toContain(
|
|
201
|
-
expect(resolved).toContain(
|
|
202
|
-
expect(resolved).toContain(
|
|
203
|
-
expect(resolved).toContain(
|
|
204
|
-
expect(resolved).toContain(
|
|
230
|
+
expect(resolved).toContain('position: absolute;')
|
|
231
|
+
expect(resolved).toContain('display: flex;')
|
|
232
|
+
expect(resolved).toContain('height: 2.5rem;')
|
|
233
|
+
expect(resolved).toContain('background-color: #0070f3;')
|
|
234
|
+
expect(resolved).toContain('color: #fff;')
|
|
235
|
+
expect(resolved).toContain('font-weight: 500;')
|
|
205
236
|
})
|
|
206
237
|
|
|
207
|
-
it(
|
|
238
|
+
it('resolves the full chain with responsive breakpoints', () => {
|
|
208
239
|
const unistyleStyles = ({
|
|
209
240
|
theme: t,
|
|
210
241
|
css: cssFn,
|
|
@@ -213,8 +244,8 @@ describe("resolve composition chain", () => {
|
|
|
213
244
|
css: typeof css
|
|
214
245
|
}) => {
|
|
215
246
|
const fragments = [
|
|
216
|
-
t.position ? `position: ${t.position};` :
|
|
217
|
-
t.height ? `height: ${t.height}rem;` :
|
|
247
|
+
t.position ? `position: ${t.position};` : '',
|
|
248
|
+
t.height ? `height: ${t.height}rem;` : '',
|
|
218
249
|
]
|
|
219
250
|
return cssFn`${fragments}`
|
|
220
251
|
}
|
|
@@ -240,7 +271,7 @@ describe("resolve composition chain", () => {
|
|
|
240
271
|
const breakpoints = { xs: 0, md: 768 }
|
|
241
272
|
const rootSize = 16
|
|
242
273
|
const media = createMedia(css, breakpoints, rootSize)
|
|
243
|
-
const sortedBreakpoints = [
|
|
274
|
+
const sortedBreakpoints = ['xs', 'md']
|
|
244
275
|
|
|
245
276
|
// Simulate makeItResponsive with responsive path
|
|
246
277
|
const makeItResponsiveResp = (config: {
|
|
@@ -256,13 +287,13 @@ describe("resolve composition chain", () => {
|
|
|
256
287
|
// position: 'absolute' -> only first breakpoint
|
|
257
288
|
// height: { xs: 2.5, md: 5 } -> different per breakpoint
|
|
258
289
|
const optimizedTheme: Record<string, Record<string, any>> = {
|
|
259
|
-
xs: { position:
|
|
290
|
+
xs: { position: 'absolute', height: 2.5 },
|
|
260
291
|
md: { height: 5 },
|
|
261
292
|
}
|
|
262
293
|
|
|
263
294
|
return sortedBreakpoints.map((item) => {
|
|
264
295
|
const breakpointTheme = optimizedTheme[item]
|
|
265
|
-
if (!breakpointTheme || !media) return
|
|
296
|
+
if (!breakpointTheme || !media) return ''
|
|
266
297
|
const result = renderStyles(breakpointTheme)
|
|
267
298
|
return (media as Record<string, any>)[item]`
|
|
268
299
|
${result};
|
|
@@ -287,23 +318,23 @@ describe("resolve composition chain", () => {
|
|
|
287
318
|
|
|
288
319
|
const stylesArray = [stylesCb(css)]
|
|
289
320
|
|
|
290
|
-
const templateStrings = Object.assign([
|
|
291
|
-
raw: [
|
|
321
|
+
const templateStrings = Object.assign(['\n ', ';\n'], {
|
|
322
|
+
raw: ['\n ', ';\n'],
|
|
292
323
|
}) as unknown as TemplateStringsArray
|
|
293
324
|
|
|
294
325
|
const resolved = normalizeCSS(
|
|
295
326
|
resolve(templateStrings, [stylesArray], {
|
|
296
|
-
$rocketstyle: { position:
|
|
327
|
+
$rocketstyle: { position: 'absolute', height: { xs: 2.5, md: 5 } },
|
|
297
328
|
}),
|
|
298
329
|
)
|
|
299
330
|
|
|
300
331
|
// Base breakpoint (xs) should have position + height
|
|
301
|
-
expect(resolved).toContain(
|
|
302
|
-
expect(resolved).toContain(
|
|
332
|
+
expect(resolved).toContain('position: absolute;')
|
|
333
|
+
expect(resolved).toContain('height: 2.5rem;')
|
|
303
334
|
|
|
304
335
|
// md breakpoint should be in @media
|
|
305
|
-
expect(resolved).toContain(
|
|
306
|
-
expect(resolved).toContain(
|
|
336
|
+
expect(resolved).toContain('@media')
|
|
337
|
+
expect(resolved).toContain('height: 5rem;')
|
|
307
338
|
})
|
|
308
339
|
})
|
|
309
340
|
})
|
|
@@ -312,28 +343,28 @@ describe("resolve composition chain", () => {
|
|
|
312
343
|
// LAYER 2: styled component rendering — verify CSS injection + className
|
|
313
344
|
// =====================================================================
|
|
314
345
|
|
|
315
|
-
describe(
|
|
316
|
-
it(
|
|
346
|
+
describe('styled component composition', () => {
|
|
347
|
+
it('handles array of functions as single interpolation (calculateStyles pattern)', () => {
|
|
317
348
|
// This is EXACTLY what rocketstyle does:
|
|
318
349
|
// styled(component, { boost: true })`${calculateStyles(styles)};`
|
|
319
350
|
// calculateStyles returns an array of function results
|
|
320
351
|
|
|
321
|
-
const fn1 = (props: any) => `position: ${props.$rocketstyle?.position ??
|
|
322
|
-
const fn2 = (props: any) => `color: ${props.$rocketstyle?.color ??
|
|
352
|
+
const fn1 = (props: any) => `position: ${props.$rocketstyle?.position ?? 'static'};`
|
|
353
|
+
const fn2 = (props: any) => `color: ${props.$rocketstyle?.color ?? 'inherit'};`
|
|
323
354
|
|
|
324
|
-
const Comp = styled(
|
|
355
|
+
const Comp = styled('div')`
|
|
325
356
|
${[fn1, fn2]};
|
|
326
357
|
`
|
|
327
358
|
|
|
328
|
-
const vnode = Comp({ $rocketstyle: { position:
|
|
359
|
+
const vnode = Comp({ $rocketstyle: { position: 'absolute', color: 'red' } }) as VNode
|
|
329
360
|
expect(vnode.props.class).toMatch(/^pyr-/)
|
|
330
361
|
})
|
|
331
362
|
|
|
332
|
-
it(
|
|
363
|
+
it('handles function returning css`...` with nested function returning array', () => {
|
|
333
364
|
// This mimics the full .styles() -> makeItResponsive -> unistyle chain
|
|
334
365
|
const innerFn = (props: any) => {
|
|
335
366
|
const t = props.$rocketstyle
|
|
336
|
-
return [t.position ? `position: ${t.position};` :
|
|
367
|
+
return [t.position ? `position: ${t.position};` : '', t.color ? `color: ${t.color};` : '']
|
|
337
368
|
}
|
|
338
369
|
|
|
339
370
|
const outerCssResult = css`
|
|
@@ -341,25 +372,25 @@ describe("styled component composition", () => {
|
|
|
341
372
|
${innerFn};
|
|
342
373
|
`
|
|
343
374
|
|
|
344
|
-
const Comp = styled(
|
|
375
|
+
const Comp = styled('div')`
|
|
345
376
|
${[outerCssResult]};
|
|
346
377
|
`
|
|
347
378
|
|
|
348
|
-
const vnode = Comp({ $rocketstyle: { position:
|
|
379
|
+
const vnode = Comp({ $rocketstyle: { position: 'absolute', color: 'blue' } }) as VNode
|
|
349
380
|
expect(vnode.props.class).toMatch(/^pyr-/)
|
|
350
381
|
|
|
351
382
|
// Verify the CSS resolves correctly
|
|
352
383
|
const cssText = normalizeCSS(
|
|
353
384
|
resolve(outerCssResult.strings, outerCssResult.values, {
|
|
354
|
-
$rocketstyle: { position:
|
|
385
|
+
$rocketstyle: { position: 'absolute', color: 'blue' },
|
|
355
386
|
}),
|
|
356
387
|
)
|
|
357
|
-
expect(cssText).toContain(
|
|
358
|
-
expect(cssText).toContain(
|
|
359
|
-
expect(cssText).toContain(
|
|
388
|
+
expect(cssText).toContain('position: absolute;')
|
|
389
|
+
expect(cssText).toContain('color: blue;')
|
|
390
|
+
expect(cssText).toContain('font-weight: bold;')
|
|
360
391
|
})
|
|
361
392
|
|
|
362
|
-
it(
|
|
393
|
+
it('handles css result wrapping a makeItResponsive-like function', () => {
|
|
363
394
|
// makeItResponsive returns a FUNCTION
|
|
364
395
|
// This function is used as interpolation in css`${baseTheme};`
|
|
365
396
|
// That css result is used as interpolation in css`${fn};`
|
|
@@ -367,67 +398,81 @@ describe("styled component composition", () => {
|
|
|
367
398
|
|
|
368
399
|
const makeItResponsiveLike = (theme: Record<string, any>) => (_props: any) => {
|
|
369
400
|
const fragments = Object.entries(theme).map(([k, v]) => `${k}: ${v};`)
|
|
370
|
-
return css
|
|
401
|
+
return css`
|
|
402
|
+
${fragments}
|
|
403
|
+
`
|
|
371
404
|
}
|
|
372
405
|
|
|
373
406
|
const styleCallback = css`
|
|
374
407
|
font-weight: 500;
|
|
375
408
|
${(props: any) => {
|
|
376
409
|
const baseTheme = makeItResponsiveLike(props.$rocketstyle)
|
|
377
|
-
return css
|
|
410
|
+
return css`
|
|
411
|
+
${baseTheme};
|
|
412
|
+
`
|
|
378
413
|
}};
|
|
379
414
|
`
|
|
380
415
|
|
|
381
|
-
const Comp = styled(
|
|
416
|
+
const Comp = styled('div')`
|
|
382
417
|
${[styleCallback]};
|
|
383
418
|
`
|
|
384
419
|
|
|
385
|
-
const vnode = Comp({ $rocketstyle: { position:
|
|
420
|
+
const vnode = Comp({ $rocketstyle: { position: 'absolute', display: 'flex' } }) as VNode
|
|
386
421
|
expect(vnode.props.class).toMatch(/^pyr-/)
|
|
387
422
|
|
|
388
423
|
// Resolve manually to verify CSS content
|
|
389
424
|
const cssText = normalizeCSS(
|
|
390
425
|
resolve(styleCallback.strings, styleCallback.values, {
|
|
391
|
-
$rocketstyle: { position:
|
|
426
|
+
$rocketstyle: { position: 'absolute', display: 'flex' },
|
|
392
427
|
}),
|
|
393
428
|
)
|
|
394
|
-
expect(cssText).toContain(
|
|
395
|
-
expect(cssText).toContain(
|
|
429
|
+
expect(cssText).toContain('position: absolute;')
|
|
430
|
+
expect(cssText).toContain('display: flex;')
|
|
396
431
|
})
|
|
397
432
|
|
|
398
|
-
it(
|
|
433
|
+
it('wrapping a component: outer styled inherits inner className', () => {
|
|
399
434
|
// Inner is a Pyreon component wrapped by rocketstyle's styled()
|
|
400
|
-
const Inner = (props: { class?: string; $rocketstyle?: any;
|
|
401
|
-
h(
|
|
435
|
+
const Inner = (props: { class?: string; $rocketstyle?: any; 'data-testid'?: string }) =>
|
|
436
|
+
h('div', { class: props.class, 'data-testid': 'inner' })
|
|
402
437
|
|
|
403
438
|
const Outer = styled(Inner)`
|
|
404
439
|
${(props: any) => {
|
|
405
440
|
const t = props.$rocketstyle || {}
|
|
406
|
-
return `position: ${t.position ||
|
|
441
|
+
return `position: ${t.position || 'static'};`
|
|
407
442
|
}};
|
|
408
443
|
`
|
|
409
444
|
|
|
410
|
-
const vnode = Outer({ $rocketstyle: { position:
|
|
445
|
+
const vnode = Outer({ $rocketstyle: { position: 'absolute' } }) as VNode
|
|
411
446
|
// Outer renders Inner, passing className
|
|
412
447
|
expect(vnode.props.class).toMatch(/^pyr-/)
|
|
413
448
|
})
|
|
414
449
|
|
|
415
|
-
it(
|
|
450
|
+
it('CSS output contains all properties from composition chain', () => {
|
|
416
451
|
const fragments = [
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
452
|
+
'position: absolute;',
|
|
453
|
+
'',
|
|
454
|
+
'display: flex;',
|
|
455
|
+
'height: 2.5rem;',
|
|
456
|
+
'',
|
|
457
|
+
'background-color: #0070f3;',
|
|
423
458
|
]
|
|
424
|
-
const cssText = normalizeCSS(
|
|
459
|
+
const cssText = normalizeCSS(
|
|
460
|
+
resolve(
|
|
461
|
+
css`
|
|
462
|
+
${fragments}
|
|
463
|
+
`.strings,
|
|
464
|
+
css`
|
|
465
|
+
${fragments}
|
|
466
|
+
`.values,
|
|
467
|
+
{},
|
|
468
|
+
),
|
|
469
|
+
)
|
|
425
470
|
|
|
426
471
|
// Verify the resolved CSS text contains all declarations
|
|
427
|
-
expect(cssText).toContain(
|
|
428
|
-
expect(cssText).toContain(
|
|
429
|
-
expect(cssText).toContain(
|
|
430
|
-
expect(cssText).toContain(
|
|
472
|
+
expect(cssText).toContain('position: absolute;')
|
|
473
|
+
expect(cssText).toContain('display: flex;')
|
|
474
|
+
expect(cssText).toContain('height: 2.5rem;')
|
|
475
|
+
expect(cssText).toContain('background-color: #0070f3;')
|
|
431
476
|
|
|
432
477
|
// Verify it can be inserted into a sheet
|
|
433
478
|
const s = createSheet()
|
|
@@ -435,7 +480,7 @@ describe("styled component composition", () => {
|
|
|
435
480
|
expect(className).toMatch(/^pyr-/)
|
|
436
481
|
})
|
|
437
482
|
|
|
438
|
-
it(
|
|
483
|
+
it('handles the exact rocketstyle pattern with ThemeProvider context', () => {
|
|
439
484
|
// Full pattern: styled component -> function interpolation
|
|
440
485
|
// -> css result -> function -> css result -> array fragments
|
|
441
486
|
// Note: In VNode-level testing, we verify resolve output directly
|
|
@@ -444,30 +489,34 @@ describe("styled component composition", () => {
|
|
|
444
489
|
const innerFn = (props: any) => {
|
|
445
490
|
const t = props.$rocketstyle || {}
|
|
446
491
|
const fragments = [
|
|
447
|
-
t.position ? `position: ${t.position};` :
|
|
448
|
-
t.color ? `color: ${t.color};` :
|
|
449
|
-
t.fontSize ? `font-size: ${t.fontSize};` :
|
|
492
|
+
t.position ? `position: ${t.position};` : '',
|
|
493
|
+
t.color ? `color: ${t.color};` : '',
|
|
494
|
+
t.fontSize ? `font-size: ${t.fontSize};` : '',
|
|
450
495
|
]
|
|
451
|
-
return css
|
|
496
|
+
return css`
|
|
497
|
+
${fragments}
|
|
498
|
+
`
|
|
452
499
|
}
|
|
453
500
|
|
|
454
|
-
const Comp = styled(
|
|
501
|
+
const Comp = styled('div')`
|
|
455
502
|
${(props: any) => {
|
|
456
503
|
const t = props.$rocketstyle || {}
|
|
457
504
|
const fragments = [
|
|
458
|
-
t.position ? `position: ${t.position};` :
|
|
459
|
-
t.color ? `color: ${t.color};` :
|
|
460
|
-
t.fontSize ? `font-size: ${t.fontSize};` :
|
|
505
|
+
t.position ? `position: ${t.position};` : '',
|
|
506
|
+
t.color ? `color: ${t.color};` : '',
|
|
507
|
+
t.fontSize ? `font-size: ${t.fontSize};` : '',
|
|
461
508
|
]
|
|
462
|
-
return css
|
|
509
|
+
return css`
|
|
510
|
+
${fragments}
|
|
511
|
+
`
|
|
463
512
|
}};
|
|
464
513
|
`
|
|
465
514
|
|
|
466
515
|
const vnode = Comp({
|
|
467
516
|
$rocketstyle: {
|
|
468
|
-
position:
|
|
469
|
-
color:
|
|
470
|
-
fontSize:
|
|
517
|
+
position: 'absolute',
|
|
518
|
+
color: '#fff',
|
|
519
|
+
fontSize: '14px',
|
|
471
520
|
},
|
|
472
521
|
}) as VNode
|
|
473
522
|
expect(vnode.props.class).toMatch(/^pyr-/)
|
|
@@ -476,14 +525,14 @@ describe("styled component composition", () => {
|
|
|
476
525
|
const resolved = normalizeCSS(
|
|
477
526
|
resolveValue(innerFn, {
|
|
478
527
|
$rocketstyle: {
|
|
479
|
-
position:
|
|
480
|
-
color:
|
|
481
|
-
fontSize:
|
|
528
|
+
position: 'absolute',
|
|
529
|
+
color: '#fff',
|
|
530
|
+
fontSize: '14px',
|
|
482
531
|
},
|
|
483
532
|
}),
|
|
484
533
|
)
|
|
485
|
-
expect(resolved).toContain(
|
|
486
|
-
expect(resolved).toContain(
|
|
487
|
-
expect(resolved).toContain(
|
|
534
|
+
expect(resolved).toContain('position: absolute;')
|
|
535
|
+
expect(resolved).toContain('color: #fff;')
|
|
536
|
+
expect(resolved).toContain('font-size: 14px;')
|
|
488
537
|
})
|
|
489
538
|
})
|