All files breakpoint-tracker.tsx

86.96% Statements 20/23
37.5% Branches 3/8
83.33% Functions 5/6
86.96% Lines 20/23

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 1041x 1x 1x                               1x             1x                     5x   5x                         5x 5x   5x               1x 1x               10x           5x 5x 5x         5x 5x         5x           5x              
import React from "react";
import { canUseDOM } from "exenv-es6";
import { Breakpoints, defaultBreakpoints, identifyBreakpoint } from "./breakpoints";
 
export interface BreakpointTrackerProps {
    /**
     * The render method
     */
    render: (activeBreakpoint: number | void) => React.ReactNode;
}
 
export interface BreakpointTrackerState {
    /**
     * The active breakpoint as an index of the Breakpoints array
     */
    activeBreakpoint: number | void;
}
 
export default class BreakpointTracker extends React.Component<
    BreakpointTrackerProps,
    BreakpointTrackerState
> {
    /**
     * The array of breakpoint values
     */
    public static breakpoints: Breakpoints = defaultBreakpoints;
 
    /**
     * Track if we have an open animation frame request
     */
    private openRequestAnimationFrame: boolean;
 
    /**
     * Constructor for the BreakpointTracker component.
     */
    constructor(props: BreakpointTrackerProps) {
        super(props);
 
        this.state = {
            activeBreakpoint: null,
        };
    }
 
    /**
     * React life-cycle method
     */
    public componentDidMount(): void {
        // We are doing this work in a lifecycle method instead of the constructor to ensure that
        // server rendered instances with conditional DOM rendering by breakpoint will be re-rendered.
        // ReactDOM.hydrate() will call the constructor again, but it does not trigger a re-render. It will only bind event handlers.
        // The only way to ensure the correct DOM is consistently rendered on the client is to perform this work here.
        Eif (canUseDOM()) {
            this.updateBreakpoint();
 
            window.addEventListener("resize", this.requestFrame);
        }
    }
 
    /**
     * React life-cycle method
     */
    public componentWillUnmount(): void {
        Eif (canUseDOM()) {
            window.removeEventListener("resize", this.requestFrame);
        }
    }
 
    /**
     * React render method
     */
    public render(): React.ReactNode {
        return this.props.render(this.state.activeBreakpoint);
    }
 
    /**
     * Updates the active breakpoint
     */
    private updateBreakpoint = (): void => {
        const windowWidth: number = window.innerWidth;
        const breakpoint: number | void = identifyBreakpoint(
            windowWidth,
            BreakpointTracker.breakpoints
        );
 
        Eif (this.state.activeBreakpoint !== breakpoint) {
            this.setState({
                activeBreakpoint: breakpoint,
            });
        }
 
        this.openRequestAnimationFrame = false;
    };
 
    /**
     * Request's an animation frame if there are currently no open animation frame requests
     */
    private requestFrame = (): void => {
        if (!this.openRequestAnimationFrame) {
            this.openRequestAnimationFrame = true;
            window.requestAnimationFrame(this.updateBreakpoint);
        }
    };
}