getVariants.test.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import postcss from 'postcss'
  2. import selectorParser from 'postcss-selector-parser'
  3. import resolveConfig from '../src/public/resolve-config'
  4. import { createContext } from '../src/lib/setupContextUtils'
  5. it('should return a list of variants with meta information about the variant', () => {
  6. let config = {}
  7. let context = createContext(resolveConfig(config))
  8. let variants = context.getVariants()
  9. expect(variants).toContainEqual({
  10. name: 'hover',
  11. isArbitrary: false,
  12. hasDash: true,
  13. values: [],
  14. selectors: expect.any(Function),
  15. })
  16. expect(variants).toContainEqual({
  17. name: 'group',
  18. isArbitrary: true,
  19. hasDash: true,
  20. values: expect.any(Array),
  21. selectors: expect.any(Function),
  22. })
  23. // `group-hover` now belongs to the `group` variant. The information exposed for the `group`
  24. // variant is all you need.
  25. expect(variants.find((v) => v.name === 'group-hover')).toBeUndefined()
  26. })
  27. it('should provide selectors for simple variants', () => {
  28. let config = {}
  29. let context = createContext(resolveConfig(config))
  30. let variants = context.getVariants()
  31. let variant = variants.find((v) => v.name === 'hover')
  32. expect(variant.selectors()).toEqual(['&:hover'])
  33. })
  34. it('should provide selectors for parallel variants', () => {
  35. let config = {}
  36. let context = createContext(resolveConfig(config))
  37. let variants = context.getVariants()
  38. let variant = variants.find((v) => v.name === 'marker')
  39. expect(variant.selectors()).toEqual(['& *::marker', '&::marker'])
  40. })
  41. it('should provide selectors for complex matchVariant variants like `group`', () => {
  42. let config = {}
  43. let context = createContext(resolveConfig(config))
  44. let variants = context.getVariants()
  45. let variant = variants.find((v) => v.name === 'group')
  46. expect(variant.selectors()).toEqual(['.group &'])
  47. expect(variant.selectors({})).toEqual(['.group &'])
  48. expect(variant.selectors({ value: 'hover' })).toEqual(['.group:hover &'])
  49. expect(variant.selectors({ value: '.foo_&' })).toEqual(['.foo .group &'])
  50. expect(variant.selectors({ modifier: 'foo', value: 'hover' })).toEqual(['.group\\/foo:hover &'])
  51. expect(variant.selectors({ modifier: 'foo', value: '.foo_&' })).toEqual(['.foo .group\\/foo &'])
  52. })
  53. it('should provide selectors for complex matchVariant variants like `group` (when using a prefix)', () => {
  54. let config = { prefix: 'tw-' }
  55. let context = createContext(resolveConfig(config))
  56. let variants = context.getVariants()
  57. let variant = variants.find((v) => v.name === 'group')
  58. expect(variant.selectors()).toEqual(['.tw-group &'])
  59. expect(variant.selectors({})).toEqual(['.tw-group &'])
  60. expect(variant.selectors({ value: 'hover' })).toEqual(['.tw-group:hover &'])
  61. expect(variant.selectors({ value: '.foo_&' })).toEqual(['.foo .tw-group &'])
  62. expect(variant.selectors({ modifier: 'foo', value: 'hover' })).toEqual([
  63. '.tw-group\\/foo:hover &',
  64. ])
  65. expect(variant.selectors({ modifier: 'foo', value: '.foo_&' })).toEqual([
  66. '.foo .tw-group\\/foo &',
  67. ])
  68. })
  69. it('should provide selectors for variants with atrules', () => {
  70. let config = {}
  71. let context = createContext(resolveConfig(config))
  72. let variants = context.getVariants()
  73. let variant = variants.find((v) => v.name === 'supports')
  74. expect(variant.selectors({ value: 'display:grid' })).toEqual(['@supports (display:grid)'])
  75. expect(variant.selectors({ value: 'aspect-ratio' })).toEqual([
  76. '@supports (aspect-ratio: var(--tw))',
  77. ])
  78. })
  79. it('should provide selectors for custom plugins that do a combination of parallel variants with modifiers with arbitrary values and with atrules', () => {
  80. let config = {
  81. plugins: [
  82. function ({ matchVariant }) {
  83. matchVariant('foo', (value, { modifier }) => {
  84. return [
  85. `
  86. @supports (foo: ${modifier}) {
  87. @media (max-width: 400px) {
  88. &:hover
  89. }
  90. }
  91. `,
  92. `.${modifier}\\/${value} &:focus`,
  93. ]
  94. })
  95. },
  96. ],
  97. }
  98. let context = createContext(resolveConfig(config))
  99. let variants = context.getVariants()
  100. let variant = variants.find((v) => v.name === 'foo')
  101. expect(variant.selectors({ modifier: 'bar', value: 'baz' })).toEqual([
  102. '@supports (foo: bar) { @media (max-width: 400px) { &:hover } }',
  103. '.bar\\/baz &:focus',
  104. ])
  105. })
  106. it('should work for plugins that still use the modifySelectors API', () => {
  107. let config = {
  108. plugins: [
  109. function ({ addVariant }) {
  110. addVariant('foo', ({ modifySelectors, container }) => {
  111. // Manually mutating the selector
  112. modifySelectors(({ selector }) => {
  113. return selectorParser((selectors) => {
  114. selectors.walkClasses((classNode) => {
  115. classNode.value = `foo:${classNode.value}`
  116. classNode.parent.insertBefore(classNode, selectorParser().astSync(`.foo `))
  117. })
  118. }).processSync(selector)
  119. })
  120. // Manually wrap in supports query
  121. let wrapper = postcss.atRule({ name: 'supports', params: 'display: grid' })
  122. let nodes = container.nodes
  123. container.removeAll()
  124. wrapper.append(nodes)
  125. container.append(wrapper)
  126. })
  127. },
  128. ],
  129. }
  130. let context = createContext(resolveConfig(config))
  131. let variants = context.getVariants()
  132. let variant = variants.find((v) => v.name === 'foo')
  133. expect(variant.selectors({})).toEqual(['@supports (display: grid) { .foo .foo\\:& }'])
  134. })
  135. it('should special case the `@`', () => {
  136. let config = {
  137. plugins: [
  138. ({ matchVariant }) => {
  139. matchVariant(
  140. '@',
  141. (value, { modifier }) => `@container ${modifier ?? ''} (min-width: ${value})`,
  142. {
  143. modifiers: 'any',
  144. values: {
  145. xs: '20rem',
  146. sm: '24rem',
  147. md: '28rem',
  148. lg: '32rem',
  149. xl: '36rem',
  150. '2xl': '42rem',
  151. '3xl': '48rem',
  152. '4xl': '56rem',
  153. '5xl': '64rem',
  154. '6xl': '72rem',
  155. '7xl': '80rem',
  156. },
  157. }
  158. )
  159. },
  160. ],
  161. }
  162. let context = createContext(resolveConfig(config))
  163. let variants = context.getVariants()
  164. let variant = variants.find((v) => v.name === '@')
  165. expect(variant).toEqual({
  166. name: '@',
  167. isArbitrary: true,
  168. hasDash: false,
  169. values: expect.any(Array),
  170. selectors: expect.any(Function),
  171. })
  172. expect(variant.selectors({ value: 'xs', modifier: 'foo' })).toEqual([
  173. '@container foo (min-width: 20rem)',
  174. ])
  175. })