Skip to content

Commit

Permalink
feat: introduce createSystemComponent to avoid undesired HTML attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
gregberge committed Jul 8, 2019
1 parent a4fec55 commit 3187f68
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 23 deletions.
24 changes: 6 additions & 18 deletions packages/styled-components/src/styled.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import React from 'react'
import scStyled from 'styled-components'
import { createBox } from '@xstyled/core'
import { createSystemComponent } from '@xstyled/system'
import { css } from './css'

function getCreateStyle(baseCreateStyle) {
Expand All @@ -21,28 +22,15 @@ export function styled(component) {
return getCreateStyle(scStyled(component))
}

function omit(object, values) {
const result = {}
// eslint-disable-next-line no-restricted-syntax
for (const key in object) {
if (values.indexOf(key) === -1) {
result[key] = object[key]
}
}
return result
}

const createBoxComponent = component => ({ as, ...props }) => {
const omittedProps = omit(props, createBox.meta.props)
const Component = as || component
return <Component {...omittedProps} />
}
const InnerBox = createSystemComponent(React.createElement)

export const Box = styled(createBoxComponent('div'))(createBox)
export const Box = styled(InnerBox)(createBox)

styled.box = styled(Box)

Object.keys(scStyled).forEach(key => {
styled[key] = styled(key)
styled[`${key}Box`] = styled(Box.withComponent(createBoxComponent(key)))
styled[`${key}Box`] = styled(
Box.withComponent(createSystemComponent(React.createElement, key)),
)
})
2 changes: 1 addition & 1 deletion packages/system/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export * from './th'
export * from './variant'
export * from './breakpoints'
export { getBreakpoints } from './media'
export { merge } from './util'
export { createSystemComponent } from './systemComponent'
16 changes: 16 additions & 0 deletions packages/system/src/systemComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { omit } from './util'
import { system as allSystem } from './styles/index'

export const createSystemComponent = (
createElement,
defaultComponent = 'div',
system = allSystem,
) => {
function SystemComponent({ as, ...props }) {
const omittedProps = omit(props, system.meta.props)
const Component = as || defaultComponent
return createElement(Component, omittedProps)
}
SystemComponent.displayName = 'SystemComponent'
return SystemComponent
}
24 changes: 24 additions & 0 deletions packages/system/src/systemComponent.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react'
import 'jest-dom/extend-expect'
import { render, cleanup } from '@testing-library/react'
import { createSystemComponent } from './systemComponent'

afterEach(cleanup)

describe('systemComponent', () => {
describe('#createSystemComponent', () => {
it('creates a div that omit props', () => {
const Box = createSystemComponent(React.createElement)
const { container } = render(<Box display="block" />)
expect(container.firstChild.tagName).toBe('DIV')
expect(container.firstChild).not.toHaveAttribute('display')
})

it('supports "as" prop', () => {
const Box = createSystemComponent(React.createElement)
const { container } = render(<Box as="header" display="block" />)
expect(container.firstChild.tagName).toBe('HEADER')
expect(container.firstChild).not.toHaveAttribute('display')
})
})
})
11 changes: 11 additions & 0 deletions packages/system/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,14 @@ export const cascade = (fn, ...args) => {

export const getThemeValue = (props, path, initial = props.theme) =>
cascade(get(initial, path), props)

export function omit(object, values) {
const result = {}
// eslint-disable-next-line no-restricted-syntax
for (const key in object) {
if (values.indexOf(key) === -1) {
result[key] = object[key]
}
}
return result
}
7 changes: 7 additions & 0 deletions packages/system/src/util.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
assign,
cascade,
getThemeValue,
omit,
} from './util'

describe('util', () => {
Expand Down Expand Up @@ -152,4 +153,10 @@ describe('util', () => {
).toBe('x')
})
})

describe('#omit', () => {
it('omits values', () => {
expect(omit({ a: 'x', b: 'y' }, ['b'])).toEqual({ a: 'x' })
})
})
})
12 changes: 8 additions & 4 deletions website/src/pages/docs/system-components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const ABox = styled.aBox`

## Use `as` property

The `as` property is also supported, exactly like in `styled-components`.
On all `Box` components, use `forwardedAs` property instead of `as` to avoid undesired attributes on HTML element.

```js
import styled from '@xstyled/styled-components'
Expand All @@ -59,18 +59,22 @@ const RedBox = styled.box`
`

/* Replace `div` by `p`, but it is still a box! */
<RedBox as="p" m={2} />
<RedBox forwardedAs="p" m={2} />
```

## Create your own box

If you don't want to use `@xstyled/styled-components`, you can create your own box component:

```js
import React from 'react'
import styled from 'styled-components'
import { system } from '@xstyled/system'
import { system, createSystemComponent } from '@xstyled/system'

const Box = styled.div(system)
const InnerBox = createSystemComponent(React.createElement)
const Box = styled(InnerBox)(system)
```

> We use `createSystemComponent` to avoid undesired attributes on HTML element.
See all available [system prop utilities](/docs/system-props/).
26 changes: 26 additions & 0 deletions website/src/pages/docs/system-props.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,32 @@ const Box = styled.div`
<Box color="primary" backgroundColor="gray" />
```

## Avoid undesired props

With `styled-components`, all props are forwarded, to avoid that behaviour, use `createSystemComponent` method, it omits all properties.

```js
import React from 'react'
import { compose, color, backgroundColor, createSystemComponent } from '@xstyled/system'

const colors = compose(
color,
backgroundColor,
)

const InnerBox = createSystemComponent(React.createElement, 'div', colors)

const Box = styled(InnerBox)`
${colors}
`

// color and backgroundColor are available
<Box color="primary" backgroundColor="gray" />

// use forwardedAs instead of as to keep using the system component
<Box forwardedAs="header" color="primary" backgroundColor="gray" />
```

## Space

The space utility converts shorthand margin and padding props to margin and padding CSS declarations.
Expand Down

0 comments on commit 3187f68

Please sign in to comment.