From 8ff2b590960d4bce875a61d414406aedcfd4ab6b Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Wed, 13 Feb 2019 09:01:49 -0500 Subject: [PATCH 1/6] Add first class support for extending the default theme --- __tests__/resolveConfig.test.js | 225 ++++++++++++++++++++++++++++++++ src/util/resolveConfig.js | 32 ++++- 2 files changed, 252 insertions(+), 5 deletions(-) diff --git a/__tests__/resolveConfig.test.js b/__tests__/resolveConfig.test.js index 7ef2cbbe3..ecdce1ac5 100644 --- a/__tests__/resolveConfig.test.js +++ b/__tests__/resolveConfig.test.js @@ -430,3 +430,228 @@ test('functions in the user theme section are lazily evaluated', () => { }, }) }) + +test('theme values in the extend section extend the existing theme', () => { + const userConfig = { + theme: { + extend: { + opacity: { + '25': '25', + '75': '.75', + }, + backgroundColors: { + customBackground: '#bada55', + }, + } + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + }, + opacity: { + '0': '0', + '50': '.5', + '100': '1', + }, + backgroundColors: ({ colors }) => colors, + }, + variants: { + backgroundColors: ['responsive', 'hover', 'focus'], + opacity: ['responsive', 'hover', 'focus'], + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toEqual({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + }, + opacity: { + '0': '0', + '50': '.5', + '100': '1', + '25': '25', + '75': '.75', + }, + backgroundColors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + customBackground: '#bada55', + }, + }, + variants: { + backgroundColors: ['responsive', 'hover', 'focus'], + opacity: ['responsive', 'hover', 'focus'], + }, + }) +}) + +test('theme values in the extend section extend the user theme', () => { + const userConfig = { + theme: { + opacity: { + '0': '0', + '20': '.2', + '40': '.4', + }, + height: theme => theme.width, + extend: { + opacity: { + '60': '.6', + '80': '.8', + '100': '1', + }, + height: { + customHeight: '500vh', + }, + } + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + theme: { + opacity: { + '0': '0', + '50': '.5', + '100': '1', + }, + height: { + '0': 0, + 'full': '100%', + }, + width: { + '0': 0, + '1': '.25rem', + '2': '.5rem', + '3': '.75rem', + '4': '1rem', + }, + }, + variants: { + opacity: ['responsive', 'hover', 'focus'], + height: ['responsive'], + width: ['responsive'], + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toEqual({ + prefix: '-', + important: false, + separator: ':', + theme: { + opacity: { + '0': '0', + '20': '.2', + '40': '.4', + '60': '.6', + '80': '.8', + '100': '1', + }, + height: { + '0': 0, + '1': '.25rem', + '2': '.5rem', + '3': '.75rem', + '4': '1rem', + customHeight: '500vh', + }, + width: { + '0': 0, + '1': '.25rem', + '2': '.5rem', + '3': '.75rem', + '4': '1rem', + }, + }, + variants: { + opacity: ['responsive', 'hover', 'focus'], + height: ['responsive'], + width: ['responsive'], + }, + }) +}) + +test('theme values in the extend section can extend values that are depended on lazily', () => { + const userConfig = { + theme: { + extend: { + colors: { + red: 'red', + green: 'green', + blue: 'blue', + }, + backgroundColors: { + customBackground: '#bada55', + }, + } + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + }, + backgroundColors: ({ colors }) => colors, + }, + variants: { + backgroundColors: ['responsive', 'hover', 'focus'], + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toEqual({ + prefix: '-', + important: false, + separator: ':', + theme: { + colors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + red: 'red', + green: 'green', + blue: 'blue', + }, + backgroundColors: { + cyan: 'cyan', + magenta: 'magenta', + yellow: 'yellow', + red: 'red', + green: 'green', + blue: 'blue', + customBackground: '#bada55', + }, + }, + variants: { + backgroundColors: ['responsive', 'hover', 'focus'], + }, + }) +}) diff --git a/src/util/resolveConfig.js b/src/util/resolveConfig.js index 2f1677307..165ba4811 100644 --- a/src/util/resolveConfig.js +++ b/src/util/resolveConfig.js @@ -1,19 +1,41 @@ -import _ from 'lodash' +import mergeWith from 'lodash/mergeWith' +import isFunction from 'lodash/isFunction' +import defaults from 'lodash/defaults' +import map from 'lodash/map' +import get from 'lodash/get' function resolveFunctionKeys(object) { return Object.keys(object).reduce((resolved, key) => { return { ...resolved, - [key]: _.isFunction(object[key]) ? object[key](object) : object[key], + [key]: isFunction(object[key]) ? object[key](object) : object[key], } }, {}) } +function without(object, key) { + return (({[key]: _, ...rest }) => rest)(object) +} + +function mergeExtensions(theme) { + return mergeWith({}, without(theme, 'extend'), theme.extend, (_, value, key) => { + return isFunction(theme[key]) + ? mergedTheme => ({ + ...theme[key](mergedTheme), + ...get(theme.extend, key, {}) + }) + : { + ...theme[key], + ...get(theme.extend, key, {}), + } + }) +} + export default function(configs) { - return _.defaults( + return defaults( { - theme: resolveFunctionKeys(_.defaults(..._.map(configs, 'theme'))), - variants: _.defaults(..._.map(configs, 'variants')), + theme: resolveFunctionKeys(mergeExtensions(defaults(...map(configs, 'theme')))), + variants: defaults(...map(configs, 'variants')), }, ...configs ) From 2342d72c5e331bc052d507d6acb55d92d24ab663 Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Wed, 13 Feb 2019 14:22:45 -0500 Subject: [PATCH 2/6] Use existing parameter --- src/util/resolveConfig.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/util/resolveConfig.js b/src/util/resolveConfig.js index 165ba4811..7e82aef13 100644 --- a/src/util/resolveConfig.js +++ b/src/util/resolveConfig.js @@ -2,7 +2,6 @@ import mergeWith from 'lodash/mergeWith' import isFunction from 'lodash/isFunction' import defaults from 'lodash/defaults' import map from 'lodash/map' -import get from 'lodash/get' function resolveFunctionKeys(object) { return Object.keys(object).reduce((resolved, key) => { @@ -18,15 +17,15 @@ function without(object, key) { } function mergeExtensions(theme) { - return mergeWith({}, without(theme, 'extend'), theme.extend, (_, value, key) => { + return mergeWith({}, without(theme, 'extend'), theme.extend, (_, extensions, key) => { return isFunction(theme[key]) ? mergedTheme => ({ ...theme[key](mergedTheme), - ...get(theme.extend, key, {}) + ...extensions }) : { ...theme[key], - ...get(theme.extend, key, {}), + ...extensions, } }) } From d95d28eec8cd0906ec04d011edaa93ff45095acc Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Wed, 13 Feb 2019 14:25:37 -0500 Subject: [PATCH 3/6] Fix code style --- __tests__/resolveConfig.test.js | 8 ++++---- src/util/resolveConfig.js | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/__tests__/resolveConfig.test.js b/__tests__/resolveConfig.test.js index ecdce1ac5..45b8498c4 100644 --- a/__tests__/resolveConfig.test.js +++ b/__tests__/resolveConfig.test.js @@ -442,7 +442,7 @@ test('theme values in the extend section extend the existing theme', () => { backgroundColors: { customBackground: '#bada55', }, - } + }, }, } @@ -520,7 +520,7 @@ test('theme values in the extend section extend the user theme', () => { height: { customHeight: '500vh', }, - } + }, }, } @@ -536,7 +536,7 @@ test('theme values in the extend section extend the user theme', () => { }, height: { '0': 0, - 'full': '100%', + full: '100%', }, width: { '0': 0, @@ -604,7 +604,7 @@ test('theme values in the extend section can extend values that are depended on backgroundColors: { customBackground: '#bada55', }, - } + }, }, } diff --git a/src/util/resolveConfig.js b/src/util/resolveConfig.js index 7e82aef13..523ccf0a9 100644 --- a/src/util/resolveConfig.js +++ b/src/util/resolveConfig.js @@ -13,7 +13,8 @@ function resolveFunctionKeys(object) { } function without(object, key) { - return (({[key]: _, ...rest }) => rest)(object) + /* eslint-disable no-unused-vars */ + return (({ [key]: _, ...rest }) => rest)(object) } function mergeExtensions(theme) { @@ -21,7 +22,7 @@ function mergeExtensions(theme) { return isFunction(theme[key]) ? mergedTheme => ({ ...theme[key](mergedTheme), - ...extensions + ...extensions, }) : { ...theme[key], From e1dd08dad3e3eef9b8b00779864e541b9ff435d0 Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Thu, 14 Feb 2019 07:43:50 -0500 Subject: [PATCH 4/6] Add test to document extend is not deeply merged --- __tests__/resolveConfig.test.js | 70 +++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/__tests__/resolveConfig.test.js b/__tests__/resolveConfig.test.js index 45b8498c4..86e1a14e3 100644 --- a/__tests__/resolveConfig.test.js +++ b/__tests__/resolveConfig.test.js @@ -655,3 +655,73 @@ test('theme values in the extend section can extend values that are depended on }, }) }) + +test('theme values in the extend section are not deeply merged', () => { + const userConfig = { + theme: { + extend: { + fonts: { + sans: [ + 'Comic Sans', + ], + } + }, + }, + } + + const defaultConfig = { + prefix: '-', + important: false, + separator: ':', + theme: { + fonts: { + sans: [ + 'system-ui', + 'Helvetica Neue', + 'sans-serif', + ], + serif: [ + 'Constantia', + 'Georgia', + 'serif', + ], + mono: [ + 'Menlo', + 'Courier New', + 'monospace', + ], + }, + }, + variants: { + fonts: ['responsive'], + }, + } + + const result = resolveConfig([userConfig, defaultConfig]) + + expect(result).toEqual({ + prefix: '-', + important: false, + separator: ':', + theme: { + fonts: { + sans: [ + 'Comic Sans', + ], + serif: [ + 'Constantia', + 'Georgia', + 'serif', + ], + mono: [ + 'Menlo', + 'Courier New', + 'monospace', + ], + }, + }, + variants: { + fonts: ['responsive'], + }, + }) +}) From 4754d225afff024fb5f70f595d9a96168f2291b3 Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Thu, 14 Feb 2019 07:47:32 -0500 Subject: [PATCH 5/6] Remove need for without function --- src/util/resolveConfig.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/util/resolveConfig.js b/src/util/resolveConfig.js index 523ccf0a9..6189d6cdc 100644 --- a/src/util/resolveConfig.js +++ b/src/util/resolveConfig.js @@ -12,13 +12,8 @@ function resolveFunctionKeys(object) { }, {}) } -function without(object, key) { - /* eslint-disable no-unused-vars */ - return (({ [key]: _, ...rest }) => rest)(object) -} - -function mergeExtensions(theme) { - return mergeWith({}, without(theme, 'extend'), theme.extend, (_, extensions, key) => { +function mergeExtensions({ extend, ...theme }) { + return mergeWith({}, theme, extend, (_, extensions, key) => { return isFunction(theme[key]) ? mergedTheme => ({ ...theme[key](mergedTheme), From c56b56db3e7fbf7f18f1213d47b1f162dee7aee7 Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Thu, 14 Feb 2019 07:48:57 -0500 Subject: [PATCH 6/6] Fix code style --- __tests__/resolveConfig.test.js | 40 +++++++-------------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/__tests__/resolveConfig.test.js b/__tests__/resolveConfig.test.js index 86e1a14e3..d80eb0e91 100644 --- a/__tests__/resolveConfig.test.js +++ b/__tests__/resolveConfig.test.js @@ -661,10 +661,8 @@ test('theme values in the extend section are not deeply merged', () => { theme: { extend: { fonts: { - sans: [ - 'Comic Sans', - ], - } + sans: ['Comic Sans'], + }, }, }, } @@ -675,21 +673,9 @@ test('theme values in the extend section are not deeply merged', () => { separator: ':', theme: { fonts: { - sans: [ - 'system-ui', - 'Helvetica Neue', - 'sans-serif', - ], - serif: [ - 'Constantia', - 'Georgia', - 'serif', - ], - mono: [ - 'Menlo', - 'Courier New', - 'monospace', - ], + sans: ['system-ui', 'Helvetica Neue', 'sans-serif'], + serif: ['Constantia', 'Georgia', 'serif'], + mono: ['Menlo', 'Courier New', 'monospace'], }, }, variants: { @@ -705,19 +691,9 @@ test('theme values in the extend section are not deeply merged', () => { separator: ':', theme: { fonts: { - sans: [ - 'Comic Sans', - ], - serif: [ - 'Constantia', - 'Georgia', - 'serif', - ], - mono: [ - 'Menlo', - 'Courier New', - 'monospace', - ], + sans: ['Comic Sans'], + serif: ['Constantia', 'Georgia', 'serif'], + mono: ['Menlo', 'Courier New', 'monospace'], }, }, variants: {