aliaset
Version:
twind monorepo
237 lines (197 loc) • 5.99 kB
text/typescript
import { assert, test, afterEach } from 'vitest'
import { twind, virtual, css, cx, shortcut, apply, colorFromTheme, escape } from '..'
const tw = twind(
{
theme: {
screens: {
sm: '640px',
md: '768px',
},
spacing: {
lg: '2rem',
xl: '3rem',
'0.5': '0.5rem',
},
colors: {
gray: {
50: '#f9fafb',
100: '#f3f4f6',
200: '#e5e7eb',
300: '#d1d5db',
400: '#9ca3af',
500: '#6b7280',
600: '#4b5563',
700: '#374151',
800: '#1f2937',
900: '#111827',
},
},
},
rules: [
['bg-', colorFromTheme({ section: 'colors', property: 'background-color' })],
['text-', colorFromTheme({ section: 'colors', property: 'color' })],
],
},
virtual(),
)
afterEach(() => tw.clear())
test('create css', () => {
const style = css({
backgroundColor: 'hotpink',
'&:hover': {
color: 'darkgreen',
},
})
assert.strictEqual(style, 'css#a22z42')
// It is lazy
assert.deepEqual(tw.target, [])
assert.strictEqual(tw(style), 'css#a22z42')
assert.deepEqual(tw.target, [
'.css\\#a22z42{background-color:hotpink}',
'.css\\#a22z42:hover{color:darkgreen}',
])
// it is cached
assert.strictEqual(tw(style), 'css#a22z42')
assert.deepEqual(tw.target, [
'.css\\#a22z42{background-color:hotpink}',
'.css\\#a22z42:hover{color:darkgreen}',
])
})
test('can be used with variants', () => {
const style = css({
backgroundColor: 'hotpink',
'&:hover': {
backgroundColor: 'darkgreen',
},
})
assert.strictEqual(tw(`sm:${style} focus:${style}`), 'css#hxclon')
assert.deepEqual(tw.target, [
'@media (min-width:640px){.css\\#hxclon{background-color:hotpink}}',
'@media (min-width:640px){.css\\#hxclon:hover{background-color:darkgreen}}',
'.css\\#hxclon:focus{background-color:hotpink}',
'.css\\#hxclon:focus:hover{background-color:darkgreen}',
])
})
test('using custom label', () => {
const style = css({
label: 'link',
color: 'hotpink',
'&:hover': {
color: 'darkgreen',
},
})
assert.strictEqual(tw(style), 'link#1xpxeti')
assert.deepEqual(tw.target, [
'.link\\#1xpxeti{color:hotpink}',
'.link\\#1xpxeti:hover{color:darkgreen}',
])
})
test('nesting in template literal', () => {
const style = css`
/* all declarations will be prefixed */
padding: 2em 1em;
background: papayawhip;
/* pseudo selectors work as well */
&:hover {
background: palevioletred;
}
/* media queries are no problem */
@media (max-width: 600px) {
background: tomato;
}
@media screen(sm) {
/* nested rules work as expected */
&:hover {
background: yellow;
}
}
& > p {
/* descendant-selectors work as well, but are more of an escape hatch */
text-decoration-line: underline;
}
/* Contextual selectors work as well */
html.test & {
display: none;
}
`
assert.strictEqual(tw(style), 'css#1h873n0')
assert.deepEqual(tw.target, [
'.css\\#1h873n0{padding:2em 1em;background:papayawhip}',
'.css\\#1h873n0:hover{background:palevioletred}',
'@media (max-width: 600px){.css\\#1h873n0{background:tomato}}',
'@media (min-width:640px){.css\\#1h873n0:hover{background:yellow}}',
'.css\\#1h873n0 > p{text-decoration-line:underline}',
'html.test .css\\#1h873n0{display:none}',
])
})
test('interpolation values', () => {
const random = Math.ceil(Math.random() * 4)
const style = css`
background: dodgerblue;
color: white;
border: ${random}px solid white;
margin: ${tw.theme('spacing', 'lg')};
&:focus,
&:hover {
color: theme('colors.gray.600');
}
& .otherClass {
padding: theme('spacing[0.5]');
}
/* Added to css layer */
body {
@apply ${css`
color: darkgreen;
`};
}
/* Explictily add to base layer */
@layer base {
body {
@apply text-gray-700 bg-gray-100;
}
}
`
// No assert because of random
const className = tw(style)
assert.deepEqual(tw.target, [
'body{--tw-text-opacity:1;color:rgba(55,65,81,var(--tw-text-opacity));--tw-bg-opacity:1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}',
`.${escape(
className,
)}{background:dodgerblue;color:white;border:${random}px solid white;margin:2rem}`,
`.${escape(className)}:focus,.${escape(className)}:hover{color:#4b5563}`,
`.${escape(className)} .otherClass{padding:0.5rem}`,
'body{color:darkgreen}',
])
})
test('with shortcut', () => {
const className = tw(cx('bg-gray-900', shortcut(css({ lineHeight: '1' }), 'text-gray-300')))
assert.strictEqual(className, '~(css#185oa3u,text-gray-300) bg-gray-900')
assert.deepEqual(tw.target, [
'.\\~\\(css\\#185oa3u\\,text-gray-300\\){--tw-text-opacity:1;color:rgba(209,213,219,var(--tw-text-opacity));line-height:1}',
'.bg-gray-900{--tw-bg-opacity:1;background-color:rgba(17,24,39,var(--tw-bg-opacity))}',
])
})
test('with apply', () => {
const className = tw(cx('bg-gray-900', apply(css({ lineHeight: '1' }), 'text-gray-300')))
assert.strictEqual(className, '@(css#185oa3u,text-gray-300) bg-gray-900')
assert.deepEqual(tw.target, [
'.\\@\\(css\\#185oa3u\\,text-gray-300\\){line-height:1;--tw-text-opacity:1;color:rgba(209,213,219,var(--tw-text-opacity))}',
'.bg-gray-900{--tw-bg-opacity:1;background-color:rgba(17,24,39,var(--tw-bg-opacity))}',
])
})
test('css within apply and nested selectors', () => {
const nestedListItems = css`
& > li::before {
@apply text-gray-500;
}
`
const prose = css`
& ul {
@apply ${nestedListItems};
}
`
assert.strictEqual(tw(prose), 'css#1e8vcf')
assert.deepEqual(tw.target, [
'.css\\#1e8vcf ul > li::before{--tw-text-opacity:1;color:rgba(107,114,128,var(--tw-text-opacity))}',
])
})