@pyreon/rocketstyle 0.11.5 → 0.11.7
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 +32 -32
- package/lib/index.d.ts +25 -6
- package/lib/index.js +122 -97
- package/package.json +25 -24
- package/src/__tests__/attrs.test.ts +49 -49
- package/src/__tests__/chaining.test.ts +27 -27
- package/src/__tests__/collection.test.ts +12 -12
- package/src/__tests__/compose.test.ts +10 -10
- package/src/__tests__/context.test.ts +65 -65
- package/src/__tests__/createLocalProvider.test.ts +53 -53
- package/src/__tests__/dimensions.test.ts +54 -54
- package/src/__tests__/e2e-styler.test.ts +142 -136
- package/src/__tests__/hooks.test.ts +41 -70
- package/src/__tests__/isRocketComponent.test.ts +11 -11
- package/src/__tests__/misc.test.ts +91 -91
- package/src/__tests__/providerConsumer.test.ts +54 -126
- package/src/__tests__/rocketstyleIntegration.test.ts +182 -255
- package/src/__tests__/themeUtils.test.ts +173 -173
- package/src/cache/index.ts +1 -1
- package/src/constants/booleanTags.ts +25 -25
- package/src/constants/defaultDimensions.ts +5 -5
- package/src/constants/index.ts +16 -16
- package/src/context/context.ts +13 -10
- package/src/context/createLocalProvider.ts +26 -13
- package/src/context/localContext.ts +2 -2
- package/src/hoc/index.ts +1 -1
- package/src/hoc/rocketstyleAttrsHoc.ts +26 -29
- package/src/hooks/index.ts +2 -2
- package/src/hooks/usePseudoState.ts +3 -3
- package/src/hooks/useTheme.ts +14 -17
- package/src/index.ts +32 -15
- package/src/init.ts +12 -12
- package/src/isRocketComponent.ts +2 -2
- package/src/rocketstyle.ts +125 -112
- package/src/types/attrs.ts +2 -2
- package/src/types/config.ts +4 -4
- package/src/types/configuration.ts +5 -5
- package/src/types/dimensions.ts +5 -5
- package/src/types/hoc.ts +1 -1
- package/src/types/rocketComponent.ts +4 -4
- package/src/types/rocketstyle.ts +10 -10
- package/src/types/styles.ts +9 -4
- package/src/types/theme.ts +4 -4
- package/src/types/utils.ts +1 -1
- package/src/utils/attrs.ts +2 -2
- package/src/utils/chaining.ts +2 -2
- package/src/utils/compose.ts +1 -1
- package/src/utils/dimensions.ts +6 -6
- package/src/utils/statics.ts +2 -2
- package/src/utils/styles.ts +2 -2
- package/src/utils/theme.ts +10 -10
|
@@ -7,89 +7,89 @@ import {
|
|
|
7
7
|
getValues,
|
|
8
8
|
isMultiKey,
|
|
9
9
|
isValidKey,
|
|
10
|
-
} from
|
|
10
|
+
} from '../utils/dimensions'
|
|
11
11
|
|
|
12
|
-
describe(
|
|
13
|
-
it(
|
|
14
|
-
expect(isValidKey(
|
|
12
|
+
describe('isValidKey', () => {
|
|
13
|
+
it('returns true for truthy values', () => {
|
|
14
|
+
expect(isValidKey('a')).toBe(true)
|
|
15
15
|
expect(isValidKey(1)).toBe(true)
|
|
16
16
|
expect(isValidKey(true)).toBe(true)
|
|
17
17
|
expect(isValidKey(0)).toBe(true)
|
|
18
|
-
expect(isValidKey(
|
|
18
|
+
expect(isValidKey('')).toBe(true)
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
it(
|
|
21
|
+
it('returns false for undefined, null, false', () => {
|
|
22
22
|
expect(isValidKey(undefined)).toBe(false)
|
|
23
23
|
expect(isValidKey(null)).toBe(false)
|
|
24
24
|
expect(isValidKey(false)).toBe(false)
|
|
25
25
|
})
|
|
26
26
|
})
|
|
27
27
|
|
|
28
|
-
describe(
|
|
29
|
-
it(
|
|
30
|
-
expect(isMultiKey({ propName:
|
|
28
|
+
describe('isMultiKey', () => {
|
|
29
|
+
it('returns [true, propName] for object with propName', () => {
|
|
30
|
+
expect(isMultiKey({ propName: 'tags', multi: true })).toEqual([true, 'tags'])
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
it(
|
|
34
|
-
expect(isMultiKey(
|
|
33
|
+
it('returns [false, value] for string', () => {
|
|
34
|
+
expect(isMultiKey('variant')).toEqual([false, 'variant'])
|
|
35
35
|
})
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
describe(
|
|
39
|
-
it(
|
|
40
|
-
expect(getKeys({ a: 1, b: 2 })).toEqual([
|
|
38
|
+
describe('getKeys', () => {
|
|
39
|
+
it('returns object keys', () => {
|
|
40
|
+
expect(getKeys({ a: 1, b: 2 })).toEqual(['a', 'b'])
|
|
41
41
|
})
|
|
42
42
|
|
|
43
|
-
it(
|
|
43
|
+
it('returns empty array for empty object', () => {
|
|
44
44
|
expect(getKeys({})).toEqual([])
|
|
45
45
|
})
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
-
describe(
|
|
49
|
-
it(
|
|
48
|
+
describe('getValues', () => {
|
|
49
|
+
it('returns object values', () => {
|
|
50
50
|
expect(getValues({ a: 1, b: 2 })).toEqual([1, 2])
|
|
51
51
|
})
|
|
52
52
|
})
|
|
53
53
|
|
|
54
|
-
describe(
|
|
55
|
-
it(
|
|
56
|
-
const dimensions = { size:
|
|
57
|
-
expect(getDimensionsValues(dimensions)).toEqual([
|
|
54
|
+
describe('getDimensionsValues', () => {
|
|
55
|
+
it('extracts string dimension values', () => {
|
|
56
|
+
const dimensions = { size: 'size', variant: 'variant' }
|
|
57
|
+
expect(getDimensionsValues(dimensions)).toEqual(['size', 'variant'])
|
|
58
58
|
})
|
|
59
59
|
|
|
60
|
-
it(
|
|
60
|
+
it('extracts propName from object dimensions', () => {
|
|
61
61
|
const dimensions = {
|
|
62
|
-
size:
|
|
63
|
-
tags: { propName:
|
|
62
|
+
size: 'size',
|
|
63
|
+
tags: { propName: 'tags', multi: true },
|
|
64
64
|
}
|
|
65
|
-
expect(getDimensionsValues(dimensions)).toEqual([
|
|
65
|
+
expect(getDimensionsValues(dimensions)).toEqual(['size', 'tags'])
|
|
66
66
|
})
|
|
67
67
|
})
|
|
68
68
|
|
|
69
|
-
describe(
|
|
70
|
-
it(
|
|
69
|
+
describe('getMultipleDimensions', () => {
|
|
70
|
+
it('identifies multi-key dimensions', () => {
|
|
71
71
|
const dimensions = {
|
|
72
|
-
size:
|
|
73
|
-
tags: { propName:
|
|
72
|
+
size: 'size',
|
|
73
|
+
tags: { propName: 'tags', multi: true },
|
|
74
74
|
}
|
|
75
75
|
expect(getMultipleDimensions(dimensions)).toEqual({ tags: true })
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
it(
|
|
79
|
-
const dimensions = { size:
|
|
78
|
+
it('returns empty for no multi dimensions', () => {
|
|
79
|
+
const dimensions = { size: 'size', variant: 'variant' }
|
|
80
80
|
expect(getMultipleDimensions(dimensions)).toEqual({})
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
-
it(
|
|
83
|
+
it('skips multi=false', () => {
|
|
84
84
|
const dimensions = {
|
|
85
|
-
tags: { propName:
|
|
85
|
+
tags: { propName: 'tags', multi: false },
|
|
86
86
|
}
|
|
87
87
|
expect(getMultipleDimensions(dimensions)).toEqual({})
|
|
88
88
|
})
|
|
89
89
|
})
|
|
90
90
|
|
|
91
|
-
describe(
|
|
92
|
-
it(
|
|
91
|
+
describe('getDimensionsMap', () => {
|
|
92
|
+
it('builds keysMap and keywords from themes', () => {
|
|
93
93
|
const themes = {
|
|
94
94
|
size: { small: { fontSize: 12 }, large: { fontSize: 18 } },
|
|
95
95
|
}
|
|
@@ -100,7 +100,7 @@ describe("getDimensionsMap", () => {
|
|
|
100
100
|
expect(result.keywords.size).toBe(true)
|
|
101
101
|
})
|
|
102
102
|
|
|
103
|
-
it(
|
|
103
|
+
it('adds dimension keys to keywords when useBooleans', () => {
|
|
104
104
|
const themes = {
|
|
105
105
|
size: { small: { fontSize: 12 }, large: { fontSize: 18 } },
|
|
106
106
|
}
|
|
@@ -109,7 +109,7 @@ describe("getDimensionsMap", () => {
|
|
|
109
109
|
expect(result.keywords.large).toBe(true)
|
|
110
110
|
})
|
|
111
111
|
|
|
112
|
-
it(
|
|
112
|
+
it('skips invalid values (false, null, undefined)', () => {
|
|
113
113
|
const themes = {
|
|
114
114
|
size: { small: { fontSize: 12 }, disabled: false, hidden: null },
|
|
115
115
|
}
|
|
@@ -117,17 +117,17 @@ describe("getDimensionsMap", () => {
|
|
|
117
117
|
expect(result.keysMap.size).toEqual({ small: true })
|
|
118
118
|
})
|
|
119
119
|
|
|
120
|
-
it(
|
|
120
|
+
it('returns empty for empty themes', () => {
|
|
121
121
|
const result = getDimensionsMap({ themes: {} })
|
|
122
122
|
expect(result.keysMap).toEqual({})
|
|
123
123
|
expect(result.keywords).toEqual({})
|
|
124
124
|
})
|
|
125
125
|
|
|
126
|
-
it(
|
|
126
|
+
it('includes function values (transform modifiers) as valid keys', () => {
|
|
127
127
|
const themes = {
|
|
128
128
|
modifier: {
|
|
129
129
|
outlined: (theme: any) => ({ color: theme.bg }),
|
|
130
|
-
ghost: () => ({ bg:
|
|
130
|
+
ghost: () => ({ bg: 'transparent' }),
|
|
131
131
|
},
|
|
132
132
|
}
|
|
133
133
|
const result = getDimensionsMap({ themes })
|
|
@@ -137,7 +137,7 @@ describe("getDimensionsMap", () => {
|
|
|
137
137
|
expect(result.keywords.modifier).toBe(true)
|
|
138
138
|
})
|
|
139
139
|
|
|
140
|
-
it(
|
|
140
|
+
it('includes function values as boolean keywords when useBooleans', () => {
|
|
141
141
|
const themes = {
|
|
142
142
|
modifier: {
|
|
143
143
|
outlined: (theme: any) => ({ color: theme.bg }),
|
|
@@ -148,35 +148,35 @@ describe("getDimensionsMap", () => {
|
|
|
148
148
|
})
|
|
149
149
|
})
|
|
150
150
|
|
|
151
|
-
describe(
|
|
152
|
-
it(
|
|
151
|
+
describe('getTransformDimensions', () => {
|
|
152
|
+
it('identifies transform dimensions', () => {
|
|
153
153
|
const dimensions = {
|
|
154
|
-
states:
|
|
155
|
-
modifiers: { propName:
|
|
154
|
+
states: 'state',
|
|
155
|
+
modifiers: { propName: 'modifier', multi: true, transform: true },
|
|
156
156
|
}
|
|
157
157
|
expect(getTransformDimensions(dimensions)).toEqual({ modifier: true })
|
|
158
158
|
})
|
|
159
159
|
|
|
160
|
-
it(
|
|
160
|
+
it('returns empty when no transform dimensions exist', () => {
|
|
161
161
|
const dimensions = {
|
|
162
|
-
states:
|
|
163
|
-
sizes:
|
|
164
|
-
multiple: { propName:
|
|
162
|
+
states: 'state',
|
|
163
|
+
sizes: 'size',
|
|
164
|
+
multiple: { propName: 'multiple', multi: true },
|
|
165
165
|
}
|
|
166
166
|
expect(getTransformDimensions(dimensions)).toEqual({})
|
|
167
167
|
})
|
|
168
168
|
|
|
169
|
-
it(
|
|
169
|
+
it('skips dimensions without transform flag', () => {
|
|
170
170
|
const dimensions = {
|
|
171
|
-
states:
|
|
172
|
-
tags: { propName:
|
|
171
|
+
states: 'state',
|
|
172
|
+
tags: { propName: 'tags', multi: true },
|
|
173
173
|
}
|
|
174
174
|
expect(getTransformDimensions(dimensions)).toEqual({})
|
|
175
175
|
})
|
|
176
176
|
|
|
177
|
-
it(
|
|
177
|
+
it('skips transform=false', () => {
|
|
178
178
|
const dimensions = {
|
|
179
|
-
modifiers: { propName:
|
|
179
|
+
modifiers: { propName: 'modifier', multi: true, transform: false },
|
|
180
180
|
}
|
|
181
181
|
expect(getTransformDimensions(dimensions)).toEqual({})
|
|
182
182
|
})
|
|
@@ -7,221 +7,163 @@
|
|
|
7
7
|
* Unlike the React version which tested CSS injection in the DOM,
|
|
8
8
|
* this Pyreon version tests the computed $rocketstyle output directly.
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import { context } from "../context/context"
|
|
13
|
-
import rocketstyle from "../init"
|
|
14
|
-
|
|
15
|
-
// Mock styled that passes through the component unchanged
|
|
16
|
-
const mockStyled = (component: any) => {
|
|
17
|
-
const taggedTemplate = (_strings: any, ..._args: any[]) => component
|
|
18
|
-
return taggedTemplate
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const mockCss = (_strings: any, ..._args: any[]) => ""
|
|
22
|
-
|
|
23
|
-
const originalStyled = config.styled
|
|
24
|
-
const originalCss = config.css
|
|
10
|
+
import { ThemeCapture, getComputedTheme, initTestConfig, withThemeContext } from '@pyreon/test-utils'
|
|
11
|
+
import rocketstyle from '../init'
|
|
25
12
|
|
|
13
|
+
let cleanup: () => void
|
|
26
14
|
beforeAll(() => {
|
|
27
|
-
|
|
28
|
-
css: mockCss as any,
|
|
29
|
-
styled: mockStyled as any,
|
|
30
|
-
component: "div",
|
|
31
|
-
textComponent: "span",
|
|
32
|
-
})
|
|
15
|
+
cleanup = initTestConfig()
|
|
33
16
|
})
|
|
17
|
+
afterAll(() => cleanup())
|
|
34
18
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
config.css = originalCss
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
/** Component that captures $rocketstyle for inspection */
|
|
41
|
-
const ThemeCapture: any = ({ $rocketstyle, $rocketstate, ...rest }: any) => ({
|
|
42
|
-
type: "div",
|
|
43
|
-
props: rest,
|
|
44
|
-
children: [],
|
|
45
|
-
key: null,
|
|
46
|
-
$rocketstyle,
|
|
47
|
-
$rocketstate,
|
|
48
|
-
})
|
|
49
|
-
ThemeCapture.displayName = "ThemeCapture"
|
|
50
|
-
|
|
51
|
-
/** Helper to render within theme context and return $rocketstyle */
|
|
52
|
-
const getComputedTheme = (Component: any, props: Record<string, any> = {}) => {
|
|
53
|
-
pushContext(
|
|
54
|
-
new Map([
|
|
55
|
-
[
|
|
56
|
-
context.id,
|
|
57
|
-
{
|
|
58
|
-
theme: { rootSize: 16 },
|
|
59
|
-
mode: "light",
|
|
60
|
-
isDark: false,
|
|
61
|
-
isLight: true,
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
]),
|
|
65
|
-
)
|
|
66
|
-
try {
|
|
67
|
-
let vnode = Component(props) as any
|
|
68
|
-
// EnhancedComponent returns a reactive accessor (function) for mode switching.
|
|
69
|
-
// In tests we evaluate it directly to get the VNode.
|
|
70
|
-
while (typeof vnode === "function") vnode = vnode()
|
|
71
|
-
return vnode.$rocketstyle
|
|
72
|
-
} finally {
|
|
73
|
-
popContext()
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
describe("e2e: rocketstyle theme computation", () => {
|
|
78
|
-
it("base theme values are passed through", () => {
|
|
19
|
+
describe('e2e: rocketstyle theme computation', () => {
|
|
20
|
+
it('base theme values are passed through', () => {
|
|
79
21
|
const Comp: any = rocketstyle()({
|
|
80
|
-
name:
|
|
22
|
+
name: 'BaseComp',
|
|
81
23
|
component: ThemeCapture,
|
|
82
24
|
}).theme({
|
|
83
|
-
backgroundColor:
|
|
84
|
-
color:
|
|
25
|
+
backgroundColor: '#0070f3',
|
|
26
|
+
color: '#fff',
|
|
85
27
|
})
|
|
86
28
|
|
|
87
29
|
const theme = getComputedTheme(Comp)
|
|
88
|
-
expect(theme.backgroundColor).toBe(
|
|
89
|
-
expect(theme.color).toBe(
|
|
30
|
+
expect(theme.backgroundColor).toBe('#0070f3')
|
|
31
|
+
expect(theme.color).toBe('#fff')
|
|
90
32
|
})
|
|
91
33
|
|
|
92
|
-
it(
|
|
34
|
+
it('state dimension overrides base theme values', () => {
|
|
93
35
|
const Comp: any = rocketstyle()({
|
|
94
|
-
name:
|
|
36
|
+
name: 'StateComp',
|
|
95
37
|
component: ThemeCapture,
|
|
96
38
|
})
|
|
97
39
|
.theme({
|
|
98
|
-
backgroundColor:
|
|
99
|
-
color:
|
|
40
|
+
backgroundColor: '#0070f3',
|
|
41
|
+
color: '#fff',
|
|
100
42
|
})
|
|
101
43
|
.states({
|
|
102
44
|
danger: {
|
|
103
|
-
backgroundColor:
|
|
104
|
-
color:
|
|
45
|
+
backgroundColor: '#dc3545',
|
|
46
|
+
color: '#fff',
|
|
105
47
|
},
|
|
106
48
|
})
|
|
107
49
|
|
|
108
|
-
const theme = getComputedTheme(Comp, { state:
|
|
109
|
-
expect(theme.backgroundColor).toBe(
|
|
110
|
-
expect(theme.color).toBe(
|
|
50
|
+
const theme = getComputedTheme(Comp, { state: 'danger' })
|
|
51
|
+
expect(theme.backgroundColor).toBe('#dc3545')
|
|
52
|
+
expect(theme.color).toBe('#fff')
|
|
111
53
|
})
|
|
112
54
|
|
|
113
|
-
it(
|
|
55
|
+
it('modifier transform derives styles from accumulated state theme', () => {
|
|
114
56
|
const Comp: any = rocketstyle()({
|
|
115
|
-
name:
|
|
57
|
+
name: 'ModifierComp',
|
|
116
58
|
component: ThemeCapture,
|
|
117
59
|
})
|
|
118
60
|
.theme({
|
|
119
|
-
backgroundColor:
|
|
120
|
-
color:
|
|
61
|
+
backgroundColor: '#0070f3',
|
|
62
|
+
color: '#fff',
|
|
121
63
|
})
|
|
122
64
|
.states({
|
|
123
65
|
danger: {
|
|
124
|
-
backgroundColor:
|
|
125
|
-
color:
|
|
66
|
+
backgroundColor: '#dc3545',
|
|
67
|
+
color: '#fff',
|
|
126
68
|
},
|
|
127
69
|
})
|
|
128
70
|
.modifiers({
|
|
129
71
|
outlined: (accTheme: any) => ({
|
|
130
72
|
color: accTheme.backgroundColor,
|
|
131
|
-
backgroundColor:
|
|
73
|
+
backgroundColor: 'transparent',
|
|
132
74
|
}),
|
|
133
75
|
})
|
|
134
76
|
|
|
135
77
|
// danger state + outlined modifier
|
|
136
78
|
const theme = getComputedTheme(Comp, {
|
|
137
|
-
state:
|
|
138
|
-
modifier:
|
|
79
|
+
state: 'danger',
|
|
80
|
+
modifier: 'outlined',
|
|
139
81
|
})
|
|
140
82
|
// outlined should flip: color becomes the danger backgroundColor
|
|
141
|
-
expect(theme.color).toBe(
|
|
142
|
-
expect(theme.backgroundColor).toBe(
|
|
83
|
+
expect(theme.color).toBe('#dc3545')
|
|
84
|
+
expect(theme.backgroundColor).toBe('transparent')
|
|
143
85
|
})
|
|
144
86
|
|
|
145
|
-
it(
|
|
87
|
+
it('modifier without active state uses base theme only', () => {
|
|
146
88
|
const Comp: any = rocketstyle()({
|
|
147
|
-
name:
|
|
89
|
+
name: 'ModifierBaseComp',
|
|
148
90
|
component: ThemeCapture,
|
|
149
91
|
})
|
|
150
92
|
.theme({
|
|
151
|
-
backgroundColor:
|
|
152
|
-
color:
|
|
93
|
+
backgroundColor: '#0070f3',
|
|
94
|
+
color: '#fff',
|
|
153
95
|
})
|
|
154
96
|
.modifiers({
|
|
155
97
|
outlined: (accTheme: any) => ({
|
|
156
98
|
color: accTheme.backgroundColor,
|
|
157
|
-
backgroundColor:
|
|
99
|
+
backgroundColor: 'transparent',
|
|
158
100
|
}),
|
|
159
101
|
})
|
|
160
102
|
|
|
161
103
|
// just outlined modifier, no state — derive from base theme
|
|
162
|
-
const theme = getComputedTheme(Comp, { modifier:
|
|
163
|
-
expect(theme.color).toBe(
|
|
164
|
-
expect(theme.backgroundColor).toBe(
|
|
104
|
+
const theme = getComputedTheme(Comp, { modifier: 'outlined' })
|
|
105
|
+
expect(theme.color).toBe('#0070f3')
|
|
106
|
+
expect(theme.backgroundColor).toBe('transparent')
|
|
165
107
|
})
|
|
166
108
|
|
|
167
|
-
it(
|
|
109
|
+
it('variant dimension values are applied correctly', () => {
|
|
168
110
|
const Comp: any = rocketstyle()({
|
|
169
|
-
name:
|
|
111
|
+
name: 'VariantComp',
|
|
170
112
|
component: ThemeCapture,
|
|
171
113
|
})
|
|
172
114
|
.theme({
|
|
173
|
-
backgroundColor:
|
|
115
|
+
backgroundColor: '#FFFFFF',
|
|
174
116
|
borderRadius: 8,
|
|
175
117
|
})
|
|
176
118
|
.variants({
|
|
177
119
|
box: {
|
|
178
120
|
height: 64,
|
|
179
121
|
padding: 8,
|
|
180
|
-
backgroundColor:
|
|
122
|
+
backgroundColor: 'transparent',
|
|
181
123
|
},
|
|
182
124
|
circle: {
|
|
183
125
|
width: 72,
|
|
184
126
|
height: 72,
|
|
185
127
|
padding: 4,
|
|
186
|
-
backgroundColor:
|
|
128
|
+
backgroundColor: '#F0F0F0',
|
|
187
129
|
borderRadius: 180,
|
|
188
130
|
},
|
|
189
131
|
})
|
|
190
132
|
|
|
191
|
-
const theme = getComputedTheme(Comp, { variant:
|
|
133
|
+
const theme = getComputedTheme(Comp, { variant: 'circle' })
|
|
192
134
|
expect(theme.width).toBe(72)
|
|
193
135
|
expect(theme.height).toBe(72)
|
|
194
|
-
expect(theme.backgroundColor).toBe(
|
|
136
|
+
expect(theme.backgroundColor).toBe('#F0F0F0')
|
|
195
137
|
expect(theme.borderRadius).toBe(180)
|
|
196
138
|
})
|
|
197
139
|
|
|
198
|
-
it(
|
|
140
|
+
it('variant box values override base theme', () => {
|
|
199
141
|
const Comp: any = rocketstyle()({
|
|
200
|
-
name:
|
|
142
|
+
name: 'VariantBoxComp',
|
|
201
143
|
component: ThemeCapture,
|
|
202
144
|
})
|
|
203
145
|
.theme({
|
|
204
|
-
backgroundColor:
|
|
146
|
+
backgroundColor: '#FFFFFF',
|
|
205
147
|
borderRadius: 8,
|
|
206
148
|
})
|
|
207
149
|
.variants({
|
|
208
150
|
box: {
|
|
209
151
|
height: 64,
|
|
210
152
|
padding: 8,
|
|
211
|
-
backgroundColor:
|
|
153
|
+
backgroundColor: 'transparent',
|
|
212
154
|
},
|
|
213
155
|
})
|
|
214
156
|
|
|
215
|
-
const theme = getComputedTheme(Comp, { variant:
|
|
216
|
-
expect(theme.backgroundColor).toBe(
|
|
157
|
+
const theme = getComputedTheme(Comp, { variant: 'box' })
|
|
158
|
+
expect(theme.backgroundColor).toBe('transparent')
|
|
217
159
|
expect(theme.borderRadius).toBe(8) // inherited from base
|
|
218
160
|
expect(theme.height).toBe(64)
|
|
219
161
|
expect(theme.padding).toBe(8)
|
|
220
162
|
})
|
|
221
163
|
|
|
222
|
-
it(
|
|
164
|
+
it('size dimension values are applied', () => {
|
|
223
165
|
const Comp: any = rocketstyle()({
|
|
224
|
-
name:
|
|
166
|
+
name: 'SizeComp',
|
|
225
167
|
component: ThemeCapture,
|
|
226
168
|
})
|
|
227
169
|
.theme({ fontSize: 14 })
|
|
@@ -230,65 +172,129 @@ describe("e2e: rocketstyle theme computation", () => {
|
|
|
230
172
|
large: { fontSize: 18, padding: 8 },
|
|
231
173
|
})
|
|
232
174
|
|
|
233
|
-
const theme = getComputedTheme(Comp, { size:
|
|
175
|
+
const theme = getComputedTheme(Comp, { size: 'large' })
|
|
234
176
|
expect(theme.fontSize).toBe(18)
|
|
235
177
|
expect(theme.padding).toBe(8)
|
|
236
178
|
})
|
|
237
179
|
|
|
238
|
-
it(
|
|
180
|
+
it('multiple dimensions combine', () => {
|
|
239
181
|
const Comp: any = rocketstyle()({
|
|
240
|
-
name:
|
|
182
|
+
name: 'MultiDimComp',
|
|
241
183
|
component: ThemeCapture,
|
|
242
184
|
})
|
|
243
|
-
.theme({ color:
|
|
244
|
-
.states({ primary: { color:
|
|
185
|
+
.theme({ color: 'black' })
|
|
186
|
+
.states({ primary: { color: 'blue' } })
|
|
245
187
|
.sizes({ large: { fontSize: 18 } })
|
|
246
188
|
|
|
247
189
|
const theme = getComputedTheme(Comp, {
|
|
248
|
-
state:
|
|
249
|
-
size:
|
|
190
|
+
state: 'primary',
|
|
191
|
+
size: 'large',
|
|
250
192
|
})
|
|
251
|
-
expect(theme.color).toBe(
|
|
193
|
+
expect(theme.color).toBe('blue')
|
|
252
194
|
expect(theme.fontSize).toBe(18)
|
|
253
195
|
})
|
|
254
196
|
|
|
255
|
-
it(
|
|
197
|
+
it('multiple modifier transforms compose sequentially', () => {
|
|
256
198
|
const Comp: any = rocketstyle()({
|
|
257
|
-
name:
|
|
199
|
+
name: 'MultiModComp',
|
|
258
200
|
component: ThemeCapture,
|
|
259
201
|
})
|
|
260
|
-
.theme({ backgroundColor:
|
|
202
|
+
.theme({ backgroundColor: 'blue', color: 'white' })
|
|
261
203
|
.modifiers({
|
|
262
204
|
outlined: (accTheme: any) => ({
|
|
263
205
|
color: accTheme.backgroundColor,
|
|
264
|
-
backgroundColor:
|
|
206
|
+
backgroundColor: 'transparent',
|
|
265
207
|
}),
|
|
266
|
-
rounded: () => ({ borderRadius:
|
|
208
|
+
rounded: () => ({ borderRadius: '999px' }),
|
|
267
209
|
})
|
|
268
210
|
|
|
269
211
|
const theme = getComputedTheme(Comp, {
|
|
270
|
-
modifier: [
|
|
212
|
+
modifier: ['outlined', 'rounded'],
|
|
271
213
|
})
|
|
272
|
-
expect(theme.color).toBe(
|
|
273
|
-
expect(theme.backgroundColor).toBe(
|
|
274
|
-
expect(theme.borderRadius).toBe(
|
|
214
|
+
expect(theme.color).toBe('blue')
|
|
215
|
+
expect(theme.backgroundColor).toBe('transparent')
|
|
216
|
+
expect(theme.borderRadius).toBe('999px')
|
|
275
217
|
})
|
|
276
218
|
|
|
277
|
-
it(
|
|
219
|
+
it('later transform sees earlier transform results', () => {
|
|
278
220
|
const Comp: any = rocketstyle()({
|
|
279
|
-
name:
|
|
221
|
+
name: 'ChainedModComp',
|
|
280
222
|
component: ThemeCapture,
|
|
281
223
|
})
|
|
282
224
|
.theme({})
|
|
283
225
|
.modifiers({
|
|
284
|
-
first: () => ({ step:
|
|
226
|
+
first: () => ({ step: 'one' }),
|
|
285
227
|
second: (accTheme: any) => ({ sawStep: accTheme.step }),
|
|
286
228
|
})
|
|
287
229
|
|
|
288
230
|
const theme = getComputedTheme(Comp, {
|
|
289
|
-
modifier: [
|
|
231
|
+
modifier: ['first', 'second'],
|
|
232
|
+
})
|
|
233
|
+
expect(theme.step).toBe('one')
|
|
234
|
+
expect(theme.sawStep).toBe('one')
|
|
235
|
+
})
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
// ── Reactive dimension props ──────────────────────────────────────────────────
|
|
239
|
+
|
|
240
|
+
describe('reactive $rocketstyle accessor', () => {
|
|
241
|
+
it('$rocketstyleAccessor resolves different themes for different dimension props', () => {
|
|
242
|
+
const Comp: any = rocketstyle()({
|
|
243
|
+
name: 'ReactiveComp',
|
|
244
|
+
component: ThemeCapture,
|
|
245
|
+
})
|
|
246
|
+
.theme({ color: 'black', bg: 'white' })
|
|
247
|
+
.states({
|
|
248
|
+
primary: { color: 'blue' },
|
|
249
|
+
secondary: { color: 'green' },
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
// First call with state=primary
|
|
253
|
+
const theme1 = getComputedTheme(Comp, { state: 'primary' })
|
|
254
|
+
expect(theme1.color).toBe('blue')
|
|
255
|
+
|
|
256
|
+
// Second call with state=secondary — should produce different theme
|
|
257
|
+
const theme2 = getComputedTheme(Comp, { state: 'secondary' })
|
|
258
|
+
expect(theme2.color).toBe('green')
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
it('$rocketstyleAccessor is a function, not a plain object', () => {
|
|
262
|
+
const Comp: any = rocketstyle()({
|
|
263
|
+
name: 'AccessorComp',
|
|
264
|
+
component: ThemeCapture,
|
|
265
|
+
}).theme({ color: 'red' })
|
|
266
|
+
|
|
267
|
+
const vnode = withThemeContext(() => Comp({}))
|
|
268
|
+
// ThemeCapture resolves the accessor — result should be the theme object
|
|
269
|
+
expect(vnode.$rocketstyle).toBeDefined()
|
|
270
|
+
expect(vnode.$rocketstyle.color).toBe('red')
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
it('$rocketstateAccessor resolves active dimensions', () => {
|
|
274
|
+
const Comp: any = rocketstyle()({
|
|
275
|
+
name: 'StateAccessorComp',
|
|
276
|
+
component: ThemeCapture,
|
|
277
|
+
}).states({
|
|
278
|
+
primary: { color: 'blue' },
|
|
290
279
|
})
|
|
291
|
-
|
|
292
|
-
|
|
280
|
+
|
|
281
|
+
const vnode = withThemeContext(() => Comp({ state: 'primary' }))
|
|
282
|
+
expect(vnode.$rocketstate).toBeDefined()
|
|
283
|
+
expect(vnode.$rocketstate.state).toBe('primary')
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('mode change produces different theme via accessor', () => {
|
|
287
|
+
const Comp: any = rocketstyle()({
|
|
288
|
+
name: 'ModeReactiveComp',
|
|
289
|
+
component: ThemeCapture,
|
|
290
|
+
}).theme((t: any, m: any) => ({
|
|
291
|
+
color: m('light-color', 'dark-color'),
|
|
292
|
+
}))
|
|
293
|
+
|
|
294
|
+
const lightTheme = getComputedTheme(Comp, {}, { mode: 'light' })
|
|
295
|
+
expect(lightTheme.color).toBe('light-color')
|
|
296
|
+
|
|
297
|
+
const darkTheme = getComputedTheme(Comp, {}, { mode: 'dark' })
|
|
298
|
+
expect(darkTheme.color).toBe('dark-color')
|
|
293
299
|
})
|
|
294
300
|
})
|