Skip to content

Commit 3a90aa3

Browse files
committed
Implement injectStyled, resolve #4
1 parent 0ad79df commit 3a90aa3

7 files changed

Lines changed: 142 additions & 39 deletions

File tree

.eslintrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ parser: babel-eslint
33

44
env:
55
jest: true
6+
7+
globals:
8+
ReactClass: true

src/index.js

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,36 @@
11
import {PureComponent, createElement} from 'react'
22
import {create as createJss, getDynamicStyles} from 'jss'
33
import preset from 'jss-preset-default'
4+
45
import filterProps from './utils/filter-props'
6+
import composeClasses from './utils/compose-classes'
7+
import type {
8+
StyledElementAttrsType,
9+
StyledElementType,
10+
tagOrStyledElementTypeype,
11+
StyledElementPropsType
12+
} from './types'
513

614
const jssDefault = createJss(preset())
715

8-
type StyledElementAttrsType = { tag: string, styles: Object }
9-
type StyledElementType = Function & StyledElementAttrsType
10-
type tagOrStyledElementTypeype = string | StyledElementType
11-
type StyledElementPropsType = {
12-
classes: Object,
13-
children: ?any,
14-
className: ?string,
15-
}
16-
1716
const createStyled = (jss?: Function = jssDefault) => (baseStyles: Object = {}) => {
18-
let sheet
19-
let dynamicSheet
17+
const sheets = {}
2018
let counter = 0
2119

20+
const mountSheets = () => {
21+
if (!sheets.staticSheet) {
22+
sheets.staticSheet = jss.createStyleSheet(baseStyles, {
23+
link: true,
24+
meta: 'StaticBaseSheet',
25+
}).attach()
26+
27+
sheets.dynamicSheet = jss.createStyleSheet({}, {
28+
link: true,
29+
meta: 'DynamicComponentSheet',
30+
}).attach()
31+
}
32+
}
33+
2234
const styled = (
2335
tagOrStyledElement: tagOrStyledElementTypeype,
2436
ownStyles: Object
@@ -46,62 +58,54 @@ const createStyled = (jss?: Function = jssDefault) => (baseStyles: Object = {})
4658
}
4759

4860
componentWillMount() {
49-
if (!sheet) {
50-
sheet = jss.createStyleSheet(baseStyles, {
51-
link: true,
52-
meta: 'StaticBaseSheet',
53-
}).attach()
54-
55-
dynamicSheet = jss.createStyleSheet({}, {
56-
link: true,
57-
meta: 'DynamicComponentSheet',
58-
}).attach()
59-
}
61+
mountSheets()
6062

61-
if (!sheet.getRule(staticTag)) {
62-
sheet.addRule(staticTag, elementStyles)
63+
if (!sheets.staticSheet.getRule(staticTag)) {
64+
sheets.staticSheet.addRule(staticTag, elementStyles)
6365
}
6466

65-
if (dynamicStyles && !dynamicSheet.getRule(this.tagScoped)) {
66-
dynamicSheet.addRule(this.tagScoped, dynamicStyles)
67-
dynamicSheet
67+
if (dynamicStyles && !sheets.dynamicSheet.getRule(this.tagScoped)) {
68+
sheets.dynamicSheet.addRule(this.tagScoped, dynamicStyles)
69+
sheets.dynamicSheet
6870
.update(this.tagScoped, this.props)
6971
.deploy()
7072
}
7173
}
7274

7375
componentWillReceiveProps(nextProps: StyledElementPropsType) {
7476
if (dynamicStyles) {
75-
dynamicSheet
77+
sheets.dynamicSheet
7678
.update(this.tagScoped, nextProps)
7779
.deploy()
7880
}
7981
}
8082

8183
componentWillUnmount() {
82-
dynamicSheet.deleteRule(this.tagScoped)
84+
sheets.dynamicSheet.deleteRule(this.tagScoped)
8385
}
8486

8587
render() {
86-
if (!sheet) return null
88+
if (!sheets.staticSheet) return null
8789

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

9092
const props = filterProps(attrs)
91-
const tagClass = [
92-
sheet.classes[staticTag],
93-
dynamicSheet.classes[this.tagScoped],
94-
className,
95-
]
96-
.filter(Boolean)
97-
.join(' ')
93+
const tagClass = composeClasses(
94+
sheets.staticSheet.classes[staticTag],
95+
sheets.dynamicSheet.classes[this.tagScoped],
96+
className
97+
)
9898

9999
return createElement(tag, {...props, className: tagClass}, children)
100100
}
101101
}
102102
}
103103

104-
return Object.assign(styled, {styles: baseStyles})
104+
return Object.assign(styled, {
105+
sheets,
106+
mountSheets,
107+
styles: baseStyles
108+
})
105109
}
106110

107111
const defaultStyledCreator = createStyled()
@@ -113,3 +117,5 @@ export {
113117
}
114118

115119
export default defaultStyled
120+
121+
export {default as injectStyled} from './injectStyled'

src/injectStyled.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {createElement} from 'react'
2+
3+
import composeClasses from './utils/compose-classes'
4+
import type {styledType} from './types'
5+
6+
const injectStyled = (styled: styledType) => (InnerComponent: ReactClass<any>) => {
7+
styled.mountSheets()
8+
9+
const {sheets} = styled
10+
const {staticSheet, dynamicSheet} = sheets
11+
12+
const classNames = new Set([
13+
...Object.keys(staticSheet.classes),
14+
...Object.keys(dynamicSheet.classes)
15+
])
16+
17+
const classes = [...classNames]
18+
.reduce((acc, name) => ({
19+
...acc,
20+
[name]: composeClasses(staticSheet.classes[name], dynamicSheet.classes[name]),
21+
}), {})
22+
23+
return (...props: any) => createElement(InnerComponent, {sheets, classes, ...props})
24+
}
25+
26+
export default injectStyled

src/tests/__snapshots__/index.spec.jsx.snap

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,34 @@ exports[`test renders correctly App with default styled 1`] = `
5353
</section>
5454
</div>
5555
`;
56+
57+
exports[`test renders correctly App with injectStyled 1`] = `
58+
<div
59+
className="root-0-17">
60+
<div
61+
className="div-1-0-19">
62+
<header
63+
className="header-2-0-20">
64+
<h1
65+
className="h1-5-0-21">
66+
Title
67+
</h1>
68+
</header>
69+
<section
70+
className="section-3-0-22">
71+
<button
72+
className="button-6-0-23 button-11-0-24">
73+
primitive test
74+
</button>
75+
<button
76+
className="button-6-0-23 button-12-0-25">
77+
dynamic primitive test
78+
</button>
79+
</section>
80+
<section
81+
className="section-4-0-26">
82+
Another section
83+
</section>
84+
</div>
85+
</div>
86+
`;

src/tests/index.spec.jsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import renderer from 'react-test-renderer'
22
import React from 'react'
33

4-
import styled, {Styled} from '../'
4+
import styled, {Styled, injectStyled} from '../'
55

66
import CreateApp from './App'
77

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

2626
expect(tree).toMatchSnapshot()
2727
})
28+
29+
it('renders correctly App with injectStyled', () => {
30+
const customStyled = Styled({
31+
root: {
32+
fontSize: 16
33+
},
34+
baseButton: {
35+
color: 'red',
36+
},
37+
})
38+
39+
const App = CreateApp(customStyled)
40+
const StyledApp = injectStyled(customStyled)(({classes}) => (
41+
<div className={classes.root}><App /></div>
42+
))
43+
const tree = renderer.create(<StyledApp />).toJSON()
44+
45+
expect(tree).toMatchSnapshot()
46+
})

src/types/index.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export type styledType = {
2+
sheets: {
3+
// TODO: use types from jss
4+
staticSheet: Object,
5+
dynamicSheet: Object,
6+
},
7+
mountSheets: Function,
8+
styles: Object
9+
}
10+
export type StyledElementAttrsType = {tag: string, styles: Object}
11+
export type StyledElementType = Function & StyledElementAttrsType
12+
export type tagOrStyledElementTypeype = string | StyledElementType
13+
export type StyledElementPropsType = {
14+
classes: Object,
15+
children: ?any,
16+
className: ?string,
17+
}

src/utils/compose-classes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default (...args: any) => args.filter(Boolean).join(' ')

0 commit comments

Comments
 (0)