Skip to content

Commit 1717497

Browse files
authored
Merge pull request #45 from cssinjs/feature/compose-react-components
Resolve #39, support React Components composing
2 parents 319c17d + c3577a6 commit 1717497

5 files changed

Lines changed: 161 additions & 58 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ const PrimaryButton = styled(Button)({
3535
color: 'red'
3636
})
3737

38+
// Composition with unstyled React Components too.
39+
const Button = styled(UnstyledButton)({
40+
color: 'blue'
41+
})
42+
3843
// Component Selectors.
3944
const ButtonContainer = styled(Container)({
4045
[`& ${PrimaryButton}`]: {

src/createStyled.js

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,10 @@ import styled from './styled'
22

33
import type {
44
BaseStylesType,
5-
ComponentStyleType,
65
StyledType,
7-
StyledElementAttrsType,
86
StyledElementType,
9-
TagNameOrStyledElementType
107
} from './types'
118

12-
const getStyledArgs = (
13-
tagNameOrStyledElement: TagNameOrStyledElementType
14-
): StyledElementAttrsType => {
15-
if (typeof tagNameOrStyledElement === 'string') {
16-
return {tagName: tagNameOrStyledElement, style: {}}
17-
}
18-
19-
const {tagName, style} = tagNameOrStyledElement
20-
return {tagName, style}
21-
}
22-
239
const createStyled = (jss: Function) => (baseStyles: BaseStylesType = {}): StyledType => {
2410
let sheet
2511

@@ -34,16 +20,9 @@ const createStyled = (jss: Function) => (baseStyles: BaseStylesType = {}): Style
3420
return sheet
3521
}
3622

37-
const styledWrapper = (
38-
tagNameOrStyledElement: TagNameOrStyledElementType
39-
) => (
40-
ownStyle: ComponentStyleType
41-
): StyledElementType => {
42-
const {tagName, style} = getStyledArgs(tagNameOrStyledElement)
43-
const elementStyle = {...style, ...ownStyle}
44-
45-
return styled({tagName, baseStyles, elementStyle, mountSheet, jss})
46-
}
23+
const styledWrapper = element =>
24+
(ownStyle): StyledElementType =>
25+
styled({element, ownStyle, mountSheet, jss})
4726

4827
Object.defineProperty(styledWrapper, 'sheet', ({
4928
get: () => sheet,

src/styled.js

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,46 @@ import getSeparatedStyles from './utils/getSeparatedStyles'
77

88
import type {
99
JssSheet,
10+
TagNameOrStyledElementType,
1011
ComponentStyleType,
1112
StyledElementPropsType
1213
} from './types'
1314

15+
type Comp = Function & Component<*>
16+
1417
type StyledArgs = {
15-
tagName: string,
16-
elementStyle: ComponentStyleType,
18+
element: TagNameOrStyledElementType | Comp,
19+
ownStyle: ComponentStyleType,
1720
mountSheet: Function,
1821
jss: Function
1922
}
2023

21-
const styled = ({tagName, elementStyle, mountSheet, jss}: StyledArgs) => {
24+
const getElementName = (element: Comp): string =>
25+
element.displayName || element.name || 'StyledElement'
26+
27+
const getParamsByElement = (element) => {
28+
if (typeof element === 'string') return {tagName: element}
29+
if (element.tagName) return element
30+
31+
return {
32+
tagName: getElementName(element),
33+
reactComponent: element
34+
}
35+
}
36+
37+
const styled = ({element, ownStyle, mountSheet, jss}: StyledArgs) => {
38+
const {
39+
style,
40+
tagName,
41+
reactComponent = tagName
42+
}: {
43+
style?: ComponentStyleType,
44+
tagName: string,
45+
reactComponent?: string | typeof element
46+
} = getParamsByElement(element)
47+
48+
const elementStyle = {...style, ...ownStyle}
49+
2250
const {dynamicStyle, staticStyle} = getSeparatedStyles(elementStyle)
2351
const staticTagName = staticStyle && generateTagName(tagName)
2452

@@ -94,7 +122,7 @@ const styled = ({tagName, elementStyle, mountSheet, jss}: StyledArgs) => {
94122
className
95123
])
96124

97-
return createElement(tagName, {...props, className: tagClass}, children)
125+
return createElement(reactComponent, {...props, className: tagClass}, children)
98126
}
99127
}
100128

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

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,65 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`functional tests Compose React Components should use .displayName 1`] = `
4+
<StyledElement>
5+
<TestDisplayName
6+
className="TestDisplayName-1-id"
7+
>
8+
<h1
9+
className="TestDisplayName-1-id"
10+
>
11+
test
12+
</h1>
13+
</TestDisplayName>
14+
</StyledElement>
15+
`;
16+
17+
exports[`functional tests Compose React Components should use .displayName 2`] = `
18+
".TestDisplayName-1-id {
19+
padding: 10px;
20+
}"
21+
`;
22+
23+
exports[`functional tests Compose React Components should use .name 1`] = `
24+
<StyledElement>
25+
<Test
26+
className="Test-1-id"
27+
>
28+
<h1
29+
className="Test-1-id"
30+
>
31+
test
32+
</h1>
33+
</Test>
34+
</StyledElement>
35+
`;
36+
37+
exports[`functional tests Compose React Components should use .name 2`] = `
38+
".Test-1-id {
39+
padding: 10px;
40+
}"
41+
`;
42+
43+
exports[`functional tests Compose React Components should use .name 3`] = `
44+
<StyledElement>
45+
<Component
46+
className="StyledElement-1-id"
47+
>
48+
<h1
49+
className="StyledElement-1-id"
50+
>
51+
test
52+
</h1>
53+
</Component>
54+
</StyledElement>
55+
`;
56+
57+
exports[`functional tests Compose React Components should use .name 4`] = `
58+
".StyledElement-1-id {
59+
padding: 30px;
60+
}"
61+
`;
62+
363
exports[`functional tests should update dynamic props for conditional rules 1`] = `
464
".button-1-id {
565
padding: 10px;
@@ -101,6 +161,32 @@ exports[`functional tests should update props and unmount 2`] = `
101161
`;
102162

103163
exports[`functional tests should use Styled Component classname in string 1`] = `
164+
<Component>
165+
<StyledElement>
166+
<div
167+
className="div-4-id"
168+
>
169+
<StyledElement>
170+
<div
171+
className=".static-2-id div-1-id"
172+
>
173+
name
174+
</div>
175+
</StyledElement>
176+
<StyledElement
177+
width={30}
178+
>
179+
<img
180+
className=".static-3-id img-5-id"
181+
width={30}
182+
/>
183+
</StyledElement>
184+
</div>
185+
</StyledElement>
186+
</Component>
187+
`;
188+
189+
exports[`functional tests should use Styled Component classname in string 2`] = `
104190
".div-4-id:not(:first-child) .static-2-id {
105191
display: none;
106192
}
@@ -118,30 +204,6 @@ exports[`functional tests should use Styled Component classname in string 1`] =
118204
}"
119205
`;
120206

121-
exports[`functional tests should use Styled Component classname in string 2`] = `
122-
<StyledElement>
123-
<div
124-
className="div-4-id"
125-
>
126-
<StyledElement>
127-
<div
128-
className=".static-2-id div-1-id"
129-
>
130-
name
131-
</div>
132-
</StyledElement>
133-
<StyledElement
134-
width={30}
135-
>
136-
<img
137-
className=".static-3-id img-5-id"
138-
width={30}
139-
/>
140-
</StyledElement>
141-
</div>
142-
</StyledElement>
143-
`;
144-
145207
exports[`functional tests should use props on remount 1`] = `
146208
".button-1-id {
147209
color: black;

src/tests/functional.spec.jsx

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ const assertSheet = (sheet) => {
3434
expect(getCss(sheet)).toBe(removeWhitespace(sheet.toString()))
3535
}
3636

37+
const assertComponent = (Comp) => {
38+
const wrapper = mount(<Comp />)
39+
expect(wrapper).toMatchSnapshot()
40+
wrapper.unmount()
41+
}
42+
3743
describe('functional tests', () => {
3844
beforeEach(() => {
3945
mockNameGenerators()
@@ -151,19 +157,42 @@ describe('functional tests', () => {
151157
}
152158
})
153159

154-
const wrapper = mount(
160+
assertComponent(() => (
155161
<Message>
156162
<AuthorName>name</AuthorName>
157163
<Avatar width={30} />
158164
</Message>
159-
)
165+
))
160166

161-
const {sheet} = styled
167+
assertSheet(styled.sheet)
168+
})
162169

163-
assertSheet(sheet)
170+
describe('Compose React Components', () => {
171+
it('should use .name', () => {
172+
const Test = props => <h1 {...props}>test</h1>
173+
const StyledTest = styled(Test)({
174+
padding: 10,
175+
})
176+
assertComponent(StyledTest)
177+
assertSheet(styled.sheet)
178+
})
164179

165-
expect(wrapper).toMatchSnapshot()
180+
it('should use .displayName', () => {
181+
const Test = props => <h1 {...props}>test</h1>
182+
Test.displayName = 'TestDisplayName'
183+
const StyledTest = styled(Test)({
184+
padding: 10,
185+
})
186+
assertComponent(StyledTest)
187+
assertSheet(styled.sheet)
188+
})
166189

167-
wrapper.unmount()
190+
it('should use .name', () => {
191+
const StyledTest = styled(props => <h1 {...props}>test</h1>)({
192+
padding: 30,
193+
})
194+
assertComponent(StyledTest)
195+
assertSheet(styled.sheet)
196+
})
168197
})
169198
})

0 commit comments

Comments
 (0)