Skip to content

Commit 8ff2b59

Browse files
committed
Add first class support for extending the default theme
1 parent acee883 commit 8ff2b59

File tree

2 files changed

+252
-5
lines changed

2 files changed

+252
-5
lines changed

__tests__/resolveConfig.test.js

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,3 +430,228 @@ test('functions in the user theme section are lazily evaluated', () => {
430430
},
431431
})
432432
})
433+
434+
test('theme values in the extend section extend the existing theme', () => {
435+
const userConfig = {
436+
theme: {
437+
extend: {
438+
opacity: {
439+
'25': '25',
440+
'75': '.75',
441+
},
442+
backgroundColors: {
443+
customBackground: '#bada55',
444+
},
445+
}
446+
},
447+
}
448+
449+
const defaultConfig = {
450+
prefix: '-',
451+
important: false,
452+
separator: ':',
453+
theme: {
454+
colors: {
455+
cyan: 'cyan',
456+
magenta: 'magenta',
457+
yellow: 'yellow',
458+
},
459+
opacity: {
460+
'0': '0',
461+
'50': '.5',
462+
'100': '1',
463+
},
464+
backgroundColors: ({ colors }) => colors,
465+
},
466+
variants: {
467+
backgroundColors: ['responsive', 'hover', 'focus'],
468+
opacity: ['responsive', 'hover', 'focus'],
469+
},
470+
}
471+
472+
const result = resolveConfig([userConfig, defaultConfig])
473+
474+
expect(result).toEqual({
475+
prefix: '-',
476+
important: false,
477+
separator: ':',
478+
theme: {
479+
colors: {
480+
cyan: 'cyan',
481+
magenta: 'magenta',
482+
yellow: 'yellow',
483+
},
484+
opacity: {
485+
'0': '0',
486+
'50': '.5',
487+
'100': '1',
488+
'25': '25',
489+
'75': '.75',
490+
},
491+
backgroundColors: {
492+
cyan: 'cyan',
493+
magenta: 'magenta',
494+
yellow: 'yellow',
495+
customBackground: '#bada55',
496+
},
497+
},
498+
variants: {
499+
backgroundColors: ['responsive', 'hover', 'focus'],
500+
opacity: ['responsive', 'hover', 'focus'],
501+
},
502+
})
503+
})
504+
505+
test('theme values in the extend section extend the user theme', () => {
506+
const userConfig = {
507+
theme: {
508+
opacity: {
509+
'0': '0',
510+
'20': '.2',
511+
'40': '.4',
512+
},
513+
height: theme => theme.width,
514+
extend: {
515+
opacity: {
516+
'60': '.6',
517+
'80': '.8',
518+
'100': '1',
519+
},
520+
height: {
521+
customHeight: '500vh',
522+
},
523+
}
524+
},
525+
}
526+
527+
const defaultConfig = {
528+
prefix: '-',
529+
important: false,
530+
separator: ':',
531+
theme: {
532+
opacity: {
533+
'0': '0',
534+
'50': '.5',
535+
'100': '1',
536+
},
537+
height: {
538+
'0': 0,
539+
'full': '100%',
540+
},
541+
width: {
542+
'0': 0,
543+
'1': '.25rem',
544+
'2': '.5rem',
545+
'3': '.75rem',
546+
'4': '1rem',
547+
},
548+
},
549+
variants: {
550+
opacity: ['responsive', 'hover', 'focus'],
551+
height: ['responsive'],
552+
width: ['responsive'],
553+
},
554+
}
555+
556+
const result = resolveConfig([userConfig, defaultConfig])
557+
558+
expect(result).toEqual({
559+
prefix: '-',
560+
important: false,
561+
separator: ':',
562+
theme: {
563+
opacity: {
564+
'0': '0',
565+
'20': '.2',
566+
'40': '.4',
567+
'60': '.6',
568+
'80': '.8',
569+
'100': '1',
570+
},
571+
height: {
572+
'0': 0,
573+
'1': '.25rem',
574+
'2': '.5rem',
575+
'3': '.75rem',
576+
'4': '1rem',
577+
customHeight: '500vh',
578+
},
579+
width: {
580+
'0': 0,
581+
'1': '.25rem',
582+
'2': '.5rem',
583+
'3': '.75rem',
584+
'4': '1rem',
585+
},
586+
},
587+
variants: {
588+
opacity: ['responsive', 'hover', 'focus'],
589+
height: ['responsive'],
590+
width: ['responsive'],
591+
},
592+
})
593+
})
594+
595+
test('theme values in the extend section can extend values that are depended on lazily', () => {
596+
const userConfig = {
597+
theme: {
598+
extend: {
599+
colors: {
600+
red: 'red',
601+
green: 'green',
602+
blue: 'blue',
603+
},
604+
backgroundColors: {
605+
customBackground: '#bada55',
606+
},
607+
}
608+
},
609+
}
610+
611+
const defaultConfig = {
612+
prefix: '-',
613+
important: false,
614+
separator: ':',
615+
theme: {
616+
colors: {
617+
cyan: 'cyan',
618+
magenta: 'magenta',
619+
yellow: 'yellow',
620+
},
621+
backgroundColors: ({ colors }) => colors,
622+
},
623+
variants: {
624+
backgroundColors: ['responsive', 'hover', 'focus'],
625+
},
626+
}
627+
628+
const result = resolveConfig([userConfig, defaultConfig])
629+
630+
expect(result).toEqual({
631+
prefix: '-',
632+
important: false,
633+
separator: ':',
634+
theme: {
635+
colors: {
636+
cyan: 'cyan',
637+
magenta: 'magenta',
638+
yellow: 'yellow',
639+
red: 'red',
640+
green: 'green',
641+
blue: 'blue',
642+
},
643+
backgroundColors: {
644+
cyan: 'cyan',
645+
magenta: 'magenta',
646+
yellow: 'yellow',
647+
red: 'red',
648+
green: 'green',
649+
blue: 'blue',
650+
customBackground: '#bada55',
651+
},
652+
},
653+
variants: {
654+
backgroundColors: ['responsive', 'hover', 'focus'],
655+
},
656+
})
657+
})

src/util/resolveConfig.js

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,41 @@
1-
import _ from 'lodash'
1+
import mergeWith from 'lodash/mergeWith'
2+
import isFunction from 'lodash/isFunction'
3+
import defaults from 'lodash/defaults'
4+
import map from 'lodash/map'
5+
import get from 'lodash/get'
26

37
function resolveFunctionKeys(object) {
48
return Object.keys(object).reduce((resolved, key) => {
59
return {
610
...resolved,
7-
[key]: _.isFunction(object[key]) ? object[key](object) : object[key],
11+
[key]: isFunction(object[key]) ? object[key](object) : object[key],
812
}
913
}, {})
1014
}
1115

16+
function without(object, key) {
17+
return (({[key]: _, ...rest }) => rest)(object)
18+
}
19+
20+
function mergeExtensions(theme) {
21+
return mergeWith({}, without(theme, 'extend'), theme.extend, (_, value, key) => {
22+
return isFunction(theme[key])
23+
? mergedTheme => ({
24+
...theme[key](mergedTheme),
25+
...get(theme.extend, key, {})
26+
})
27+
: {
28+
...theme[key],
29+
...get(theme.extend, key, {}),
30+
}
31+
})
32+
}
33+
1234
export default function(configs) {
13-
return _.defaults(
35+
return defaults(
1436
{
15-
theme: resolveFunctionKeys(_.defaults(..._.map(configs, 'theme'))),
16-
variants: _.defaults(..._.map(configs, 'variants')),
37+
theme: resolveFunctionKeys(mergeExtensions(defaults(...map(configs, 'theme')))),
38+
variants: defaults(...map(configs, 'variants')),
1739
},
1840
...configs
1941
)

0 commit comments

Comments
 (0)