All files / src/shared/components/LazyImage LazyImage.tsx

88.88% Statements 32/36
100% Branches 11/11
75% Functions 3/4
88.88% Lines 32/36

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 521x 1x                   1x   1x 29x 29x 29x   29x 23x 23x         23x 23x   23x 23x 29x   29x 29x 29x 29x 29x 29x 29x 29x 29x 29x 29x 29x 29x 29x 29x   29x   1x  
import { useState, useEffect, useRef } from 'react';
import './LazyImage.scss';
 
export interface ILazyImage {
  id?: string;
  className?: string;
  src?: string | null;
  alt?: string;
  onClick?: () => void;
}
 
const DEFAULT_LOGO = 'https://upload.wikimedia.org/wikipedia/commons/a/ac/No_image_available.svg';
 
const LazyImage: React.FC<ILazyImage> = ({ id, className, src = null, alt, onClick }) => {
  const [loaded, setLoaded] = useState(false);
  const [error, setError] = useState(false);
  const imgRef = useRef<HTMLImageElement>(null);
 
  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          observer.disconnect();
        }
      },
      { threshold: 0.1 }
    );
 
    if (imgRef.current) observer.observe(imgRef.current);
    return () => observer.disconnect();
  }, []);
 
  return (
    <>
      {!loaded && <div className="lazy-placeholder" data-testid="lazy-placeholder" aria-hidden="true" />}
      <img
        id={id}
        loading="lazy"
        src={error || !src ? DEFAULT_LOGO : src}
        alt={alt}
        ref={imgRef}
        onLoad={() => setLoaded(true)}
        onError={() => setError(true)}
        className={`img-fluid ${className} ${loaded ? 'lazyloaded' : 'lazyloading'}`}
        onClick={onClick}
      />
    </>
  );
};
 
export default LazyImage;