ensure DEFAULT is taken into account for matchVariant (#9603)

This means that if you define your `matchVariant` as:

```js
matchVariant('foo', (value) => '.foo-${value} &')
```

Then you can't use `foo:underline`, if you want to be able to use
`foo:underline` then you have to define a `DEFAULT` value:

```js
matchVariant('foo', (value) => '.foo-${value} &', {
  values: {
    DEFAULT: 'bar'
  }
})
```

Now `foo:underline` will generate `.foo-bar &` as a selector!
This commit is contained in:
Robin Malfait 2022-10-18 21:42:17 +02:00 committed by GitHub
parent 24fc365173
commit ddb9b4d7bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 148 additions and 5 deletions

View File

@ -145,7 +145,7 @@ function applyVariant(variant, matches, context) {
}
/** @type {{modifier: string | null, value: string | null}} */
let args = { modifier: null, value: null }
let args = { modifier: null, value: sharedState.NONE }
// Retrieve "modifier"
{

View File

@ -577,6 +577,8 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
for (let [key, value] of Object.entries(options?.values ?? {})) {
if (key === 'DEFAULT') continue
api.addVariant(
isSpecial ? `${variant}${key}` : `${variant}-${key}`,
({ args, container }) =>
@ -594,13 +596,20 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
)
}
let hasDefault = 'DEFAULT' in (options?.values ?? {})
api.addVariant(
variant,
({ args, container }) =>
variantFn(
args.value,
({ args, container }) => {
if (args.value === sharedState.NONE && !hasDefault) {
return null
}
return variantFn(
args.value === sharedState.NONE ? options.values.DEFAULT : args.value,
modifiersEnabled ? { modifier: args.modifier, container } : { container }
),
)
},
{
...options,
id,

View File

@ -8,6 +8,8 @@ export const contextSourcesMap = new Map()
export const sourceHashMap = new Map()
export const NOT_ON_DEMAND = new String('*')
export const NONE = Symbol('__NONE__')
export function resolveDebug(debug) {
if (debug === undefined) {
return false

View File

@ -656,3 +656,135 @@ it('should guarantee that we are not passing values from other variants to the w
`)
})
})
it('should default to the DEFAULT value for variants', () => {
let config = {
content: [
{
raw: html`
<div>
<div class="foo:underline"></div>
</div>
`,
},
],
corePlugins: { preflight: false },
plugins: [
({ matchVariant }) => {
matchVariant('foo', (value) => `.foo${value} &`, {
values: {
DEFAULT: '.bar',
},
})
},
],
}
let input = css`
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.foo.bar .foo\:underline {
text-decoration-line: underline;
}
`)
})
})
it('should not generate anything if the matchVariant does not have a DEFAULT value configured', () => {
let config = {
content: [
{
raw: html`
<div>
<div class="foo:underline"></div>
</div>
`,
},
],
corePlugins: { preflight: false },
plugins: [
({ matchVariant }) => {
matchVariant('foo', (value) => `.foo${value} &`)
},
],
}
let input = css`
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css``)
})
})
it('should be possible to use `null` as a DEFAULT value', () => {
let config = {
content: [
{
raw: html`
<div>
<div class="foo:underline"></div>
</div>
`,
},
],
corePlugins: { preflight: false },
plugins: [
({ matchVariant }) => {
matchVariant('foo', (value) => `.foo${value === null ? '-good' : '-bad'} &`, {
values: { DEFAULT: null },
})
},
],
}
let input = css`
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.foo-good .foo\:underline {
text-decoration-line: underline;
}
`)
})
})
it('should be possible to use `undefined` as a DEFAULT value', () => {
let config = {
content: [
{
raw: html`
<div>
<div class="foo:underline"></div>
</div>
`,
},
],
corePlugins: { preflight: false },
plugins: [
({ matchVariant }) => {
matchVariant('foo', (value) => `.foo${value === undefined ? '-good' : '-bad'} &`, {
values: { DEFAULT: undefined },
})
},
],
}
let input = css`
@tailwind utilities;
`
return run(input, config).then((result) => {
expect(result.css).toMatchFormattedCss(css`
.foo-good .foo\:underline {
text-decoration-line: underline;
}
`)
})
})