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   342x               342x                                               342x         342x     342x   342x           342x 4x   3x 3x 3x 3x 3x       342x 4x 4x       4x                                                                 8x 233x  
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;