All files / client/src/components ScrollToTopButton.tsx

96.15% Statements 25/26
100% Branches 2/2
100% Functions 16/16
96% Lines 24/25

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            520x 1975x 1975x   1975x 682x 225x   682x 682x     1975x 1834x                   1x                 131x   131x             131x                       131x 131x         131x 131x       131x       1x 77x 77x   77x 77x      
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { ReactElement, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import { scrollToTop } from "client/utils/scrollToTop";
 
export const ScrollToTopButton = (): ReactElement | null => {
  const { t } = useTranslation();
  const [visible, setVisible] = useState(false);
 
  useEffect(() => {
    const onScroll = (): void => {
      setVisible(window.scrollY > 0);
    };
    window.addEventListener("scroll", onScroll);
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
 
  if (!visible) {
    return null;
  }
 
  return (
    <FloatingButton aria-label={t("button.scrollToTop")} onClick={scrollToTop}>
      <Icon aria-hidden="true" icon="chevron-up" />
    </FloatingButton>
  );
};
 
const FloatingButton = styled.button`
  position: fixed;
  bottom: 20px;
  right: 20px;
  border-radius: 100px;
  border: none;
  cursor: pointer;
  width: 48px;
  height: 48px;
  box-shadow: ${(props) => props.theme.shadowHigher};
 
  @media (max-width: ${(props) => props.theme.breakpointPhone}) {
    width: 36px;
    height: 36px;
    bottom: 12px;
    right: 12px;
  }
 
  background: ${(props) => props.theme.buttonPrimaryBackground};
  z-index: 2;
 
  /* The tap-highlight overlay paints the bounding box, which shows as a square
     behind the round button; the :active style already gives press feedback */
  -webkit-tap-highlight-color: transparent;
 
  /* Touch devices emulate :hover on tap and keep it active after the tap,
     so only style hover when the device can actually hover */
  @media (hover: hover) {
    &:hover,
    &:focus-visible {
      background: ${(props) => props.theme.buttonPrimaryHover};
      color: ${(props) => props.theme.textMain};
    }
  }
 
  &:focus-visible {
    background: ${(props) => props.theme.buttonPrimaryHover};
    color: ${(props) => props.theme.textMain};
  }
 
  &:active {
    background: ${(props) => props.theme.buttonPrimaryClicked};
  }
`;
 
const Icon = styled(FontAwesomeIcon)`
  color: ${(props) => props.theme.buttonPrimaryText};
  font-size: ${(props) => props.theme.iconSizeNormal};
 
  @media (max-width: ${(props) => props.theme.breakpointPhone}) {
    font-size: ${(props) => props.theme.iconSizeSmall};
  }
`;