All files / src/features/Merchants/MerchantSidePanel/Modals/Signature AutoGenerated.tsx

95% Statements 19/20
62.5% Branches 5/8
100% Functions 5/5
100% Lines 17/17

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 93 94 95 96 97 98 99 100 101                                            2x   2x 1x 1x   1x     1x           1x   1x 1x 1x 1x       1x 1x 1x       2x                                                           4x                         4x          
import { Box, Stack } from "@mui/material";
import GiveText from "@shared/Text/GiveText";
import { styled } from "@theme/v2/Provider";
import { Dispatch, SetStateAction, useEffect, useState } from "react";
import { SIGNATURE_FONTS, generateSignatureFile } from "./generateSignature";
 
interface Props {
  /** Full name of the merchant's primary account holder. */
  pahName: string;
  setFileToUpload: Dispatch<SetStateAction<File | null>>;
}
 
/**
 * GB-21610 — shown when the person signing is NOT the merchant's primary
 * account holder.
 *
 * The signer gets no choice of name or style: the signature is generated from
 * the PAH's full name so the image can never disagree with the name the
 * agreement panel attributes it to. It is still displayed before signing, so
 * whoever is signing on behalf can see exactly what will be attached.
 */
function AutoGenerated({ pahName, setFileToUpload }: Props) {
  const [preview, setPreview] = useState<string | null>(null);
 
  useEffect(() => {
    let revoked = false;
    let objectUrl: string | null = null;
 
    (async () => {
      // Nothing has rendered this face on screen yet, so wait for it before
      // measuring — otherwise the centring is computed against a fallback font.
      const file = await generateSignatureFile(
        pahName,
        SIGNATURE_FONTS[0].name,
        "signature.png",
        true,
      );
      Iif (revoked) return;
 
      setFileToUpload(file);
      Eif (file) {
        objectUrl = URL.createObjectURL(file);
        setPreview(objectUrl);
      }
    })();
 
    return () => {
      revoked = true;
      Eif (objectUrl) URL.revokeObjectURL(objectUrl);
    };
  }, [pahName, setFileToUpload]);
 
  return (
    <Box mt="16px">
      <PreviewContainer>
        {preview ? (
          <PreviewImage
            src={preview}
            alt={`Generated signature for ${pahName}`}
            data-testid="auto-generated-signature-preview"
          />
        ) : (
          <GiveText variant="bodyS" color="secondary">
            Generating signature…
          </GiveText>
        )}
      </PreviewContainer>
      <Stack mt="12px" gap="4px">
        <GiveText variant="bodyS" color="primary">
          {pahName}
        </GiveText>
        <GiveText variant="bodyXS" color="secondary">
          This signature is generated from the primary account holder&apos;s name
          on file.
        </GiveText>
      </Stack>
    </Box>
  );
}
 
export default AutoGenerated;
 
const PreviewContainer = styled(Box)(({ theme }) => ({
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  minHeight: "160px",
  padding: "12px",
  borderRadius: "8px",
  border: `1.5px solid ${theme.palette.border?.primary}`,
  // The plate is rendered on a fixed white background, so keep the surround
  // light in both themes rather than letting a dark surface frame it.
  background: "#ffffff",
}));
 
const PreviewImage = styled("img")({
  maxWidth: "100%",
  height: "auto",
  display: "block",
});