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

100% Statements 35/35
75% Branches 3/4
100% Functions 6/6
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 116 117 1187x 7x 7x 7x 7x 7x   7x 7x 7x 7x 7x 7x     7x   153x               153x                                               153x         153x     153x   153x           153x 4x   3x 3x 3x 3x 3x       153x 4x 4x       4x                                                                 7x 93x  
import { Collapse, styled } from "@mui/material";
import { visuallyHidden } from "@mui/utils";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import Alert from "components/Alert";
import Button from "components/Button";
import MarkdownInput, { MarkdownInputProps } from "components/MarkdownInput";
import { RpcError } from "grpc-web";
import { useTranslation } from "i18n";
import { COMMUNITIES, GLOBAL } from "i18n/namespaces";
import React, { useRef } from "react";
import { useForm } from "react-hook-form";
import { service } from "service";
import { theme } from "theme";
 
import { PostReplyRes } from "../../../proto/threads_pb";
import { threadKey } from "../../queryKeys";
 
const StyledForm = styled("form")(() => ({
  display: "flex",
  flexDirection: "column",
  "& > :not(:last-child)": {
    marginBlockEnd: theme.spacing(1),
  },
}));
 
const StyledButtonsContainer = styled("div")(() => ({
  display: "flex",
  gap: 1,
  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 {
    control,
    handleSubmit,
    reset: resetForm,
  } = useForm<CommentData>({
    mode: "onBlur",
  });
  const resetInputRef: MarkdownInputProps["resetInputRef"] = useRef(null);
 
  const queryClient = useQueryClient();
  const {
    error,
    isPending,
    mutate: postComment,
    reset: resetMutation,
  } = useMutation<PostReplyRes.AsObject, RpcError, CommentData>({
    mutationFn: ({ content }) => service.threads.postReply(threadId, content),
    onSuccess() {
      queryClient.invalidateQueries({ queryKey: 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}>
      <StyledForm 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")}
        />
        <StyledButtonsContainer>
          {hideable && (
            <Button onClick={onClose} variant="outlined">
              {t("global:close")}
            </Button>
          )}
          <Button loading={isPending} type="submit">
            {t("communities:comment")}
          </Button>
        </StyledButtonsContainer>
      </StyledForm>
    </Collapse>
  );
}
 
const CommentForm = React.forwardRef(InternalCommentForm);
export default CommentForm;