// @flow import React, { Component } from 'react' import { mount, render } from 'enzyme' import { resetStyled, expectCSSMatches } from './utils' import ThemeProvider from '../models/ThemeProvider' import withTheme from '../hoc/withTheme' let styled describe('theming', () => { beforeEach(() => { styled = resetStyled() }) it('should inject props.theme into a styled component', () => { const Comp = styled.div` color: ${props => props.theme.color}; ` const theme = { color: 'black' } render( ) expectCSSMatches(`.sc-a {} .b { color:${theme.color}; }`) }) it('should inject props.theme into a styled component multiple levels deep', () => { const Comp = styled.div` color: ${props => props.theme.color}; ` const theme = { color: 'black' } render(
) expectCSSMatches(`.sc-a {} .b { color:${theme.color}; }`) }) it('should properly allow a component to fallback to its default props when a theme is not provided', () => { const Comp1 = styled.div` color: ${props => props.theme.test.color}; ` Comp1.defaultProps = { theme: { test: { color: "purple" } } } render(
) expectCSSMatches(`.sc-a {} .b { color:purple; }`) }) // https://github.com/styled-components/styled-components/issues/344 it('should use ThemeProvider theme instead of defaultProps theme', () => { const Comp1 = styled.div` color: ${props => props.theme.test.color}; ` Comp1.defaultProps = { theme: { test: { color: "purple" } } } const theme = { test: { color: 'green' } } render( ) expectCSSMatches(`.sc-a {} .b { color:green; }`) }) it('should properly allow a component to override the theme with a prop even if it is equal to defaultProps theme', () => { const Comp1 = styled.div` color: ${props => props.theme.test.color}; ` Comp1.defaultProps = { theme: { test: { color: "purple" } } } const theme = { test: { color: 'green' } } render( ) expectCSSMatches(`.sc-a {} .b { color:purple; }`) }) it('should properly allow a component to override the theme with a prop', () => { const Comp = styled.div` color: ${props => props.theme.color}; ` const theme = { color: 'purple', } render(
) expectCSSMatches(`.sc-a {} .b { color:red; }`) }) it('should properly set the theme with an empty object when no theme is provided and no defaults are set', () => { const Comp1 = styled.div` color: ${props => props.theme.color}; ` render(
) expectCSSMatches(`.sc-a {}`) }) it('should only inject props.theme into styled components within its child component tree', () => { const Comp1 = styled.div` color: ${props => props.theme.color}; ` const Comp2 = styled.div` background: ${props => props.theme.color}; ` const theme = { color: 'black' } render(
) expectCSSMatches(`.sc-a {} .c { color:${theme.color}; } .sc-b {}`) }) it('should inject props.theme into all styled components within the child component tree', () => { const Comp1 = styled.div` color: ${props => props.theme.color}; ` const Comp2 = styled.div` background: ${props => props.theme.color}; ` const theme = { color: 'black' } render(
) expectCSSMatches(`.sc-a {} .c { color:${theme.color}; } .sc-b {} .d { background:${theme.color}; }`) }) it('should inject new CSS when the theme changes', () => { const Comp = styled.div` color: ${props => props.theme.color}; ` const originalTheme = { color: 'black' } const newTheme = { color: 'blue' } let theme = originalTheme // Force render the component const renderComp = () => { render( ) } renderComp() const initialCSS = expectCSSMatches(`.sc-a {} .b { color:${theme.color}; }`) // Change the theme theme = newTheme renderComp() expectCSSMatches(`${initialCSS} .c { color:${newTheme.color}; }`) }) }) describe('theming', () => { beforeEach(() => { styled = resetStyled() }) it('should properly render with the same theme from default props on re-render', () => { const Comp1 = styled.div` color: ${props => props.theme.color}; ` Comp1.defaultProps = { theme: { color: "purple" } } const wrapper = mount( ) expectCSSMatches(`.sc-a {} .b { color:purple; }`) wrapper.update(); expectCSSMatches(`.sc-a {} .b { color:purple; }`) }) it('should properly update style if theme is changed', () => { const Comp1 = styled.div` color: ${props => props.theme.color}; ` Comp1.defaultProps = { theme: { color: "purple" } } const wrapper = mount( ) expectCSSMatches(`.sc-a {} .b { color:purple; }`) wrapper.setProps({ theme: { color: 'pink' } }) expectCSSMatches(`.sc-a {} .b { color:purple; } .c { color:pink; }`) }) it('should properly update style if props used in styles is changed', () => { const Comp1 = styled.div` color: ${props => props.theme.color}; z-index: ${props => props.zIndex}px; ` Comp1.defaultProps = { theme: { color: "purple" }, zIndex: 0 } const wrapper = mount( ) let expectedStyles = `.sc-a {} .b { color:purple; z-index:0px; }` expectCSSMatches(expectedStyles) wrapper.setProps({ theme: { color: 'pink' } }) expectedStyles = `${expectedStyles} .c { color:pink; z-index:0px; }` expectCSSMatches(expectedStyles) wrapper.setProps({ zIndex: 1 }); expectCSSMatches(`${expectedStyles} .d { color:pink; z-index:1px; }`) }) it('should change the classnames when the theme changes', () => { const Comp = styled.div` color: ${props => props.theme.color}; ` const originalTheme = { color: 'black' } const newTheme = { color: 'blue' } const Theme = ({ theme }) => ( ) const wrapper = mount( ) expectCSSMatches(`.sc-a {} .b { color:${originalTheme.color}; }`) expect(wrapper.find('div').prop('className')).toBe('sc-a b') // Change theme wrapper.setProps({ theme: newTheme }) expectCSSMatches(`.sc-a {} .b { color:${originalTheme.color}; } .c { color:${newTheme.color}; }`) expect(wrapper.find('div').prop('className')).toBe('sc-a c') }) it('should inject props.theme into a component that uses withTheme hoc', () => { const originalTheme = { color: 'black' } const MyDiv = ({ theme }) =>
{theme.color}
const MyDivWithTheme = withTheme(MyDiv); const wrapper = mount( ) expect(wrapper.find('div').text()).toBe('black') }) it('should properly update theme prop on hoc component when theme is changed', () => { const MyDiv = ({ theme }) =>
{theme.color}
const MyDivWithTheme = withTheme(MyDiv); const originalTheme = { color: 'black' } const newTheme = { color: 'blue' } const Theme = ({ theme }) => ( ) const wrapper = mount( ) expect(wrapper.find('div').text()).toBe('black') // Change theme wrapper.setProps({ theme: newTheme }) expect(wrapper.find('div').text()).toBe('blue') }) // https://github.com/styled-components/styled-components/issues/445 it('should use ThemeProvider theme instead of defaultProps theme after initial render', () => { const Text = styled.div` color: ${props => props.theme.color}; ` Text.defaultProps = { theme: { color: 'purple', }, } const Theme = props => ( ) const wrapper = mount( ) expectCSSMatches(`.sc-a { } .b { color:green; } `) wrapper.setProps({ prop: 'bar' }) expectCSSMatches(`.sc-a { } .b { color:green; } `) }) // https://github.com/styled-components/styled-components/issues/596 it('should hoist static properties when using withTheme', () => { class MyComponent extends Component<*, *> { static myStaticProperty: boolean = true } const MyComponentWithTheme = withTheme(MyComponent) expect(MyComponentWithTheme.myStaticProperty).toBe(true) }) it('should only pass the theme prop', () => { class Comp extends Component<*, *> { render() { return
} } const CompWithTheme = withTheme(Comp) const wrapper = mount( ) const inner = wrapper.find(Comp).first() expect(Object.keys(inner.props()).length).toEqual(1) expect(inner.props()).toEqual({ theme: {} }) }) it('should accept innerRef and pass it on as ref', () => { class Comp extends Component<*, *> { render() { return
} } const CompWithTheme = withTheme(Comp) const ref = jest.fn() const wrapper = mount( ) const inner = wrapper.find(Comp).first(); expect(ref).toHaveBeenCalledWith(inner.instance()) expect(inner.prop('innerRef')).toBe(undefined) }) it('should accept innerRef and pass it on for stateless function components', () => { const Comp = () =>
const CompWithTheme = withTheme(Comp) const ref = jest.fn() const wrapper = mount( ) const inner = wrapper.find(Comp).first() expect(ref).toHaveBeenCalledTimes(0) expect(inner.prop('innerRef')).toBe(ref) }) it('should accept innerRef and pass it on for styled components', () => { const Comp = styled.div`` const CompWithTheme = withTheme(Comp) const ref = jest.fn() const wrapper = mount( ) const inner = wrapper.find(Comp).first() expect(ref).toHaveBeenCalledWith(inner.getDOMNode()) expect(inner.prop('innerRef')).toBe(ref) }) // https://github.com/styled-components/styled-components/issues/1130 it('should not break without a ThemeProvier if it has a defaultTheme', () => { const MyDiv = ({ theme }) =>
{theme.color}
const MyDivWithTheme = withTheme(MyDiv); const theme = { color: 'red' } const newTheme = { color: 'blue' } MyDivWithTheme.defaultProps = { theme } const wrapper = mount() expect(wrapper.find('div').text()).toBe('red') // Change theme wrapper.setProps({ theme: newTheme }) expect(wrapper.find('div').text()).toBe('blue') }) // https://github.com/styled-components/styled-components/issues/1776 it('should allow module objects to be passed as themes', () => { const theme = { borderRadius: '2px', palette: { black: '#000', white: '#fff', // Flow has limited support for Symbols and computed properties; // see . // $FlowFixMe [Symbol.toStringTag]: 'Module' }, // Flow has limited support for Symbols and computed properties; // see . // $FlowFixMe [Symbol.toStringTag]: 'Module' } const Comp1 = styled.div` background-color: ${ ({ theme }) => theme.palette.white }; color: ${ ({ theme }) => theme.palette.black }; ` let wrapper expect(() => { wrapper = mount( ) }).not.toThrow('plain object') expectCSSMatches(`.sc-a {} .b {background-color:${theme.palette.white};color:${theme.palette.black};}`) }) it('should allow other complex objects to be passed as themes', () => { class Theme { borderRadius: string constructor(borderRadius) { this.borderRadius = borderRadius } } const theme = new Theme('2px') const Comp1 = styled.div` border-radius: ${ ({ theme }) => theme.borderRadius }; ` const wrapper = mount( ) expectCSSMatches(`.sc-a {} .b {border-radius:${theme.borderRadius};}`) }) it('should not allow the theme to be null', () => { expect(() => { mount( // $FlowInvalidInputTest
) }).toThrowErrorMatchingSnapshot() }) it('should not allow the theme to be an array', () => { expect(() => { mount( // $FlowInvalidInputTest
) }).toThrowErrorMatchingSnapshot() }) it('should not allow the theme to be a non-object', () => { expect(() => { mount( // $FlowInvalidInputTest
) }).toThrowErrorMatchingSnapshot() }) })