Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 8b7dab8

Browse files
Add mapThemrProps option
1 parent b7fec0d commit 8b7dab8

File tree

3 files changed

+75
-21
lines changed

3 files changed

+75
-21
lines changed

‎README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,13 @@ Makes available a `theme` context to use in styled components. The shape of the
158158
159159
Returns a `function` to wrap a component and make it themeable.
160160

161-
The returned component accepts a `theme`, `composeTheme`and `innerRef` props apart from the props of the original component. They former two are used to provide a `theme` to the component and to configure the style composition, which can be configured via options too, while the latter is used to pass a ref callback to the decorated component. The function arguments are:
161+
The returned component accepts a `theme`, `composeTheme`, `innerRef` and `mapThemrProps` props apart from the props of the original component. They former two are used to provide a `theme` to the component and to configure the style composition, which can be configured via options too. `innerRef` is used to pass a ref callback to the decorated component and `mapThemrProps` is a function that can be used to map properties to the decorated component. The function arguments are:
162162

163163
- `Identifier` *(String)* used to provide a unique identifier to the component that will be used to get a theme from context.
164164
- `[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.
165165
- `[options]` (*Object*) If specified it allows to customize the behavior:
166166
- [`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.
167+
- [`mapThemrProps = (props, theme) => ({ ref, theme })`] *(Function)* allows to customize how properties are passed down to the decorated component. By default, themr extracts all own properties passing down just `innerRef` as `ref` and the generated theme as `theme`. If you are decorating a component that needs to map the reference or any other custom property, this function is called with *all* properties given to the component plus the generated `theme` in the second parameter. It should return the properties you want to pass.
167168
168169
## About
169170

‎src/components/themr.js‎

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ const COMPOSE_SOFTLY = 'softly'
1717
const DONT_COMPOSE = false
1818

1919
const DEFAULT_OPTIONS = {
20-
composeTheme: COMPOSE_DEEPLY
20+
composeTheme: COMPOSE_DEEPLY,
21+
mapThemrProps: defaultMapThemrProps
2122
}
2223

2324
const THEMR_CONFIG = typeof Symbol !== 'undefined' ?
@@ -32,7 +33,10 @@ const THEMR_CONFIG = typeof Symbol !== 'undefined' ?
3233
* @returns {function(ThemedComponent:Function):Function} - ThemedComponent
3334
*/
3435
export default (componentName, localTheme, options = {}) => (ThemedComponent) => {
35-
const { composeTheme: optionComposeTheme } = { ...DEFAULT_OPTIONS, ...options }
36+
const {
37+
composeTheme: optionComposeTheme,
38+
mapThemrProps: optionMapThemrProps
39+
} = { ...DEFAULT_OPTIONS, ...options }
3640
validateComposeOption(optionComposeTheme)
3741

3842
let config = ThemedComponent[THEMR_CONFIG]
@@ -61,12 +65,14 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
6165
composeTheme: PropTypes.oneOf([ COMPOSE_DEEPLY, COMPOSE_SOFTLY, DONT_COMPOSE ]),
6266
innerRef: PropTypes.func,
6367
theme: PropTypes.object,
64-
themeNamespace: PropTypes.string
68+
themeNamespace: PropTypes.string,
69+
mapThemrProps: PropTypes.func
6570
}
6671

6772
static defaultProps = {
6873
...ThemedComponent.defaultProps,
69-
composeTheme: optionComposeTheme
74+
composeTheme: optionComposeTheme,
75+
mapThemrProps: optionMapThemrProps
7076
}
7177

7278
constructor(...args) {
@@ -106,14 +112,6 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
106112
: {}
107113
}
108114

109-
getPropsForComponent() {
110-
//exclude themr-only props
111-
//noinspection JSUnusedLocalSymbols
112-
const { composeTheme, innerRef, themeNamespace, ...props } = this.props //eslint-disable-line no-unused-vars
113-
114-
return props
115-
}
116-
117115
getTheme(props) {
118116
return props.composeTheme === COMPOSE_SOFTLY
119117
? {
@@ -145,14 +143,10 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
145143
}
146144

147145
render() {
148-
const { innerRef } = this.props
149-
const props = this.getPropsForComponent()
150-
151-
return React.createElement(ThemedComponent, {
152-
...props,
153-
ref: innerRef,
154-
theme: this.theme_
155-
})
146+
return React.createElement(
147+
ThemedComponent,
148+
this.props.mapThemrProps(this.props, this.theme_)
149+
)
156150
}
157151
}
158152

@@ -283,3 +277,18 @@ function removeNamespace(key, themeNamespace) {
283277
const capitalized = key.substr(themeNamespace.length)
284278
return capitalized.slice(0, 1).toLowerCase() + capitalized.slice(1)
285279
}
280+
281+
function defaultMapThemrProps(ownProps, theme) {
282+
const {
283+
composeTheme, //eslint-disable-line no-unused-vars
284+
innerRef,
285+
themeNamespace, //eslint-disable-line no-unused-vars
286+
...rest
287+
} = ownProps
288+
289+
return {
290+
...rest,
291+
ref: innerRef,
292+
theme
293+
}
294+
}

‎test/components/themr.spec.js‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,50 @@ describe('Themr decorator function', () => {
293293
expect(spy.withArgs(stub).calledOnce).toBe(true)
294294
})
295295

296+
it('allows to customize props passing using mapThemrProps from props', () => {
297+
class Container extends Component {
298+
render() {
299+
return <Passthrough {...this.props} />
300+
}
301+
}
302+
303+
const spy = sinon.stub()
304+
const hoc = C => ({ withRef, ...rest }) => (<C ref={withRef} {...rest} />)
305+
const customMapper = (props, theme) => {
306+
const { composeTheme, innerRef, mapThemrProps, themeNamespace, ...rest } = props //eslint-disable-line no-unused-vars
307+
return { withRef: innerRef, theme, className: 'fooClass', ...rest }
308+
}
309+
const theme = {}
310+
const DecoratedContainer = hoc(Container)
311+
const ThemedDecoratedContainer = themr('Container', theme)(DecoratedContainer)
312+
const tree = TestUtils.renderIntoDocument(<ThemedDecoratedContainer innerRef={spy} mapThemrProps={customMapper} />)
313+
const stub = TestUtils.findRenderedComponentWithType(tree, Container)
314+
expect(spy.withArgs(stub).calledOnce).toBe(true)
315+
expect(stub.props).toMatch({ theme, className: 'fooClass' })
316+
})
317+
318+
it('allows to customize props passing using mapThemrProps from options', () => {
319+
class Container extends Component {
320+
render() {
321+
return <Passthrough {...this.props} />
322+
}
323+
}
324+
325+
const spy = sinon.stub()
326+
const hoc = C => ({ withRef, ...rest }) => (<C ref={withRef} {...rest} />)
327+
const customMapper = (props, theme) => {
328+
const { composeTheme, innerRef, mapThemrProps, themeNamespace, ...rest } = props //eslint-disable-line no-unused-vars
329+
return { withRef: innerRef, theme, className: 'fooClass', ...rest }
330+
}
331+
const theme = {}
332+
const DecoratedContainer = hoc(Container)
333+
const ThemedDecoratedContainer = themr('Container', {}, { mapThemrProps: customMapper })(DecoratedContainer)
334+
const tree = TestUtils.renderIntoDocument(<ThemedDecoratedContainer innerRef={spy} />)
335+
const stub = TestUtils.findRenderedComponentWithType(tree, Container)
336+
expect(spy.withArgs(stub).calledOnce).toBe(true)
337+
expect(stub.props).toMatch({ theme, className: 'fooClass' })
338+
})
339+
296340
it('should throw if themeNamespace passed without theme', () => {
297341
const theme = { Container: { foo: 'foo_1234' } }
298342

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /