Merge pull request #648 from tailwindcss/responsive-nested-at-rules

Properly handle deeply nested at-rules inside of `@responsive`
This commit is contained in:
Adam Wathan 2019-02-06 14:27:50 -05:00 • committed by GitHub
commit c2118be312
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 136 additions and 133 deletions

View file

@ -6,114 +6,6 @@ function run(input, opts = config) {
return postcss([plugin(opts)]).process(input, { from: undefined })
}
test('it can generate responsive variants for nested at rules', () => {
const input = `
@responsive {
.banana { color: yellow; }
.chocolate { color: brown; }
@supports(display: grid) {
.grid\\:banana { color: blue; }
.grid\\:chocolate { color: green; }
}
}
`
const output = `
.banana {
color: yellow;
}
.chocolate {
color: brown;
}
@supports(display: grid) {
.grid\\:banana {
color: blue;
}
.grid\\:chocolate {
color: green;
}
}
@media (min-width: 500px) {
.sm\\:banana {
color: yellow;
}
.sm\\:chocolate {
color: brown;
}
@supports(display: grid) {
.sm\\:grid\\:banana {
color: blue;
}
.sm\\:grid\\:chocolate {
color: green;
}
}
}
@media (min-width: 750px) {
.md\\:banana {
color: yellow;
}
.md\\:chocolate {
color: brown;
}
@supports(display: grid) {
.md\\:grid\\:banana {
color: blue;
}
.md\\:grid\\:chocolate {
color: green;
}
}
}
@media (min-width: 1000px) {
.lg\\:banana {
color: yellow;
}
.lg\\:chocolate {
color: brown;
}
@supports(display: grid) {
.lg\\:grid\\:banana {
color: blue;
}
.lg\\:grid\\:chocolate {
color: green;
}
}
}
`
return run(input, {
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
options: {
separator: ':',
},
}).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})
test('it can generate responsive variants', () => {
const input = `
@responsive {
@ -280,6 +172,136 @@ test('responsive variants are grouped', () => {
})
})
test('it can generate responsive variants for nested at-rules', () => {
const input = `
@responsive {
.banana { color: yellow; }
@supports(display: grid) {
.grid\\:banana { color: blue; }
}
}
`
const output = `
.banana {
color: yellow;
}
@supports(display: grid) {
.grid\\:banana {
color: blue;
}
}
@media (min-width: 500px) {
.sm\\:banana {
color: yellow;
}
@supports(display: grid) {
.sm\\:grid\\:banana {
color: blue;
}
}
}
@media (min-width: 1000px) {
.lg\\:banana {
color: yellow;
}
@supports(display: grid) {
.lg\\:grid\\:banana {
color: blue;
}
}
}
`
return run(input, {
screens: {
sm: '500px',
lg: '1000px',
},
options: {
separator: ':',
},
}).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})
test('it can generate responsive variants for deeply nested at-rules', () => {
const input = `
@responsive {
.banana { color: yellow; }
@supports(display: grid) {
@supports(display: flex) {
.flex-grid\\:banana { color: blue; }
}
}
}
`
const output = `
.banana {
color: yellow;
}
@supports(display: grid) {
@supports(display: flex) {
.flex-grid\\:banana {
color: blue;
}
}
}
@media (min-width: 500px) {
.sm\\:banana {
color: yellow;
}
@supports(display: grid) {
@supports(display: flex) {
.sm\\:flex-grid\\:banana {
color: blue;
}
}
}
}
@media (min-width: 1000px) {
.lg\\:banana {
color: yellow;
}
@supports(display: grid) {
@supports(display: flex) {
.lg\\:flex-grid\\:banana {
color: blue;
}
}
}
}
`
return run(input, {
screens: {
sm: '500px',
lg: '1000px',
},
options: {
separator: ':',
},
}).then(result => {
expect(result.css).toMatchCss(output)
expect(result.warnings().length).toBe(0)
})
})
test('screen prefix is only applied to the last class in a selector', () => {
const input = `
@responsive {

View file

@ -8,12 +8,12 @@ export default function(config) {
return function(css) {
const screens = config.screens
const separator = config.options.separator
const responsiveRules = []
const responsiveRules = postcss.root()
let finalRules = []
css.walkAtRules('responsive', atRule => {
const nodes = atRule.nodes
responsiveRules.push(...cloneNodes(nodes))
responsiveRules.append(...cloneNodes(nodes))
atRule.before(nodes)
atRule.remove()
})
@ -24,34 +24,15 @@ export default function(config) {
params: buildMediaQuery(screens[screen]),
})
mediaQuery.append(
// Filter out nested `atRules`; we'll process those separately
responsiveRules.filter(rule => rule.type !== 'atrule').map(rule => {
const cloned = rule.clone()
cloned.selectors = _.map(rule.selectors, selector =>
mediaQuery.append(_.tap(responsiveRules.clone(), clonedRoot => {
clonedRoot.walkRules(rule => {
rule.selectors = _.map(rule.selectors, selector =>
buildSelectorVariant(selector, screen, separator, message => {
throw rule.error(message)
})
)
return cloned
})
)
mediaQuery.append(
// Process nested `atRules`.
responsiveRules.filter(rule => rule.type === 'atrule').map(atRule => {
const clonedAtRule = atRule.clone()
clonedAtRule.nodes.forEach(rule => {
rule.selectors = _.map(rule.selectors, selector => {
const selectorVariant = buildSelectorVariant(selector, screen, separator, message => {
throw rule.error(message)
})
return selectorVariant
})
})
return clonedAtRule
})
)
}))
finalRules.push(mediaQuery)
})