Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge styles into single object #473

Merged
merged 5 commits into from May 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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',
})
})