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

100% Statements 34/34
75% Branches 3/4
100% Functions 5/5
100% Lines 32/32

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 109 110 111 112 113 114 115 1168x 8x 8x 8x 8x 8x   8x 8x   8x 8x 8x 8x 8x   23x                                                               106x 106x         106x     106x   106x           106x 4x     3x 3x 3x 3x 3x         106x 4x 4x       4x                                                         8x 104x  
import { Collapse } from "@mui/material";
import { visuallyHidden } from "@mui/utils";
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 InternalCommentForm(
  { 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) => {
    const trimmedValue = data.content.trim();
    const newData = {
      content: trimmedValue,
    };
 
    postComment(newData);
  });
 
  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>}
        <span style={visuallyHidden} id={`comment-${threadId}-reply-label`}>
          {t("communities:write_comment_a11y_label")}
        </span>
        <MarkdownInput
          control={control}
          id={`comment-${threadId}-reply`}
          resetInputRef={resetInputRef}
          labelId={`comment-${threadId}-reply-label`}
          name="content"
          required={t("communities:fill_out_comment")}
        />
        <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(InternalCommentForm);
export default CommentForm;