All files / app/features/communities/discussions CommentForm.tsx

100% Statements 31/31
83.33% Branches 5/6
100% Functions 5/5
100% Lines 30/30

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 102 103 104 105 106 107 108 1098x 8x 8x 8x 8x   8x 8x   8x 8x 8x 8x 8x   20x                                                               152x 152x         152x     152x   152x           152x 3x     2x 2x 2x 2x 2x         152x 3x     152x                                                 8x 8x  
import { Collapse, Typography } from "@material-ui/core";
import Alert from "components/Alert";
import Button from "components/Button";
import MarkdownInput, { MarkdownInputProps } from "components/MarkdownInput";
import { threadKey } from "features/queryKeys";
import { RpcError } from "grpc-web";
import { useTranslation } from "i18n";
import { COMMUNITIES, GLOBAL } from "i18n/namespaces";
import { PostReplyRes } from "proto/threads_pb";
import React, { useRef } from "react";
import { useForm } from "react-hook-form";
import { useMutation, useQueryClient } from "react-query";
import { service } from "service";
import makeStyles from "utils/makeStyles";
 
const useStyles = makeStyles((theme) => ({
  commentForm: {
    display: "flex",
    flexDirection: "column",
    "& > :not(:last-child)": {
      marginBlockEnd: theme.spacing(1),
    },
  },
  buttonsContainer: {
    display: "flex",
    justifyContent: "flex-end",
    "& > * + *": {
      marginInlineStart: theme.spacing(2),
    },
  },
}));
 
interface CommentFormProps {
  hideable?: boolean;
  onClose?(): void;
  shown?: boolean;
  threadId: number;
}
 
interface CommentData {
  content: string;
}
 
function _CommentForm(
  { hideable = false, onClose, shown = false, threadId }: CommentFormProps,
  ref: React.ForwardedRef<HTMLFormElement>
) {
  const { t } = useTranslation([GLOBAL, COMMUNITIES]);
  const classes = useStyles();
  const {
    control,
    handleSubmit,
    reset: resetForm,
  } = useForm<CommentData>({
    mode: "onBlur",
  });
  const resetInputRef: MarkdownInputProps["resetInputRef"] = useRef(null);
 
  const queryClient = useQueryClient();
  const {
    error,
    isLoading,
    mutate: postComment,
    reset: resetMutation,
  } = useMutation<PostReplyRes.AsObject, RpcError, CommentData>(
    ({ content }) => service.threads.postReply(threadId, content),
    {
      onSuccess() {
        queryClient.invalidateQueries(threadKey(threadId));
        resetForm();
        resetInputRef.current?.();
        resetMutation();
        onClose?.();
      },
    }
  );
 
  const onSubmit = handleSubmit((data) => {
    postComment(data);
  });
 
  return (
    <Collapse data-testid={`comment-${threadId}-comment-form`} in={shown}>
      <form className={classes.commentForm} onSubmit={onSubmit} ref={ref}>
        {error && <Alert severity="error">{error.message}</Alert>}
        <Typography id={`comment-${threadId}-reply-label`} variant="srOnly">
          {t("communities:write_comment_a11y_label")}
        </Typography>
        <MarkdownInput
          control={control}
          id={`comment-${threadId}-reply`}
          resetInputRef={resetInputRef}
          labelId={`comment-${threadId}-reply-label`}
          name="content"
        />
        <div className={classes.buttonsContainer}>
          {hideable && <Button onClick={onClose}>{t("global:close")}</Button>}
          <Button loading={isLoading} type="submit">
            {t("communities:comment")}
          </Button>
        </div>
      </form>
    </Collapse>
  );
}
 
const CommentForm = React.forwardRef(_CommentForm);
export default CommentForm;