Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
view-container / src / test / theme.test.tsx
Size: Mime:
// @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(
      <ThemeProvider theme={theme}>
        <Comp />
      </ThemeProvider>
    )
    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(
      <ThemeProvider theme={theme}>
        <div>
          <div>
            <Comp />
          </div>
        </div>
      </ThemeProvider>
    )
    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(
      <div>
        <Comp1 />
      </div>
    )
    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(
      <ThemeProvider theme={theme}>
        <Comp1 />
      </ThemeProvider>
    )
    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(
      <ThemeProvider theme={theme}>
        <Comp1 theme={{ test: { color: 'purple' } }} />
      </ThemeProvider>
    )
    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(
      <div>
        <ThemeProvider theme={theme}>
          <Comp theme={{ color: 'red' }} />
        </ThemeProvider>
      </div>
    )
    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(
      <div>
        <Comp1 />
      </div>
    )
    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(
      <div>
        <ThemeProvider theme={theme}>
          <div>
            <Comp1 />
          </div>
        </ThemeProvider>
        <Comp2 />
      </div>
    )
    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(
      <ThemeProvider theme={theme}>
        <div>
          <div>
            <Comp1 />
          </div>
          <Comp2 />
        </div>
      </ThemeProvider>
    )
    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(
        <ThemeProvider theme={theme}>
          <Comp />
        </ThemeProvider>
      )
    }
    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(<Comp1 />)
    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(<Comp1 />)
    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(<Comp1 />)
    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 }) => (
      <ThemeProvider theme={theme}>
        <Comp someProps={theme} />
      </ThemeProvider>
    )

    const wrapper = mount(<Theme theme={originalTheme} />)

    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 }) => <div>{theme.color}</div>
    const MyDivWithTheme = withTheme(MyDiv)

    const wrapper = mount(
      <ThemeProvider theme={originalTheme}>
        <MyDivWithTheme />
      </ThemeProvider>
    )

    expect(wrapper.find('div').text()).toBe('black')
  })

  it('should properly update theme prop on hoc component when theme is changed', () => {
    const MyDiv = ({ theme }) => <div>{theme.color}</div>
    const MyDivWithTheme = withTheme(MyDiv)

    const originalTheme = { color: 'black' }
    const newTheme = { color: 'blue' }

    const Theme = ({ theme }) => (
      <ThemeProvider theme={theme}>
        <MyDivWithTheme />
      </ThemeProvider>
    )

    const wrapper = mount(<Theme theme={originalTheme} />)

    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 => (
      <ThemeProvider theme={{ color: 'green' }}>
        <Text {...props} />
      </ThemeProvider>
    )

    const wrapper = mount(<Theme prop="foo" />)

    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<any, any> {
      static myStaticProperty: boolean = true
    }

    const MyComponentWithTheme = withTheme(MyComponent)

    expect(MyComponentWithTheme.myStaticProperty).toBe(true)
  })

  it('should only pass the theme prop', () => {
    class Comp extends Component<any, any> {
      render() {
        return <div />
      }
    }

    const CompWithTheme = withTheme(Comp)

    const wrapper = mount(
      <ThemeProvider theme={{}}>
        <CompWithTheme />
      </ThemeProvider>
    )

    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<any, any> {
      render() {
        return <div />
      }
    }

    const CompWithTheme = withTheme(Comp)
    const ref = jest.fn()

    const wrapper = mount(
      <ThemeProvider theme={{}}>
        <CompWithTheme innerRef={ref} />
      </ThemeProvider>
    )

    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 = () => <div />
    const CompWithTheme = withTheme(Comp)
    const ref = jest.fn()

    const wrapper = mount(
      <ThemeProvider theme={{}}>
        <CompWithTheme innerRef={ref} />
      </ThemeProvider>
    )

    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(
      <ThemeProvider theme={{}}>
        <CompWithTheme innerRef={ref} />
      </ThemeProvider>
    )

    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 }) => <div>{theme.color}</div>
    const MyDivWithTheme = withTheme(MyDiv)
    const theme = { color: 'red' }
    const newTheme = { color: 'blue' }

    MyDivWithTheme.defaultProps = { theme }

    const wrapper = mount(<MyDivWithTheme />)

    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 <https://github.com/facebook/flow/issues/3258>.
        // $FlowFixMe
        [Symbol.toStringTag]: 'Module',
      },
      // Flow has limited support for Symbols and computed properties;
      // see <https://github.com/facebook/flow/issues/3258>.
      // $FlowFixMe
      [Symbol.toStringTag]: 'Module',
    }

    const Comp1 = styled.div`
      background-color: ${({ theme }) => theme.palette.white};
      color: ${({ theme }) => theme.palette.black};
    `

    let wrapper
    expect(() => {
      wrapper = mount(
        <ThemeProvider theme={theme}>
          <Comp1 />
        </ThemeProvider>
      )
    }).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(
      <ThemeProvider theme={theme}>
        <Comp1 />
      </ThemeProvider>
    )

    expectCSSMatches(`.sc-a {} .b {border-radius:${theme.borderRadius};}`)
  })

  it('should not allow the theme to be null', () => {
    expect(() => {
      mount(
        // $FlowInvalidInputTest
        <ThemeProvider theme={null}>
          <div />
        </ThemeProvider>
      )
    }).toThrowErrorMatchingSnapshot()
  })

  it('should not allow the theme to be an array', () => {
    expect(() => {
      mount(
        // $FlowInvalidInputTest
        <ThemeProvider theme={['a', 'b', 'c']}>
          <div />
        </ThemeProvider>
      )
    }).toThrowErrorMatchingSnapshot()
  })

  it('should not allow the theme to be a non-object', () => {
    expect(() => {
      mount(
        // $FlowInvalidInputTest
        <ThemeProvider theme={42}>
          <div />
        </ThemeProvider>
      )
    }).toThrowErrorMatchingSnapshot()
  })
})