diff --git a/README.md b/README.md
index 7635d32..356f1f0 100644
--- a/README.md
+++ b/README.md
@@ -162,10 +162,9 @@ The returned component accepts a `theme` and `composeTheme` apart from the prop
- `Identifier` *(String)* used to provide a unique identifier to the component that will be used to get a theme from context.
- `[defaultTheme]` (*Object*) is classname object resolved from CSS modules. It will be used as the default theme to calculate a new theme that will be passed to the component.
-- `[options]` (*Object*) is an option object that for now only accepts one value: `composeTheme` which accepts:
- - `deeply` to deeply merge themes.
- - `softly` to softly merge themes.
- - `false` to disable theme merging.
+- `[options]` (*Object*) If specified it allows to customize the behavior:
+ - [`composeTheme = 'deeply'`] *(String)* allows to customize the way themes are merged or to disable merging completely. The accepted values are `deeply` to deeply merge themes, `softly` to softly merge themes and `false` to disable theme merging.
+ - [`withRef = false`] *(Boolean)* if true, stores a ref to the wrapped component instance and makes it available via `getWrappedInstance()` method. Defaults to false.
## About
diff --git a/package.json b/package.json
index e9e6e24..ec4bf03 100644
--- a/package.json
+++ b/package.json
@@ -56,5 +56,8 @@
"license": "MIT",
"peerDependencies": {
"react": "^0.14.0 || ^15.0.0-0"
+ },
+ "dependencies": {
+ "invariant": "^2.2.1"
}
}
diff --git a/src/components/themr.js b/src/components/themr.js
index 0673342..1ea0ca7 100644
--- a/src/components/themr.js
+++ b/src/components/themr.js
@@ -1,15 +1,17 @@
import React, { Component, PropTypes } from 'react'
+import invariant from 'invariant'
const COMPOSE_DEEPLY = 'deeply'
const COMPOSE_SOFTLY = 'softly'
const DONT_COMPOSE = false
const DEFAULT_OPTIONS = {
- composeTheme: COMPOSE_DEEPLY
+ composeTheme: COMPOSE_DEEPLY,
+ withRef: false
}
-export default (componentName, localTheme, options = DEFAULT_OPTIONS) => (ThemedComponent) => {
- const { composeTheme: optionComposeTheme } = options
+export default (componentName, localTheme, options = {}) => (ThemedComponent) => {
+ const { composeTheme: optionComposeTheme, withRef: optionWithRef } = { ...DEFAULT_OPTIONS, ...options }
validateComposeOption(optionComposeTheme)
return class Themed extends Component {
static displayName = `Themed ${ThemedComponent.name}`;
@@ -27,6 +29,15 @@ export default (componentName, localTheme, options = DEFAULT_OPTIONS) => (Themed
composeTheme: optionComposeTheme
}
+ getWrappedInstance() {
+ invariant(optionWithRef,
+ 'To access the wrapped instance, you need to specify ' +
+ '{ withRef: true } as the third argument of the themr() call.'
+ )
+
+ return this.refs.wrappedInstance
+ }
+
getThemeNotComposed() {
if (this.props.theme) return this.props.theme
if (localTheme) return localTheme
@@ -47,12 +58,26 @@ export default (componentName, localTheme, options = DEFAULT_OPTIONS) => (Themed
render() {
const { composeTheme, ...rest } = this.props
- return React.createElement(ThemedComponent, {
- ...rest,
- theme: composeTheme
- ? this.getTheme()
- : this.getThemeNotComposed()
- })
+ let renderedElement
+
+ if (optionWithRef) {
+ renderedElement = React.createElement(ThemedComponent, {
+ ...rest,
+ ref: 'wrappedInstance',
+ theme: composeTheme
+ ? this.getTheme()
+ : this.getThemeNotComposed()
+ })
+ } else {
+ renderedElement = React.createElement(ThemedComponent, {
+ ...rest,
+ theme: composeTheme
+ ? this.getTheme()
+ : this.getThemeNotComposed()
+ })
+ }
+
+ return renderedElement
}
}
}
diff --git a/test/components/themr.spec.js b/test/components/themr.spec.js
index 0990fc7..1114292 100644
--- a/test/components/themr.spec.js
+++ b/test/components/themr.spec.js
@@ -273,4 +273,55 @@ describe('Themr decorator function', () => {
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough)
expect(stub.props.theme).toEqual({})
})
+
+ it('should throw when trying to access the wrapped instance if withRef is not specified', () => {
+ const theme = { Container: { foo: 'foo_1234' } }
+
+ @themr('Container')
+ class Container extends Component {
+ render() {
+ return
+ }
+ }
+
+ const tree = TestUtils.renderIntoDocument(
+
+
+
+ )
+
+ const container = TestUtils.findRenderedComponentWithType(tree, Container)
+ expect(() => container.getWrappedInstance()).toThrow(
+ /To access the wrapped instance, you need to specify \{ withRef: true \} as the third argument of the themr\(\) call\./
+ )
+ })
+
+ it('should return the instance of the wrapped component for use in calling child methods', () => {
+ const someData = {
+ some: 'data'
+ }
+
+ class Container extends Component {
+ someInstanceMethod() {
+ return someData
+ }
+
+ render() {
+ return
+ }
+ }
+
+ const decorator = themr('Component', null, { withRef: true })
+ const Decorated = decorator(Container)
+
+ const tree = TestUtils.renderIntoDocument(
+
+ )
+
+ const decorated = TestUtils.findRenderedComponentWithType(tree, Decorated)
+
+ expect(() => decorated.someInstanceMethod()).toThrow()
+ expect(decorated.getWrappedInstance().someInstanceMethod()).toBe(someData)
+ expect(decorated.refs.wrappedInstance.someInstanceMethod()).toBe(someData)
+ })
})