tailwindcss/tests/arbitrary-variants.test.js
Jordan Pittman d6bec49934
Fix parallel variant ordering clash (#9282)
* Remove remnants of the user layer

It hasn’t been used in a while

* Rewrite sort offset generation

* wip

* wip

wip

* Handle parasite utilities

* wip

* wip

* Make parallel variants sorting more resillient

It’s not perfect but it’s close

* fix

* remove todo

it adds a new bit so it can’t

* Simplify getClassOrder usage

* Simplify

oops

oops

* Add parasite utility for `dark`

dark mode class name

* Cleanup

* Cleanup

* Simplify

* format files

* Fix prettier plugin to use git build of Tailwind CSS

Symlink and build instead of adding a recursive dev dependency

It breaks node < 16

* Fix prettier error

* wip

* fix test

* Update changelog

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2022-09-09 13:12:43 -04:00

618 lines
14 KiB
JavaScript

import { run, html, css, defaults } from './util/run'
test('basic arbitrary variants', () => {
let config = {
content: [{ raw: html`<div class="[&>*]:underline"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.\[\&\>\*\]\:underline > * {
text-decoration-line: underline;
}
`)
})
})
test('spaces in selector (using _)', () => {
let config = {
content: [
{
raw: html`<div class="[.a.b_&]:underline"></div>`,
},
],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.a.b .\[\.a\.b_\&\]\:underline {
text-decoration-line: underline;
}
`)
})
})
test('arbitrary variants with modifiers', () => {
let config = {
content: [{ raw: html`<div class="dark:lg:hover:[&>*]:underline"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
@media (prefers-color-scheme: dark) {
@media (min-width: 1024px) {
.dark\:lg\:hover\:\[\&\>\*\]\:underline > *:hover {
text-decoration-line: underline;
}
}
}
`)
})
})
test('variants without & or an at-rule are ignored', () => {
let config = {
content: [
{
raw: html`
<div class="[div]:underline"></div>
<div class="[:hover]:underline"></div>
<div class="[wtf-bbq]:underline"></div>
<div class="[lol]:hover:underline"></div>
`,
},
],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
`)
})
})
test('arbitrary variants are sorted after other variants', () => {
let config = {
content: [{ raw: html`<div class="underline lg:underline [&>*]:underline"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.underline {
text-decoration-line: underline;
}
@media (min-width: 1024px) {
.lg\:underline {
text-decoration-line: underline;
}
}
.\[\&\>\*\]\:underline > * {
text-decoration-line: underline;
}
`)
})
})
test('using the important modifier', () => {
let config = {
content: [{ raw: html`<div class="[&>*]:!underline"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.\[\&\>\*\]\:\!underline > * {
text-decoration-line: underline !important;
}
`)
})
})
test('at-rules', () => {
let config = {
content: [{ raw: html`<div class="[@supports(what:ever)]:underline"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
@supports (what: ever) {
.\[\@supports\(what\:ever\)\]\:underline {
text-decoration-line: underline;
}
}
`)
})
})
test('nested at-rules', () => {
let config = {
content: [
{
raw: html`<div class="[@media_screen{@media(hover:hover)}]:underline"></div>`,
},
],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
@media screen {
@media (hover: hover) {
.\[\@media_screen\{\@media\(hover\:hover\)\}\]\:underline {
text-decoration-line: underline;
}
}
}
`)
})
})
test('at-rules with selector modifications', () => {
let config = {
content: [{ raw: html`<div class="[@media(hover:hover){&:hover}]:underline"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
@media (hover: hover) {
.\[\@media\(hover\:hover\)\{\&\:hover\}\]\:underline:hover {
text-decoration-line: underline;
}
}
`)
})
})
test('nested at-rules with selector modifications', () => {
let config = {
content: [
{
raw: html`<div class="[@media_screen{@media(hover:hover){&:hover}}]:underline"></div>`,
},
],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
@media screen {
@media (hover: hover) {
.\[\@media_screen\{\@media\(hover\:hover\)\{\&\:hover\}\}\]\:underline:hover {
text-decoration-line: underline;
}
}
}
`)
})
})
test('attribute selectors', () => {
let config = {
content: [{ raw: html`<div class="[&[data-open]]:underline"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.\[\&\[data-open\]\]\:underline[data-open] {
text-decoration-line: underline;
}
`)
})
})
test('multiple attribute selectors', () => {
let config = {
content: [{ raw: html`<div class="[&[data-foo][data-bar]:not([data-baz])]:underline"></div>` }],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]\:underline[data-foo][data-bar]:not([data-baz]) {
text-decoration-line: underline;
}
`)
})
})
test('multiple attribute selectors with custom separator (1)', () => {
let config = {
separator: '__',
content: [
{ raw: html`<div class="[&[data-foo][data-bar]:not([data-baz])]__underline"></div>` },
],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]__underline[data-foo][data-bar]:not([data-baz]) {
text-decoration-line: underline;
}
`)
})
})
test('multiple attribute selectors with custom separator (2)', () => {
let config = {
separator: '_@',
content: [
{ raw: html`<div class="[&[data-foo][data-bar]:not([data-baz])]_@underline"></div>` },
],
corePlugins: { preflight: false },
}
let input = css`
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.\[\&\[data-foo\]\[data-bar\]\:not\(\[data-baz\]\)\]_\@underline[data-foo][data-bar]:not([data-baz]) {
text-decoration-line: underline;
}
`)
})
})
test('with @apply', () => {
let config = {
content: [
{
raw: html`<div class="foo"></div>`,
},
],
corePlugins: { preflight: false },
}
let input = `
@tailwind base;
@tailwind components;
@tailwind utilities;
.foo {
@apply [@media_screen{@media(hover:hover){&:hover}}]:underline;
}
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
@media screen {
@media (hover: hover) {
.foo:hover {
text-decoration-line: underline;
}
}
}
`)
})
})
test('keeps escaped underscores', () => {
let config = {
content: [
{
raw: '<div class="[&_.foo\\_\\_bar]:underline"></div>',
},
],
corePlugins: { preflight: false },
}
let input = `
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.\[\&_\.foo\\_\\_bar\]\:underline .foo__bar {
text-decoration-line: underline;
}
`)
})
})
test('keeps escaped underscores with multiple arbitrary variants', () => {
let config = {
content: [
{
raw: '<div class="[&_.foo\\_\\_bar]:[&_.bar\\_\\_baz]:underline"></div>',
},
],
corePlugins: { preflight: false },
}
let input = `
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.\[\&_\.foo\\_\\_bar\]\:\[\&_\.bar\\_\\_baz\]\:underline .bar__baz .foo__bar {
text-decoration-line: underline;
}
`)
})
})
test('keeps escaped underscores in arbitrary variants mixed with normal variants', () => {
let config = {
content: [
{
raw: `
<div class="[&_.foo\\_\\_bar]:hover:underline"></div>
<div class="hover:[&_.foo\\_\\_bar]:underline"></div>
`,
},
],
corePlugins: { preflight: false },
}
let input = `
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.\[\&_\.foo\\_\\_bar\]\:hover\:underline:hover .foo__bar {
text-decoration-line: underline;
}
.hover\:\[\&_\.foo\\_\\_bar\]\:underline .foo__bar:hover {
text-decoration-line: underline;
}
`)
})
})
test('allows attribute variants with quotes', () => {
let config = {
content: [
{
raw: `
<div class="[&[data-test='2']]:underline"></div>
<div class='[&[data-test="2"]]:underline'></div>
`,
},
],
corePlugins: { preflight: false },
}
let input = `
@tailwind base;
@tailwind components;
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
${defaults}
.\[\&\[data-test\=\'2\'\]\]\:underline[data-test="2"] {
text-decoration-line: underline;
}
.\[\&\[data-test\=\"2\"\]\]\:underline[data-test='2'] {
text-decoration-line: underline;
}
`)
})
})
test('classes in arbitrary variants should not be prefixed', () => {
let config = {
prefix: 'tw-',
content: [
{
raw: `
<div class="[.foo_&]:tw-text-red-400">should not be red</div>
<div class="foo">
<div class="[.foo_&]:tw-text-red-400">should be red</div>
</div>
<div class="[&_.foo]:tw-text-red-400">
<div>should not be red</div>
<div class="foo">should be red</div>
</div>
`,
},
],
corePlugins: { preflight: false },
}
let input = `
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.foo .\[\.foo_\&\]\:tw-text-red-400 {
--tw-text-opacity: 1;
color: rgb(248 113 113 / var(--tw-text-opacity));
}
.\[\&_\.foo\]\:tw-text-red-400 .foo {
--tw-text-opacity: 1;
color: rgb(248 113 113 / var(--tw-text-opacity));
}
`)
})
})
test('classes in the same arbitrary variant should not be prefixed', () => {
let config = {
prefix: 'tw-',
content: [
{
raw: `
<div class="[.foo_&]:tw-text-red-400 [.foo_&]:tw-bg-white">should not be red</div>
<div class="foo">
<div class="[.foo_&]:tw-text-red-400 [.foo_&]:tw-bg-white">should be red</div>
</div>
<div class="[&_.foo]:tw-text-red-400 [&_.foo]:tw-bg-white">
<div>should not be red</div>
<div class="foo">should be red</div>
</div>
`,
},
],
corePlugins: { preflight: false },
}
let input = `
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.foo .\[\.foo_\&\]\:tw-bg-white {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
}
.foo .\[\.foo_\&\]\:tw-text-red-400 {
--tw-text-opacity: 1;
color: rgb(248 113 113 / var(--tw-text-opacity));
}
.\[\&_\.foo\]\:tw-bg-white .foo {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
}
.\[\&_\.foo\]\:tw-text-red-400 .foo {
--tw-text-opacity: 1;
color: rgb(248 113 113 / var(--tw-text-opacity));
}
`)
})
})