Skip to content

Commit

Permalink
Support extend in variants config (#2651)
Browse files Browse the repository at this point in the history
* WIP

* It's alive

* Pull default variant order from config
  • Loading branch information
adamwathan committed Oct 23, 2020
1 parent 82b7be6 commit 11af870
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 38 deletions.
127 changes: 127 additions & 0 deletions __tests__/resolveConfig.test.js
Expand Up @@ -1738,6 +1738,133 @@ test('user theme extensions take precedence over plugin theme extensions with th
})
})

test('variants can be extended', () => {
const userConfig = {
variants: {
borderColor: ({ after }) => after(['group-focus'], 'hover'),
extend: {
backgroundColor: ['active', 'disabled', 'group-hover'],
},
},
}

const otherConfig = {
variants: {
extend: {
textColor: ['hover', 'focus-within'],
},
},
}

const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {
borderColor: ['hover', 'focus'],
backgroundColor: ['responsive', 'hover', 'focus'],
textColor: ['responsive', 'focus'],
},
}

const result = resolveConfig([userConfig, otherConfig, defaultConfig])

expect(result).toMatchObject({
variants: {
borderColor: ['hover', 'group-focus', 'focus'],
backgroundColor: ['responsive', 'group-hover', 'hover', 'focus', 'active', 'disabled'],
textColor: ['responsive', 'focus-within', 'hover', 'focus'],
},
})
})

test('variant sort order can be customized', () => {
const userConfig = {
variantOrder: [
'disabled',
'focus',
'group-hover',
'focus-within',
'active',
'hover',
'responsive',
],
variants: {
borderColor: ({ after }) => after(['group-focus'], 'hover'),
extend: {
backgroundColor: ['active', 'disabled', 'group-hover'],
},
},
}

const otherConfig = {
variants: {
extend: {
textColor: ['hover', 'focus-within'],
},
},
}

const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {
borderColor: ['hover', 'focus'],
backgroundColor: ['responsive', 'hover', 'focus'],
textColor: ['responsive', 'focus'],
},
}

const result = resolveConfig([userConfig, otherConfig, defaultConfig])

expect(result).toMatchObject({
variants: {
borderColor: ['hover', 'group-focus', 'focus'],
backgroundColor: ['disabled', 'focus', 'group-hover', 'active', 'hover', 'responsive'],
textColor: ['focus', 'focus-within', 'hover', 'responsive'],
},
})
})

test('custom variants go to the beginning by default when sort is applied', () => {
const userConfig = {
variants: {
extend: {
backgroundColor: ['active', 'custom-variant-1', 'group-hover', 'custom-variant-2'],
},
},
}

const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
},
}

const result = resolveConfig([userConfig, defaultConfig])

expect(result).toMatchObject({
variants: {
backgroundColor: [
'responsive',
'custom-variant-1',
'custom-variant-2',
'group-hover',
'hover',
'focus',
'active',
],
},
})
})

test('variants can be defined as a function', () => {
const userConfig = {
variants: {
Expand Down
106 changes: 69 additions & 37 deletions src/util/resolveConfig.js
Expand Up @@ -5,10 +5,12 @@ import isUndefined from 'lodash/isUndefined'
import defaults from 'lodash/defaults'
import map from 'lodash/map'
import get from 'lodash/get'
import uniq from 'lodash/uniq'
import toPath from 'lodash/toPath'
import negateValue from './negateValue'
import { corePluginList } from '../corePluginList'
import configurePlugins from './configurePlugins'
import defaultConfig from '../../stubs/defaultConfig.stub'

const configUtils = {
negative(scale) {
Expand Down Expand Up @@ -39,31 +41,29 @@ function value(valueToResolve, ...args) {
return isFunction(valueToResolve) ? valueToResolve(...args) : valueToResolve
}

function mergeThemes(themes) {
const theme = (({ extend: _, ...t }) => t)(
themes.reduce((merged, t) => {
return defaults(merged, t)
}, {})
)
function collectExtends(items) {
return items.reduce((merged, { extend }) => {
return mergeWith(merged, extend, (mergedValue, extendValue) => {
if (isUndefined(mergedValue)) {
return [extendValue]
}

if (Array.isArray(mergedValue)) {
return [extendValue, ...mergedValue]
}

return [extendValue, mergedValue]
})
}, {})
}

function mergeThemes(themes) {
return {
...theme,
...themes.reduce((merged, theme) => defaults(merged, theme), {}),

// In order to resolve n config objects, we combine all of their `extend` properties
// into arrays instead of objects so they aren't overridden.
extend: themes.reduce((merged, { extend }) => {
return mergeWith(merged, extend, (mergedValue, extendValue) => {
if (isUndefined(mergedValue)) {
return [extendValue]
}

if (Array.isArray(mergedValue)) {
return [extendValue, ...mergedValue]
}

return [extendValue, mergedValue]
})
}, {}),
extend: collectExtends(themes),
}
}

Expand Down Expand Up @@ -130,12 +130,8 @@ function extractPluginConfigs(configs) {
return allConfigs
}

function resolveVariants([firstConfig, ...variantConfigs]) {
if (Array.isArray(firstConfig)) {
return firstConfig
}

return [firstConfig, ...variantConfigs].reverse().reduce((resolved, variants) => {
function mergeVariants(variants) {
const mergedVariants = variants.reduce((resolved, variants) => {
Object.entries(variants || {}).forEach(([plugin, pluginVariants]) => {
if (isFunction(pluginVariants)) {
resolved[plugin] = pluginVariants({
Expand Down Expand Up @@ -187,10 +183,39 @@ function resolveVariants([firstConfig, ...variantConfigs]) {

return resolved
}, {})

return {
...mergedVariants,
extend: collectExtends(variants),
}
}

function mergeVariantExtensions({ extend, ...variants }, variantOrder) {
return mergeWith(variants, extend, (variantsValue, extensions) => {
const merged = uniq([...variantsValue, ...extensions].flat())

if (extensions.flat().length === 0) {
return merged
}

return merged.sort((a, z) => variantOrder.indexOf(a) - variantOrder.indexOf(z))
})
}

function resolveVariants([firstConfig, ...variantConfigs], variantOrder) {
// Global variants configuration like `variants: ['hover', 'focus']`
if (Array.isArray(firstConfig)) {
return firstConfig
}

return mergeVariantExtensions(
mergeVariants([firstConfig, ...variantConfigs].reverse()),
variantOrder
)
}

function resolveCorePlugins(corePluginConfigs) {
const result = [...corePluginConfigs].reverse().reduce((resolved, corePluginConfig) => {
const result = [...corePluginConfigs].reduceRight((resolved, corePluginConfig) => {
if (isFunction(corePluginConfig)) {
return corePluginConfig({ corePlugins: resolved })
}
Expand All @@ -201,31 +226,38 @@ function resolveCorePlugins(corePluginConfigs) {
}

function resolvePluginLists(pluginLists) {
const result = [...pluginLists].reverse().reduce((resolved, pluginList) => {
const result = [...pluginLists].reduceRight((resolved, pluginList) => {
return [...resolved, ...pluginList]
}, [])

return result
}

export default function resolveConfig(configs) {
const allConfigs = extractPluginConfigs(configs)
const allConfigs = [
...extractPluginConfigs(configs),
{
darkMode: false,
prefix: '',
important: false,
separator: ':',
variantOrder: defaultConfig.variantOrder,
},
]
const { variantOrder } = allConfigs.find((c) => c.variantOrder)

return defaults(
{
theme: resolveFunctionKeys(
mergeExtensions(mergeThemes(map(allConfigs, (t) => get(t, 'theme', {}))))
),
variants: resolveVariants(allConfigs.map((c) => c.variants)),
variants: resolveVariants(
allConfigs.map((c) => get(c, 'variants', {})),
variantOrder
),
corePlugins: resolveCorePlugins(allConfigs.map((c) => c.corePlugins)),
plugins: resolvePluginLists(configs.map((c) => get(c, 'plugins', []))),
},
...allConfigs,
{
darkMode: false,
prefix: '',
important: false,
separator: ':',
}
...allConfigs
)
}
16 changes: 16 additions & 0 deletions stubs/defaultConfig.stub.js
Expand Up @@ -668,6 +668,22 @@ module.exports = {
},
},
},
variantOrder: [
'first',
'last',
'odd',
'even',
'visited',
'checked',
'group-hover',
'group-focus',
'focus-within',
'hover',
'focus',
'focus-visible',
'active',
'disabled',
],
variants: {
accessibility: ['responsive', 'focus'],
alignContent: ['responsive'],
Expand Down
4 changes: 3 additions & 1 deletion stubs/simpleConfig.stub.js
Expand Up @@ -4,6 +4,8 @@ module.exports = {
theme: {
extend: {},
},
variants: {},
variants: {
extend: {},
},
plugins: [],
}

0 comments on commit 11af870

Please sign in to comment.