mardi 23 octobre 2018

Cannot read property 'addEventListener' of null in React Jest testing

My application runs properly. I use the EventListeners to swipe a side menu in and out. I pass the functions from the app component above as props to the child components. But when I run a snapshot test I get the following error:

TypeError: Cannot read property 'addEventListener' of null

  21 |   componentDidMount() {
  22 |     const aside = this.sideMenu.current;
> 23 |     aside.addEventListener("touchstart", this.props.onTouchStart);
     |           ^
  24 |     aside.addEventListener("touchmove", this.props.onTouchMove);
  25 |     aside.addEventListener("touchend", this.props.onTouchEnd);
  26 |   }

App.js (Parent component):

import React, { Component } from "react";
import styled from "styled-components";
import Header from "components/Header";
import Navbar from "components/Navbar";
import SideMenu from "components/SideMenu";
import MainContent from "components/Content";

const AppWrapper = styled.section`
  height: 100vh;
  width: 100vw;
  display: grid;
  grid-template-rows: 70px auto;
  @media (max-width: 884px) {
    grid-template-columns: 60vw auto;
  }
  @media (min-width: 885px) {
    grid-template-columns: 200px auto;
  }
`;

class App extends Component {
  swipe = {
    firstPositionX: null,
    firstPositionY: null,
    positionDifferenceX: null,
    positionDifferenceY: null
  };
  constructor(props) {
    super(props);
    this.state = {
      sideMenuDisplay: window.innerWidth >= 885 ? true : false
      // movement: -60
    };
  }


  componentDidMount() {
    window.addEventListener("resize", this.updateSideMenuDisplay);
  }

  componentWillUnmount() {
    window.removeEventListener("resize", this.updateSideMenuDisplay);
  }

  updateSideMenuDisplay = () => {
    this.setState({ sideMenuDisplay: window.innerWidth >= 885 ? true : false });
  };

  toggleSideMenu = () => {
    this.setState(prevState => ({
      sideMenuDisplay: !prevState.sideMenuDisplay
    }));
  };


  handleTouchStart = e => {
    this.swipe.firstPositionX = e.touches[0].screenX;
    this.swipe.firstPositionY = e.touches[0].screenY;
  };

  handleTouchMove = e => {
    this.swipe.positionDifferenceX =
      e.touches[0].screenX - this.swipe.firstPositionX;
    this.swipe.positionDifferenceY =
      e.touches[0].screenY - this.swipe.firstPositionY;
  };

  handleTouchEnd = () => {
    if (Math.abs(this.swipe.positionDifferenceY) <= 150) {
      this.setState(prevState => ({
        sideMenuDisplay:
          prevState.sideMenuDisplay === false
            ? this.swipe.positionDifferenceX > 50
            : !(this.swipe.positionDifferenceX < -50)
      }));
    }

    this.swipe.firstPositionX = null;
    this.swipe.positionDifferenceX = null;
    this.swipe.firstPositionY = null;
    this.swipe.positionDifferenceY = null;
  };

  render() {
    const sideMenuDisplay = this.state.sideMenuDisplay;
    return (
      <AppWrapper>
        <Header display={sideMenuDisplay} />
        <Navbar onClick={this.toggleSideMenu} display={sideMenuDisplay} />
        <SideMenu
          display={sideMenuDisplay}
          onTouchStart={this.handleTouchStart}
          onTouchMove={this.handleTouchMove}
          onTouchEnd={this.handleTouchEnd}
        />
        <MainContent
          display={sideMenuDisplay}
          onTouchStart={this.handleTouchStart}
          onTouchMove={this.handleTouchMove}
          onTouchEnd={this.handleTouchEnd}
        />
      </AppWrapper>
    );
  }
}

export default App;

Sidemenu.js (Child component):

import React, { Component } from "react";
import SideMenuElement from "./SideMenuElement";
import MembersIcon from "./MembersIcon";
import DashboardIcon from "./DashboardIcon";
import {
  SideMenuSection,
  SideMenuItems,
  SideMenuElementLogo,
  AppMobileLogo
} from "./styles";
import logoIcon from "static/logo@3x.png";

class SideMenu extends Component {
  constructor(props) {
    super(props);
    this.state = { selectedItem: "dashboard" };
    // Create the ref
    this.sideMenu = React.createRef();
  }

  componentDidMount() {
    const aside = this.sideMenu.current;
    aside.addEventListener("touchstart", this.props.onTouchStart);
    aside.addEventListener("touchmove", this.props.onTouchMove);
    aside.addEventListener("touchend", this.props.onTouchEnd);
  }

  componentWillUnmount() {
    const aside = this.sideMenu.current;
    aside.removeEventListener("touchstart", this.props.onTouchStart);
    aside.removeEventListener("touchmove", this.props.onTouchMove);
    aside.removeEventListener("touchend", this.props.onTouchEnd);
  }

  selectItem = item => {
    this.setState({ selectedItem: item });
  };

  render() {
    const selectedItem = this.state.selectedItem;
    return (
      <SideMenuSection display={`${this.props.display}`} ref={this.sideMenu}>
        <SideMenuItems>
          <SideMenuElementLogo>
            <AppMobileLogo src={logoIcon} alt="App Logo" />
          </SideMenuElementLogo>

          <SideMenuElement
            item="dashboard"
            icon={DashboardIcon}
            selected={selectedItem === "dashboard"}
            onClick={this.selectItem}
          >
            Dashboard
          </SideMenuElement>

          <SideMenuElement
            item="members"
            icon={MembersIcon}
            selected={selectedItem === "members"}
            onClick={this.selectItem}
          >
            Members
          </SideMenuElement>
        </SideMenuItems>
      </SideMenuSection>
    );
  }
}

export default SideMenu;

Aucun commentaire:

Enregistrer un commentaire