mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
* Refactor * Don’t prefix classes in arbitrary values for group and peer * use `foo` instead of `lol` * handle the prefix inside the group/peer variants Then add the `NoPrefix` feature to the variant itself, which will skip prefixing any other class in the generated selector (because we already took care of prefixing `.group` and `.peer`). We are using an internal symbol such that: - We can keep it as a private API - We don't introduce a breaking change * refactor to simple object instead We will still use a symbol as an internal/private marker, but the data itself will be a simple object for now. If we want to refactor this (and more) in the future using bitflags then we can refactor that in a separate PR. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
209 lines
6.8 KiB
JavaScript
209 lines
6.8 KiB
JavaScript
import postcss from 'postcss'
|
|
import selectorParser from 'postcss-selector-parser'
|
|
import resolveConfig from '../src/public/resolve-config'
|
|
import { createContext } from '../src/lib/setupContextUtils'
|
|
import { crosscheck } from './util/run'
|
|
|
|
crosscheck(() => {
|
|
it('should return a list of variants with meta information about the variant', () => {
|
|
let config = {}
|
|
let context = createContext(resolveConfig(config))
|
|
|
|
let variants = context.getVariants()
|
|
|
|
expect(variants).toContainEqual({
|
|
name: 'hover',
|
|
isArbitrary: false,
|
|
hasDash: true,
|
|
values: [],
|
|
selectors: expect.any(Function),
|
|
})
|
|
|
|
expect(variants).toContainEqual({
|
|
name: 'group',
|
|
isArbitrary: true,
|
|
hasDash: true,
|
|
values: expect.any(Array),
|
|
selectors: expect.any(Function),
|
|
})
|
|
|
|
// `group-hover` now belongs to the `group` variant. The information exposed for the `group`
|
|
// variant is all you need.
|
|
expect(variants.find((v) => v.name === 'group-hover')).toBeUndefined()
|
|
})
|
|
|
|
it('should provide selectors for simple variants', () => {
|
|
let config = {}
|
|
let context = createContext(resolveConfig(config))
|
|
|
|
let variants = context.getVariants()
|
|
|
|
let variant = variants.find((v) => v.name === 'hover')
|
|
expect(variant.selectors()).toEqual(['&:hover'])
|
|
})
|
|
|
|
it('should provide selectors for parallel variants', () => {
|
|
let config = {}
|
|
let context = createContext(resolveConfig(config))
|
|
|
|
let variants = context.getVariants()
|
|
|
|
let variant = variants.find((v) => v.name === 'marker')
|
|
expect(variant.selectors()).toEqual(['& *::marker', '&::marker'])
|
|
})
|
|
|
|
it('should provide selectors for complex matchVariant variants like `group`', () => {
|
|
let config = {}
|
|
let context = createContext(resolveConfig(config))
|
|
|
|
let variants = context.getVariants()
|
|
|
|
let variant = variants.find((v) => v.name === 'group')
|
|
expect(variant.selectors()).toEqual(['.group &'])
|
|
expect(variant.selectors({})).toEqual(['.group &'])
|
|
expect(variant.selectors({ value: 'hover' })).toEqual(['.group:hover &'])
|
|
expect(variant.selectors({ value: '.foo_&' })).toEqual(['.foo .group &'])
|
|
expect(variant.selectors({ modifier: 'foo', value: 'hover' })).toEqual(['.group\\/foo:hover &'])
|
|
expect(variant.selectors({ modifier: 'foo', value: '.foo_&' })).toEqual(['.foo .group\\/foo &'])
|
|
})
|
|
|
|
it('should provide selectors for complex matchVariant variants like `group` (when using a prefix)', () => {
|
|
let config = { prefix: 'tw-' }
|
|
let context = createContext(resolveConfig(config))
|
|
|
|
let variants = context.getVariants()
|
|
|
|
let variant = variants.find((v) => v.name === 'group')
|
|
expect(variant.selectors()).toEqual(['.tw-group &'])
|
|
expect(variant.selectors({})).toEqual(['.tw-group &'])
|
|
expect(variant.selectors({ value: 'hover' })).toEqual(['.tw-group:hover &'])
|
|
expect(variant.selectors({ value: '.foo_&' })).toEqual(['.foo .tw-group &'])
|
|
expect(variant.selectors({ modifier: 'foo', value: 'hover' })).toEqual([
|
|
'.tw-group\\/foo:hover &',
|
|
])
|
|
expect(variant.selectors({ modifier: 'foo', value: '.foo_&' })).toEqual([
|
|
'.foo .tw-group\\/foo &',
|
|
])
|
|
})
|
|
|
|
it('should provide selectors for variants with atrules', () => {
|
|
let config = {}
|
|
let context = createContext(resolveConfig(config))
|
|
|
|
let variants = context.getVariants()
|
|
|
|
let variant = variants.find((v) => v.name === 'supports')
|
|
expect(variant.selectors({ value: 'display:grid' })).toEqual(['@supports (display:grid)'])
|
|
expect(variant.selectors({ value: 'aspect-ratio' })).toEqual([
|
|
'@supports (aspect-ratio: var(--tw))',
|
|
])
|
|
})
|
|
|
|
it('should provide selectors for custom plugins that do a combination of parallel variants with modifiers with arbitrary values and with atrules', () => {
|
|
let config = {
|
|
plugins: [
|
|
function ({ matchVariant }) {
|
|
matchVariant('foo', (value, { modifier }) => {
|
|
return [
|
|
`
|
|
@supports (foo: ${modifier}) {
|
|
@media (width <= 400px) {
|
|
&:hover
|
|
}
|
|
}
|
|
`,
|
|
`.${modifier}\\/${value} &:focus`,
|
|
]
|
|
})
|
|
},
|
|
],
|
|
}
|
|
let context = createContext(resolveConfig(config))
|
|
|
|
let variants = context.getVariants()
|
|
|
|
let variant = variants.find((v) => v.name === 'foo')
|
|
expect(variant.selectors({ modifier: 'bar', value: 'baz' })).toEqual([
|
|
'@supports (foo: bar) { @media (width <= 400px) { &:hover } }',
|
|
'.bar\\/baz &:focus',
|
|
])
|
|
})
|
|
|
|
it('should work for plugins that still use the modifySelectors API', () => {
|
|
let config = {
|
|
plugins: [
|
|
function ({ addVariant }) {
|
|
addVariant('foo', ({ modifySelectors, container }) => {
|
|
// Manually mutating the selector
|
|
modifySelectors(({ selector }) => {
|
|
return selectorParser((selectors) => {
|
|
selectors.walkClasses((classNode) => {
|
|
classNode.value = `foo:${classNode.value}`
|
|
classNode.parent.insertBefore(classNode, selectorParser().astSync(`.foo `))
|
|
})
|
|
}).processSync(selector)
|
|
})
|
|
|
|
// Manually wrap in supports query
|
|
let wrapper = postcss.atRule({ name: 'supports', params: 'display: grid' })
|
|
let nodes = container.nodes
|
|
container.removeAll()
|
|
wrapper.append(nodes)
|
|
container.append(wrapper)
|
|
})
|
|
},
|
|
],
|
|
}
|
|
let context = createContext(resolveConfig(config))
|
|
|
|
let variants = context.getVariants()
|
|
|
|
let variant = variants.find((v) => v.name === 'foo')
|
|
expect(variant.selectors({})).toEqual(['@supports (display: grid) { .foo .foo\\:& }'])
|
|
})
|
|
|
|
it('should special case the `@`', () => {
|
|
let config = {
|
|
plugins: [
|
|
({ matchVariant }) => {
|
|
matchVariant(
|
|
'@',
|
|
(value, { modifier }) => `@container ${modifier ?? ''} (min-width: ${value})`,
|
|
{
|
|
modifiers: 'any',
|
|
values: {
|
|
xs: '20rem',
|
|
sm: '24rem',
|
|
md: '28rem',
|
|
lg: '32rem',
|
|
xl: '36rem',
|
|
'2xl': '42rem',
|
|
'3xl': '48rem',
|
|
'4xl': '56rem',
|
|
'5xl': '64rem',
|
|
'6xl': '72rem',
|
|
'7xl': '80rem',
|
|
},
|
|
}
|
|
)
|
|
},
|
|
],
|
|
}
|
|
let context = createContext(resolveConfig(config))
|
|
|
|
let variants = context.getVariants()
|
|
|
|
let variant = variants.find((v) => v.name === '@')
|
|
expect(variant).toEqual({
|
|
name: '@',
|
|
isArbitrary: true,
|
|
hasDash: false,
|
|
values: expect.any(Array),
|
|
selectors: expect.any(Function),
|
|
})
|
|
expect(variant.selectors({ value: 'xs', modifier: 'foo' })).toEqual([
|
|
'@container foo (min-width: 20rem)',
|
|
])
|
|
})
|
|
})
|