All files / react-app/src/components/Banner banner.tsx

100% Statements 29/29
83.33% Branches 10/12
100% Functions 9/9
100% Lines 29/29

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 84 85 86 87 88 89 90 91 92                            72x 10x 10x       2x 2x       72x   72x 10x     72x 14x   14x 14x 14x   14x 2x     14x 6x 6x 7x     6x 6x 6x         14x         14x 1x 1x         14x     14x                                     10x    
import { useEffect, useRef, useState } from "react";
import { Alert, AlertVariant } from "../Alert";
import { Check, X } from "lucide-react";
import { useLocation } from "react-router";
import { Observer } from "@/utils/basic-observable";
 
export type Banner = {
  header: string;
  body: string;
  variant?: AlertVariant;
  pathnameToDisplayOn: string;
};
 
class BannerObserver extends Observer<Banner> {
  create = (data: Banner) => {
    this.publish(data);
    this.observed = { ...data };
  };
 
  dismiss = () => {
    this.publish(null);
    this.observed = null;
  };
}
 
const bannerState = new BannerObserver();
 
export const banner = (newBanner: Banner) => {
  return bannerState.create(newBanner);
};
 
export const Banner = () => {
  const bannerObserverRef = useRef<(() => void) | null>(null);
 
  const [activeBanner, setActiveBanner] = useState<Banner | null>(null);
  const { pathname } = useLocation();
  const previousPathRef = useRef(pathname);
 
  const onClose = () => {
    bannerState.dismiss();
  };
 
  useEffect(() => {
    Eif (bannerObserverRef.current === null) {
      bannerObserverRef.current = bannerState.subscribe((banner) => {
        setActiveBanner(banner);
      });
 
      return () => {
        bannerObserverRef.current?.();
        bannerObserverRef.current = null;
      };
    }
  }, []);
 
  useEffect(() => {
    // only run cleanup if:
    // 1. we've actually navigated (pathname changed from previous render)
    // 2. there's an active banner to clean up
    // 3. the banner's target pathname doesn't match where we navigated to
    if (pathname !== previousPathRef.current) {
      Eif (activeBanner && activeBanner.pathnameToDisplayOn !== pathname) {
        onClose();
      }
    }
 
    // store current pathname for next render's comparison
    previousPathRef.current = pathname;
  }, [pathname, activeBanner]);
 
  if (activeBanner && activeBanner.pathnameToDisplayOn === pathname) {
    return (
      <Alert variant={activeBanner.variant} className="mt-4 mb-8 flex-row text-sm">
        <div className="flex items-start justify-between">
          <Check />
          <div className="ml-2 w-full">
            <h3 className="text-lg font-bold" data-testid="banner-header">
              {activeBanner.header}
            </h3>
            <p data-testid="banner-body">{activeBanner.body}</p>
          </div>
          <button onClick={onClose} data-testid="banner-close">
            <X size={20} />
          </button>
        </div>
      </Alert>
    );
  }
 
  return null;
};