All files / components/CopyToClipboard CopyToClipboard.svelte

100% Statements 78/78
60% Branches 3/5
100% Functions 1/1
100% Lines 78/78

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 791x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
<script lang="ts">
  import "./CopyToClipboard.scss";
  import Button from "$lib/components/Button";
  import Icon from "$lib/components/Icon";
  import { url as copyIcon } from "$icons/content_copy";
  import { url as successIcon } from "$icons/check_circle";
  import { url as errorIcon } from "$icons/error";
 
  export let contentToCopy: string;
  export let successMessage = "";
 
  type Status = "pending" | "success" | "error";
  let status: Status = "pending";
 
  type StatusDisplayMap = {
    [statusType in Status]: {
      icon: string;
      buttonClass: string;
      messageClass: string;
      messageContent: string;
      ariaAlert: boolean;
    };
  };
  const statusDisplayMap: StatusDisplayMap = {
    pending: {
      icon: copyIcon,
      buttonClass: "",
      messageClass: "display--none",
      messageContent: "",
      ariaAlert: false,
    },
    success: {
      icon: successIcon,
      buttonClass: "copy-success-icon",
      messageClass: "display--inline copy-success-message",
      messageContent: successMessage ? successMessage : "Copied to clipboard!",
      ariaAlert: true,
    },
    error: {
      icon: errorIcon,
      buttonClass: "copy-error-icon",
      messageClass: "display--inline copy-error-message",
      messageContent: "Failed to copy to clipboard.",
      ariaAlert: true,
    },
  };
 
  $: statusDisplay = statusDisplayMap[status];
 
  const copyToClipboard = async (content: string) => {
    try {
      await navigator.clipboard.writeText(content);
      status = "success";
    } catch (err) {
      console.error("failed to copy: ", err);
      status = "error";
    }
    setTimeout(() => (status = "pending"), 3000);
  };
</script>
 
<Button
  class={statusDisplay.buttonClass}
  unstyled={true}
  on:click={() => copyToClipboard(contentToCopy)}
>
  <span class="usa-sr-only">Copy to Clipboard</span><Icon
    src={statusDisplay.icon}
    title="Copy to clipboard"
    aria-hidden="true"
  />
</Button>
<div
  class="font-sans-3xs {statusDisplay.messageClass}"
  role={statusDisplay.ariaAlert ? "alert" : ""}
>
  {statusDisplay.messageContent}
</div>