🎫DNT-2423 Throttle scroll event listeners

    // TODO Throttle event listeners
    window.addEventListener('scroll', handleScroll, { passive: true });

    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []);

  if (suppressNav || !hasSubPages) {
    return null;
  }

See: src/components/SecondaryNav/index.tsx - L:99

We probably don't need event fired for every single scroll.

Secondary Nav
/* eslint-disable complexity */
/* eslint-disable max-lines */
import React, { useState, useRef, useEffect } from "react";
import { SecondaryNavType } from "./types";
import { NavItem } from "./components/NavItem";
import { DropDownMenu } from "./components/DropDownMenu";
import { useLocation } from "react-router-dom";

const SecondaryNav = ({ items, suppressNav }: SecondaryNavType) => {
  const currentItems = items[0];
  const [active, setActive] = useState(-1);
  let { pathname } = useLocation();
  pathname = pathname?.split(" ").join("-").toLowerCase();

  const isItemActive = (index: number) => active === index;
  // Need to check if the current page is the route of a top level page or any subpages or subpages' subpages so we can display the glorious teal bar
  const selectedIndex: Array<number> = [];
  let topLevelSelected: number;
  if (currentItems) {
    // check if the current page is one of the top level navItems in the blue bar
    topLevelSelected = currentItems.subpages.findIndex(
      (x) => x.route.toLowerCase() === pathname
    );
    // check if the current page is in any of the subpages
    for (let i = 0; i < currentItems.subpages?.length; i++) {
      const select = currentItems.subpages[i]?.subpages?.findIndex(
        (x) => x.route.toLowerCase() === pathname
      );
      if (select !== -1) {
        selectedIndex.push(i);
        break;
      } else {
        for (
          let ii = 0;
          ii < currentItems.subpages[i]?.subpages[ii]?.subpages?.length;
          ii++
        ) {
          const select = currentItems.subpages[i]?.subpages[
            ii
          ]?.subpages?.findIndex((x) => x.route.toLowerCase() === pathname);
          if (select !== -1) {
            selectedIndex.push(i);
            break;
          }
        }
      }
    }
  }

  const isItemSelected = (index: number) => selectedIndex.indexOf(index) !== -1;

  const hasSubPages = currentItems?.subpages.length > 0;

  const getNumberOfCols = (index: number) => {
    if (currentItems?.subpages[index].subpages[0]?.subpages?.length) {
      return currentItems.subpages[index].subpages.length;
    }
    return 1;
  };

  const dropdownDirection = (index: number) => {
    const navItemsLength = currentItems?.subpages.length;
    const numberOfCols = getNumberOfCols(index);
    const placeInNav = index + 1;
    const displayToRight = navItemsLength - placeInNav >= numberOfCols;
    const displayToLeft = placeInNav > numberOfCols;
    const leftSide = placeInNav <= navItemsLength / 2;

    if (displayToRight) {
      return { side: "left-0", position: "relative", cols: numberOfCols };
    } else if (displayToLeft) {
      return { side: "right-0", position: "relative", cols: numberOfCols };
    } else if (leftSide) {
      return { side: "left-0", position: "", cols: numberOfCols };
    } else {
      return { side: "right-0", position: "", cols: numberOfCols };
    }
  };

  const rootElement = useRef<HTMLDivElement | null>(null);
  const activeButton = useRef<HTMLButtonElement>(null);
  useEffect(() => {
    const handleClick = (event: Event) => {
      const cTarget = event.target as Element;
      if (
        (cTarget instanceof HTMLElement &&
          !rootElement.current?.contains(cTarget)) ||
        cTarget.tagName === "A" ||
        cTarget.tagName === "SPAN"
      ) {
        setActive(-1);
      }
    };
    window.addEventListener("click", handleClick);

    return () => {
      window.removeEventListener("click", handleClick);
    };
  }, []);

  useEffect(() => {
    const handlePress = (event: KeyboardEvent) => {
      const { key } = event;
      if (key === "Escape") {
        activeButton?.current?.focus();
        setActive(-1);
      }
    };
    window.addEventListener("keyup", handlePress);

    return () => {
      window.removeEventListener("keyup", handlePress);
    };
  }, []);

  // Close menu when scrolled off screen
  useEffect(() => {
    const handleScroll = ({ currentTarget }: Event) => {
      if (!rootElement.current) {
        return;
      }

      const headerDimensions = rootElement.current.getBoundingClientRect();

      const headerDistanceFromTop = headerDimensions.top;
      const headerHeight = headerDimensions.height;

      const scrollingDown = (currentTarget as Window).scrollY > 0;
      const headerOutOfView = headerDistanceFromTop + headerHeight < 0;

      // offScreen
      const offScreen = headerOutOfView && scrollingDown;
      if (offScreen) {
        // Close menu
        setActive(-1);
      }
    };

    // TODO Throttle event listeners
    window.addEventListener("scroll", handleScroll, { passive: true });

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  if (suppressNav || !hasSubPages) {
    return null;
  }

  return (
    <div
      className="hidden xl:block xl:-mt-px px-16 md:px-24 bg-blue text-white"
      ref={rootElement}
    >
      <nav className="relative container-4xl h-full" aria-label="Secondary">
        <ul className="flex justify-between -mx-16 h-full">
          {currentItems.subpages.map(({ subpages, ...rest }, index) => {
            const isActive = isItemActive(index);
            const isSelected =
              isItemSelected(index) || topLevelSelected === index;
            return (
              <li className={dropdownDirection(index).position} key={index}>
                <NavItem
                  {...rest}
                  currentRef={isActive ? activeButton : null}
                  isActive={isActive}
                  isSelected={isSelected}
                  onClick={() => setActive(isActive ? -1 : index)}
                  type={subpages.length ? "button" : "link"}
                />
                <DropDownMenu
                  {...{
                    subpages,
                    ...{
                      ...rest,
                      isActive,
                      dropdownDirection: dropdownDirection(index),
                    },
                  }}
                />
              </li>
            );
          })}
        </ul>
      </nav>
    </div>
  );
};

export default SecondaryNav;

Last updated