Skip to content

Commit

Permalink
Merge pull request #473 from styled-system/merge-styles
Browse files Browse the repository at this point in the history
Merge styles into single object
  • Loading branch information
jxnblk committed May 4, 2019
2 parents ef126ca + 6f5d329 commit 67fc9a2
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 58 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "styled-system",
"version": "4.1.1",
"version": "4.2.0-0",
"description": "Responsive, theme-based style props for building design systems with React",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
Expand Down Expand Up @@ -58,6 +58,11 @@
"bench",
"examples",
"packages/**/*"
],
"reporter": [
"lcov",
"text-summary",
"html"
]
},
"bundlesize": [
Expand Down
28 changes: 26 additions & 2 deletions src/index.js
Expand Up @@ -35,6 +35,30 @@ export const createMediaQuery = n => `@media screen and (min-width: ${px(n)})`

const getValue = (n, scale) => get(scale, n)

// loosely based on deepmerge package
export const merge = (a, b) => {
const result = {}
for (const key in a) {
result[key] = a[key]
}
for (const key in b) {
if (!a[key]) {
result[key] = b[key]
} else {
result[key] = merge(a[key], b[key])
}
}
return result
}

const mergeAll = (...args) => {
let result = {}
for (let i = 0; i < args.length; i++) {
result = merge(result, args[i])
}
return result
}

export const style = ({
prop,
cssProperty,
Expand Down Expand Up @@ -83,7 +107,7 @@ export const style = ({
styles.sort()
}

return styles
return mergeAll(...styles)
}

func.propTypes = {
Expand All @@ -108,7 +132,7 @@ export const style = ({
export const compose = (...funcs) => {
const func = props => {
const n = funcs.map(fn => fn(props)).filter(Boolean)
return n
return mergeAll(...n)
}

func.propTypes = {}
Expand Down
54 changes: 34 additions & 20 deletions test/index.js
Expand Up @@ -10,6 +10,7 @@ import {
variant,
cloneFunction,
mapProps,
merge,
} from '../src'

const width = style({
Expand Down Expand Up @@ -88,32 +89,32 @@ test('returns an array of responsive style objects', t => {
const style = width({
width: ['100%', '50%'],
})
t.deepEqual(style, [
{ width: '100%' },
{ '@media screen and (min-width: 40em)': { width: '50%' } },
])
t.deepEqual(style, {
width: '100%',
'@media screen and (min-width: 40em)': { width: '50%' },
})
})

test('returns an array of responsive style objects for all breakpoints', t => {
const style = width({
width: ['100%', '75%', '50%', '33%', '25%'],
})
t.deepEqual(style, [
{ width: '100%' },
{ '@media screen and (min-width: 40em)': { width: '75%' } },
{ '@media screen and (min-width: 52em)': { width: '50%' } },
{ '@media screen and (min-width: 64em)': { width: '33%' } },
])
t.deepEqual(style, {
width: '100%',
'@media screen and (min-width: 40em)': { width: '75%' },
'@media screen and (min-width: 52em)': { width: '50%' },
'@media screen and (min-width: 64em)': { width: '33%' },
})
})

test('skips undefined responsive values', t => {
const style = width({
width: ['100%', , '50%'],
})
t.deepEqual(style, [
{ width: '100%' },
{ '@media screen and (min-width: 52em)': { width: '50%' } },
])
t.deepEqual(style, {
width: '100%',
'@media screen and (min-width: 52em)': { width: '50%' },
})
})

test('parses object values', t => {
Expand All @@ -123,10 +124,10 @@ test('parses object values', t => {
2: '50%',
},
})
t.deepEqual(style, [
{ width: '100%' },
{ '@media screen and (min-width: 64em)': { width: '50%' } },
])
t.deepEqual(style, {
width: '100%',
'@media screen and (min-width: 64em)': { width: '50%' },
})
})

test('get returns a value', t => {
Expand Down Expand Up @@ -191,7 +192,7 @@ test('compose combines style functions', t => {
bg: 'black',
})
t.is(typeof colors, 'function')
t.deepEqual(styles, [{ color: 'tomato' }, { backgroundColor: 'black' }])
t.deepEqual(styles, { color: 'tomato', backgroundColor: 'black' })
})

test('num returns true for numbers', t => {
Expand Down Expand Up @@ -265,11 +266,24 @@ test('array values longer than breakpoints does not reset returned style object'
const a = width({
width: ['100%', , , , , '50%', '25%'],
})
t.deepEqual(a, [{ width: '100%' }])
t.deepEqual(a, { width: '100%' })
})

test('mapProps copies propTypes', t => {
const margin = style({ prop: 'margin' })
const func = mapProps(props => props)(margin)
t.is(typeof func.propTypes, 'object')
})

test('merge deeply merges', t => {
const result = merge(
{ hello: { hi: 'beep' } },
{ hello: { hey: 'boop' } },
)
t.deepEqual(result, {
hello: {
hi: 'beep',
hey: 'boop',
}
})
})
66 changes: 32 additions & 34 deletions test/styles.js
Expand Up @@ -22,7 +22,7 @@ const theme = {

test('returns color values from theme', t => {
const a = color({ theme, color: 'blue', bg: 'black' })
t.deepEqual(a, [{ color: '#07c' }, { backgroundColor: '#111' }])
t.deepEqual(a, { color: '#07c', backgroundColor: '#111' })
})

test('returns raw color values', t => {
Expand All @@ -31,15 +31,15 @@ test('returns raw color values', t => {
color: 'inherit',
bg: 'tomato',
})
t.deepEqual(a, [{ color: 'inherit' }, { backgroundColor: 'tomato' }])
t.deepEqual(a, { color: 'inherit', backgroundColor: 'tomato' })
})

test('backgroundColor prop overrides bg prop', t => {
const a = color({
backgroundColor: 'tomato',
bg: 'blue',
})
t.deepEqual(a, [{ backgroundColor: 'tomato' }])
t.deepEqual(a, { backgroundColor: 'tomato' })
})

test('returns a pixel font-size', t => {
Expand Down Expand Up @@ -76,22 +76,22 @@ test('returns an array of style objects', t => {
const styles = space({
m: '4px',
})
t.deepEqual(styles, [{ margin: '4px' }])
t.deepEqual(styles, { margin: '4px' })
})

test('returns 0 values', t => {
const styles = space({ m: 0 })
t.deepEqual(styles, [{ margin: 0 }])
t.deepEqual(styles, { margin: 0 })
})

test('returns negative pixel values', t => {
const styles = space({ m: -2 })
t.deepEqual(styles, [{ margin: '-8px' }])
t.deepEqual(styles, { margin: '-8px' })
})

test('returns negative em values', t => {
const styles = space({ m: '-16em' })
t.deepEqual(styles, [{ margin: '-16em' }])
t.deepEqual(styles, { margin: '-16em' })
})

test('returns negative theme values', t => {
Expand All @@ -101,7 +101,7 @@ test('returns negative theme values', t => {
},
m: -2,
})
t.deepEqual(styles, [{ margin: '-8px' }])
t.deepEqual(styles, { margin: '-8px' })
})

test('returns positive theme values', t => {
Expand All @@ -111,27 +111,25 @@ test('returns positive theme values', t => {
},
m: 2,
})
t.deepEqual(styles, [{ margin: '2em' }])
t.deepEqual(styles, { margin: '2em' })
})

test('returns responsive values', t => {
const styles = space({
m: [0, 2, 3],
})
t.deepEqual(styles, [
[
{ margin: 0 },
{ '@media screen and (min-width: 40em)': { margin: '8px' } },
{ '@media screen and (min-width: 52em)': { margin: '16px' } },
],
])
t.deepEqual(styles, {
margin: 0,
'@media screen and (min-width: 40em)': { margin: '8px' },
'@media screen and (min-width: 52em)': { margin: '16px' },
})
})

test('returns aliased values', t => {
const styles = space({
px: 2,
})
t.deepEqual(styles, [{ paddingLeft: '8px' }, { paddingRight: '8px' }])
t.deepEqual(styles, { paddingLeft: '8px', paddingRight: '8px' })
})

test('returns string values from theme', t => {
Expand All @@ -141,7 +139,7 @@ test('returns string values from theme', t => {
},
padding: 1,
})
t.deepEqual(styles, [{ padding: '1em' }])
t.deepEqual(styles, { padding: '1em' })
})

test('returns negative string values from theme', t => {
Expand All @@ -151,7 +149,7 @@ test('returns negative string values from theme', t => {
},
margin: -1,
})
t.deepEqual(styles, [{ margin: '-1em' }])
t.deepEqual(styles, { margin: '-1em' })
})

test('returns values from theme object', t => {
Expand All @@ -162,57 +160,57 @@ test('returns values from theme object', t => {
margin: 'sm',
})

t.deepEqual(styles, [{ margin: '1px' }])
t.deepEqual(styles, { margin: '1px' })
})

test('pl prop sets paddingLeft', t => {
const styles = space({ pl: 2 })
t.deepEqual(styles, [{ paddingLeft: '8px' }])
t.deepEqual(styles, { paddingLeft: '8px' })
})

test('pl prop sets paddingLeft 0', t => {
const styles = space({ pl: 0 })
t.deepEqual(styles, [{ paddingLeft: 0 }])
t.deepEqual(styles, { paddingLeft: 0 })
})

test('px prop overrides pl prop', t => {
const styles = space({
pl: 1,
px: 2,
})
t.deepEqual(styles, [{ paddingLeft: '8px' }, { paddingRight: '8px' }])
t.deepEqual(styles, { paddingLeft: '8px', paddingRight: '8px' })
})

test('py prop overrides pb prop', t => {
const styles = space({
pb: 1,
py: 2,
})
t.deepEqual(styles, [{ paddingTop: '8px' }, { paddingBottom: '8px' }])
t.deepEqual(styles, { paddingTop: '8px', paddingBottom: '8px' })
})

test('mx prop overrides mr prop', t => {
const styles = space({
mr: 1,
mx: 2,
})
t.deepEqual(styles, [{ marginLeft: '8px' }, { marginRight: '8px' }])
t.deepEqual(styles, { marginLeft: '8px', marginRight: '8px' })
})

test('my prop overrides mt prop', t => {
const styles = space({
mt: 1,
my: 2,
})
t.deepEqual(styles, [{ marginTop: '8px' }, { marginBottom: '8px' }])
t.deepEqual(styles, { marginTop: '8px', marginBottom: '8px' })
})

test('margin overrides m prop', t => {
const styles = space({
m: 1,
margin: 2,
})
t.deepEqual(styles, [{ margin: '8px' }])
t.deepEqual(styles, { margin: '8px' })
})

test('space includes propTypes', t => {
Expand All @@ -225,7 +223,7 @@ test('size returns width and height', t => {
const styles = size({
size: 4,
})
t.deepEqual(styles, [{ width: '4px' }, { height: '4px' }])
t.deepEqual(styles, { width: '4px', height: '4px' })
})

// grid
Expand Down Expand Up @@ -332,10 +330,10 @@ test('borders prop returns correct sequence', t => {
borderStyle: 'dashed',
borderColor: 'red',
})
t.deepEqual(a, [
{ borderBottom: '1px solid' },
{ borderWidth: '2px' },
{ borderStyle: 'dashed' },
{ borderColor: 'red' },
])
t.deepEqual(a, {
borderBottom: '1px solid',
borderWidth: '2px',
borderStyle: 'dashed',
borderColor: 'red',
})
})

0 comments on commit 67fc9a2

Please sign in to comment.