diff --git a/README.md b/README.md index 159b0ff..e29976b 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,13 @@ const Container = div({ const PrimaryButton = styled(Button)({ color: 'red' }) + +// Component Selectors. +const ButtonContainer = styled(Container)({ + [`& ${PrimaryButton}`]: { + color: 'green' + } +}) ``` ## Base Style Sheet diff --git a/package.json b/package.json index 6cd85a3..5d64f25 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "coveralls": "^3.0.0", "enzyme": "^3.1.0", "enzyme-adapter-react-16": "^1.0.1", + "enzyme-to-json": "^3.1.2", "eslint": "^3.13.0", "eslint-config-airbnb": "^14.1.0", "eslint-config-jss": "^3.0.0", @@ -92,6 +93,9 @@ "coverageDirectory": "../coverage", "setupFiles": [ "raf/polyfill" + ], + "snapshotSerializers": [ + "enzyme-to-json/serializer" ] } } diff --git a/src/createStyled.js b/src/createStyled.js index da80321..62c02c9 100644 --- a/src/createStyled.js +++ b/src/createStyled.js @@ -42,7 +42,7 @@ const createStyled = (jss: Function) => (baseStyles: BaseStylesType = {}): Style const {tagName, style} = getStyledArgs(tagNameOrStyledElement) const elementStyle = {...style, ...ownStyle} - return styled({tagName, baseStyles, elementStyle, mountSheet}) + return styled({tagName, baseStyles, elementStyle, mountSheet, jss}) } Object.defineProperty(styledWrapper, 'sheet', ({ diff --git a/src/styled.js b/src/styled.js index bf8b5df..f94ca70 100644 --- a/src/styled.js +++ b/src/styled.js @@ -14,29 +14,30 @@ import type { type StyledArgs = { tagName: string, elementStyle: ComponentStyleType, - mountSheet: Function + mountSheet: Function, + jss: Function } -const styled = ({tagName, elementStyle, mountSheet}: StyledArgs) => { +const styled = ({tagName, elementStyle, mountSheet, jss}: StyledArgs) => { const {dynamicStyle, staticStyle} = getSeparatedStyles(elementStyle) const staticTagName = staticStyle && generateTagName(tagName) const availableDynamicTagNames = [] const classMap = {} - return class StyledElement extends Component { + let staticClassName + + class StyledElement extends Component { static tagName: string = tagName static style: ComponentStyleType = elementStyle - dynamicTagName = '' - - sheet: JssSheet - constructor(props: StyledElementPropsType) { super(props) if (!this.dynamicTagName && dynamicStyle) { this.dynamicTagName = availableDynamicTagNames.pop() || generateTagName(tagName) } + + this.staticClassName = staticClassName } componentWillMount() { @@ -66,6 +67,10 @@ const styled = ({tagName, elementStyle, mountSheet}: StyledArgs) => { availableDynamicTagNames.push(this.dynamicTagName) } + dynamicTagName = '' + sheet: JssSheet + staticClassName = '' + updateSheet(props: StyledElementPropsType) { let rule let ruleIndex = 0 @@ -83,6 +88,7 @@ const styled = ({tagName, elementStyle, mountSheet}: StyledArgs) => { const props = filterProps(tagName, attrs) const tagClass = composeClasses([ + this.staticClassName, staticTagName && this.sheet.classes[staticTagName], this.dynamicTagName && this.sheet.classes[this.dynamicTagName], className @@ -91,6 +97,22 @@ const styled = ({tagName, elementStyle, mountSheet}: StyledArgs) => { return createElement(tagName, {...props, className: tagClass}, children) } } + + // $FlowIgnore + StyledElement.valueOf = () => { + if (!staticClassName) { + staticClassName = `.${jss.generateClassName({ + key: generateTagName('static') + })}` + } + + return staticClassName + } + + // $FlowIgnore + StyledElement.toString = StyledElement.valueOf + + return StyledElement } export default styled diff --git a/src/tests/__snapshots__/functional.spec.jsx.snap b/src/tests/__snapshots__/functional.spec.jsx.snap index 7e2e489..e77d73f 100644 --- a/src/tests/__snapshots__/functional.spec.jsx.snap +++ b/src/tests/__snapshots__/functional.spec.jsx.snap @@ -100,6 +100,48 @@ exports[`functional tests should update props and unmount 2`] = ` }" `; +exports[`functional tests should use Styled Component classname in string 1`] = ` +".div-4-id:not(:first-child) .static-2-id { + display: none; +} +.div-4-id:not(:last-child) .static-3-id { + visibility: hidden; +} +.div-4-id .static-2-id { + color: green; +} +.div-1-id { + color: red; +} +.img-5-id { + width: 30px; +}" +`; + +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 15aa6f7..f7ad9c2 100644 --- a/src/tests/functional.spec.jsx +++ b/src/tests/functional.spec.jsx @@ -134,4 +134,36 @@ describe('functional tests', () => { wrapper.unmount() }) + + it('should use Styled Component classname in string', () => { + const AuthorName = styled('div')({color: 'red'}) + const Avatar = styled('img')({width: props => props.width}) + + const Message = styled('div')({ + [`&:not(:first-child) ${AuthorName}`]: { + display: 'none' + }, + [`&:not(:last-child) ${Avatar}`]: { + visibility: 'hidden' + }, + [`& ${AuthorName}`]: { + color: 'green' + } + }) + + const wrapper = mount( + + name + + + ) + + const {sheet} = styled + + assertSheet(sheet) + + expect(wrapper).toMatchSnapshot() + + wrapper.unmount() + }) }) diff --git a/yarn.lock b/yarn.lock index 7c9875f..3c0346c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1433,6 +1433,12 @@ enzyme-adapter-utils@^1.0.0: object.assign "^4.0.4" prop-types "^15.5.10" +enzyme-to-json@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.1.2.tgz#7681e04c5747bb140c4c658ba7f07a6679e1f3b6" + dependencies: + lodash "^4.17.4" + enzyme@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.1.0.tgz#d8ca84085790fbcec6ed40badd14478faee4c25a"