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   171x               171x                                               171x         171x     171x   171x           171x 4x   3x 3x 3x 3x 3x       171x 4x 4x       4x                                                                 7x 105x  
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;