All files / src/components/ScrollToTopButton index.tsx

100% Statements 54/54
100% Branches 9/9
100% Functions 4/4
100% Lines 54/54

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 761x     1x     1x     1x             1x 15x 15x 15x 15x   15x 10x 6x 6x 6x   10x   10x 10x 10x 15x   15x 3x 3x 3x 3x 3x   15x 3x 3x 2x 3x 2x 2x 2x 3x   15x 15x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x   5x 5x     15x  
import { useState, useEffect } from 'react';
 
// Icons
import { ArrowBoxUpIcon } from '../icons/reacts';
 
// Utils
import { combineClasses } from '@shared/utils';
 
// Constants
import { KEYBOARD_EVENT } from '@shared/constants';
 
interface ScrollToTopButtonProps {
  className?: string;
  showAfterScrollY?: number;
}
 
export const ScrollToTopButton = ({
  className,
  showAfterScrollY = 100,
}: ScrollToTopButtonProps) => {
  const [shouldShow, setShouldShow] = useState(false);
 
  useEffect(() => {
    const handleScroll = () => {
      const scrollY = window.scrollY;
      setShouldShow(scrollY > showAfterScrollY);
    };
 
    window.addEventListener('scroll', handleScroll, { passive: true });
 
    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, [showAfterScrollY]);
 
  const handleScrollToTop = () => {
    window.scrollTo({
      top: 0,
      behavior: 'smooth',
    });
  };
 
  const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {
    if (
      event.key === KEYBOARD_EVENT.ENTER ||
      event.key === KEYBOARD_EVENT.SPACE
    ) {
      event.preventDefault();
      handleScrollToTop();
    }
  };
 
  return (
    shouldShow && (
      <button
        tabIndex={0}
        aria-label="Scroll to top of page"
        title="Scroll to top"
        className={combineClasses(
          'flex size-fit rounded-[12px]',
          'focus:border-none focus:outline-none',
          'focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2',
          'transition-all duration-200 ease-in-out',
          'hover:scale-105 active:scale-95',
          'disabled:opacity-50 disabled:cursor-not-allowed',
          className,
        )}
        onClick={handleScrollToTop}
        onKeyDown={handleKeyDown}
      >
        <ArrowBoxUpIcon dataTestId="arrow-icon" aria-hidden="true" />
      </button>
    )
  );
};