diff --git a/__tests__/generateModules.test.js b/__tests__/generateModules.test.js new file mode 100644 index 000000000..e8cdc4b7d --- /dev/null +++ b/__tests__/generateModules.test.js @@ -0,0 +1,130 @@ +import generateModules from '../src/util/generateModules' +import defineClasses from '../src/util/defineClasses' + +function textAlign() { + return defineClasses({ + 'text-left': { 'text-align': 'left' }, + 'text-right': { 'text-align': 'right' }, + 'text-center': { 'text-align': 'center' }, + }) +} + +function display() { + return defineClasses({ + 'block': { 'display': 'block' }, + 'inline': { 'display': 'inline' }, + 'inline-block': { 'display': 'inline-block' }, + }) +} + +function borderStyle() { + return defineClasses({ + 'border-solid': { 'border-style': 'solid' }, + 'border-dashed': { 'border-style': 'dashed' }, + 'border-dotted': { 'border-style': 'dotted' }, + }) +} + +test('an empty variants list generates a @variants at-rule with no parameters', () => { + const result = generateModules([ + { name: 'textAlign', generator: textAlign }, + ], { + textAlign: [], + }) + + const expected = ` + @variants { + .text-left { text-align: left } + .text-right { text-align: right } + .text-center { text-align: center } + } + ` + expect(result.toString()).toMatchCss(expected) +}) + +test('a `false` variants list generates no output', () => { + const result = generateModules([ + { name: 'textAlign', generator: textAlign }, + ], { + textAlign: false, + }) + + expect(result.toString()).toMatchCss('') +}) + +test('specified variants are included in the @variants at-rule', () => { + const result = generateModules([ + { name: 'textAlign', generator: textAlign }, + ], { + textAlign: ['responsive', 'hover'], + }) + + const expected = ` + @variants responsive, hover { + .text-left { text-align: left } + .text-right { text-align: right } + .text-center { text-align: center } + } + ` + expect(result.toString()).toMatchCss(expected) +}) + +test('options must provide variants for every module', () => { + expect(() => { + generateModules([ + { name: 'textAlign', generator: textAlign }, + { name: 'display', generator: display }, + ], { + textAlign: [], + }) + }).toThrow() +}) + +test('variants can be different for each module', () => { + const result = generateModules([ + { name: 'textAlign', generator: textAlign }, + { name: 'display', generator: display }, + { name: 'borderStyle', generator: borderStyle }, + ], { + textAlign: [], + display: false, + borderStyle: ['responsive', 'hover', 'focus'] + }) + + const expected = ` + @variants { + .text-left { text-align: left } + .text-right { text-align: right } + .text-center { text-align: center } + } + + @variants responsive, hover, focus { + .border-solid { border-style: solid } + .border-dashed { border-style: dashed } + .border-dotted { border-style: dotted } + } + ` + expect(result.toString()).toMatchCss(expected) +}) + +test('generators can reference the generatorOptions object', () => { + const result = generateModules([{ + name: 'parameterized', + generator: (generatorParams) => { + return defineClasses({ + 'foo': { 'color': generatorParams.color }, + }) + } + }], { + parameterized: [], + }, { + color: 'blue' + }) + + const expected = ` + @variants { + .foo { color: blue } + } + ` + expect(result.toString()).toMatchCss(expected) +}) diff --git a/src/util/generateModules.js b/src/util/generateModules.js new file mode 100644 index 000000000..f8609d803 --- /dev/null +++ b/src/util/generateModules.js @@ -0,0 +1,18 @@ +import _ from 'lodash' +import postcss from 'postcss' +import wrapWithVariants from '../util/wrapWithVariants' + +export default function(modules, moduleOptions, generatorOptions = {}) { + modules.forEach(module => { + if (! _.has(moduleOptions, module.name)) { + throw new Error(`Module \`${module.name}\` is missing from moduleOptions.`) + } + }) + + return postcss.root({ + nodes: _(modules) + .reject(module => moduleOptions[module.name] === false) + .flatMap(module => wrapWithVariants(module.generator(generatorOptions), moduleOptions[module.name])) + .value() + }) +}