@codeleap/styles 5.8.1 → 5.8.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/package.json +9 -15
- package/package.json.bak +8 -14
- package/src/classes/Cacher.ts +169 -0
- package/src/classes/StaleControl.ts +125 -0
- package/src/classes/StyleCache.ts +116 -0
- package/src/classes/StylePersistor.ts +62 -0
- package/src/{lib → classes}/StyleRegistry.ts +7 -12
- package/src/classes/index.ts +2 -0
- package/src/classes/tests/Cache.spec.ts +371 -0
- package/src/classes/tests/StaleControl.spec.ts +175 -0
- package/src/classes/tests/StyleCache.spec.ts +452 -0
- package/src/classes/tests/StylePersistor.spec.ts +231 -0
- package/src/{lib/constants.ts → constants.ts} +1 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/tests/useCompositionStyles.spec.ts +107 -0
- package/src/hooks/tests/useStyleObserver.spec.ts +89 -0
- package/src/hooks/useCompositionStyles.ts +33 -0
- package/src/hooks/useNestedStylesByKey.ts +13 -0
- package/src/hooks/useStyleObserver.ts +19 -0
- package/src/hooks/useTheme.ts +16 -0
- package/src/index.ts +9 -5
- package/src/lib/createStyles.ts +10 -1
- package/src/lib/createTheme.ts +22 -13
- package/src/lib/index.ts +1 -10
- package/src/lib/tests/createStyles.spec.ts +151 -0
- package/src/tests/colors/baseColors.ts +166 -0
- package/src/tests/colors/darkMode.ts +232 -0
- package/src/tests/colors/lightMode.ts +285 -0
- package/src/tests/measures.ts +31 -0
- package/src/tests/theme.ts +58 -0
- package/src/theme/generateColorScheme.ts +53 -0
- package/src/theme/index.ts +3 -0
- package/src/theme/tests/generateColorScheme.spec.ts +118 -0
- package/src/theme/tests/themeStore.spec.ts +698 -0
- package/src/theme/tests/validateTheme.spec.ts +173 -0
- package/src/{lib → theme}/themeStore.ts +68 -3
- package/src/{lib → theme}/validateTheme.ts +13 -0
- package/src/tools/colors.ts +83 -39
- package/src/tools/deepClone.ts +10 -0
- package/src/tools/deepmerge.ts +10 -0
- package/src/{lib → tools}/hashKey.ts +7 -0
- package/src/tools/index.ts +6 -1
- package/src/tools/minifier.ts +38 -0
- package/src/tools/tests/colors.spec.ts +233 -0
- package/src/tools/tests/deepClone.spec.ts +102 -0
- package/src/tools/tests/deepmerge.spec.ts +155 -0
- package/src/tools/tests/hashKey.spec.ts +69 -0
- package/src/tools/tests/minifier.spec.ts +173 -0
- package/src/types/store.ts +2 -2
- package/src/types/style.ts +3 -3
- package/src/types/theme.ts +4 -4
- package/src/{lib/utils.ts → utils.ts} +3 -3
- package/src/{lib → variants}/borderCreator.ts +2 -2
- package/src/{lib → variants}/createAppVariants.ts +1 -1
- package/src/{lib → variants}/dynamicVariants.ts +1 -1
- package/src/variants/index.ts +6 -0
- package/src/{lib → variants}/spacing.ts +37 -24
- package/src/variants/tests/borderCreator.spec.ts +180 -0
- package/src/variants/tests/dynamicVariants.spec.ts +194 -0
- package/src/variants/tests/spacing.spec.ts +177 -0
- package/src/lib/Cacher.ts +0 -112
- package/src/lib/StaleControl.ts +0 -73
- package/src/lib/StyleCache.ts +0 -81
- package/src/lib/StylePersistor.ts +0 -28
- package/src/lib/hooks.ts +0 -60
- package/src/lib/minifier.ts +0 -20
- /package/src/{lib → tools}/multiplierProperty.ts +0 -0
- /package/src/{lib → variants}/defaultVariants.ts +0 -0
- /package/src/{lib → variants}/mediaQuery.ts +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react'
|
|
2
|
+
import { describe, it, expect } from 'bun:test'
|
|
3
|
+
import { useCompositionStyles } from '../useCompositionStyles'
|
|
4
|
+
import { getNestedStylesByKey } from '../../utils'
|
|
5
|
+
|
|
6
|
+
describe('useCompositionStyles', () => {
|
|
7
|
+
const mockComponentStyles = {
|
|
8
|
+
button: {
|
|
9
|
+
backgroundColor: 'blue',
|
|
10
|
+
color: 'white',
|
|
11
|
+
padding: '10px'
|
|
12
|
+
},
|
|
13
|
+
input: {
|
|
14
|
+
border: '1px solid gray',
|
|
15
|
+
padding: '5px'
|
|
16
|
+
},
|
|
17
|
+
container: {
|
|
18
|
+
display: 'flex',
|
|
19
|
+
gap: '10px'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
it('should return styles for single element', () => {
|
|
24
|
+
const { result } = renderHook(() =>
|
|
25
|
+
useCompositionStyles('button', mockComponentStyles)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
expect(result.current).toEqual({
|
|
29
|
+
button: getNestedStylesByKey('button', mockComponentStyles)
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should return styles for array of elements', () => {
|
|
34
|
+
const composition = ['button', 'input']
|
|
35
|
+
|
|
36
|
+
const { result } = renderHook(() =>
|
|
37
|
+
useCompositionStyles(composition, mockComponentStyles)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
expect(result.current).toEqual({
|
|
41
|
+
button: getNestedStylesByKey('button', mockComponentStyles),
|
|
42
|
+
input: getNestedStylesByKey('input', mockComponentStyles)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should handle empty array', () => {
|
|
47
|
+
const { result } = renderHook(() =>
|
|
48
|
+
useCompositionStyles([], mockComponentStyles)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
expect(result.current).toEqual({})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should memoize results with same props', () => {
|
|
55
|
+
const { result, rerender } = renderHook(
|
|
56
|
+
({ composition, styles }) => useCompositionStyles(composition, styles),
|
|
57
|
+
{
|
|
58
|
+
initialProps: {
|
|
59
|
+
composition: 'button',
|
|
60
|
+
styles: mockComponentStyles
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
const firstResult = result.current
|
|
66
|
+
rerender({ composition: 'button', styles: mockComponentStyles })
|
|
67
|
+
expect(result.current).toEqual(firstResult)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should update when composition changes', () => {
|
|
71
|
+
const { result, rerender } = renderHook(
|
|
72
|
+
({ composition }) => useCompositionStyles(composition, mockComponentStyles),
|
|
73
|
+
{
|
|
74
|
+
initialProps: { composition: 'button' }
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
expect(result.current).toEqual({
|
|
79
|
+
button: getNestedStylesByKey('button', mockComponentStyles)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
rerender({ composition: 'input' })
|
|
83
|
+
expect(result.current).toEqual({
|
|
84
|
+
input: getNestedStylesByKey('input', mockComponentStyles)
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('should handle non-existent keys', () => {
|
|
89
|
+
const { result } = renderHook(() =>
|
|
90
|
+
useCompositionStyles('nonexistent', mockComponentStyles)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
expect(result.current).toEqual({
|
|
94
|
+
nonexistent: getNestedStylesByKey('nonexistent', mockComponentStyles)
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('should preserve original componentStyles object', () => {
|
|
99
|
+
const originalStyles = { ...mockComponentStyles }
|
|
100
|
+
|
|
101
|
+
renderHook(() =>
|
|
102
|
+
useCompositionStyles('button', mockComponentStyles)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
expect(mockComponentStyles).toEqual(originalStyles)
|
|
106
|
+
})
|
|
107
|
+
})
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react'
|
|
2
|
+
import { describe, it, expect } from 'bun:test'
|
|
3
|
+
import { useStyleObserver } from '../useStyleObserver'
|
|
4
|
+
|
|
5
|
+
describe('useStyleObserver', () => {
|
|
6
|
+
it('should return string representation for primitive values', () => {
|
|
7
|
+
const { result } = renderHook(() => useStyleObserver('red'))
|
|
8
|
+
expect(result.current).toBe('red')
|
|
9
|
+
|
|
10
|
+
const { result: numberResult } = renderHook(() => useStyleObserver(10))
|
|
11
|
+
expect(numberResult.current).toBe(10)
|
|
12
|
+
|
|
13
|
+
const { result: boolResult } = renderHook(() => useStyleObserver(true))
|
|
14
|
+
expect(boolResult.current).toBe(true)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('should stringify object styles', () => {
|
|
18
|
+
const style = { color: 'red', fontSize: '14px' }
|
|
19
|
+
const { result } = renderHook(() => useStyleObserver(style))
|
|
20
|
+
|
|
21
|
+
expect(result.current).toBe(JSON.stringify(style))
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should stringify array styles and filter falsy values', () => {
|
|
25
|
+
const style = ['btn', 'btn-primary', null, '', undefined, false]
|
|
26
|
+
const { result } = renderHook(() => useStyleObserver(style))
|
|
27
|
+
|
|
28
|
+
expect(result.current).toBe(JSON.stringify(['btn', 'btn-primary']))
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should handle empty array', () => {
|
|
32
|
+
const { result } = renderHook(() => useStyleObserver([]))
|
|
33
|
+
expect(result.current).toBe(JSON.stringify([]))
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should handle array with all falsy values', () => {
|
|
37
|
+
const { result } = renderHook(() => useStyleObserver([null, '', undefined, false]))
|
|
38
|
+
expect(result.current).toBe(JSON.stringify([]))
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should handle null and undefined', () => {
|
|
42
|
+
const { result: nullResult } = renderHook(() => useStyleObserver(null))
|
|
43
|
+
expect(nullResult.current).toBe(JSON.stringify(null))
|
|
44
|
+
|
|
45
|
+
const { result: undefinedResult } = renderHook(() => useStyleObserver(undefined))
|
|
46
|
+
expect(undefinedResult.current).toBe(undefined)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should memoize results with same input', () => {
|
|
50
|
+
const style = { color: 'blue' }
|
|
51
|
+
const { result, rerender } = renderHook(
|
|
52
|
+
({ styleInput }) => useStyleObserver(styleInput),
|
|
53
|
+
{ initialProps: { styleInput: style } }
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
const firstResult = result.current
|
|
57
|
+
rerender({ styleInput: style })
|
|
58
|
+
expect(result.current).toBe(firstResult)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('should update when style changes', () => {
|
|
62
|
+
const { result, rerender } = renderHook(
|
|
63
|
+
({ style }) => useStyleObserver(style),
|
|
64
|
+
{ initialProps: { style: { color: 'red' } } }
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
expect(result.current).toBe(JSON.stringify({ color: 'red' }))
|
|
68
|
+
|
|
69
|
+
rerender({ style: { color: 'blue' } })
|
|
70
|
+
expect(result.current).toBe(JSON.stringify({ color: 'blue' }))
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('should handle nested objects', () => {
|
|
74
|
+
const style = {
|
|
75
|
+
button: { color: 'red' },
|
|
76
|
+
container: { padding: '10px' }
|
|
77
|
+
}
|
|
78
|
+
const { result } = renderHook(() => useStyleObserver(style))
|
|
79
|
+
|
|
80
|
+
expect(result.current).toBe(JSON.stringify(style))
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should handle mixed array with objects and strings', () => {
|
|
84
|
+
const style = ['btn', { active: true }, null, 'primary']
|
|
85
|
+
const { result } = renderHook(() => useStyleObserver(style))
|
|
86
|
+
|
|
87
|
+
expect(result.current).toBe(JSON.stringify(['btn', { active: true }, 'primary']))
|
|
88
|
+
})
|
|
89
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import { ICSS } from '../types'
|
|
3
|
+
import { getNestedStylesByKey } from '../utils'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook that processes composition styles based on component styles.
|
|
7
|
+
*
|
|
8
|
+
* @param {T | Array<T>} composition - Composition element(s)
|
|
9
|
+
* @param {Partial<Record<C, ICSS>>} componentStyles - Component styles object
|
|
10
|
+
* @returns {Partial<Record<T, ICSS>>} Mapped styles for each composition element
|
|
11
|
+
*/
|
|
12
|
+
export function useCompositionStyles<T extends string, C extends string>(
|
|
13
|
+
composition: (T | Array<T>),
|
|
14
|
+
componentStyles: Partial<Record<C, ICSS>>
|
|
15
|
+
): Partial<Record<T, ICSS>> {
|
|
16
|
+
const styles = {
|
|
17
|
+
...componentStyles
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return useMemo(() => {
|
|
21
|
+
const compositionStyles = {}
|
|
22
|
+
|
|
23
|
+
if (Array.isArray(composition)) {
|
|
24
|
+
for (const element of composition) {
|
|
25
|
+
compositionStyles[element as string] = getNestedStylesByKey(element, styles)
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
compositionStyles[composition as string] = getNestedStylesByKey(composition, styles)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return compositionStyles
|
|
32
|
+
}, [styles])
|
|
33
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import { ICSS } from '../types'
|
|
3
|
+
import { getNestedStylesByKey } from '../utils'
|
|
4
|
+
|
|
5
|
+
export function useNestedStylesByKey<T extends string>(match: string, componentStyles: Partial<Record<T, ICSS>>) {
|
|
6
|
+
const styles = {
|
|
7
|
+
...componentStyles
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return useMemo(() => {
|
|
11
|
+
return getNestedStylesByKey(match, styles)
|
|
12
|
+
}, [styles])
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hook that observes style changes by creating a memoized string representation.
|
|
5
|
+
*
|
|
6
|
+
* @param {any} style - Style value to observe (array, object, or primitive)
|
|
7
|
+
* @returns {string} Serialized string representation of the style for comparison
|
|
8
|
+
*/
|
|
9
|
+
export const useStyleObserver = (style) => {
|
|
10
|
+
return useMemo(() => {
|
|
11
|
+
if (Array.isArray(style)) {
|
|
12
|
+
return JSON.stringify(style?.filter(v => !!v))
|
|
13
|
+
} else if (typeof style === 'object'){
|
|
14
|
+
return JSON.stringify(style)
|
|
15
|
+
} else {
|
|
16
|
+
return style
|
|
17
|
+
}
|
|
18
|
+
}, [style])
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ThemeState, themeStoreComputed } from '../theme'
|
|
2
|
+
import { useStore } from '@nanostores/react'
|
|
3
|
+
|
|
4
|
+
type ThemeSelector<T> = (state: ThemeState) => T
|
|
5
|
+
|
|
6
|
+
export const useTheme = <T = ThemeState>(
|
|
7
|
+
selector?: ThemeSelector<T>
|
|
8
|
+
): T => {
|
|
9
|
+
const state = useStore(themeStoreComputed)
|
|
10
|
+
|
|
11
|
+
if (!selector) {
|
|
12
|
+
return state as T
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return selector(state)
|
|
16
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import './
|
|
2
|
-
import './
|
|
1
|
+
import './constants'
|
|
2
|
+
import './utils'
|
|
3
3
|
|
|
4
|
+
export * from './classes'
|
|
5
|
+
export * from './hooks'
|
|
4
6
|
export * from './lib'
|
|
5
|
-
export * from './
|
|
7
|
+
export * from './theme'
|
|
6
8
|
export * from './tools'
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
+
export * from './types'
|
|
10
|
+
export * from './variants'
|
|
11
|
+
export * from './constants'
|
|
12
|
+
export * from './utils'
|
package/src/lib/createStyles.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { AnyRecord, ICSS, ITheme } from '../types'
|
|
2
|
-
import { themeStore } from '
|
|
2
|
+
import { themeStore } from '../theme'
|
|
3
3
|
|
|
4
4
|
type Value = AnyRecord
|
|
5
5
|
|
|
6
6
|
type StylesShape<K extends string, V extends Value> = Partial<Record<K, ICSS & Partial<Omit<V, keyof ICSS>>>>
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Creates a reactive styles object that automatically updates when theme changes.
|
|
10
|
+
* Uses a proxy to re-compute styles on each access, ensuring theme changes are reflected.
|
|
11
|
+
*
|
|
12
|
+
* @template K - Style keys (extends string)
|
|
13
|
+
* @template V - Additional value type (extends AnyRecord)
|
|
14
|
+
* @param {StylesShape<K, V> | ((theme: ITheme) => StylesShape<K, V>)} styles - Static styles object or function that receives theme
|
|
15
|
+
* @returns {StylesShape<K, V>} Proxied styles object that recomputes on theme changes
|
|
16
|
+
*/
|
|
8
17
|
export function createStyles<K extends string, V extends Value = {}>(
|
|
9
18
|
styles: StylesShape<K, V> | ((theme: ITheme) => StylesShape<K, V>),
|
|
10
19
|
) {
|
package/src/lib/createTheme.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { AppTheme, Theme } from '../types'
|
|
2
|
-
import { borderCreator } from '
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { defaultVariants } from './defaultVariants'
|
|
6
|
-
import { spacingFactory } from './spacing'
|
|
7
|
-
import { themeStore } from './themeStore'
|
|
2
|
+
import { borderCreator, createMediaQueries, defaultVariants, spacingFactory } from '../variants'
|
|
3
|
+
import { minifier, multiplierProperty } from '../tools'
|
|
4
|
+
import { themeStore } from '../theme'
|
|
8
5
|
|
|
9
6
|
type ThemePersistor = {
|
|
10
7
|
get: (name: string) => any
|
|
@@ -30,9 +27,20 @@ export const createTheme = <T extends Theme>(theme: T, themePersistor: ThemePers
|
|
|
30
27
|
...otherThemeValues
|
|
31
28
|
} = theme
|
|
32
29
|
|
|
33
|
-
|
|
30
|
+
const persistor = {
|
|
31
|
+
get: (key: string) => {
|
|
32
|
+
const value = themePersistor.get(key)
|
|
33
|
+
if (!value) return null
|
|
34
|
+
return minifier.decompress(value)
|
|
35
|
+
},
|
|
36
|
+
set: (key: string, value: any) => {
|
|
37
|
+
return themePersistor.set(key, !value ? '' : minifier.compress(value))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
themeStore.setColorScheme(persistor.get(colorSchemeKey) ?? 'default')
|
|
34
42
|
|
|
35
|
-
const persistedAlternateColors =
|
|
43
|
+
const persistedAlternateColors = persistor.get(alternateColorsKey)
|
|
36
44
|
|
|
37
45
|
const alternateColors = {
|
|
38
46
|
...(persistedAlternateColors ?? {}),
|
|
@@ -80,31 +88,32 @@ export const createTheme = <T extends Theme>(theme: T, themePersistor: ThemePers
|
|
|
80
88
|
|
|
81
89
|
themeStore.setColorScheme(colorScheme)
|
|
82
90
|
|
|
83
|
-
|
|
91
|
+
persistor.set(colorSchemeKey, colorScheme)
|
|
84
92
|
},
|
|
85
93
|
|
|
86
94
|
injectColorScheme(name, colorMap) {
|
|
87
95
|
themeStore.injectColorScheme(name, colorMap)
|
|
88
96
|
|
|
89
|
-
const persistedAlternateColors =
|
|
97
|
+
const persistedAlternateColors = persistor.get(alternateColorsKey)
|
|
90
98
|
|
|
91
99
|
const unpersistedAlternateColors = {
|
|
92
100
|
...(persistedAlternateColors ?? {}),
|
|
93
101
|
[name]: colorMap,
|
|
94
102
|
}
|
|
95
103
|
|
|
96
|
-
|
|
104
|
+
persistor.set(alternateColorsKey, unpersistedAlternateColors)
|
|
97
105
|
},
|
|
106
|
+
|
|
98
107
|
ejectColorScheme(name) {
|
|
99
108
|
themeStore.ejectColorScheme(name)
|
|
100
109
|
|
|
101
|
-
const persistedAlternateColors =
|
|
110
|
+
const persistedAlternateColors = persistor.get(alternateColorsKey)
|
|
102
111
|
|
|
103
112
|
if (name in persistedAlternateColors) {
|
|
104
113
|
delete persistedAlternateColors[name]
|
|
105
114
|
}
|
|
106
115
|
|
|
107
|
-
|
|
116
|
+
persistor.set(alternateColorsKey, persistedAlternateColors)
|
|
108
117
|
},
|
|
109
118
|
|
|
110
119
|
baseSpacing: theme.baseSpacing,
|
package/src/lib/index.ts
CHANGED
|
@@ -1,11 +1,2 @@
|
|
|
1
|
-
export { validateTheme } from './validateTheme'
|
|
2
|
-
export { themeStore } from './themeStore'
|
|
3
|
-
export { createTheme } from './createTheme'
|
|
4
|
-
export { createAppVariants } from './createAppVariants'
|
|
5
1
|
export { createStyles } from './createStyles'
|
|
6
|
-
export {
|
|
7
|
-
export { CodeleapStyleRegistry } from './StyleRegistry'
|
|
8
|
-
export { StylePersistor } from './StylePersistor'
|
|
9
|
-
|
|
10
|
-
export * from './constants'
|
|
11
|
-
export * from './hooks'
|
|
2
|
+
export { createTheme } from './createTheme'
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'bun:test'
|
|
2
|
+
import { createStyles } from '../createStyles'
|
|
3
|
+
import { themeStore } from '../../theme'
|
|
4
|
+
import { mockTheme, MockedTheme } from '../../tests/theme'
|
|
5
|
+
|
|
6
|
+
describe('createStyles', () => {
|
|
7
|
+
let currentTheme: MockedTheme = null
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
mockTheme()
|
|
11
|
+
|
|
12
|
+
currentTheme = themeStore.theme as unknown as MockedTheme
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('should return static styles when provided as object', () => {
|
|
16
|
+
const staticStyles = {
|
|
17
|
+
button: { color: 'red', padding: '10px' },
|
|
18
|
+
input: { border: '1px solid gray' }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const styles = createStyles(staticStyles)
|
|
22
|
+
|
|
23
|
+
expect(styles.button).toEqual({ color: 'red', padding: '10px' })
|
|
24
|
+
expect(styles.input).toEqual({ border: '1px solid gray' })
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('should handle empty static styles object', () => {
|
|
28
|
+
const styles = createStyles({})
|
|
29
|
+
expect(Object.keys(styles)).toHaveLength(0)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should compute styles from function when theme is available', () => {
|
|
33
|
+
const functionStyles = (theme: MockedTheme) => ({
|
|
34
|
+
button: {
|
|
35
|
+
color: theme.colors.neutralSo,
|
|
36
|
+
backgroundColor: theme.colors.secondary
|
|
37
|
+
},
|
|
38
|
+
container: {
|
|
39
|
+
padding: theme.spacing.value(2)
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const styles = createStyles(functionStyles)
|
|
44
|
+
|
|
45
|
+
expect(styles.button).toEqual({
|
|
46
|
+
color: currentTheme.colors.neutralSo,
|
|
47
|
+
backgroundColor: currentTheme.colors.secondary
|
|
48
|
+
})
|
|
49
|
+
expect(styles.container).toEqual({
|
|
50
|
+
padding: 16
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should recompute styles when theme changes (proxy behavior)', () => {
|
|
55
|
+
const functionStyles = (theme: MockedTheme) => ({
|
|
56
|
+
button: { color: theme.colors.buttonRegularPrimaryBgDefault }
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const styles = createStyles(functionStyles)
|
|
60
|
+
|
|
61
|
+
// Initial theme
|
|
62
|
+
expect(styles.button).toEqual({ color: currentTheme.colors.primarySolid800 })
|
|
63
|
+
|
|
64
|
+
currentTheme.setColorScheme('dark')
|
|
65
|
+
|
|
66
|
+
// Should reflect new theme due to proxy
|
|
67
|
+
expect(styles.button).toEqual({ color: currentTheme.colors.primarySolid500 })
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should handle complex theme-based styles', () => {
|
|
71
|
+
const functionStyles = (theme: MockedTheme) => ({
|
|
72
|
+
button: {
|
|
73
|
+
color: theme.colors.redSolid100,
|
|
74
|
+
border: `1px solid ${theme.colors.neutralSolid1000}`,
|
|
75
|
+
'@media (max-width: 768px)': {
|
|
76
|
+
fontSize: '14px'
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
alert: {
|
|
80
|
+
backgroundColor: theme.colors.redSolid600
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const styles = createStyles(functionStyles)
|
|
85
|
+
|
|
86
|
+
expect(styles.button).toEqual({
|
|
87
|
+
color: currentTheme.colors.redSolid100,
|
|
88
|
+
border: `1px solid ${currentTheme.colors.neutralSolid1000}`,
|
|
89
|
+
'@media (max-width: 768px)': {
|
|
90
|
+
fontSize: '14px'
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
expect(styles.alert).toEqual({
|
|
94
|
+
backgroundColor: currentTheme.colors.redSolid600
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('should handle accessing non-existent properties', () => {
|
|
99
|
+
const staticStyles = {
|
|
100
|
+
button: { color: 'red' }
|
|
101
|
+
} as any
|
|
102
|
+
|
|
103
|
+
const styles = createStyles(staticStyles)
|
|
104
|
+
|
|
105
|
+
expect(styles.nonExistent).toBeUndefined()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should work with mixed ICSS and custom properties', () => {
|
|
109
|
+
const functionStyles = (theme: MockedTheme) => ({
|
|
110
|
+
component: {
|
|
111
|
+
// ICSS properties
|
|
112
|
+
color: theme.colors.blueSolid900,
|
|
113
|
+
padding: '10px',
|
|
114
|
+
// Custom properties (merged with ICSS)
|
|
115
|
+
customProp: 'custom-value',
|
|
116
|
+
dataAttribute: 'test'
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const styles = createStyles(functionStyles)
|
|
121
|
+
|
|
122
|
+
expect(styles.component).toEqual({
|
|
123
|
+
color: currentTheme.colors.blueSolid900,
|
|
124
|
+
padding: '10px',
|
|
125
|
+
customProp: 'custom-value',
|
|
126
|
+
dataAttribute: 'test'
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('should maintain proxy behavior across multiple accesses', () => {
|
|
131
|
+
let computeCount = 0
|
|
132
|
+
|
|
133
|
+
const functionStyles = (theme: MockedTheme) => {
|
|
134
|
+
computeCount++
|
|
135
|
+
return {
|
|
136
|
+
button: { color: theme.colors.blueSolid100 }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const styles = createStyles(functionStyles)
|
|
141
|
+
|
|
142
|
+
computeCount = 0
|
|
143
|
+
|
|
144
|
+
// Each property access should trigger recomputation
|
|
145
|
+
styles.button
|
|
146
|
+
styles.button
|
|
147
|
+
styles.button
|
|
148
|
+
|
|
149
|
+
expect(computeCount).toBe(3)
|
|
150
|
+
})
|
|
151
|
+
})
|