mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
* introduce `public` folder This can contain all of the `public` functions we want to expose. This will be a bit nicer for example when you want to use internal/private functions (we use some in the vscode intellisense plugin). * use public `resolveConfig` function * expose resolveConfig in the root This will use the resolveConfig we expose from the `public` folder. We can probably generate these as well. * make `colors` public * make `default config` public * make `default theme` public * make `create plugin` public * update to public paths * remove `@tailwindcss/aspect-ratio` from tests This should be tested in its own repo instead. * remove `@tailwindcss/aspect-ratio` as a dependency * drop `Build` step from CI The build step is not a prerequisite anymore for running the tests. When we want to release a new (insiders) release, the `prepublishOnly` step will be executed for us. Before this change, it would have been executed twice: - Once before the tests - Once before the actual release * improve paths for caching purposes * add pretest scrip for generating the plugin list Now that we can use `SWC`, automatically generating the plugin list before running the tests is super fast and you don't even have to think about it anymore!
1891 lines
43 KiB
JavaScript
1891 lines
43 KiB
JavaScript
import createPlugin from '../src/public/create-plugin'
|
|
import { run, html, css } from './util/run'
|
|
|
|
test('plugins can create utilities with object syntax', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<div class="custom-object-fill custom-object-contain custom-object-cover"></div>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addUtilities }) {
|
|
addUtilities({
|
|
'.custom-object-fill': {
|
|
'object-fit': 'fill',
|
|
},
|
|
'.custom-object-contain': {
|
|
'object-fit': 'contain',
|
|
},
|
|
'.custom-object-cover': {
|
|
'object-fit': 'cover',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-object-fill {
|
|
object-fit: fill;
|
|
}
|
|
.custom-object-contain {
|
|
object-fit: contain;
|
|
}
|
|
.custom-object-cover {
|
|
object-fit: cover;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can create utilities with arrays of objects', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<div class="custom-object-fill custom-object-contain custom-object-cover"></div>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addUtilities }) {
|
|
addUtilities([
|
|
{
|
|
'.custom-object-fill': {
|
|
'object-fit': 'fill',
|
|
},
|
|
'.custom-object-contain': {
|
|
'object-fit': 'contain',
|
|
},
|
|
'.custom-object-cover': {
|
|
'object-fit': 'cover',
|
|
},
|
|
},
|
|
])
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-object-fill {
|
|
object-fit: fill;
|
|
}
|
|
.custom-object-contain {
|
|
object-fit: contain;
|
|
}
|
|
.custom-object-cover {
|
|
object-fit: cover;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can create utilities with raw PostCSS nodes', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<div class="custom-object-fill custom-object-contain custom-object-cover"></div>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addUtilities, postcss }) {
|
|
addUtilities([
|
|
postcss.rule({ selector: '.custom-object-fill' }).append([
|
|
postcss.decl({
|
|
prop: 'object-fit',
|
|
value: 'fill',
|
|
}),
|
|
]),
|
|
postcss.rule({ selector: '.custom-object-contain' }).append([
|
|
postcss.decl({
|
|
prop: 'object-fit',
|
|
value: 'contain',
|
|
}),
|
|
]),
|
|
postcss.rule({ selector: '.custom-object-cover' }).append([
|
|
postcss.decl({
|
|
prop: 'object-fit',
|
|
value: 'cover',
|
|
}),
|
|
]),
|
|
])
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-object-fill {
|
|
object-fit: fill;
|
|
}
|
|
.custom-object-contain {
|
|
object-fit: contain;
|
|
}
|
|
.custom-object-cover {
|
|
object-fit: cover;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can create utilities with mixed object styles and PostCSS nodes', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<div class="custom-object-fill custom-object-contain custom-object-cover"></div>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addUtilities, postcss }) {
|
|
addUtilities([
|
|
{
|
|
'.custom-object-fill': {
|
|
objectFit: 'fill',
|
|
},
|
|
},
|
|
postcss.rule({ selector: '.custom-object-contain' }).append([
|
|
postcss.decl({
|
|
prop: 'object-fit',
|
|
value: 'contain',
|
|
}),
|
|
]),
|
|
postcss.rule({ selector: '.custom-object-cover' }).append([
|
|
postcss.decl({
|
|
prop: 'object-fit',
|
|
value: 'cover',
|
|
}),
|
|
]),
|
|
])
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-object-fill {
|
|
object-fit: fill;
|
|
}
|
|
.custom-object-contain {
|
|
object-fit: contain;
|
|
}
|
|
.custom-object-cover {
|
|
object-fit: cover;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can create components with object syntax', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<button class="btn-blue"></button>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addComponents }) {
|
|
addComponents({
|
|
'.btn-blue': {
|
|
backgroundColor: 'blue',
|
|
color: 'white',
|
|
padding: '.5rem 1rem',
|
|
borderRadius: '.25rem',
|
|
},
|
|
'.btn-blue:hover': {
|
|
backgroundColor: 'darkblue',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.btn-blue {
|
|
background-color: blue;
|
|
color: white;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 0.25rem;
|
|
}
|
|
.btn-blue:hover {
|
|
background-color: darkblue;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can add base styles with object syntax', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<img /><button></button>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addBase }) {
|
|
addBase({
|
|
img: {
|
|
maxWidth: '100%',
|
|
},
|
|
button: {
|
|
fontFamily: 'inherit',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
corePlugins: { preflight: false },
|
|
}
|
|
|
|
return run('@tailwind base', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
img {
|
|
max-width: 100%;
|
|
}
|
|
button {
|
|
font-family: inherit;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can add base styles with raw PostCSS nodes', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<img /><button></button>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addBase, postcss }) {
|
|
addBase([
|
|
postcss.rule({ selector: 'img' }).append([
|
|
postcss.decl({
|
|
prop: 'max-width',
|
|
value: '100%',
|
|
}),
|
|
]),
|
|
postcss.rule({ selector: 'button' }).append([
|
|
postcss.decl({
|
|
prop: 'font-family',
|
|
value: 'inherit',
|
|
}),
|
|
]),
|
|
])
|
|
},
|
|
],
|
|
corePlugins: { preflight: false },
|
|
}
|
|
|
|
return run('@tailwind base', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
img {
|
|
max-width: 100%;
|
|
}
|
|
button {
|
|
font-family: inherit;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can create components with raw PostCSS nodes', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<button class="btn-blue"></button>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addComponents, postcss }) {
|
|
addComponents([
|
|
postcss.rule({ selector: '.btn-blue' }).append([
|
|
postcss.decl({
|
|
prop: 'background-color',
|
|
value: 'blue',
|
|
}),
|
|
postcss.decl({
|
|
prop: 'color',
|
|
value: 'white',
|
|
}),
|
|
postcss.decl({
|
|
prop: 'padding',
|
|
value: '.5rem 1rem',
|
|
}),
|
|
postcss.decl({
|
|
prop: 'border-radius',
|
|
value: '.25rem',
|
|
}),
|
|
]),
|
|
postcss.rule({ selector: '.btn-blue:hover' }).append([
|
|
postcss.decl({
|
|
prop: 'background-color',
|
|
value: 'darkblue',
|
|
}),
|
|
]),
|
|
])
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.btn-blue {
|
|
background-color: blue;
|
|
color: white;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 0.25rem;
|
|
}
|
|
.btn-blue:hover {
|
|
background-color: darkblue;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can create components with mixed object styles and raw PostCSS nodes', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<button class="btn-blue"></button>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addComponents, postcss }) {
|
|
addComponents([
|
|
postcss.rule({ selector: '.btn-blue' }).append([
|
|
postcss.decl({
|
|
prop: 'background-color',
|
|
value: 'blue',
|
|
}),
|
|
postcss.decl({
|
|
prop: 'color',
|
|
value: 'white',
|
|
}),
|
|
postcss.decl({
|
|
prop: 'padding',
|
|
value: '.5rem 1rem',
|
|
}),
|
|
postcss.decl({
|
|
prop: 'border-radius',
|
|
value: '.25rem',
|
|
}),
|
|
]),
|
|
{
|
|
'.btn-blue:hover': {
|
|
backgroundColor: 'darkblue',
|
|
},
|
|
},
|
|
])
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.btn-blue {
|
|
background-color: blue;
|
|
color: white;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 0.25rem;
|
|
}
|
|
.btn-blue:hover {
|
|
background-color: darkblue;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can create components with media queries with object syntax', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<div class="custom-container"></div>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addComponents }) {
|
|
addComponents({
|
|
'.custom-container': {
|
|
width: '100%',
|
|
},
|
|
'@media (min-width: 100px)': {
|
|
'.custom-container': {
|
|
maxWidth: '100px',
|
|
},
|
|
},
|
|
'@media (min-width: 200px)': {
|
|
'.custom-container': {
|
|
maxWidth: '200px',
|
|
},
|
|
},
|
|
'@media (min-width: 300px)': {
|
|
'.custom-container': {
|
|
maxWidth: '300px',
|
|
},
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-container {
|
|
width: 100%;
|
|
}
|
|
@media (min-width: 100px) {
|
|
.custom-container {
|
|
max-width: 100px;
|
|
}
|
|
}
|
|
@media (min-width: 200px) {
|
|
.custom-container {
|
|
max-width: 200px;
|
|
}
|
|
}
|
|
@media (min-width: 300px) {
|
|
.custom-container {
|
|
max-width: 300px;
|
|
}
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('media queries can be defined multiple times using objects-in-array syntax', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<div class="custom-container"></div>
|
|
<button class="btn"></button>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addComponents }) {
|
|
addComponents([
|
|
{
|
|
'.custom-container': {
|
|
width: '100%',
|
|
},
|
|
'@media (min-width: 100px)': {
|
|
'.custom-container': {
|
|
maxWidth: '100px',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
'.btn': {
|
|
padding: '1rem .5rem',
|
|
display: 'block',
|
|
},
|
|
'@media (min-width: 100px)': {
|
|
'.btn': {
|
|
display: 'inline-block',
|
|
},
|
|
},
|
|
},
|
|
])
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-container {
|
|
width: 100%;
|
|
}
|
|
@media (min-width: 100px) {
|
|
.custom-container {
|
|
max-width: 100px;
|
|
}
|
|
}
|
|
.btn {
|
|
padding: 1rem 0.5rem;
|
|
display: block;
|
|
}
|
|
@media (min-width: 100px) {
|
|
.btn {
|
|
display: inline-block;
|
|
}
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can create nested rules', () => {
|
|
let config = {
|
|
content: [{ raw: html`<button class="btn-blue"></button>` }],
|
|
plugins: [
|
|
function ({ addComponents }) {
|
|
addComponents({
|
|
'.btn-blue': {
|
|
backgroundColor: 'blue',
|
|
color: 'white',
|
|
padding: '.5rem 1rem',
|
|
borderRadius: '.25rem',
|
|
'&:hover': {
|
|
backgroundColor: 'darkblue',
|
|
},
|
|
'@media (min-width: 500px)': {
|
|
'&:hover': {
|
|
backgroundColor: 'orange',
|
|
},
|
|
},
|
|
'> a': {
|
|
color: 'red',
|
|
},
|
|
'h1 &': {
|
|
color: 'purple',
|
|
},
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.btn-blue {
|
|
background-color: blue;
|
|
color: white;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 0.25rem;
|
|
}
|
|
.btn-blue:hover {
|
|
background-color: darkblue;
|
|
}
|
|
@media (min-width: 500px) {
|
|
.btn-blue:hover {
|
|
background-color: orange;
|
|
}
|
|
}
|
|
.btn-blue > a {
|
|
color: red;
|
|
}
|
|
h1 .btn-blue {
|
|
color: purple;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can create rules with escaped selectors', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="custom-top-1/4"></div>` }],
|
|
plugins: [
|
|
function ({ e, addUtilities }) {
|
|
addUtilities({
|
|
[`.${e('custom-top-1/4')}`]: {
|
|
top: '25%',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-top-1\\/4 {
|
|
top: 25%;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can access the current config', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="custom-container"></div>` }],
|
|
plugins: [
|
|
function ({ addComponents, config }) {
|
|
let containerClasses = [
|
|
{
|
|
'.custom-container': {
|
|
width: '100%',
|
|
},
|
|
},
|
|
]
|
|
|
|
for (let maxWidth of Object.values(config('theme.customScreens'))) {
|
|
containerClasses.push({
|
|
[`@media (min-width: ${maxWidth})`]: {
|
|
'.custom-container': { maxWidth },
|
|
},
|
|
})
|
|
}
|
|
|
|
addComponents(containerClasses)
|
|
},
|
|
],
|
|
theme: {
|
|
customScreens: {
|
|
sm: '576px',
|
|
md: '768px',
|
|
lg: '992px',
|
|
xl: '1200px',
|
|
},
|
|
},
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-container {
|
|
width: 100%;
|
|
}
|
|
@media (min-width: 576px) {
|
|
.custom-container {
|
|
max-width: 576px;
|
|
}
|
|
}
|
|
@media (min-width: 768px) {
|
|
.custom-container {
|
|
max-width: 768px;
|
|
}
|
|
}
|
|
@media (min-width: 992px) {
|
|
.custom-container {
|
|
max-width: 992px;
|
|
}
|
|
}
|
|
@media (min-width: 1200px) {
|
|
.custom-container {
|
|
max-width: 1200px;
|
|
}
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can check if corePlugins are enabled', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="test"></div>` }],
|
|
plugins: [
|
|
function ({ addUtilities, corePlugins }) {
|
|
addUtilities({
|
|
'.test': {
|
|
'text-color': corePlugins('textColor') ? 'true' : 'false',
|
|
opacity: corePlugins('opacity') ? 'true' : 'false',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
corePlugins: { textColor: false },
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.test {
|
|
text-color: false;
|
|
opacity: true;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can check if corePlugins are enabled when using array white-listing', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="test"></div>` }],
|
|
plugins: [
|
|
function ({ addUtilities, corePlugins }) {
|
|
addUtilities({
|
|
'.test': {
|
|
'text-color': corePlugins('textColor') ? 'true' : 'false',
|
|
opacity: corePlugins('opacity') ? 'true' : 'false',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
corePlugins: ['textColor'],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.test {
|
|
text-color: true;
|
|
opacity: false;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can provide fallbacks to keys missing from the config', () => {
|
|
let config = {
|
|
content: [{ raw: html`<button class="btn"></button>` }],
|
|
plugins: [
|
|
function ({ addComponents, config }) {
|
|
addComponents({
|
|
'.btn': {
|
|
borderRadius: config('borderRadius.default', '.25rem'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
theme: {
|
|
borderRadius: {
|
|
1: '1px',
|
|
2: '2px',
|
|
4: '4px',
|
|
8: '8px',
|
|
},
|
|
},
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.btn {
|
|
border-radius: 0.25rem;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can add multiple sets of utilities and components', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<button class="btn"></button>
|
|
<div class="card custom-skew-12deg custom-border-collapse"></div>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addUtilities, addComponents }) {
|
|
addComponents({
|
|
'.card': {
|
|
padding: '1rem',
|
|
borderRadius: '.25rem',
|
|
},
|
|
})
|
|
|
|
addUtilities({
|
|
'.custom-skew-12deg': {
|
|
transform: 'skewY(-12deg)',
|
|
},
|
|
})
|
|
|
|
addComponents({
|
|
'.btn': {
|
|
padding: '1rem .5rem',
|
|
display: 'inline-block',
|
|
},
|
|
})
|
|
|
|
addUtilities({
|
|
'.custom-border-collapse': {
|
|
borderCollapse: 'collapse',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components; @tailwind utilities;', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.card {
|
|
padding: 1rem;
|
|
border-radius: 0.25rem;
|
|
}
|
|
.btn {
|
|
padding: 1rem 0.5rem;
|
|
display: inline-block;
|
|
}
|
|
.custom-skew-12deg {
|
|
transform: skewY(-12deg);
|
|
}
|
|
.custom-border-collapse {
|
|
border-collapse: collapse;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins respect prefix and important options by default when adding utilities', () => {
|
|
let config = {
|
|
prefix: 'tw-',
|
|
important: true,
|
|
content: [{ raw: html`<div class="tw-custom-rotate-90"></div>` }],
|
|
plugins: [
|
|
function ({ addUtilities }) {
|
|
addUtilities({
|
|
'.custom-rotate-90': {
|
|
transform: 'rotate(90deg)',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.tw-custom-rotate-90 {
|
|
transform: rotate(90deg) !important;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('when important is a selector it is used to scope utilities instead of adding !important', () => {
|
|
let config = {
|
|
prefix: 'tw-',
|
|
important: '#app',
|
|
content: [{ raw: html`<div class="tw-custom-rotate-90"></div>` }],
|
|
plugins: [
|
|
function ({ addUtilities }) {
|
|
addUtilities({
|
|
'.custom-rotate-90': {
|
|
transform: 'rotate(90deg)',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
#app .tw-custom-rotate-90 {
|
|
transform: rotate(90deg);
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('when important is a selector it scopes all selectors in a rule, even though defining utilities like this is stupid', () => {
|
|
let config = {
|
|
important: '#app',
|
|
content: [{ raw: html`<div class="custom-rotate-90 custom-rotate-1/4"></div>` }],
|
|
plugins: [
|
|
function ({ addUtilities }) {
|
|
addUtilities({
|
|
'.custom-rotate-90, .custom-rotate-1\\/4': {
|
|
transform: 'rotate(90deg)',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
#app .custom-rotate-90,
|
|
#app .custom-rotate-1\\/4 {
|
|
transform: rotate(90deg);
|
|
transform: rotate(90deg);
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('important utilities are not made double important when important option is used', () => {
|
|
let config = {
|
|
important: true,
|
|
content: [{ raw: html`<div class="custom-rotate-90"></div>` }],
|
|
plugins: [
|
|
function ({ addUtilities }) {
|
|
addUtilities({
|
|
'.custom-rotate-90': {
|
|
transform: 'rotate(90deg) !important',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-rotate-90 {
|
|
transform: rotate(90deg) !important;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test("component declarations respect the 'prefix' option by default", () => {
|
|
let config = {
|
|
prefix: 'tw-',
|
|
content: [{ raw: html`<button class="tw-btn-blue"></button>` }],
|
|
plugins: [
|
|
function ({ addComponents }) {
|
|
addComponents({
|
|
'.btn-blue': {
|
|
backgroundColor: 'blue',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.tw-btn-blue {
|
|
background-color: blue;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('all selectors in a rule are prefixed', () => {
|
|
let config = {
|
|
prefix: 'tw-',
|
|
content: [
|
|
{
|
|
raw: html`<button class="tw-btn-blue tw-btn-red"></button>
|
|
<div class="tw-custom-rotate-90 tw-custom-rotate-1/4"></div>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
function ({ addUtilities, addComponents }) {
|
|
addUtilities({
|
|
'.custom-rotate-90, .custom-rotate-1\\/4': {
|
|
transform: 'rotate(90deg)',
|
|
},
|
|
})
|
|
addComponents({
|
|
'.btn-blue, .btn-red': {
|
|
padding: '10px',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.tw-btn-blue,
|
|
.tw-btn-red {
|
|
padding: 10px;
|
|
padding: 10px;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test("component declarations can optionally ignore 'prefix' option", () => {
|
|
let config = {
|
|
prefix: 'tw-',
|
|
content: [{ raw: html`<button class="btn-blue"></button>` }],
|
|
plugins: [
|
|
function ({ addComponents }) {
|
|
addComponents(
|
|
{
|
|
'.btn-blue': {
|
|
backgroundColor: 'blue',
|
|
},
|
|
},
|
|
{ respectPrefix: false }
|
|
)
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.btn-blue {
|
|
background-color: blue;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test("component declarations are not affected by the 'important' option", () => {
|
|
let config = {
|
|
important: true,
|
|
content: [{ raw: html`<button class="btn-blue"></button>` }],
|
|
plugins: [
|
|
function ({ addComponents }) {
|
|
addComponents({
|
|
'.btn-blue': {
|
|
backgroundColor: 'blue',
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.btn-blue {
|
|
background-color: blue;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test("plugins can apply the user's chosen prefix to components manually", () => {
|
|
let config = {
|
|
prefix: 'tw-',
|
|
content: [{ raw: html`<button class="tw-btn-blue"></button>` }],
|
|
plugins: [
|
|
function ({ addComponents, prefix }) {
|
|
addComponents(
|
|
{
|
|
[prefix('.btn-blue')]: {
|
|
backgroundColor: 'blue',
|
|
},
|
|
},
|
|
{ respectPrefix: false }
|
|
)
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.tw-btn-blue {
|
|
background-color: blue;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('prefix can optionally be ignored for utilities', () => {
|
|
let config = {
|
|
prefix: 'tw-',
|
|
content: [{ raw: html`<div class="custom-rotate-90"></div>` }],
|
|
plugins: [
|
|
function ({ addUtilities }) {
|
|
addUtilities(
|
|
{
|
|
'.custom-rotate-90': {
|
|
transform: 'rotate(90deg)',
|
|
},
|
|
},
|
|
{ respectPrefix: false }
|
|
)
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-rotate-90 {
|
|
transform: rotate(90deg);
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('important can optionally be ignored for utilities', () => {
|
|
let config = {
|
|
important: true,
|
|
content: [{ raw: html`<div class="custom-rotate-90"></div>` }],
|
|
plugins: [
|
|
function ({ addUtilities }) {
|
|
addUtilities(
|
|
{
|
|
'.custom-rotate-90': {
|
|
transform: 'rotate(90deg)',
|
|
},
|
|
},
|
|
{ respectImportant: false }
|
|
)
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-rotate-90 {
|
|
transform: rotate(90deg);
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('prefix will prefix all classes in a selector', () => {
|
|
let config = {
|
|
prefix: 'tw-',
|
|
content: [{ raw: html`<div class="tw-btn-blue tw-w-1/4"></div>` }],
|
|
plugins: [
|
|
function ({ addComponents, prefix }) {
|
|
addComponents(
|
|
{
|
|
[prefix('.btn-blue .w-1\\/4 > h1.text-xl + a .bar')]: {
|
|
backgroundColor: 'blue',
|
|
},
|
|
},
|
|
{ respectPrefix: false }
|
|
)
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.tw-btn-blue .tw-w-1\\/4 > h1.tw-text-xl + a .tw-bar {
|
|
background-color: blue;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can be provided as an object with a handler function', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<div class="custom-object-fill custom-object-contain custom-object-cover"></div>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
{
|
|
handler({ addUtilities }) {
|
|
addUtilities({
|
|
'.custom-object-fill': {
|
|
'object-fit': 'fill',
|
|
},
|
|
'.custom-object-contain': {
|
|
'object-fit': 'contain',
|
|
},
|
|
'.custom-object-cover': {
|
|
'object-fit': 'cover',
|
|
},
|
|
})
|
|
},
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.custom-object-fill {
|
|
object-fit: fill;
|
|
}
|
|
.custom-object-contain {
|
|
object-fit: contain;
|
|
}
|
|
.custom-object-cover {
|
|
object-fit: cover;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can provide a config but no handler', () => {
|
|
let config = {
|
|
content: [
|
|
{
|
|
raw: html`<div
|
|
class="tw-custom-object-fill tw-custom-object-contain tw-custom-object-cover"
|
|
></div>`,
|
|
},
|
|
],
|
|
plugins: [
|
|
{
|
|
config: {
|
|
prefix: 'tw-',
|
|
},
|
|
},
|
|
{
|
|
handler({ addUtilities }) {
|
|
addUtilities({
|
|
'.custom-object-fill': {
|
|
'object-fit': 'fill',
|
|
},
|
|
'.custom-object-contain': {
|
|
'object-fit': 'contain',
|
|
},
|
|
'.custom-object-cover': {
|
|
'object-fit': 'cover',
|
|
},
|
|
})
|
|
},
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.tw-custom-object-fill {
|
|
object-fit: fill;
|
|
}
|
|
.tw-custom-object-contain {
|
|
object-fit: contain;
|
|
}
|
|
.tw-custom-object-cover {
|
|
object-fit: cover;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins can be created using the `createPlugin` function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="test-sm test-md test-lg hover:test-sm sm:test-sm"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
screens: {
|
|
sm: '400px',
|
|
},
|
|
},
|
|
plugins: [
|
|
createPlugin(
|
|
function ({ addUtilities, theme }) {
|
|
addUtilities(
|
|
Object.fromEntries(
|
|
Object.entries(theme('testPlugin')).map(([k, v]) => [
|
|
`.test-${k}`,
|
|
{ testProperty: v },
|
|
])
|
|
)
|
|
)
|
|
},
|
|
{
|
|
theme: {
|
|
testPlugin: {
|
|
sm: '1rem',
|
|
md: '2rem',
|
|
lg: '3rem',
|
|
},
|
|
},
|
|
}
|
|
),
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.test-sm {
|
|
test-property: 1rem;
|
|
}
|
|
.test-md {
|
|
test-property: 2rem;
|
|
}
|
|
.test-lg {
|
|
test-property: 3rem;
|
|
}
|
|
.hover\\:test-sm:hover {
|
|
test-property: 1rem;
|
|
}
|
|
@media (min-width: 400px) {
|
|
.sm\\:test-sm {
|
|
test-property: 1rem;
|
|
}
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins with extra options can be created using the `createPlugin.withOptions` function', () => {
|
|
let plugin = createPlugin.withOptions(
|
|
function ({ className }) {
|
|
return function ({ addUtilities, theme }) {
|
|
addUtilities(
|
|
Object.fromEntries(
|
|
Object.entries(theme('testPlugin')).map(([k, v]) => [
|
|
`.${className}-${k}`,
|
|
{ testProperty: v },
|
|
])
|
|
)
|
|
)
|
|
}
|
|
},
|
|
function () {
|
|
return {
|
|
theme: {
|
|
testPlugin: {
|
|
sm: '1rem',
|
|
md: '2rem',
|
|
lg: '3rem',
|
|
},
|
|
},
|
|
}
|
|
}
|
|
)
|
|
|
|
let config = {
|
|
content: [
|
|
{ raw: html`<div class="banana-sm banana-md banana-lg hover:banana-sm sm:banana-sm"></div>` },
|
|
],
|
|
corePlugins: [],
|
|
theme: {
|
|
screens: {
|
|
sm: '400px',
|
|
},
|
|
},
|
|
plugins: [plugin({ className: 'banana' })],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.banana-sm {
|
|
test-property: 1rem;
|
|
}
|
|
.banana-md {
|
|
test-property: 2rem;
|
|
}
|
|
.banana-lg {
|
|
test-property: 3rem;
|
|
}
|
|
.hover\\:banana-sm:hover {
|
|
test-property: 1rem;
|
|
}
|
|
@media (min-width: 400px) {
|
|
.sm\\:banana-sm {
|
|
test-property: 1rem;
|
|
}
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('plugins should cache correctly', () => {
|
|
let plugin = createPlugin.withOptions(({ className = 'banana' } = {}) => ({ addComponents }) => {
|
|
addComponents({ [`.${className}`]: { position: 'absolute' } })
|
|
})
|
|
|
|
let config = {
|
|
content: [{ raw: html`<div class="banana sm:banana apple sm:apple"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
screens: {
|
|
sm: '400px',
|
|
},
|
|
},
|
|
}
|
|
|
|
function internalRun(options = {}) {
|
|
return run('@tailwind components', {
|
|
...config,
|
|
plugins: [plugin(options)],
|
|
})
|
|
}
|
|
|
|
return Promise.all([internalRun(), internalRun({ className: 'apple' })]).then(
|
|
([result1, result2]) => {
|
|
let expected1 = css`
|
|
.banana {
|
|
position: absolute;
|
|
}
|
|
|
|
@media (min-width: 400px) {
|
|
.sm\\:banana {
|
|
position: absolute;
|
|
}
|
|
}
|
|
`
|
|
|
|
let expected2 = css`
|
|
.apple {
|
|
position: absolute;
|
|
}
|
|
|
|
@media (min-width: 400px) {
|
|
.sm\\:apple {
|
|
position: absolute;
|
|
}
|
|
}
|
|
`
|
|
|
|
expect(result1.css).toMatchCss(expected1)
|
|
expect(result2.css).toMatchCss(expected2)
|
|
}
|
|
)
|
|
})
|
|
|
|
test('plugins created using `createPlugin.withOptions` do not need to be invoked if the user wants to use the default options', () => {
|
|
let plugin = createPlugin.withOptions(
|
|
function ({ className } = { className: 'banana' }) {
|
|
return function ({ addUtilities, theme }) {
|
|
addUtilities(
|
|
Object.fromEntries(
|
|
Object.entries(theme('testPlugin')).map(([k, v]) => [
|
|
`.${className}-${k}`,
|
|
{ testProperty: v },
|
|
])
|
|
)
|
|
)
|
|
}
|
|
},
|
|
function () {
|
|
return {
|
|
theme: {
|
|
testPlugin: {
|
|
sm: '1rem',
|
|
md: '2rem',
|
|
lg: '3rem',
|
|
},
|
|
},
|
|
}
|
|
}
|
|
)
|
|
|
|
let config = {
|
|
content: [
|
|
{ raw: html`<div class="banana-sm banana-md banana-lg hover:banana-sm sm:banana-sm"></div>` },
|
|
],
|
|
corePlugins: [],
|
|
theme: {
|
|
screens: {
|
|
sm: '400px',
|
|
},
|
|
},
|
|
plugins: [plugin],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.banana-sm {
|
|
test-property: 1rem;
|
|
}
|
|
.banana-md {
|
|
test-property: 2rem;
|
|
}
|
|
.banana-lg {
|
|
test-property: 3rem;
|
|
}
|
|
.hover\\:banana-sm:hover {
|
|
test-property: 1rem;
|
|
}
|
|
@media (min-width: 400px) {
|
|
.sm\\:banana-sm {
|
|
test-property: 1rem;
|
|
}
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('the configFunction parameter is optional when using the `createPlugin.withOptions` function', () => {
|
|
let plugin = createPlugin.withOptions(function ({ className }) {
|
|
return function ({ addUtilities, theme }) {
|
|
addUtilities(
|
|
Object.fromEntries(
|
|
Object.entries(theme('testPlugin')).map(([k, v]) => [
|
|
`.${className}-${k}`,
|
|
{ testProperty: v },
|
|
])
|
|
)
|
|
)
|
|
}
|
|
})
|
|
|
|
let config = {
|
|
content: [
|
|
{ raw: html`<div class="banana-sm banana-md banana-lg hover:banana-sm sm:banana-sm"></div>` },
|
|
],
|
|
corePlugins: [],
|
|
theme: {
|
|
screens: {
|
|
sm: '400px',
|
|
},
|
|
testPlugin: {
|
|
sm: '1px',
|
|
md: '2px',
|
|
lg: '3px',
|
|
},
|
|
},
|
|
plugins: [plugin({ className: 'banana' })],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.banana-sm {
|
|
test-property: 1px;
|
|
}
|
|
.banana-md {
|
|
test-property: 2px;
|
|
}
|
|
.banana-lg {
|
|
test-property: 3px;
|
|
}
|
|
.hover\\:banana-sm:hover {
|
|
test-property: 1px;
|
|
}
|
|
@media (min-width: 400px) {
|
|
.sm\\:banana-sm {
|
|
test-property: 1px;
|
|
}
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('keyframes are not escaped', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo-[abc]"></div>` }],
|
|
plugins: [
|
|
function ({ matchUtilities }) {
|
|
matchUtilities({
|
|
foo: (value, { includeRules }) => {
|
|
includeRules(
|
|
[
|
|
{
|
|
[`@keyframes ${value}`]: {
|
|
'25.001%': {
|
|
color: 'black',
|
|
},
|
|
},
|
|
},
|
|
],
|
|
{ respectImportant: false }
|
|
)
|
|
|
|
return {
|
|
animation: `${value} 1s infinite`,
|
|
}
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind utilities', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
@keyframes abc {
|
|
25.001% {
|
|
color: black;
|
|
}
|
|
}
|
|
.foo-\\[abc\\] {
|
|
animation: abc 1s infinite;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('font sizes are retrieved without default line-heights or letter-spacing using theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
fontSize: {
|
|
sm: ['14px', '20px'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
fontSize: theme('fontSize.sm'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
font-size: 14px;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('outlines are retrieved without outline-offset using theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
outline: {
|
|
black: ['2px dotted black', '4px'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
outline: theme('outline.black'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
outline: 2px dotted black;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('box-shadow values are joined when retrieved using the theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
boxShadow: {
|
|
lol: ['width', 'height'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
boxShadow: theme('boxShadow.lol'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
box-shadow: width, height;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('transition-property values are joined when retrieved using the theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
transitionProperty: {
|
|
lol: ['width', 'height'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
transitionProperty: theme('transitionProperty.lol'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
transition-property: width, height;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('transition-duration values are joined when retrieved using the theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
transitionDuration: {
|
|
lol: ['width', 'height'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
transitionDuration: theme('transitionDuration.lol'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
transition-duration: width, height;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('transition-delay values are joined when retrieved using the theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
transitionDuration: {
|
|
lol: ['width', 'height'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
transitionDuration: theme('transitionDuration.lol'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
transition-duration: width, height;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('transition-timing-function values are joined when retrieved using the theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
transitionTimingFunction: {
|
|
lol: ['width', 'height'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
transitionTimingFunction: theme('transitionTimingFunction.lol'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
transition-timing-function: width, height;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('background-image values are joined when retrieved using the theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
backgroundImage: {
|
|
lol: ['width', 'height'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
backgroundImage: theme('backgroundImage.lol'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
background-image: width, height;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('background-size values are joined when retrieved using the theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
backgroundSize: {
|
|
lol: ['width', 'height'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
backgroundSize: theme('backgroundSize.lol'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
background-size: width, height;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('background-color values are joined when retrieved using the theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
backgroundColor: {
|
|
lol: ['width', 'height'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
backgroundColor: theme('backgroundColor.lol'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
background-color: width, height;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('cursor values are joined when retrieved using the theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
cursor: {
|
|
lol: ['width', 'height'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
cursor: theme('cursor.lol'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
cursor: width, height;
|
|
}
|
|
`)
|
|
})
|
|
})
|
|
|
|
test('animation values are joined when retrieved using the theme function', () => {
|
|
let config = {
|
|
content: [{ raw: html`<div class="foo"></div>` }],
|
|
corePlugins: [],
|
|
theme: {
|
|
animation: {
|
|
lol: ['width', 'height'],
|
|
},
|
|
},
|
|
plugins: [
|
|
function ({ addComponents, theme }) {
|
|
addComponents({
|
|
'.foo': {
|
|
animation: theme('animation.lol'),
|
|
},
|
|
})
|
|
},
|
|
],
|
|
}
|
|
|
|
return run('@tailwind components', config).then((result) => {
|
|
expect(result.css).toMatchFormattedCss(css`
|
|
.foo {
|
|
animation: width, height;
|
|
}
|
|
`)
|
|
})
|
|
})
|