diff --git a/README.md b/README.md index e29976b..f69af1a 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,11 @@ const PrimaryButton = styled(Button)({ color: 'red' }) +// Composition with unstyled React Components too. +const Button = styled(UnstyledButton)({ + color: 'blue' +}) + // Component Selectors. const ButtonContainer = styled(Container)({ [`& ${PrimaryButton}`]: { diff --git a/src/createStyled.js b/src/createStyled.js index 62c02c9..8a14402 100644 --- a/src/createStyled.js +++ b/src/createStyled.js @@ -2,24 +2,10 @@ import styled from './styled' import type { BaseStylesType, - ComponentStyleType, StyledType, - StyledElementAttrsType, StyledElementType, - TagNameOrStyledElementType } from './types' -const getStyledArgs = ( - tagNameOrStyledElement: TagNameOrStyledElementType -): StyledElementAttrsType => { - if (typeof tagNameOrStyledElement === 'string') { - return {tagName: tagNameOrStyledElement, style: {}} - } - - const {tagName, style} = tagNameOrStyledElement - return {tagName, style} -} - const createStyled = (jss: Function) => (baseStyles: BaseStylesType = {}): StyledType => { let sheet @@ -34,16 +20,9 @@ const createStyled = (jss: Function) => (baseStyles: BaseStylesType = {}): Style return sheet } - const styledWrapper = ( - tagNameOrStyledElement: TagNameOrStyledElementType - ) => ( - ownStyle: ComponentStyleType - ): StyledElementType => { - const {tagName, style} = getStyledArgs(tagNameOrStyledElement) - const elementStyle = {...style, ...ownStyle} - - return styled({tagName, baseStyles, elementStyle, mountSheet, jss}) - } + const styledWrapper = element => + (ownStyle): StyledElementType => + styled({element, ownStyle, mountSheet, jss}) Object.defineProperty(styledWrapper, 'sheet', ({ get: () => sheet, diff --git a/src/styled.js b/src/styled.js index f94ca70..20e4431 100644 --- a/src/styled.js +++ b/src/styled.js @@ -7,18 +7,46 @@ import getSeparatedStyles from './utils/getSeparatedStyles' import type { JssSheet, + TagNameOrStyledElementType, ComponentStyleType, StyledElementPropsType } from './types' +type Comp = Function & Component<*> + type StyledArgs = { - tagName: string, - elementStyle: ComponentStyleType, + element: TagNameOrStyledElementType | Comp, + ownStyle: ComponentStyleType, mountSheet: Function, jss: Function } -const styled = ({tagName, elementStyle, mountSheet, jss}: StyledArgs) => { +const getElementName = (element: Comp): string => + element.displayName || element.name || 'StyledElement' + +const getParamsByElement = (element) => { + if (typeof element === 'string') return {tagName: element} + if (element.tagName) return element + + return { + tagName: getElementName(element), + reactComponent: element + } +} + +const styled = ({element, ownStyle, mountSheet, jss}: StyledArgs) => { + const { + style, + tagName, + reactComponent = tagName + }: { + style?: ComponentStyleType, + tagName: string, + reactComponent?: string | typeof element + } = getParamsByElement(element) + + const elementStyle = {...style, ...ownStyle} + const {dynamicStyle, staticStyle} = getSeparatedStyles(elementStyle) const staticTagName = staticStyle && generateTagName(tagName) @@ -94,7 +122,7 @@ const styled = ({tagName, elementStyle, mountSheet, jss}: StyledArgs) => { className ]) - return createElement(tagName, {...props, className: tagClass}, children) + return createElement(reactComponent, {...props, className: tagClass}, children) } } diff --git a/src/tests/__snapshots__/functional.spec.jsx.snap b/src/tests/__snapshots__/functional.spec.jsx.snap index e77d73f..78af8a3 100644 --- a/src/tests/__snapshots__/functional.spec.jsx.snap +++ b/src/tests/__snapshots__/functional.spec.jsx.snap @@ -1,5 +1,65 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`functional tests Compose React Components should use .displayName 1`] = ` + + +

+ test +

+
+
+`; + +exports[`functional tests Compose React Components should use .displayName 2`] = ` +".TestDisplayName-1-id { + padding: 10px; +}" +`; + +exports[`functional tests Compose React Components should use .name 1`] = ` + + +

+ test +

+
+
+`; + +exports[`functional tests Compose React Components should use .name 2`] = ` +".Test-1-id { + padding: 10px; +}" +`; + +exports[`functional tests Compose React Components should use .name 3`] = ` + + +

+ test +

+
+
+`; + +exports[`functional tests Compose React Components should use .name 4`] = ` +".StyledElement-1-id { + padding: 30px; +}" +`; + exports[`functional tests should update dynamic props for conditional rules 1`] = ` ".button-1-id { padding: 10px; @@ -101,6 +161,32 @@ exports[`functional tests should update props and unmount 2`] = ` `; exports[`functional tests should use Styled Component classname in string 1`] = ` + + +
+ +
+ name +
+
+ + + +
+
+
+`; + +exports[`functional tests should use Styled Component classname in string 2`] = ` ".div-4-id:not(:first-child) .static-2-id { display: none; } @@ -118,30 +204,6 @@ exports[`functional tests should use Styled Component classname in string 1`] = }" `; -exports[`functional tests should use Styled Component classname in string 2`] = ` - -
- -
- name -
-
- - - -
-
-`; - exports[`functional tests should use props on remount 1`] = ` ".button-1-id { color: black; diff --git a/src/tests/functional.spec.jsx b/src/tests/functional.spec.jsx index f7ad9c2..cba86fa 100644 --- a/src/tests/functional.spec.jsx +++ b/src/tests/functional.spec.jsx @@ -34,6 +34,12 @@ const assertSheet = (sheet) => { expect(getCss(sheet)).toBe(removeWhitespace(sheet.toString())) } +const assertComponent = (Comp) => { + const wrapper = mount() + expect(wrapper).toMatchSnapshot() + wrapper.unmount() +} + describe('functional tests', () => { beforeEach(() => { mockNameGenerators() @@ -151,19 +157,42 @@ describe('functional tests', () => { } }) - const wrapper = mount( + assertComponent(() => ( name - ) + )) - const {sheet} = styled + assertSheet(styled.sheet) + }) - assertSheet(sheet) + describe('Compose React Components', () => { + it('should use .name', () => { + const Test = props =>

test

+ const StyledTest = styled(Test)({ + padding: 10, + }) + assertComponent(StyledTest) + assertSheet(styled.sheet) + }) - expect(wrapper).toMatchSnapshot() + it('should use .displayName', () => { + const Test = props =>

test

+ Test.displayName = 'TestDisplayName' + const StyledTest = styled(Test)({ + padding: 10, + }) + assertComponent(StyledTest) + assertSheet(styled.sheet) + }) - wrapper.unmount() + it('should use .name', () => { + const StyledTest = styled(props =>

test

)({ + padding: 30, + }) + assertComponent(StyledTest) + assertSheet(styled.sheet) + }) }) })