• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CBIIT / crdc-datahub-ui / 26241948143

21 May 2026 05:23PM UTC coverage: 84.756% (+5.7%) from 79.008%
26241948143

push

github

web-flow
Merge pull request #998 from CBIIT/3.6.0

3.6.0 Release

6022 of 6644 branches covered (90.64%)

Branch coverage included in aggregate %.

3572 of 3715 new or added lines in 81 files covered. (96.15%)

15 existing lines in 6 files now uncovered.

35928 of 42851 relevant lines covered (83.84%)

243.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.51
/src/components/Questionnaire/ReviewFormDialog.tsx
1
import { LoadingButton } from "@mui/lab";
1✔
2
import { Box, Button, ButtonProps, DialogProps, Typography, styled } from "@mui/material";
1✔
3
import { isEqual } from "lodash";
1✔
4
import { FC, ReactNode, memo, useMemo } from "react";
1✔
5
import { Controller, useForm } from "react-hook-form";
1✔
6

7
import Dialog from "../GenericDialog";
1✔
8
import StyledHelperText from "../StyledFormComponents/StyledHelperText";
1✔
9
import BaseOutlinedInput from "../StyledFormComponents/StyledOutlinedInput";
1✔
10

11
const StyledOutlinedInput = styled(BaseOutlinedInput)({
1✔
12
  marginTop: "24px",
1✔
13
  width: "fit-content",
1✔
14
  maxWidth: "100%",
1✔
15
  "&.MuiInputBase-multiline": {
1✔
16
    padding: "12px",
1✔
17
    alignItems: "flex-start",
1✔
18
  },
1✔
19
  "& textarea.MuiInputBase-inputMultiline": {
1✔
20
    resize: "both",
1✔
21
    overflow: "auto !important",
1✔
22
    padding: 0,
1✔
23
    lineHeight: "25px",
1✔
24
    width: "min(600px, calc(100vw - 150px))",
1✔
25
    minWidth: "min(750px, calc(100vw - 150px))",
1✔
26
    maxWidth: "min(1440px, 80vw)",
1✔
27
    height: "min(375px, calc(100vh - 340px))",
1✔
28
    minHeight: "clamp(100px, calc(100vh - 340px), 375px)",
1✔
29
    maxHeight: "min(500px, calc(100vh - 340px))",
1✔
30
    boxSizing: "border-box",
1✔
31
  },
1✔
32
});
1✔
33

34
const StyledCharacterCount = styled(Box)({
1✔
35
  display: "flex",
1✔
36
  justifyContent: "flex-end",
1✔
37
  alignItems: "flex-start",
1✔
38
  gap: "8px",
1✔
39
  marginTop: "4px",
1✔
40
  width: 0,
1✔
41
  minWidth: "100%",
1✔
42
  overflow: "hidden",
1✔
43
});
1✔
44

45
const StyledErrorText = styled(StyledHelperText)({
1✔
46
  marginTop: 0,
1✔
47
  flex: 1,
1✔
48
  minWidth: 0,
1✔
49
  wordBreak: "break-word",
1✔
50
});
1✔
51

52
const StyledCountLabel = styled(Typography)({
1✔
53
  fontSize: "12px",
1✔
54
  lineHeight: "20px",
1✔
55
  whiteSpace: "nowrap",
1✔
56
});
1✔
57

58
const StyledDialog = styled(Dialog)({
1✔
59
  "& .MuiDialog-paper": {
1✔
60
    width: "fit-content",
1✔
61
    maxWidth: "calc(100% - 64px)",
1✔
62
    maxHeight: "calc(100vh - 64px)",
1✔
63
    borderRadius: "8px",
1✔
64
    "& .MuiDialogContent-root": {
1✔
65
      overflow: "hidden",
1✔
66
    },
1✔
67
  },
1✔
68
});
1✔
69

70
const MAX_REVIEW_COMMENT_LIMIT = 10_000;
1✔
71

72
type ReviewFormFields = {
73
  reviewComment: string;
74
};
75

76
type Props = {
77
  header?: string;
78
  confirmText?: string;
79
  confirmButtonProps?: Omit<ButtonProps, "children" | "onClick">;
80
  loading?: boolean;
81
  onCancel?: () => void;
82
  onSubmit?: (reviewComment: string) => void;
83
  children?: ReactNode;
84
} & Omit<DialogProps, "onClose" | "onSubmit" | "children" | "title">;
85

86
const ReviewFormDialog: FC<Props> = ({
1✔
87
  open,
201✔
88
  header,
201✔
89
  confirmText = "Confirm",
201✔
90
  confirmButtonProps = {},
201✔
91
  loading,
201✔
92
  onCancel,
201✔
93
  onSubmit,
201✔
94
  children,
201✔
95
  ...rest
201✔
96
}) => {
201✔
97
  const {
201✔
98
    handleSubmit,
201✔
99
    watch,
201✔
100
    control,
201✔
101
    reset,
201✔
102
    formState: { errors },
201✔
103
  } = useForm<ReviewFormFields>({
201✔
104
    mode: "onSubmit",
201✔
105
    reValidateMode: "onSubmit",
201✔
106
    defaultValues: {
201✔
107
      reviewComment: "",
201✔
108
    },
201✔
109
  });
201✔
110

111
  const reviewComment = watch("reviewComment");
201✔
112
  const reviewCommentLengthLabel = useMemo(
201✔
113
    () =>
201✔
114
      Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(reviewComment?.length || 0),
125✔
115
    [reviewComment]
201✔
116
  );
201✔
117
  const reviewCommentLimitLabel = Intl.NumberFormat("en-US", {
201✔
118
    maximumFractionDigits: 0,
201✔
119
  }).format(MAX_REVIEW_COMMENT_LIMIT);
201✔
120

121
  const handleOnSubmit = (data: ReviewFormFields) => {
201✔
122
    onSubmit?.(data.reviewComment);
3✔
123
  };
3✔
124

125
  const handleOnCancel = () => {
201✔
126
    onCancel?.();
2✔
127
  };
2✔
128

129
  return (
201✔
130
    <StyledDialog
201✔
131
      open={open}
201✔
132
      onClose={handleOnCancel}
201✔
133
      TransitionProps={{ onExited: () => reset() }}
201✔
134
      title={header}
201✔
135
      scroll="body"
201✔
136
      actions={
201✔
137
        <>
201✔
138
          <Button
201✔
139
            data-testid="review-form-dialog-cancel-button"
201✔
140
            onClick={handleOnCancel}
201✔
141
            disabled={loading}
201✔
142
          >
143
            Cancel
144
          </Button>
201✔
145
          <LoadingButton
201✔
146
            data-testid="review-form-dialog-confirm-button"
201✔
147
            onClick={handleSubmit(handleOnSubmit)}
201✔
148
            disabled={!reviewComment?.trim()?.length || loading}
201✔
149
            loading={loading}
201✔
150
            {...confirmButtonProps}
201✔
151
          >
152
            {confirmText}
201✔
153
          </LoadingButton>
201✔
154
        </>
201✔
155
      }
156
      {...rest}
201✔
157
    >
158
      <Controller
201✔
159
        name="reviewComment"
201✔
160
        control={control}
201✔
161
        rules={{
201✔
162
          validate: {
201✔
163
            required: (v: string) => v.trim() !== "" || "This field is required",
201!
164
            maxLength: (v: string) =>
201✔
165
              v.trim().length <= MAX_REVIEW_COMMENT_LIMIT ||
3!
NEW
166
              `Maximum of ${reviewCommentLimitLabel} characters allowed`,
×
167
          },
201✔
168
        }}
201✔
169
        render={({ field }) => (
201✔
170
          <StyledOutlinedInput
95✔
171
            {...field}
95✔
172
            inputProps={{
95✔
173
              maxLength: MAX_REVIEW_COMMENT_LIMIT,
95✔
174
              "aria-label": "Review comment input",
95✔
175
            }}
95✔
176
            name="reviewComment"
95✔
177
            placeholder={`${reviewCommentLimitLabel} characters allowed`}
95✔
178
            data-testid="review-comment"
95✔
179
            sx={{ paddingY: "16px" }}
95✔
180
            required
95✔
181
            multiline
95✔
182
          />
183
        )}
201✔
184
      />
185

186
      <StyledCharacterCount>
201✔
187
        {errors?.reviewComment?.message?.length > 0 && (
201!
NEW
188
          <StyledErrorText data-testid="review-comment-dialog-error">
×
NEW
189
            {errors.reviewComment.message}
×
NEW
190
          </StyledErrorText>
×
191
        )}
192
        <StyledCountLabel data-testid="review-comment-character-count">
201✔
193
          {reviewCommentLengthLabel} / {reviewCommentLimitLabel}
201✔
194
        </StyledCountLabel>
201✔
195
      </StyledCharacterCount>
201✔
196

197
      {children}
201✔
198
    </StyledDialog>
201✔
199
  );
200
};
201✔
201

202
export default memo<Props>(ReviewFormDialog, isEqual);
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc