mirror of
https://github.com/hanzoai/gui.git
synced 2026-10-25 02:31:41 +00:00
277 lines
8.5 KiB
TypeScript
277 lines
8.5 KiB
TypeScript
import { beforeAll, describe, expect, test } from 'vitest'
|
|
|
|
import config from '../config-default'
|
|
import { View, createGui, styled, StyleObjectValue, StyleObjectRules } from '../web/src'
|
|
import { simplifiedGetSplitStyles } from './utils'
|
|
|
|
beforeAll(() => {
|
|
createGui(config.getDefaultGuiConfig())
|
|
})
|
|
|
|
// helper to get style value from either style object or rulesToInsert
|
|
function getStyleValue(
|
|
styles: ReturnType<typeof simplifiedGetSplitStyles>,
|
|
prop: string
|
|
): string | undefined {
|
|
if (styles.style?.[prop]) {
|
|
return styles.style[prop] as string
|
|
}
|
|
if (styles.rulesToInsert) {
|
|
const rule = Object.values(styles.rulesToInsert).find(
|
|
(r: any) => r[0] === prop
|
|
) as any
|
|
return rule?.[StyleObjectValue]
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
describe('shorthand variables - web', () => {
|
|
// boxShadow/filter/backgroundImage support embedded $tokens which resolve to CSS vars
|
|
|
|
test('boxShadow with $variable resolves to CSS var', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
boxShadow: '0 0 10px $white',
|
|
})
|
|
const value = getStyleValue(styles, 'boxShadow')
|
|
|
|
// CSS var format is --c-white (c- prefix for color tokens)
|
|
expect(value).toMatch(/^0 0 10px var\(--.*white.*\)$/)
|
|
})
|
|
|
|
test('boxShadow with multiple tokens resolves all to CSS vars', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
boxShadow: '0 0 10px $white, 0 0 20px $black',
|
|
})
|
|
const value = getStyleValue(styles, 'boxShadow')
|
|
|
|
expect(value).toMatch(/var\(--.*white/)
|
|
expect(value).toMatch(/var\(--.*black/)
|
|
expect(value?.match(/var\(--/g)?.length).toBe(2)
|
|
})
|
|
|
|
test('boxShadow without variables passed through unchanged', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
boxShadow: '0 0 10px red',
|
|
})
|
|
const value = getStyleValue(styles, 'boxShadow')
|
|
|
|
expect(value).toBe('0 0 10px red')
|
|
})
|
|
|
|
test('boxShadow with unresolvable $variable keeps token string', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
boxShadow: '0 0 10px $nonexistent',
|
|
})
|
|
const value = getStyleValue(styles, 'boxShadow')
|
|
|
|
expect(value).toBe('0 0 10px $nonexistent')
|
|
})
|
|
|
|
test('boxShadow with dotted token path resolves', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
boxShadow: '0 0 10px $color.white',
|
|
})
|
|
const value = getStyleValue(styles, 'boxShadow')
|
|
|
|
// dotted paths like $color.white resolve to the token value
|
|
expect(value).toMatch(/var\(--.*white/)
|
|
})
|
|
|
|
// backgroundImage - supports linear-gradient with tokens
|
|
test('backgroundImage with $variable resolves to CSS var', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
backgroundImage: 'linear-gradient(to bottom, $white, $black)',
|
|
})
|
|
const value = getStyleValue(styles, 'backgroundImage')
|
|
|
|
expect(value).toMatch(
|
|
/linear-gradient\(to bottom, var\(--.*white.*\), var\(--.*black.*\)\)/
|
|
)
|
|
})
|
|
|
|
test('backgroundImage with angle and multiple color stops', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
backgroundImage: 'linear-gradient(45deg, $black 0%, $white 50%, $black 100%)',
|
|
})
|
|
const value = getStyleValue(styles, 'backgroundImage')
|
|
|
|
expect(value).toMatch(/linear-gradient\(45deg/)
|
|
expect(value?.match(/var\(--/g)?.length).toBe(3)
|
|
})
|
|
|
|
test('backgroundImage without variables passed through unchanged', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
backgroundImage: 'linear-gradient(to bottom, red, blue)',
|
|
})
|
|
const value = getStyleValue(styles, 'backgroundImage')
|
|
|
|
expect(value).toBe('linear-gradient(to bottom, red, blue)')
|
|
})
|
|
|
|
test('backgroundImage with unresolvable $variable keeps token string', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
backgroundImage: 'linear-gradient($nonexistent, $white)',
|
|
})
|
|
const value = getStyleValue(styles, 'backgroundImage')
|
|
|
|
expect(value).toMatch(/linear-gradient\(\$nonexistent, var\(--.*white/)
|
|
})
|
|
|
|
test('backgroundImage with $token/NN opacity modifier resolves to color-mix', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
backgroundImage: 'linear-gradient(180deg, $white/50, $white/0)',
|
|
})
|
|
const value = getStyleValue(styles, 'backgroundImage')
|
|
|
|
expect(value).toMatch(
|
|
/linear-gradient\(180deg, color-mix\(in srgb, var\(--.*white.*\) 50%, transparent\), color-mix\(in srgb, var\(--.*white.*\) 0%, transparent\)\)/
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('border shorthand - web', () => {
|
|
// border shorthand passed through as CSS string on web
|
|
|
|
test('border with width, style and color', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
border: '1px solid red',
|
|
})
|
|
const value = getStyleValue(styles, 'border')
|
|
|
|
expect(value).toBe('1px solid red')
|
|
})
|
|
|
|
test('border with $variable color resolves to CSS var', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
border: '2px dashed $white',
|
|
})
|
|
const value = getStyleValue(styles, 'border')
|
|
|
|
expect(value).toMatch(/2px dashed var\(--.*white.*\)/)
|
|
})
|
|
|
|
test('border without variables passed through unchanged', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
border: '1px solid blue',
|
|
})
|
|
const value = getStyleValue(styles, 'border')
|
|
|
|
expect(value).toBe('1px solid blue')
|
|
})
|
|
|
|
test('border "none" passed through', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
border: 'none',
|
|
})
|
|
const value = getStyleValue(styles, 'border')
|
|
|
|
expect(value).toBe('none')
|
|
})
|
|
})
|
|
|
|
describe('outline shorthand - web', () => {
|
|
// outline passes through as CSS string on web (like border)
|
|
|
|
test('outline with width, style and color', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
outline: '2px solid red',
|
|
})
|
|
const value = getStyleValue(styles, 'outline')
|
|
|
|
expect(value).toBe('2px solid red')
|
|
})
|
|
|
|
test('outline with $variable color resolves to CSS var', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
outline: '2px solid $white',
|
|
})
|
|
const value = getStyleValue(styles, 'outline')
|
|
|
|
expect(value).toMatch(/2px solid var\(--.*white.*\)/)
|
|
})
|
|
|
|
test('outline "none" passed through', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
outline: 'none',
|
|
})
|
|
const value = getStyleValue(styles, 'outline')
|
|
|
|
expect(value).toBe('none')
|
|
})
|
|
})
|
|
|
|
describe('tokens in variant styles - web', () => {
|
|
test('boxShadow with embedded $token in variant resolves to CSS var', () => {
|
|
const Comp = styled(View, {
|
|
variants: {
|
|
floating: {
|
|
true: {
|
|
boxShadow: '0 20px 40px $white',
|
|
},
|
|
},
|
|
} as const,
|
|
})
|
|
const styles = simplifiedGetSplitStyles(Comp, { floating: true })
|
|
const value = getStyleValue(styles, 'boxShadow')
|
|
|
|
// token should resolve, not remain as literal $white
|
|
expect(value).toMatch(/var\(--.*white/)
|
|
})
|
|
|
|
test('border with embedded $token in variant resolves to CSS var', () => {
|
|
const Comp = styled(View, {
|
|
variants: {
|
|
outlined: {
|
|
true: {
|
|
border: '2px solid $white',
|
|
},
|
|
},
|
|
} as const,
|
|
})
|
|
const styles = simplifiedGetSplitStyles(Comp, { outlined: true })
|
|
const value = getStyleValue(styles, 'border')
|
|
|
|
expect(value).toMatch(/2px solid var\(--.*white/)
|
|
})
|
|
|
|
test('backgroundImage with embedded $tokens in variant resolves', () => {
|
|
const Comp = styled(View, {
|
|
variants: {
|
|
gradient: {
|
|
true: {
|
|
backgroundImage: 'linear-gradient(to bottom, $white, $black)',
|
|
},
|
|
},
|
|
} as const,
|
|
})
|
|
const styles = simplifiedGetSplitStyles(Comp, { gradient: true })
|
|
const value = getStyleValue(styles, 'backgroundImage')
|
|
|
|
expect(value).toMatch(/var\(--.*white/)
|
|
expect(value).toMatch(/var\(--.*black/)
|
|
})
|
|
})
|
|
|
|
describe('border shorthand with media queries - web', () => {
|
|
test('border in $sm generates className with media key', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
$sm: { border: '2px solid green' },
|
|
})
|
|
|
|
// on web, $sm should generate a className with media key
|
|
expect(styles.hasMedia).toBe(true)
|
|
expect(styles.classNames?.['border-sm']).toBeDefined()
|
|
// classname contains _sm_ marker
|
|
expect(styles.classNames?.['border-sm']).toContain('_sm_')
|
|
})
|
|
|
|
test('border in $sm with token generates className', () => {
|
|
const styles = simplifiedGetSplitStyles(View, {
|
|
$sm: { border: '1px dashed $white' },
|
|
})
|
|
|
|
expect(styles.hasMedia).toBe(true)
|
|
expect(styles.classNames?.['border-sm']).toBeDefined()
|
|
})
|
|
})
|