Skip to content

Commit 6143221

Browse files
Add namespace support
1 parent 2bf1595 commit 6143221

File tree

2 files changed

+62
-4
lines changed

2 files changed

+62
-4
lines changed

src/components/themr.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
2222

2323
static propTypes = {
2424
composeTheme: PropTypes.oneOf([ COMPOSE_DEEPLY, COMPOSE_SOFTLY, DONT_COMPOSE ]),
25-
theme: PropTypes.object
25+
theme: PropTypes.object,
26+
namespace: PropTypes.string
2627
}
2728

2829
static defaultProps = {
@@ -38,8 +39,19 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
3839
return this.refs.wrappedInstance
3940
}
4041

42+
getNamespacedTheme() {
43+
const { namespace, theme } = this.props
44+
if (!namespace) return theme
45+
if (namespace && !theme) throw new Error('Invalid namespace use in react-css-themr. ' +
46+
'Namespace prop should be used only with theme prop.')
47+
48+
return Object.keys(theme)
49+
.filter(key => key.startsWith(namespace))
50+
.reduce((p, c) => ({ ...p, [removeNamespace(c, namespace)]: theme[c] }), {})
51+
}
52+
4153
getThemeNotComposed() {
42-
if (this.props.theme) return this.props.theme
54+
if (this.props.theme) return this.getNamespacedTheme()
4355
if (localTheme) return localTheme
4456
return this.getContextTheme()
4557
}
@@ -52,8 +64,8 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
5264

5365
getTheme() {
5466
return this.props.composeTheme === COMPOSE_SOFTLY
55-
? Object.assign({}, this.getContextTheme(), localTheme, this.props.theme)
56-
: themeable(themeable(this.getContextTheme(), localTheme), this.props.theme)
67+
? Object.assign({}, this.getContextTheme(), localTheme, this.getNamespacedTheme())
68+
: themeable(themeable(this.getContextTheme(), localTheme), this.getNamespacedTheme())
5769
}
5870

5971
render() {
@@ -101,3 +113,8 @@ function validateComposeOption(composeTheme) {
101113
)
102114
}
103115
}
116+
117+
function removeNamespace(key, namespace) {
118+
const capitilized = key.substr(namespace.length)
119+
return capitilized.slice(0, 1).toLowerCase() + capitilized.slice(1)
120+
}

test/components/themr.spec.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,45 @@ describe('Themr decorator function', () => {
324324
expect(decorated.getWrappedInstance().someInstanceMethod()).toBe(someData)
325325
expect(decorated.refs.wrappedInstance.someInstanceMethod()).toBe(someData)
326326
})
327+
328+
it('should throw if namespace passed without theme', () => {
329+
const theme = { Container: { foo: 'foo_1234' } }
330+
331+
@themr('Container')
332+
class Container extends Component {
333+
render() {
334+
return <Passthrough {...this.props} />
335+
}
336+
}
337+
338+
expect(() => TestUtils.renderIntoDocument(
339+
<ProviderMock theme={theme}>
340+
<Container namespace="container"/>
341+
</ProviderMock>
342+
)).toThrow(/Invalid namespace use in react-css-themr. Namespace prop should be used only with theme prop./)
343+
})
344+
345+
it('when providing a namespace prop composes a theme', () => {
346+
const containerTheme = { foo: 'foo_123' }
347+
const containerThemeLocal = { foo: 'foo_567' }
348+
const containerThemeProps = { foo: 'foo_89', containerFoo: 'foo_000' }
349+
const theme = { Container: containerTheme }
350+
351+
@themr('Container', containerThemeLocal)
352+
class Container extends Component {
353+
render() {
354+
return <Passthrough {...this.props} />
355+
}
356+
}
357+
358+
const tree = TestUtils.renderIntoDocument(
359+
<ProviderMock theme={theme}>
360+
<Container theme={containerThemeProps} namespace="container" />
361+
</ProviderMock>
362+
)
363+
364+
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough)
365+
const expectedTheme = { foo: 'foo_123 foo_567 foo_000' }
366+
expect(stub.props.theme).toEqual(expectedTheme)
367+
})
327368
})

0 commit comments

Comments
 (0)