diff --git a/.changeset/rotten-onions-hide.md b/.changeset/rotten-onions-hide.md
new file mode 100644
index 000000000..b61eb8a0e
--- /dev/null
+++ b/.changeset/rotten-onions-hide.md
@@ -0,0 +1,5 @@
+---
+'@emotion/styled': minor
+---
+
+Custom `shouldForwardProp` is being preserved now when using `.withComponent`. Also, when passing an additional `shouldForwardProp` in `.withComponent`'s options (like this: `SomeComponent.withComponent('span', { shouldForwardProp })`) it's being composed with the potentially existing `shouldForwardProp`.
diff --git a/packages/styled/__tests__/styled.js b/packages/styled/__tests__/styled.js
index ddcc9509c..3cd6047fb 100644
--- a/packages/styled/__tests__/styled.js
+++ b/packages/styled/__tests__/styled.js
@@ -640,6 +640,7 @@ describe('styled', () => {
const tree = renderer.create().toJSON()
expect(tree).toMatchSnapshot()
})
+
test('theming', () => {
const Div = styled.div`
color: ${props => props.theme.primary};
diff --git a/packages/styled/src/base.js b/packages/styled/src/base.js
index 8d9e8c4e2..754059037 100644
--- a/packages/styled/src/base.js
+++ b/packages/styled/src/base.js
@@ -3,6 +3,7 @@ import * as React from 'react'
import type { ElementType } from 'react'
import {
getDefaultShouldForwardProp,
+ composeShouldForwardProps,
type StyledOptions,
type CreateStyled,
type PrivateStyledComponent
@@ -26,27 +27,18 @@ let createStyled: CreateStyled = (tag: any, options?: StyledOptions) => {
)
}
}
+ const isReal = tag.__emotion_real === tag
+ const baseTag = (isReal && tag.__emotion_base) || tag
+
let identifierName
- let shouldForwardProp
let targetClassName
if (options !== undefined) {
identifierName = options.label
targetClassName = options.target
- shouldForwardProp =
- tag.__emotion_forwardProp && options.shouldForwardProp
- ? propName =>
- tag.__emotion_forwardProp(propName) &&
- // $FlowFixMe
- options.shouldForwardProp(propName)
- : options.shouldForwardProp
}
- const isReal = tag.__emotion_real === tag
- const baseTag = (isReal && tag.__emotion_base) || tag
- if (typeof shouldForwardProp !== 'function' && isReal) {
- shouldForwardProp = tag.__emotion_forwardProp
- }
- let defaultShouldForwardProp =
+ const shouldForwardProp = composeShouldForwardProps(tag, options, isReal)
+ const defaultShouldForwardProp =
shouldForwardProp || getDefaultShouldForwardProp(baseTag)
const shouldUseAs = !defaultShouldForwardProp('as')
@@ -196,12 +188,12 @@ let createStyled: CreateStyled = (tag: any, options?: StyledOptions) => {
nextTag: ElementType,
nextOptions?: StyledOptions
) => {
- return createStyled(
- nextTag,
- nextOptions !== undefined
- ? { ...(options || {}), ...nextOptions }
- : options
- )(...styles)
+ return createStyled(nextTag, {
+ ...options,
+ // $FlowFixMe
+ ...nextOptions,
+ shouldForwardProp: composeShouldForwardProps(Styled, nextOptions, true)
+ })(...styles)
}
return Styled
diff --git a/packages/styled/src/utils.js b/packages/styled/src/utils.js
index b533e46b6..274bcd311 100644
--- a/packages/styled/src/utils.js
+++ b/packages/styled/src/utils.js
@@ -38,6 +38,29 @@ export const getDefaultShouldForwardProp = (tag: ElementType) =>
? testOmitPropsOnStringTag
: testOmitPropsOnComponent
+export const composeShouldForwardProps = (
+ tag: PrivateStyledComponent,
+ options: StyledOptions | void,
+ isReal: boolean
+) => {
+ let shouldForwardProp
+ if (options) {
+ const optionsShouldForwardProp = options.shouldForwardProp
+ shouldForwardProp =
+ tag.__emotion_forwardProp && optionsShouldForwardProp
+ ? (propName: string) =>
+ tag.__emotion_forwardProp(propName) &&
+ optionsShouldForwardProp(propName)
+ : optionsShouldForwardProp
+ }
+
+ if (typeof shouldForwardProp !== 'function' && isReal) {
+ shouldForwardProp = tag.__emotion_forwardProp
+ }
+
+ return shouldForwardProp
+}
+
export type CreateStyledComponent = (
...args: Interpolations
) => StyledComponent
diff --git a/packages/styled/test/__snapshots__/prop-filtering.test.js.snap b/packages/styled/test/__snapshots__/prop-filtering.test.js.snap
index 2b0428ae5..5e838d732 100644
--- a/packages/styled/test/__snapshots__/prop-filtering.test.js.snap
+++ b/packages/styled/test/__snapshots__/prop-filtering.test.js.snap
@@ -1,5 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`composes shouldForwardProp on composed styled components 1`] = `
+
+`;
+
exports[`custom shouldForwardProp works 1`] = `
.emotion-0,
.emotion-0 * {
@@ -124,3 +131,61 @@ Array [
/>,
]
`;
+
+exports[`withComponent inherits explicit shouldForwardProp 1`] = `
+.emotion-0 {
+ color: hotpink;
+}
+
+
+`;
+
+exports[`withComponent inherits explicit shouldForwardProp from flattened component 1`] = `
+.emotion-0 {
+ color: hotpink;
+ background-color: blue;
+}
+
+
+`;
+
+exports[`withComponent should accept shouldForwardProp 1`] = `
+.emotion-0 {
+ color: hotpink;
+}
+
+
+`;
+
+exports[`withComponent should compose shouldForwardProp 1`] = `
+.emotion-0 {
+ color: hotpink;
+}
+
+
+`;
+
+exports[`withComponent should compose shouldForwardProp with a flattened component 1`] = `
+.emotion-0 {
+ color: hotpink;
+}
+
+
+`;
diff --git a/packages/styled/test/prop-filtering.test.js b/packages/styled/test/prop-filtering.test.js
index 22f4c0dfc..34e4be0e7 100644
--- a/packages/styled/test/prop-filtering.test.js
+++ b/packages/styled/test/prop-filtering.test.js
@@ -6,17 +6,16 @@ import styled from '@emotion/styled'
test('composes shouldForwardProp on composed styled components', () => {
const StyledDiv = styled('div', {
- shouldForwardProp: prop => prop === 'forwardMe'
+ shouldForwardProp: prop => prop !== 'foo'
})()
const ComposedDiv = styled(StyledDiv, {
- shouldForwardProp: () => true
+ shouldForwardProp: prop => prop !== 'bar'
})()
- const tree = renderer.create().toJSON()
+ const tree = renderer.create().toJSON()
- expect(tree.props.filterMe).toBeUndefined()
- expect(tree.props.forwardMe).toBeDefined()
+ expect(tree).toMatchSnapshot()
})
test('custom shouldForwardProp works', () => {
@@ -470,3 +469,68 @@ test('prop filtering on composed styled components that are string tags', () =>
expect(tree).toMatchSnapshot()
})
+
+test('withComponent inherits explicit shouldForwardProp', () => {
+ const SomeComponent = styled('div', {
+ shouldForwardProp: prop => prop === 'foo'
+ })`
+ color: hotpink;
+ `
+ const AnotherComponent = SomeComponent.withComponent('span')
+ const tree = renderer.create().toJSON()
+ expect(tree).toMatchSnapshot()
+})
+
+test('withComponent inherits explicit shouldForwardProp from flattened component', () => {
+ const SomeComponent = styled('div', {
+ shouldForwardProp: prop => prop === 'foo'
+ })`
+ color: hotpink;
+ `
+ const AnotherComponent = styled(SomeComponent)`
+ background-color: blue;
+ `
+ const YetAnotherComponent = AnotherComponent.withComponent('span')
+ const tree = renderer.create().toJSON()
+ expect(tree).toMatchSnapshot()
+})
+
+test('withComponent should accept shouldForwardProp', () => {
+ const SomeComponent = styled('div')`
+ color: hotpink;
+ `
+ const AnotherComponent = SomeComponent.withComponent('span', {
+ shouldForwardProp: prop => prop === 'xyz'
+ })
+ const tree = renderer.create().toJSON()
+ expect(tree).toMatchSnapshot()
+})
+
+test('withComponent should compose shouldForwardProp', () => {
+ const SomeComponent = styled('div', {
+ shouldForwardProp: prop => prop !== 'foo'
+ })`
+ color: hotpink;
+ `
+ const AnotherComponent = SomeComponent.withComponent('span', {
+ shouldForwardProp: prop => prop !== 'bar'
+ })
+ const tree = renderer.create().toJSON()
+ expect(tree).toMatchSnapshot()
+})
+
+test('withComponent should compose shouldForwardProp with a flattened component', () => {
+ const SomeComponent = styled('div', {
+ shouldForwardProp: prop => prop !== 'foo'
+ })`
+ color: hotpink;
+ `
+ const AnotherComponent = styled(SomeComponent)`
+ background-color: blue;
+ `
+ const YetAnotherComponent = SomeComponent.withComponent('span', {
+ shouldForwardProp: prop => prop !== 'bar'
+ })
+ const tree = renderer.create().toJSON()
+ expect(tree).toMatchSnapshot()
+})