arbitrary-properties.test.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. import { run, html, css, defaults } from './util/run'
  2. test('basic arbitrary properties', () => {
  3. let config = {
  4. content: [
  5. {
  6. raw: html`<div class="[paint-order:markers]"></div>`,
  7. },
  8. ],
  9. corePlugins: { preflight: false },
  10. }
  11. let input = css`
  12. @tailwind base;
  13. @tailwind components;
  14. @tailwind utilities;
  15. `
  16. return run(input, config).then((result) => {
  17. expect(result.css).toMatchFormattedCss(css`
  18. ${defaults}
  19. .\[paint-order\:markers\] {
  20. paint-order: markers;
  21. }
  22. `)
  23. })
  24. })
  25. test('different arbitrary properties are picked up separately', () => {
  26. let config = {
  27. content: [
  28. {
  29. raw: html`<div class="[foo:bar] [bar:baz]"></div>`,
  30. },
  31. ],
  32. corePlugins: { preflight: false },
  33. }
  34. let input = css`
  35. @tailwind base;
  36. @tailwind components;
  37. @tailwind utilities;
  38. `
  39. return run(input, config).then((result) => {
  40. expect(result.css).toMatchFormattedCss(css`
  41. ${defaults}
  42. .\[bar\:baz\] {
  43. bar: baz;
  44. }
  45. .\[foo\:bar\] {
  46. foo: bar;
  47. }
  48. `)
  49. })
  50. })
  51. test('arbitrary properties with modifiers', () => {
  52. let config = {
  53. content: [
  54. {
  55. raw: html`<div class="dark:lg:hover:[paint-order:markers]"></div>`,
  56. },
  57. ],
  58. corePlugins: { preflight: false },
  59. }
  60. let input = css`
  61. @tailwind base;
  62. @tailwind components;
  63. @tailwind utilities;
  64. `
  65. return run(input, config).then((result) => {
  66. expect(result.css).toMatchFormattedCss(css`
  67. ${defaults}
  68. @media (prefers-color-scheme: dark) {
  69. @media (min-width: 1024px) {
  70. .dark\:lg\:hover\:\[paint-order\:markers\]:hover {
  71. paint-order: markers;
  72. }
  73. }
  74. }
  75. `)
  76. })
  77. })
  78. test('arbitrary properties are sorted after utilities', () => {
  79. let config = {
  80. content: [
  81. {
  82. raw: html`<div class="content-none [paint-order:markers] hover:pointer-events-none"></div>`,
  83. },
  84. ],
  85. corePlugins: { preflight: false },
  86. }
  87. let input = css`
  88. @tailwind base;
  89. @tailwind components;
  90. @tailwind utilities;
  91. `
  92. return run(input, config).then((result) => {
  93. expect(result.css).toMatchFormattedCss(css`
  94. ${defaults}
  95. .content-none {
  96. --tw-content: none;
  97. content: var(--tw-content);
  98. }
  99. .\[paint-order\:markers\] {
  100. paint-order: markers;
  101. }
  102. .hover\:pointer-events-none:hover {
  103. pointer-events: none;
  104. }
  105. `)
  106. })
  107. })
  108. test('using CSS variables', () => {
  109. let config = {
  110. content: [
  111. {
  112. raw: html`<div class="[--my-var:auto]"></div>`,
  113. },
  114. ],
  115. corePlugins: { preflight: false },
  116. }
  117. let input = css`
  118. @tailwind base;
  119. @tailwind components;
  120. @tailwind utilities;
  121. `
  122. return run(input, config).then((result) => {
  123. expect(result.css).toMatchFormattedCss(css`
  124. ${defaults}
  125. .\[--my-var\:auto\] {
  126. --my-var: auto;
  127. }
  128. `)
  129. })
  130. })
  131. test('using underscores as spaces', () => {
  132. let config = {
  133. content: [
  134. {
  135. raw: html`<div class="[--my-var:2px_4px]"></div>`,
  136. },
  137. ],
  138. corePlugins: { preflight: false },
  139. }
  140. let input = css`
  141. @tailwind base;
  142. @tailwind components;
  143. @tailwind utilities;
  144. `
  145. return run(input, config).then((result) => {
  146. expect(result.css).toMatchFormattedCss(css`
  147. ${defaults}
  148. .\[--my-var\:2px_4px\] {
  149. --my-var: 2px 4px;
  150. }
  151. `)
  152. })
  153. })
  154. test('using the important modifier', () => {
  155. let config = {
  156. content: [
  157. {
  158. raw: html`<div class="![--my-var:2px_4px]"></div>`,
  159. },
  160. ],
  161. corePlugins: { preflight: false },
  162. }
  163. let input = css`
  164. @tailwind base;
  165. @tailwind components;
  166. @tailwind utilities;
  167. `
  168. return run(input, config).then((result) => {
  169. expect(result.css).toMatchFormattedCss(css`
  170. ${defaults}
  171. .\!\[--my-var\:2px_4px\] {
  172. --my-var: 2px 4px !important;
  173. }
  174. `)
  175. })
  176. })
  177. test('colons are allowed in quotes', () => {
  178. let config = {
  179. content: [
  180. {
  181. raw: html`<div class="[content:'foo:bar']"></div>`,
  182. },
  183. ],
  184. corePlugins: { preflight: false },
  185. }
  186. let input = css`
  187. @tailwind base;
  188. @tailwind components;
  189. @tailwind utilities;
  190. `
  191. return run(input, config).then((result) => {
  192. expect(result.css).toMatchFormattedCss(css`
  193. ${defaults}
  194. .\[content\:\'foo\:bar\'\] {
  195. content: 'foo:bar';
  196. }
  197. `)
  198. })
  199. })
  200. test('colons are allowed in braces', () => {
  201. let config = {
  202. content: [
  203. {
  204. raw: html`<div class="[background-image:url(http://example.com/picture.jpg)]"></div>`,
  205. },
  206. ],
  207. corePlugins: { preflight: false },
  208. }
  209. let input = css`
  210. @tailwind base;
  211. @tailwind components;
  212. @tailwind utilities;
  213. `
  214. return run(input, config).then((result) => {
  215. expect(result.css).toMatchFormattedCss(css`
  216. ${defaults}
  217. .\[background-image\:url\(http\:\/\/example\.com\/picture\.jpg\)\] {
  218. background-image: url('http://example.com/picture.jpg');
  219. }
  220. `)
  221. })
  222. })
  223. test('invalid class', () => {
  224. let config = {
  225. content: [
  226. {
  227. raw: html`<div class="[a:b:c:d]"></div>`,
  228. },
  229. ],
  230. corePlugins: { preflight: false },
  231. }
  232. let input = css`
  233. @tailwind base;
  234. @tailwind components;
  235. @tailwind utilities;
  236. `
  237. return run(input, config).then((result) => {
  238. expect(result.css).toMatchFormattedCss(css`
  239. ${defaults}
  240. `)
  241. })
  242. })
  243. test('invalid arbitrary property', () => {
  244. let config = {
  245. content: [
  246. {
  247. raw: html`<div class="[autoplay:\${autoplay}]"></div>`,
  248. },
  249. ],
  250. corePlugins: { preflight: false },
  251. }
  252. let input = css`
  253. @tailwind base;
  254. @tailwind components;
  255. @tailwind utilities;
  256. `
  257. return run(input, config).then((result) => {
  258. expect(result.css).toMatchFormattedCss(css`
  259. ${defaults}
  260. `)
  261. })
  262. })
  263. test('invalid arbitrary property 2', () => {
  264. let config = {
  265. content: [
  266. {
  267. raw: html`[0:02]`,
  268. },
  269. ],
  270. corePlugins: { preflight: false },
  271. }
  272. let input = css`
  273. @tailwind base;
  274. @tailwind components;
  275. @tailwind utilities;
  276. `
  277. return run(input, config).then((result) => {
  278. expect(result.css).toMatchFormattedCss(css`
  279. ${defaults}
  280. `)
  281. })
  282. })
  283. test('using fractional spacing values inside theme() function', () => {
  284. let config = {
  285. content: [
  286. {
  287. raw: html`<div
  288. class="[border:_calc(5vw_-_theme(spacing[2.5]))_double_theme('colors.fuchsia.700')]"
  289. ></div>`,
  290. },
  291. ],
  292. corePlugins: { preflight: false },
  293. }
  294. let input = css`
  295. @tailwind base;
  296. @tailwind components;
  297. @tailwind utilities;
  298. `
  299. return run(input, config).then((result) => {
  300. expect(result.css).toMatchFormattedCss(css`
  301. ${defaults}
  302. .\[border\:_calc\(5vw_-_theme\(spacing\[2\.5\]\)\)_double_theme\(\'colors\.fuchsia\.700\'\)\] {
  303. border: calc(5vw - 0.625rem) double #a21caf;
  304. }
  305. `)
  306. })
  307. })
  308. test('using multiple arbitrary props having fractional spacing values', () => {
  309. let config = {
  310. content: [
  311. {
  312. raw: html`<div
  313. class="[height:_calc(100vh_-_theme(spacing[2.5]))] [box-shadow:_0_calc(theme(spacing[0.5])_*_-1)_theme(colors.red.400)_inset]"
  314. ></div>`,
  315. },
  316. ],
  317. corePlugins: { preflight: false },
  318. }
  319. let input = css`
  320. @tailwind base;
  321. @tailwind components;
  322. @tailwind utilities;
  323. `
  324. return run(input, config).then((result) => {
  325. return expect(result.css).toMatchFormattedCss(css`
  326. ${defaults}
  327. .\[box-shadow\:_0_calc\(theme\(spacing\[0\.5\]\)_\*_-1\)_theme\(colors\.red\.400\)_inset\] {
  328. box-shadow: inset 0 -0.125rem #f87171;
  329. }
  330. .\[height\:_calc\(100vh_-_theme\(spacing\[2\.5\]\)\)\] {
  331. height: calc(100vh - 0.625rem);
  332. }
  333. `)
  334. })
  335. })
  336. it('should be possible to read theme values in arbitrary properties (without quotes)', () => {
  337. let config = {
  338. content: [{ raw: html`<div class="[--a:theme(colors.blue.500)] [color:var(--a)]"></div>` }],
  339. corePlugins: { preflight: false },
  340. }
  341. let input = css`
  342. @tailwind base;
  343. @tailwind components;
  344. @tailwind utilities;
  345. `
  346. return run(input, config).then((result) => {
  347. return expect(result.css).toMatchFormattedCss(css`
  348. ${defaults}
  349. .\[--a\:theme\(colors\.blue\.500\)\] {
  350. --a: #3b82f6;
  351. }
  352. .\[color\:var\(--a\)\] {
  353. color: var(--a);
  354. }
  355. `)
  356. })
  357. })
  358. it('should be possible to read theme values in arbitrary properties (with quotes)', () => {
  359. let config = {
  360. content: [{ raw: html`<div class="[color:var(--a)] [--a:theme('colors.blue.500')]"></div>` }],
  361. corePlugins: { preflight: false },
  362. }
  363. let input = css`
  364. @tailwind base;
  365. @tailwind components;
  366. @tailwind utilities;
  367. `
  368. return run(input, config).then((result) => {
  369. return expect(result.css).toMatchFormattedCss(css`
  370. ${defaults}
  371. .\[--a\:theme\(\'colors\.blue\.500\'\)\] {
  372. --a: #3b82f6;
  373. }
  374. .\[color\:var\(--a\)\] {
  375. color: var(--a);
  376. }
  377. `)
  378. })
  379. })
  380. it('should not generate invalid CSS', () => {
  381. let config = {
  382. content: [
  383. {
  384. raw: html`
  385. <div class="[https://en.wikipedia.org/wiki]"></div>
  386. <div class="[http://example.org]"></div>
  387. <div class="[http://example]"></div>
  388. <div class="[ftp://example]"></div>
  389. <div class="[stillworks:/example]"></div>
  390. `,
  391. // NOTE: In this case `stillworks:/example` being generated is not ideal
  392. // but it at least doesn't produce invalid CSS when run through prettier
  393. // So we can let it through since it is technically valid
  394. },
  395. ],
  396. corePlugins: { preflight: false },
  397. }
  398. return run('@tailwind utilities', config).then((result) => {
  399. return expect(result.css).toMatchFormattedCss(css`
  400. .\[stillworks\:\/example\] {
  401. stillworks: /example;
  402. }
  403. `)
  404. })
  405. })