diff --git a/src/create-emotion-styled/index.js b/src/create-emotion-styled/index.js index fce8d8b..a026d7e 100644 --- a/src/create-emotion-styled/index.js +++ b/src/create-emotion-styled/index.js @@ -1,8 +1,8 @@ // @flow import PropTypes from 'prop-types' -import type { ElementType } from 'react' +import type {ElementType} from 'react' import typeof ReactType from 'react' -import type { CreateStyled, StyledOptions } from './utils' +import type {CreateStyled, StyledOptions} from './utils' import { themeChannel as channel, testPickPropsOnComponent, @@ -13,9 +13,9 @@ import { setFrame, } from './utils' import FrameManager from './FrameManager' -import { getDocumentFromReactComponent } from '../utils' -import { channel as frameChannel } from '../FrameProvider' -import { channel as scopeChannel } from '../ScopeProvider' +import {getDocumentFromReactComponent} from '../utils' +import {channel as frameChannel} from '../FrameProvider' +import {channel as scopeChannel} from '../ScopeProvider' const contextTypes = { [channel]: PropTypes.object, @@ -28,7 +28,7 @@ function createEmotionStyled(emotion: Object, view: ReactType) { if (process.env.NODE_ENV !== 'production') { if (tag === undefined) { throw new Error( - 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.' + 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.', ) } } @@ -84,7 +84,7 @@ function createEmotionStyled(emotion: Object, view: ReactType) { } } - class Styled extends view.Component<*, { theme: Object }> { + class Styled extends view.Component<*, {theme: Object}> { unsubscribe: number unsubscribeFrame: number mergedProps: Object @@ -95,6 +95,7 @@ function createEmotionStyled(emotion: Object, view: ReactType) { static __emotion_target: string static __emotion_forwardProp: void | (string => boolean) static withComponent: (ElementType, options?: StyledOptions) => any + // $FlowFixMe state = {} // Custom instance properties emotion = emotion @@ -103,7 +104,7 @@ function createEmotionStyled(emotion: Object, view: ReactType) { componentWillMount() { if (this.context[channel] !== undefined) { this.unsubscribe = this.context[channel].subscribe( - setTheme.bind(this) + setTheme.bind(this), ) } /** @@ -111,7 +112,7 @@ function createEmotionStyled(emotion: Object, view: ReactType) { */ if (this.context[frameChannel] !== undefined) { this.unsubscribeFrame = this.context[frameChannel].subscribe( - setFrame.bind(this) + setFrame.bind(this), ) } } @@ -135,6 +136,7 @@ function createEmotionStyled(emotion: Object, view: ReactType) { * custom container. */ setEmotion() { + // $FlowFixMe const frame = this.state.frame if (!frame) return @@ -166,7 +168,7 @@ function createEmotionStyled(emotion: Object, view: ReactType) { } } render() { - const { props, state } = this + const {props, state} = this this.mergedProps = pickAssign(testAlwaysTrue, {}, props, { theme: (state !== null && state.theme) || props.theme || {}, }) @@ -179,17 +181,27 @@ function createEmotionStyled(emotion: Object, view: ReactType) { if (staticClassName === undefined) { className += this.emotion.getRegisteredStyles( classInterpolations, - props.className + props.className, ) } else { className += `${props.className} ` } } if (staticClassName === undefined) { - className += this.emotion - /* Replaces emotion.css, with enhanced emotion.cssWithScope */ - .cssWithScope(this.getScope()) - .apply(this, styles.concat(classInterpolations)) + /* Replaces emotion.css, with enhanced emotion.cssWithScope */ + if ( + this.emotion.hasOwnProperty('cssWithScope') && + typeof this.emotion.cssWithScope === 'function' + ) { + className += this.emotion + .cssWithScope(this.getScope()) + .apply(this, styles.concat(classInterpolations)) + } else { + className += this.emotion.css.apply( + this, + styles.concat(classInterpolations), + ) + } } else { className += staticClassName } @@ -204,7 +216,7 @@ function createEmotionStyled(emotion: Object, view: ReactType) { pickAssign(shouldForwardProp, {}, props, { className, ref: props.innerRef, - }) + }), ) } } @@ -242,14 +254,14 @@ function createEmotionStyled(emotion: Object, view: ReactType) { Styled.withComponent = ( nextTag: ElementType, - nextOptions?: StyledOptions + nextOptions?: StyledOptions, ) => { return createStyled( nextTag, nextOptions !== undefined ? // $FlowFixMe pickAssign(testAlwaysTrue, {}, options, nextOptions) - : options + : options, )(...styles) } @@ -270,7 +282,7 @@ function createEmotionStyled(emotion: Object, view: ReactType) { default: { throw new Error( `You're trying to use the styled shorthand without babel-plugin-this.` + - `\nPlease install and setup babel-plugin-emotion or use the function call syntax(\`styled('${property}')\` instead of \`styled.${property}\`)` + `\nPlease install and setup babel-plugin-emotion or use the function call syntax(\`styled('${property}')\` instead of \`styled.${property}\`)`, ) } } diff --git a/src/create-emotion/__tests__/index.test.js b/src/create-emotion/__tests__/index.test.js new file mode 100644 index 0000000..07fe870 --- /dev/null +++ b/src/create-emotion/__tests__/index.test.js @@ -0,0 +1,24 @@ +import createEmotion from '../index' + +describe('createEmotion', () => { + afterEach(() => { + global.__SECRET_EMOTION__ = undefined + global.__SECRET_FANCY_EMOTION__ = undefined + }) + + test('Creats unique global instance of Emotion', () => { + const inst = createEmotion(global) + + expect(global.__SECRET_FANCY_EMOTION__).toBe(inst) + }) + + test('Does not collide with stock instance of Emotion', () => { + const mockEmotion = {} + global.__SECRET_EMOTION__ = mockEmotion + + const inst = createEmotion(global) + + expect(global.__SECRET_EMOTION__).toBe(mockEmotion) + expect(global.__SECRET_FANCY_EMOTION__).toBe(inst) + }) +}) diff --git a/src/create-emotion/index.js b/src/create-emotion/index.js index fefc6d7..b5e197a 100644 --- a/src/create-emotion/index.js +++ b/src/create-emotion/index.js @@ -39,6 +39,7 @@ type CreateStyles = (...args: Interpolations) => ReturnValue export type Emotion = { css: CreateStyles, + cssWithScope: Function, cx: (...classNames: Array) => string, flush: () => void, getRegisteredStyles: ( @@ -62,11 +63,11 @@ type EmotionOptions = { } function createEmotion( - context: {__SECRET_EMOTION__?: Emotion}, + context: {__SECRET_FANCY_EMOTION__?: Emotion}, options?: EmotionOptions, ): Emotion { - if (context.__SECRET_EMOTION__ !== undefined) { - return context.__SECRET_EMOTION__ + if (context.__SECRET_FANCY_EMOTION__ !== undefined) { + return context.__SECRET_FANCY_EMOTION__ } if (options === undefined) options = {} let key = options.key || 'css' @@ -399,7 +400,7 @@ function createEmotion( sheet, caches, } - context.__SECRET_EMOTION__ = emotion + context.__SECRET_FANCY_EMOTION__ = emotion return emotion } diff --git a/src/styled/__tests__/styled.test.js b/src/styled/__tests__/styled.test.js index bd7b705..d68b69b 100644 --- a/src/styled/__tests__/styled.test.js +++ b/src/styled/__tests__/styled.test.js @@ -1,7 +1,7 @@ import React from 'react' -import { mount } from 'enzyme' +import {mount} from 'enzyme' import styled from '../index' -import { getStyleProp, resetStyleTags } from '../../utils/testHelpers' +import {getStyleProp, resetStyleTags} from '../../utils/testHelpers' describe('styled', () => { afterEach(() => { @@ -82,10 +82,28 @@ describe('styled', () => { expect(getStyleProp(el, 'background')).toBe('yellow') expect(getStyleProp(el, 'color')).not.toBe('red') - wrapper.setProps({ title: 'Clever' }) + wrapper.setProps({title: 'Clever'}) expect(getStyleProp(el, 'background')).toBe('yellow') expect(getStyleProp(el, 'color')).toBe('red') }) + + test('Falls back to emotion.css if emotion.cssWithScope is unavailable', () => { + const spy = jest.fn() + const Compo = styled('span')` + background: yellow; + ${props => props.title && 'color: red;'}; + ` + + const wrapper = mount() + const el = wrapper.find('span').getNode() + + wrapper.instance().emotion.cssWithScope = undefined + wrapper.instance().emotion.css = spy + + wrapper.setProps({title: 'Clever'}) + + expect(spy).toHaveBeenCalled() + }) }) }) diff --git a/src/utils/testHelpers.js b/src/utils/testHelpers.js index c09ad23..6a52777 100644 --- a/src/utils/testHelpers.js +++ b/src/utils/testHelpers.js @@ -12,8 +12,8 @@ export const getStyleProp = (node: HTMLElement, prop: string = 'display') => * Resets the tag to remove stray