Skip to content

Commit

Permalink
Implement injectStyled, resolve #4
Browse files Browse the repository at this point in the history
  • Loading branch information
lttb committed Apr 22, 2017
1 parent 0ad79df commit 3a90aa3
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 39 deletions.
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ parser: babel-eslint

env:
jest: true

globals:
ReactClass: true
82 changes: 44 additions & 38 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
import {PureComponent, createElement} from 'react'
import {create as createJss, getDynamicStyles} from 'jss'
import preset from 'jss-preset-default'

import filterProps from './utils/filter-props'
import composeClasses from './utils/compose-classes'
import type {
StyledElementAttrsType,
StyledElementType,
tagOrStyledElementTypeype,
StyledElementPropsType
} from './types'

const jssDefault = createJss(preset())

type StyledElementAttrsType = { tag: string, styles: Object }
type StyledElementType = Function & StyledElementAttrsType
type tagOrStyledElementTypeype = string | StyledElementType
type StyledElementPropsType = {
classes: Object,
children: ?any,
className: ?string,
}

const createStyled = (jss?: Function = jssDefault) => (baseStyles: Object = {}) => {
let sheet
let dynamicSheet
const sheets = {}
let counter = 0

const mountSheets = () => {
if (!sheets.staticSheet) {
sheets.staticSheet = jss.createStyleSheet(baseStyles, {
link: true,
meta: 'StaticBaseSheet',
}).attach()

sheets.dynamicSheet = jss.createStyleSheet({}, {
link: true,
meta: 'DynamicComponentSheet',
}).attach()
}
}

const styled = (
tagOrStyledElement: tagOrStyledElementTypeype,
ownStyles: Object
Expand Down Expand Up @@ -46,62 +58,54 @@ const createStyled = (jss?: Function = jssDefault) => (baseStyles: Object = {})
}

componentWillMount() {
if (!sheet) {
sheet = jss.createStyleSheet(baseStyles, {
link: true,
meta: 'StaticBaseSheet',
}).attach()

dynamicSheet = jss.createStyleSheet({}, {
link: true,
meta: 'DynamicComponentSheet',
}).attach()
}
mountSheets()

if (!sheet.getRule(staticTag)) {
sheet.addRule(staticTag, elementStyles)
if (!sheets.staticSheet.getRule(staticTag)) {
sheets.staticSheet.addRule(staticTag, elementStyles)
}

if (dynamicStyles && !dynamicSheet.getRule(this.tagScoped)) {
dynamicSheet.addRule(this.tagScoped, dynamicStyles)
dynamicSheet
if (dynamicStyles && !sheets.dynamicSheet.getRule(this.tagScoped)) {
sheets.dynamicSheet.addRule(this.tagScoped, dynamicStyles)
sheets.dynamicSheet
.update(this.tagScoped, this.props)
.deploy()
}
}

componentWillReceiveProps(nextProps: StyledElementPropsType) {
if (dynamicStyles) {
dynamicSheet
sheets.dynamicSheet
.update(this.tagScoped, nextProps)
.deploy()
}
}

componentWillUnmount() {
dynamicSheet.deleteRule(this.tagScoped)
sheets.dynamicSheet.deleteRule(this.tagScoped)
}

render() {
if (!sheet) return null
if (!sheets.staticSheet) return null

const {children, className, ...attrs} = this.props

const props = filterProps(attrs)
const tagClass = [
sheet.classes[staticTag],
dynamicSheet.classes[this.tagScoped],
className,
]
.filter(Boolean)
.join(' ')
const tagClass = composeClasses(
sheets.staticSheet.classes[staticTag],
sheets.dynamicSheet.classes[this.tagScoped],
className
)

return createElement(tag, {...props, className: tagClass}, children)
}
}
}

return Object.assign(styled, {styles: baseStyles})
return Object.assign(styled, {
sheets,
mountSheets,
styles: baseStyles
})
}

const defaultStyledCreator = createStyled()
Expand All @@ -113,3 +117,5 @@ export {
}

export default defaultStyled

export {default as injectStyled} from './injectStyled'
26 changes: 26 additions & 0 deletions src/injectStyled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {createElement} from 'react'

import composeClasses from './utils/compose-classes'
import type {styledType} from './types'

const injectStyled = (styled: styledType) => (InnerComponent: ReactClass<any>) => {
styled.mountSheets()

const {sheets} = styled
const {staticSheet, dynamicSheet} = sheets

const classNames = new Set([
...Object.keys(staticSheet.classes),
...Object.keys(dynamicSheet.classes)
])

const classes = [...classNames]
.reduce((acc, name) => ({
...acc,
[name]: composeClasses(staticSheet.classes[name], dynamicSheet.classes[name]),
}), {})

return (...props: any) => createElement(InnerComponent, {sheets, classes, ...props})
}

export default injectStyled
31 changes: 31 additions & 0 deletions src/tests/__snapshots__/index.spec.jsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,34 @@ exports[`test renders correctly App with default styled 1`] = `
</section>
</div>
`;

exports[`test renders correctly App with injectStyled 1`] = `
<div
className="root-0-17">
<div
className="div-1-0-19">
<header
className="header-2-0-20">
<h1
className="h1-5-0-21">
Title
</h1>
</header>
<section
className="section-3-0-22">
<button
className="button-6-0-23 button-11-0-24">
primitive test
</button>
<button
className="button-6-0-23 button-12-0-25">
dynamic primitive test
</button>
</section>
<section
className="section-4-0-26">
Another section
</section>
</div>
</div>
`;
21 changes: 20 additions & 1 deletion src/tests/index.spec.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import renderer from 'react-test-renderer'
import React from 'react'

import styled, {Styled} from '../'
import styled, {Styled, injectStyled} from '../'

import CreateApp from './App'

Expand All @@ -25,3 +25,22 @@ it('renders correctly App with default Styled', () => {

expect(tree).toMatchSnapshot()
})

it('renders correctly App with injectStyled', () => {
const customStyled = Styled({
root: {
fontSize: 16
},
baseButton: {
color: 'red',
},
})

const App = CreateApp(customStyled)
const StyledApp = injectStyled(customStyled)(({classes}) => (
<div className={classes.root}><App /></div>
))
const tree = renderer.create(<StyledApp />).toJSON()

expect(tree).toMatchSnapshot()
})
17 changes: 17 additions & 0 deletions src/types/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export type styledType = {
sheets: {
// TODO: use types from jss
staticSheet: Object,
dynamicSheet: Object,
},
mountSheets: Function,
styles: Object
}
export type StyledElementAttrsType = {tag: string, styles: Object}
export type StyledElementType = Function & StyledElementAttrsType
export type tagOrStyledElementTypeype = string | StyledElementType
export type StyledElementPropsType = {
classes: Object,
children: ?any,
className: ?string,
}
1 change: 1 addition & 0 deletions src/utils/compose-classes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default (...args: any) => args.filter(Boolean).join(' ')

0 comments on commit 3a90aa3

Please sign in to comment.