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 1188x 8x 8x 8x 8x 8x   8x 8x 8x 8x 8x 8x     8x   345x               345x                                               345x         345x     345x   345x           345x 4x   3x 3x 3x 3x 3x       345x 4x 4x       4x                                                                 8x 235x  
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;