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

Add support for combining multiple variants #586

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Adjust media query sort logic #600
- Fixed link to Gatsby Plugin page in `getting-started` page. Issue #602
- Adds support for combining multiple variants (pass `variants` an array in `sx` or as a prop on components)

## v0.3.0 2019-01-22

Expand Down
3 changes: 2 additions & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"@styled-system/color": "^5.1.2",
"@styled-system/should-forward-prop": "^5.1.2",
"@styled-system/space": "^5.1.2",
"@theme-ui/css": "^0.3.1"
"@theme-ui/css": "^0.3.1",
"deepmerge": "^4.2.2"
},
"peerDependencies": {
"react": "^16.8.0"
Expand Down
11 changes: 11 additions & 0 deletions packages/components/src/Box.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { css, get } from '@theme-ui/css'
import { createShouldForwardProp } from '@styled-system/should-forward-prop'
import space from '@styled-system/space'
import color from '@styled-system/color'
import deepmerge from 'deepmerge'

const shouldForwardProp = createShouldForwardProp([
...space.propNames,
Expand All @@ -11,8 +12,17 @@ const shouldForwardProp = createShouldForwardProp([

const sx = props => css(props.sx)(props.theme)
const base = props => css(props.__css)(props.theme)

const variant = ({ theme, variant, __themeKey = 'variants' }) =>
css(get(theme, __themeKey + '.' + variant, get(theme, variant)))
const variants = ({ theme, variants = [], __themeKey = 'variants' }) => {
if (!Array.isArray(variants)) return {}
return css(
deepmerge.all(
variants.map(v => get(theme, __themeKey + '.' + v, get(theme, v)))
)
)
}

export const Box = styled('div', {
shouldForwardProp,
Expand All @@ -23,6 +33,7 @@ export const Box = styled('div', {
minWidth: 0,
},
base,
variants,
variant,
space,
color,
Expand Down
12 changes: 12 additions & 0 deletions packages/components/test/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,18 @@ exports[`Box renders 1`] = `
</div>
`;

exports[`Box renders with incorrect type of variants prop 1`] = `
.emotion-0 {
box-sizing: border-box;
margin: 0;
min-width: 0;
}

<div
className="emotion-0"
/>
`;

exports[`Button renders 1`] = `
.emotion-0 {
box-sizing: border-box;
Expand Down
36 changes: 36 additions & 0 deletions packages/components/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ const theme = {
p: 4,
bg: 'highlight',
},
boop: {
bg: 'tomato',
color: 'white',
},
},
cards: {
primary: {
Expand Down Expand Up @@ -132,6 +136,38 @@ describe('Box', () => {
expect(json).toHaveStyleRule('padding', '32px')
})

test('renders with variants prop', () => {
const json = renderJSON(
<ThemeProvider theme={theme}>
<Box variants={['boxes.beep', 'boxes.boop']} />
</ThemeProvider>
)
expect(json).toHaveStyleRule('padding', '32px')
expect(json).toHaveStyleRule('background-color', 'tomato')
expect(json).toHaveStyleRule('color', 'white')
})

test('renders with variant and variants prop', () => {
const json = renderJSON(
<ThemeProvider theme={theme}>
<Box variant="text.heading" variants={['boxes.beep', 'boxes.boop']} />
</ThemeProvider>
)
expect(json).toHaveStyleRule('font-size', '32px')
expect(json).toHaveStyleRule('padding', '32px')
expect(json).toHaveStyleRule('background-color', 'tomato')
expect(json).toHaveStyleRule('color', 'white')
})

test('renders with incorrect type of variants prop', () => {
const json = renderJSON(
<ThemeProvider theme={theme}>
<Box variants="boxes.beep" />
</ThemeProvider>
)
expect(json).toMatchSnapshot()
})

test('renders with base styles', () => {
const json = renderJSON(
<Box
Expand Down
3 changes: 3 additions & 0 deletions packages/css/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"prepare": "microbundle --no-compress",
"watch": "microbundle watch --no-compress"
},
"dependencies": {
"deepmerge": "^4.2.2"
},
"author": "Brent Jackson",
"license": "MIT",
"publishConfig": {
Expand Down
8 changes: 8 additions & 0 deletions packages/css/src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { all } from 'deepmerge'

export const get = (obj, key, def, p, undef) => {
key = key && key.split ? key.split('.') : [key]
for (p = 0; p < key.length; p++) {
Expand Down Expand Up @@ -191,6 +193,12 @@ export const css = args => (props = {}) => {
continue
}

if (key === 'variants') {
const variants = css(get(theme, val))(theme)
result = { ...result, ...variants }
continue
}

if (val && typeof val === 'object') {
result[key] = css(val)(theme)
continue
Expand Down
15 changes: 15 additions & 0 deletions packages/css/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,21 @@ test('handles responsive variants', () => {
})
})

test('returns multiple variants from theme', () => {
const result = css({
variants: ['text.caps', 'text.title'],
})(theme)
expect(result).toEqual({
fontSize: 16,
letterSpacing: '-0.01em',
textTransform: 'uppercase',
'@media screen and (min-width: 40em)': {
fontSize: 16,
letterSpacing: '-0.02em',
},
})
})

test('handles negative margins from scale', () => {
const result = css({
mt: -3,
Expand Down
11 changes: 11 additions & 0 deletions packages/docs/src/pages/components/variants.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ For example, a secondary button style can be added to `theme.buttons.secondary`
</Button>
```

## Combining Multiple Variants

If you want to combine multiple variants on the same element, pass an array of variants with the `variants` prop.
If styles conflict, those of the variant passed later will render.

```jsx live=true
<Text variants={['caps', 'heading']}>
Title text
</Text>
```

## Example Theme

```js
Expand Down
12 changes: 12 additions & 0 deletions packages/docs/src/pages/guides/variants.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ For example, you can define `primary` and `secondary` variants for buttons and u
color: 'white',
bg: 'secondary',
},
small: {
fontSize: 1
}
},
}
```
Expand All @@ -51,6 +54,15 @@ Variants can use any name you choose, and deeply nested objects can be accessed

</Note>

## Combining Multiple Variants

If you want to combine multiple variants on the same element, pass an array to `variants` in the `sx` prop.
If styles conflict, those of the variant passed later will render.

```jsx
<button sx={{ variants: ['buttons.primary', 'buttons.small'] }} />
```

## Color Modes

Variants will also work with [color modes](/color-modes).
Expand Down