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

CBIIT / crdc-datahub-ui / 23754719636

30 Mar 2026 04:07PM UTC coverage: 83.116% (-0.001%) from 83.117%
23754719636

Pull #970

github

web-flow
Merge aa30a364e into db21440be
Pull Request #970: CRDCDH-3616 Add Pending Image DeIdentification to Submission Request Form Approval dialog

5923 of 6497 branches covered (91.17%)

Branch coverage included in aggregate %.

21 of 24 new or added lines in 2 files covered. (87.5%)

133 existing lines in 3 files now uncovered.

34930 of 42655 relevant lines covered (81.89%)

245.98 hits per line

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

89.5
/src/components/Questionnaire/ApproveFormDialog.tsx
1
import { LoadingButton } from "@mui/lab";
1✔
2
import {
1✔
3
  Button,
4
  Checkbox,
5
  CheckboxProps,
6
  DialogProps,
7
  FormControlLabel,
8
  OutlinedInputProps,
9
  styled,
10
} from "@mui/material";
11
import { isEqual } from "lodash";
1✔
12
import { FC, memo } from "react";
1✔
13
import { Controller, useForm } from "react-hook-form";
1✔
14

15
import CheckboxCheckedIconSvg from "../../assets/icons/checkbox_checked.svg?url";
1✔
16
import Dialog from "../GenericDialog";
1✔
17
import StyledHelperText from "../StyledFormComponents/StyledHelperText";
1✔
18
import BaseOutlinedInput from "../StyledFormComponents/StyledOutlinedInput";
1✔
19

20
const UncheckedIcon = styled("div")<{ readOnly?: boolean }>(({ readOnly }) => ({
1✔
21
  outline: "2px solid #1D91AB",
1,090✔
22
  outlineOffset: -2,
1,090✔
23
  width: "24px",
1,090✔
24
  height: "24px",
1,090✔
25
  backgroundColor: readOnly ? "#E5EEF4" : "initial",
1,090✔
26
  color: "#083A50",
1,090✔
27
  cursor: readOnly ? "not-allowed" : "pointer",
1,090✔
28
}));
1✔
29

30
const CheckedIcon = styled("div")<{ readOnly?: boolean }>(({ readOnly }) => ({
1✔
31
  backgroundImage: `url("${CheckboxCheckedIconSvg}")`,
3✔
32
  backgroundSize: "auto",
3✔
33
  backgroundRepeat: "no-repeat",
3✔
34
  width: "24px",
3✔
35
  height: "24px",
3✔
36
  backgroundColor: readOnly ? "#E5EEF4" : "initial",
3!
37
  color: "#1D91AB",
3✔
38
  cursor: readOnly ? "not-allowed" : "pointer",
3!
39
}));
1✔
40

41
const StyledDialog = styled(Dialog)({
1✔
42
  "& .MuiDialog-paper": {
1✔
43
    maxWidth: "none",
1✔
44
    borderRadius: "8px",
1✔
45
    width: "567px !important",
1✔
46
  },
1✔
47
});
1✔
48

49
const StyledCheckbox = styled(Checkbox)({
1✔
50
  "&.MuiCheckbox-root": {
1✔
51
    padding: "10px",
1✔
52
  },
1✔
53
  "& .MuiSvgIcon-root": {
1✔
54
    fontSize: "24px",
1✔
55
  },
1✔
56
  "&.Mui-disabled": {
1✔
57
    cursor: "not-allowed",
1✔
58
  },
1✔
59
});
1✔
60

61
const StyledOutlinedInput = styled(BaseOutlinedInput, {
1✔
62
  shouldForwardProp: (prop) => prop !== "resize" && prop !== "rowHeight",
1✔
63
})<OutlinedInputProps & { resize: boolean; rowHeight?: number }>(
1✔
64
  ({ resize, rowHeight = 25, rows, minRows, maxRows }) => ({
1✔
65
    marginTop: "24px",
546✔
66
    "&.MuiInputBase-multiline": {
546✔
67
      padding: "12px",
546✔
68
    },
546✔
69
    "& .MuiInputBase-inputMultiline": {
546✔
70
      resize: resize ? "vertical" : "none",
546!
71
      minHeight: resize && rowHeight ? `${(+rows || +minRows || 1) * rowHeight}px` : 0,
546!
72
      maxHeight: resize && maxRows && rowHeight ? `${+maxRows * rowHeight}px` : "none",
546!
73
      overflow: "auto",
546✔
74
    },
546✔
75
    "&.MuiInputBase-multiline .MuiInputBase-input": {
546✔
76
      lineHeight: `${rowHeight}px`,
546✔
77
      padding: 0,
546✔
78
    },
546✔
79
  })
546✔
80
);
1✔
81

82
export type FormInput = {
83
  pendingModelChange: boolean;
84
  pendingImageDeIdentification: boolean;
85
  reviewComment: string;
86
};
87

88
type Props = {
89
  loading?: boolean;
90
  onCancel?: () => void;
91
  onSubmit?: (data: FormInput) => void;
92
} & DialogProps;
93

94
const ApproveFormDialog: FC<Props> = ({ open, loading, onCancel, onSubmit, onClose, ...rest }) => {
1✔
95
  const {
546✔
96
    handleSubmit,
546✔
97
    watch,
546✔
98
    control,
546✔
99
    reset,
546✔
100
    formState: { errors },
546✔
101
  } = useForm<FormInput>({
546✔
102
    mode: "onSubmit",
546✔
103
    reValidateMode: "onSubmit",
546✔
104
    defaultValues: {
546✔
105
      pendingModelChange: false,
546✔
106
      pendingImageDeIdentification: false,
546✔
107
      reviewComment: "",
546✔
108
    },
546✔
109
  });
546✔
110
  const reviewComment = watch("reviewComment");
546✔
111

112
  const handleOnSubmit = (data: FormInput) => {
546✔
113
    onSubmit?.(data);
4✔
114
    reset();
4✔
115
  };
4✔
116

117
  const handleOnCancel = () => {
546✔
118
    reset();
1✔
119
    onCancel?.();
1✔
120
  };
1✔
121

122
  return (
546✔
123
    <StyledDialog
546✔
124
      open={open}
546✔
125
      onClose={onClose}
546✔
126
      scroll="body"
546✔
127
      title="Approve Submission Request"
546✔
128
      data-testid="approve-form-dialog"
546✔
129
      actions={
546✔
130
        <>
546✔
131
          <Button onClick={handleOnCancel} disabled={loading}>
546✔
132
            Cancel
133
          </Button>
546✔
134
          <LoadingButton
546✔
135
            onClick={handleSubmit(handleOnSubmit)}
546✔
136
            loading={loading}
546✔
137
            disabled={!reviewComment || loading}
546✔
138
            autoFocus
546✔
139
            data-testid="confirm-to-approve-button"
546✔
140
          >
141
            Confirm to Approve
142
          </LoadingButton>
546✔
143
        </>
546✔
144
      }
145
      {...rest}
546✔
146
    >
147
      <Controller
546✔
148
        name="reviewComment"
546✔
149
        control={control}
546✔
150
        rules={{
546✔
151
          validate: {
546✔
152
            required: (v: string) => v.trim() !== "" || "This field is required",
546!
153
            maxLength: (v: string) => v.trim().length <= 500 || "Maximum of 500 characters allowed",
546!
154
          },
546✔
155
        }}
546✔
156
        render={({ field }) => (
546✔
157
          <StyledOutlinedInput
546✔
158
            {...field}
546✔
159
            inputProps={{
546✔
160
              maxLength: 500,
546✔
161
              style: { height: "auto !important" },
546✔
162
              "aria-label": "Review comment input",
546✔
163
            }}
546✔
164
            name="reviewComment"
546✔
165
            placeholder="500 characters allowed"
546✔
166
            minRows={5}
546✔
167
            maxRows={15}
546✔
168
            data-testid="review-comment"
546✔
169
            sx={{ paddingY: "16px" }}
546✔
170
            required
546✔
171
            multiline
546✔
172
            resize
546✔
173
          />
174
        )}
546✔
175
      />
176
      {errors?.reviewComment?.message?.length > 0 && (
546!
177
        <StyledHelperText data-testid="review-comment-dialog-error">
×
UNCOV
178
          {errors.reviewComment.message}
×
UNCOV
179
        </StyledHelperText>
×
180
      )}
181

182
      <Controller
546✔
183
        name="pendingModelChange"
546✔
184
        control={control}
546✔
185
        render={({ field }) => (
546✔
186
          <FormControlLabel
547✔
187
            control={
547✔
188
              <StyledCheckbox
547✔
189
                {...field}
547✔
190
                checkedIcon={<CheckedIcon readOnly={loading} />}
547✔
191
                icon={<UncheckedIcon readOnly={loading} />}
547✔
192
                disabled={loading}
547✔
193
                inputProps={
547✔
194
                  { "data-testid": "pendingModelChange-checkbox" } as CheckboxProps["inputProps"]
547✔
195
                }
547✔
196
              />
197
            }
198
            label="Require Data Model changes"
547✔
199
          />
200
        )}
546✔
201
      />
202
      {errors?.pendingModelChange?.message?.length > 0 && (
546!
203
        <StyledHelperText data-testid="pending-model-change-dialog-error">
×
UNCOV
204
          {errors.pendingModelChange.message}
×
UNCOV
205
        </StyledHelperText>
×
206
      )}
207

208
      <Controller
546✔
209
        name="pendingImageDeIdentification"
546✔
210
        control={control}
546✔
211
        render={({ field }) => (
546✔
212
          <FormControlLabel
546✔
213
            control={
546✔
214
              <StyledCheckbox
546✔
215
                {...field}
546✔
216
                checkedIcon={<CheckedIcon readOnly={loading} />}
546✔
217
                icon={<UncheckedIcon readOnly={loading} />}
546✔
218
                disabled={loading}
546✔
219
                inputProps={
546✔
220
                  {
546✔
221
                    "data-testid": "pendingImageDeIdentification-checkbox",
546✔
222
                  } as CheckboxProps["inputProps"]
546✔
223
                }
546✔
224
              />
225
            }
226
            label="Require Image De-identification Protocol"
546✔
227
          />
228
        )}
546✔
229
      />
230
      {errors?.pendingImageDeIdentification?.message?.length > 0 && (
546!
NEW
UNCOV
231
        <StyledHelperText data-testid="pending-image-de-identification-dialog-error">
×
NEW
UNCOV
232
          {errors.pendingImageDeIdentification.message}
×
NEW
UNCOV
233
        </StyledHelperText>
×
234
      )}
235
    </StyledDialog>
546✔
236
  );
237
};
546✔
238

239
export default memo<Props>(ApproveFormDialog, 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