# All files / react/helpers withBreakpoints.jsx

70.59% Statements 24/34
66.67% Branches 4/6
53.33% Functions 8/15
68.75% Lines 22/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126

    1x
    1x
    1x
    1x

    1x

    1x
    5x
    5x

    40x

    4x
    1x

    5x
    5x

    5x

    5x

    5x
    5x
    5x

    3x

    1x

    1x

    1x

     import React, { Component } from 'react'
    import pick from 'lodash/pick'
    import compose from 'lodash/flowRight'
    import mapValues from 'lodash/mapValues'
    import throttle from 'lodash/throttle'

    const large = 1200
    const medium = 1023
    const small = 768
    const tiny = 543

    const breakpoints = {
      isExtraLarge: [large + 1],
      isLarge: [medium + 1, large],
      isMedium: [small + 1, medium],
      isSmall: [tiny + 1, small],
      isTiny: [0, tiny],
      isDesktop: [medium + 1],
      isTablet: [small + 1, medium],
      isMobile: [0, small]
    }

    const getBreakpointsStatus = breakpoints => {
      const width = window.innerWidth
      return mapValues(
        breakpoints,
        ([min, max]) => width >= min && (max === undefined || width <= max)
      )
    }

    /**
     * HOC providing the `breakpoints` property to its children to help
     * with responsive web design.
     *
     * `breakpoints` values will reflect if the window.innerWidth is under
     * those breakpoints.
     *
     * @Example
     * ````
     * // here we define `mobile` as a screen under 1000px
     * const B = withBreakpoints({ mobile: 1000 })(A)
     * ````
     *
     * `A` will receive `{ breakpoints: { mobile: true }}` if the screen
     * width is under 1000px.
     *
     * `A` will receive `{ breakpoints: { mobile: false }}` if the screen
     * width is over 1000px;
     *
     *
     */
    const withBreakpoints = (bp = breakpoints) => Wrapped =>
      class Aware extends Component {
        constructor(props) {
          super(props)
          this.state = {
            breakpoints: getBreakpointsStatus(bp)
          }
          this.checkBreakpoints = throttle(
            () => {
              this.setState({ breakpoints: getBreakpointsStatus(bp) })
            },
            100,
            { trailing: true }
          )
        }

        componentDidMount() {
          window.addEventListener('resize', this.checkBreakpoints)
        }

        componentWillUnmount() {
          window.removeEventListener('resize', this.checkBreakpoints)
        }

        render() {
          const props = this.props
          const { breakpoints } = this.state
          return <Wrapped {...props} breakpoints={breakpoints} />
        }
      }

    /**
     * HOC that tries a predicate on props + state and
     * renders a component only if the predicate returns true
     */
    export const renderOnlyIf = predicate => Wrapped =>
      class extends Component {
        render() {
          const props = this.props
          const state = this.state
          if (predicate(props, state)) {
            return <Wrapped {...props} />
          }
        }
      }

    /**
     * Use this HOC if you only want your component to be
     * rendered on mobile
     */
    export const onlyMobile = compose(
      withBreakpoints(pick(breakpoints, 'isMobile')),
      renderOnlyIf(props => props.breakpoints.isMobile)
    )

    /**
     * Use this HOC if you only want your component to be
     * rendered on tablet
     */
    export const onlyTablet = compose(
      withBreakpoints(pick(breakpoints, 'isTablet')),
      renderOnlyIf(props => props.breakpoints.isTablet)
    )

    /**
     * Use this HOC if you only want your component to be
     * rendered on desktop
     */
    export const onlyDesktop = compose(
      withBreakpoints(pick(breakpoints, 'isDesktop')),
      renderOnlyIf(props => props.breakpoints.isDesktop)
    )

    export default withBreakpoints