format-variant-selector.test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. import { formatVariantSelector, finalizeSelector } from '../src/util/formatVariantSelector'
  2. it('should be possible to add a simple variant to a simple selector', () => {
  3. let selector = '.text-center'
  4. let candidate = 'hover:text-center'
  5. let variants = ['&:hover']
  6. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  7. '.hover\\:text-center:hover'
  8. )
  9. })
  10. it('should be possible to add a multiple simple variants to a simple selector', () => {
  11. let selector = '.text-center'
  12. let candidate = 'focus:hover:text-center'
  13. let variants = ['&:hover', '&:focus']
  14. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  15. '.focus\\:hover\\:text-center:hover:focus'
  16. )
  17. })
  18. it('should be possible to add a simple variant to a selector containing escaped parts', () => {
  19. let selector = '.bg-\\[rgba\\(0\\,0\\,0\\)\\]'
  20. let candidate = 'hover:bg-[rgba(0,0,0)]'
  21. let variants = ['&:hover']
  22. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  23. '.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover'
  24. )
  25. })
  26. it('should be possible to add a simple variant to a selector containing escaped parts (escape is slightly different)', () => {
  27. let selector = '.bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]'
  28. let candidate = 'hover:bg-[rgba(0,0,0)]'
  29. let variants = ['&:hover']
  30. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  31. '.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover'
  32. )
  33. })
  34. it('should be possible to add a simple variant to a more complex selector', () => {
  35. let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])'
  36. let candidate = 'hover:space-x-4'
  37. let variants = ['&:hover']
  38. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  39. '.hover\\:space-x-4:hover > :not([hidden]) ~ :not([hidden])'
  40. )
  41. })
  42. it('should be possible to add multiple simple variants to a more complex selector', () => {
  43. let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])'
  44. let candidate = 'disabled:focus:hover:space-x-4'
  45. let variants = ['&:hover', '&:focus', '&:disabled']
  46. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  47. '.disabled\\:focus\\:hover\\:space-x-4:hover:focus:disabled > :not([hidden]) ~ :not([hidden])'
  48. )
  49. })
  50. it('should be possible to add a single merge variant to a simple selector', () => {
  51. let selector = '.text-center'
  52. let candidate = 'group-hover:text-center'
  53. let variants = [':merge(.group):hover &']
  54. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  55. '.group:hover .group-hover\\:text-center'
  56. )
  57. })
  58. it('should be possible to add multiple merge variants to a simple selector', () => {
  59. let selector = '.text-center'
  60. let candidate = 'group-focus:group-hover:text-center'
  61. let variants = [':merge(.group):hover &', ':merge(.group):focus &']
  62. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  63. '.group:focus:hover .group-focus\\:group-hover\\:text-center'
  64. )
  65. })
  66. it('should be possible to add a single merge variant to a more complex selector', () => {
  67. let selector = '.space-x-4 ~ :not([hidden]) ~ :not([hidden])'
  68. let candidate = 'group-hover:space-x-4'
  69. let variants = [':merge(.group):hover &']
  70. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  71. '.group:hover .group-hover\\:space-x-4 ~ :not([hidden]) ~ :not([hidden])'
  72. )
  73. })
  74. it('should be possible to add multiple merge variants to a more complex selector', () => {
  75. let selector = '.space-x-4 ~ :not([hidden]) ~ :not([hidden])'
  76. let candidate = 'group-focus:group-hover:space-x-4'
  77. let variants = [':merge(.group):hover &', ':merge(.group):focus &']
  78. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  79. '.group:focus:hover .group-focus\\:group-hover\\:space-x-4 ~ :not([hidden]) ~ :not([hidden])'
  80. )
  81. })
  82. it('should be possible to add multiple unique merge variants to a simple selector', () => {
  83. let selector = '.text-center'
  84. let candidate = 'peer-focus:group-hover:text-center'
  85. let variants = [':merge(.group):hover &', ':merge(.peer):focus ~ &']
  86. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  87. '.peer:focus ~ .group:hover .peer-focus\\:group-hover\\:text-center'
  88. )
  89. })
  90. it('should be possible to add multiple unique merge variants to a simple selector', () => {
  91. let selector = '.text-center'
  92. let candidate = 'group-hover:peer-focus:text-center'
  93. let variants = [':merge(.peer):focus ~ &', ':merge(.group):hover &']
  94. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  95. '.group:hover .peer:focus ~ .group-hover\\:peer-focus\\:text-center'
  96. )
  97. })
  98. it('should be possible to use multiple :merge() calls with different "arguments"', () => {
  99. let result = '&'
  100. result = formatVariantSelector(result, ':merge(.group):hover &')
  101. expect(result).toEqual(':merge(.group):hover &')
  102. result = formatVariantSelector(result, ':merge(.peer):hover ~ &')
  103. expect(result).toEqual(':merge(.peer):hover ~ :merge(.group):hover &')
  104. result = formatVariantSelector(result, ':merge(.group):focus &')
  105. expect(result).toEqual(':merge(.peer):hover ~ :merge(.group):focus:hover &')
  106. result = formatVariantSelector(result, ':merge(.peer):focus ~ &')
  107. expect(result).toEqual(':merge(.peer):focus:hover ~ :merge(.group):focus:hover &')
  108. })
  109. it('group hover and prose headings combination', () => {
  110. let selector = '.text-center'
  111. let candidate = 'group-hover:prose-headings:text-center'
  112. let variants = [
  113. ':where(&) :is(h1, h2, h3, h4)', // Prose Headings
  114. ':merge(.group):hover &', // Group Hover
  115. ]
  116. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  117. '.group:hover :where(.group-hover\\:prose-headings\\:text-center) :is(h1, h2, h3, h4)'
  118. )
  119. })
  120. it('group hover and prose headings combination flipped', () => {
  121. let selector = '.text-center'
  122. let candidate = 'prose-headings:group-hover:text-center'
  123. let variants = [
  124. ':merge(.group):hover &', // Group Hover
  125. ':where(&) :is(h1, h2, h3, h4)', // Prose Headings
  126. ]
  127. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  128. ':where(.group:hover .prose-headings\\:group-hover\\:text-center) :is(h1, h2, h3, h4)'
  129. )
  130. })
  131. it('should be possible to handle a complex utility', () => {
  132. let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])'
  133. let candidate = 'peer-disabled:peer-first-child:group-hover:group-focus:focus:hover:space-x-4'
  134. let variants = [
  135. '&:hover', // Hover
  136. '&:focus', // Focus
  137. ':merge(.group):focus &', // Group focus
  138. ':merge(.group):hover &', // Group hover
  139. ':merge(.peer):first-child ~ &', // Peer first-child
  140. ':merge(.peer):disabled ~ &', // Peer disabled
  141. ]
  142. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  143. '.peer:disabled:first-child ~ .group:hover:focus .peer-disabled\\:peer-first-child\\:group-hover\\:group-focus\\:focus\\:hover\\:space-x-4:hover:focus > :not([hidden]) ~ :not([hidden])'
  144. )
  145. })
  146. describe('real examples', () => {
  147. it('example a', () => {
  148. let selector = '.placeholder-red-500::placeholder'
  149. let candidate = 'hover:placeholder-red-500'
  150. let variants = ['&:hover']
  151. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  152. '.hover\\:placeholder-red-500:hover::placeholder'
  153. )
  154. })
  155. it('example b', () => {
  156. let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])'
  157. let candidate = 'group-hover:hover:space-x-4'
  158. let variants = ['&:hover', ':merge(.group):hover &']
  159. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  160. '.group:hover .group-hover\\:hover\\:space-x-4:hover > :not([hidden]) ~ :not([hidden])'
  161. )
  162. })
  163. it('should work for group-hover and class dark mode combinations', () => {
  164. let selector = '.text-center'
  165. let candidate = 'dark:group-hover:text-center'
  166. let variants = [':merge(.group):hover &', '.dark &']
  167. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  168. '.dark .group:hover .dark\\:group-hover\\:text-center'
  169. )
  170. })
  171. it('should work for group-hover and class dark mode combinations (reversed)', () => {
  172. let selector = '.text-center'
  173. let candidate = 'group-hover:dark:text-center'
  174. let variants = ['.dark &', ':merge(.group):hover &']
  175. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  176. '.group:hover .dark .group-hover\\:dark\\:text-center'
  177. )
  178. })
  179. describe('prose-headings', () => {
  180. it('should be possible to use hover:prose-headings:text-center', () => {
  181. let selector = '.text-center'
  182. let candidate = 'hover:prose-headings:text-center'
  183. let variants = [':where(&) :is(h1, h2, h3, h4)', '&:hover']
  184. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  185. ':where(.hover\\:prose-headings\\:text-center) :is(h1, h2, h3, h4):hover'
  186. )
  187. })
  188. it('should be possible to use prose-headings:hover:text-center', () => {
  189. let selector = '.text-center'
  190. let candidate = 'prose-headings:hover:text-center'
  191. let variants = ['&:hover', ':where(&) :is(h1, h2, h3, h4)']
  192. expect(finalizeSelector(formatVariantSelector(...variants), { selector, candidate })).toEqual(
  193. ':where(.prose-headings\\:hover\\:text-center:hover) :is(h1, h2, h3, h4)'
  194. )
  195. })
  196. })
  197. })
  198. describe('pseudo elements', () => {
  199. it.each`
  200. before | after
  201. ${'&::before'} | ${'&::before'}
  202. ${'&::before:hover'} | ${'&:hover::before'}
  203. ${'&:before:hover'} | ${'&:hover:before'}
  204. ${'&::file-selector-button:hover'} | ${'&::file-selector-button:hover'}
  205. ${'&:hover::file-selector-button'} | ${'&:hover::file-selector-button'}
  206. ${'.parent:hover &'} | ${'.parent:hover &'}
  207. ${'.parent::before &'} | ${'.parent &::before'}
  208. ${'.parent::before &:hover'} | ${'.parent &:hover::before'}
  209. ${':where(&::before) :is(h1, h2, h3, h4)'} | ${':where(&) :is(h1, h2, h3, h4)::before'}
  210. ${':where(&::file-selector-button) :is(h1, h2, h3, h4)'} | ${':where(&::file-selector-button) :is(h1, h2, h3, h4)'}
  211. `('should translate "$before" into "$after"', ({ before, after }) => {
  212. let result = finalizeSelector(formatVariantSelector('&', before), {
  213. selector: '.a',
  214. candidate: 'a',
  215. })
  216. expect(result).toEqual(after.replace('&', '.a'))
  217. })
  218. })