All files / app/components/Button Button.tsx

91.3% Statements 21/23
93.75% Branches 15/16
100% Functions 3/3
90.9% Lines 20/22

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 9745x 45x 45x 45x 45x 45x   45x   196x                                                                                         1774x 1774x 1774x 1774x   111x 111x 111x       111x     1774x     1774x                                             45x 45x  
import { Button as MuiButton, ButtonProps, useTheme } from "@material-ui/core";
import classNames from "classnames";
import Sentry from "platform/sentry";
import React, { ElementType, ForwardedRef, forwardRef } from "react";
import { useIsMounted, useSafeState } from "utils/hooks";
import makeStyles from "utils/makeStyles";
 
import CircularProgress from "../CircularProgress";
 
const useStyles = makeStyles((theme) => ({
  contained: {
    borderRadius: theme.shape.borderRadius,
    boxShadow: "0px 0px 5px rgba(0, 0, 0, 0.25)",
  },
  loading: {
    height: theme.typography.button.fontSize,
  },
  spinner: {
    position: "absolute",
    bottom: 0,
    left: 0,
    right: 0,
    top: 0,
    margin: "auto",
  },
  root: {
    minHeight: `calc(calc(${theme.typography.button.lineHeight} * ${
      theme.typography.button.fontSize
    }) + ${theme.typography.pxToRem(12)})`, //from padding
  },
}));
 
//type generics required to allow component prop
//see https://github.com/mui-org/material-ui/issues/15827
export type AppButtonProps<
  D extends ElementType = "button",
  P = Record<string, unknown>
> = ButtonProps<D, P> & {
  loading?: boolean;
};
 
function _Button<D extends ElementType = "button", P = Record<string, unknown>>(
  {
    children,
    disabled,
    className,
    loading,
    onClick,
    variant = "contained",
    color = "primary",
    ...otherProps
  }: AppButtonProps<D, P>,
  ref: ForwardedRef<any> // eslint-disable-line
) {
  const isMounted = useIsMounted();
  const [waiting, setWaiting] = useSafeState(isMounted, false);
  const classes = useStyles();
  const theme = useTheme();
  async function asyncOnClick(event: unknown) {
    try {
      setWaiting(true);
      await onClick(event);
    } catch (e) {
      Sentry.captureException(e);
    } finally {
      setWaiting(false);
    }
  }
  Iif (variant !== "contained" && color !== "primary") {
    throw new Error("Only contained buttons should have color.");
  }
  return (
    <MuiButton
      {...otherProps}
      ref={ref}
      onClick={onClick && asyncOnClick}
      disabled={disabled ? true : loading || waiting}
      className={classNames(classes.root, className, {
        [classes.contained]: variant === "contained",
      })}
      variant={variant}
      color={variant === "contained" ? color : undefined}
    >
      {(loading || waiting) && (
        <CircularProgress
          size={theme.typography.button.fontSize}
          className={classes.spinner}
        />
      )}
      {children}
    </MuiButton>
  );
}
 
const Button = forwardRef(_Button) as typeof _Button;
export default Button;