Skip to content

Commit ddb9b4d

Browse files
authored
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!
1 parent 24fc365 commit ddb9b4d

File tree

4 files changed

+148
-5
lines changed

4 files changed

+148
-5
lines changed

src/lib/generateRules.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ function applyVariant(variant, matches, context) {
145145
}
146146

147147
/** @type {{modifier: string | null, value: string | null}} */
148-
let args = { modifier: null, value: null }
148+
let args = { modifier: null, value: sharedState.NONE }
149149

150150
// Retrieve "modifier"
151151
{

src/lib/setupContextUtils.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,8 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
577577
let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
578578

579579
for (let [key, value] of Object.entries(options?.values ?? {})) {
580+
if (key === 'DEFAULT') continue
581+
580582
api.addVariant(
581583
isSpecial ? `${variant}${key}` : `${variant}-${key}`,
582584
({ args, container }) =>
@@ -594,13 +596,20 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
594596
)
595597
}
596598

599+
let hasDefault = 'DEFAULT' in (options?.values ?? {})
600+
597601
api.addVariant(
598602
variant,
599-
({ args, container }) =>
600-
variantFn(
601-
args.value,
603+
({ args, container }) => {
604+
if (args.value === sharedState.NONE && !hasDefault) {
605+
return null
606+
}
607+
608+
return variantFn(
609+
args.value === sharedState.NONE ? options.values.DEFAULT : args.value,
602610
modifiersEnabled ? { modifier: args.modifier, container } : { container }
603-
),
611+
)
612+
},
604613
{
605614
...options,
606615
id,

src/lib/sharedState.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export const contextSourcesMap = new Map()
88
export const sourceHashMap = new Map()
99
export const NOT_ON_DEMAND = new String('*')
1010

11+
export const NONE = Symbol('__NONE__')
12+
1113
export function resolveDebug(debug) {
1214
if (debug === undefined) {
1315
return false

tests/match-variants.test.js

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,3 +656,135 @@ it('should guarantee that we are not passing values from other variants to the w
656656
`)
657657
})
658658
})
659+
660+
it('should default to the DEFAULT value for variants', () => {
661+
let config = {
662+
content: [
663+
{
664+
raw: html`
665+
<div>
666+
<div class="foo:underline"></div>
667+
</div>
668+
`,
669+
},
670+
],
671+
corePlugins: { preflight: false },
672+
plugins: [
673+
({ matchVariant }) => {
674+
matchVariant('foo', (value) => `.foo${value} &`, {
675+
values: {
676+
DEFAULT: '.bar',
677+
},
678+
})
679+
},
680+
],
681+
}
682+
683+
let input = css`
684+
@tailwind utilities;
685+
`
686+
687+
return run(input, config).then((result) => {
688+
expect(result.css).toMatchFormattedCss(css`
689+
.foo.bar .foo\:underline {
690+
text-decoration-line: underline;
691+
}
692+
`)
693+
})
694+
})
695+
696+
it('should not generate anything if the matchVariant does not have a DEFAULT value configured', () => {
697+
let config = {
698+
content: [
699+
{
700+
raw: html`
701+
<div>
702+
<div class="foo:underline"></div>
703+
</div>
704+
`,
705+
},
706+
],
707+
corePlugins: { preflight: false },
708+
plugins: [
709+
({ matchVariant }) => {
710+
matchVariant('foo', (value) => `.foo${value} &`)
711+
},
712+
],
713+
}
714+
715+
let input = css`
716+
@tailwind utilities;
717+
`
718+
719+
return run(input, config).then((result) => {
720+
expect(result.css).toMatchFormattedCss(css``)
721+
})
722+
})
723+
724+
it('should be possible to use `null` as a DEFAULT value', () => {
725+
let config = {
726+
content: [
727+
{
728+
raw: html`
729+
<div>
730+
<div class="foo:underline"></div>
731+
</div>
732+
`,
733+
},
734+
],
735+
corePlugins: { preflight: false },
736+
plugins: [
737+
({ matchVariant }) => {
738+
matchVariant('foo', (value) => `.foo${value === null ? '-good' : '-bad'} &`, {
739+
values: { DEFAULT: null },
740+
})
741+
},
742+
],
743+
}
744+
745+
let input = css`
746+
@tailwind utilities;
747+
`
748+
749+
return run(input, config).then((result) => {
750+
expect(result.css).toMatchFormattedCss(css`
751+
.foo-good .foo\:underline {
752+
text-decoration-line: underline;
753+
}
754+
`)
755+
})
756+
})
757+
758+
it('should be possible to use `undefined` as a DEFAULT value', () => {
759+
let config = {
760+
content: [
761+
{
762+
raw: html`
763+
<div>
764+
<div class="foo:underline"></div>
765+
</div>
766+
`,
767+
},
768+
],
769+
corePlugins: { preflight: false },
770+
plugins: [
771+
({ matchVariant }) => {
772+
matchVariant('foo', (value) => `.foo${value === undefined ? '-good' : '-bad'} &`, {
773+
values: { DEFAULT: undefined },
774+
})
775+
},
776+
],
777+
}
778+
779+
let input = css`
780+
@tailwind utilities;
781+
`
782+
783+
return run(input, config).then((result) => {
784+
expect(result.css).toMatchFormattedCss(css`
785+
.foo-good .foo\:underline {
786+
text-decoration-line: underline;
787+
}
788+
`)
789+
})
790+
})

0 commit comments

Comments
 (0)