• 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

6.12
/src/components/Questionnaire/TableFileTypeAndExtensionInput.tsx
1
import {
1✔
2
  Autocomplete,
3
  TextField,
4
  TableCell,
5
  Tooltip,
6
  TooltipProps,
7
  styled,
8
  Paper,
9
} from "@mui/material";
10
import React, { FC, useEffect, useState, useRef } from "react";
1✔
11

12
import dropdownArrowsIcon from "../../assets/icons/dropdown_arrows.svg?url";
1✔
13
import { fileTypeExtensions } from "../../config/FileTypeConfig";
1✔
14
import useFormMode from "../../hooks/useFormMode";
1✔
15

16
const DropdownArrowsIcon = styled("div")(() => ({
1✔
17
  backgroundImage: `url("${dropdownArrowsIcon}")`,
×
18
  backgroundSize: "contain",
×
19
  backgroundRepeat: "no-repeat",
×
20
  width: "9.17px",
×
21
  height: "18px",
×
22
}));
1✔
23

24
type Props = {
25
  inputID: string;
26
  typeValue: string;
27
  extensionValue: string;
28
  options: string[];
29
  name: string;
30
  required?: boolean;
31
  helpText?: string;
32
  placeholder?: string;
33
  disableClearable?: boolean;
34
  onChange?: (e: React.SyntheticEvent, v: string, r: string) => void;
35
};
36

37
const StyledTooltip = styled((props: TooltipProps) => (
1✔
38
  <Tooltip classes={{ popper: props.className }} {...props} />
×
39
))(() => ({
1✔
40
  "& .MuiTooltip-tooltip": {
×
41
    marginTop: "4px !important",
×
42
    color: "#C93F08",
×
43
    background: "#FFFFFF",
×
44
    border: "1px solid #2B528B",
×
45
  },
×
46
  "& .MuiTooltip-arrow": {
×
47
    color: "#2B528B",
×
48
  },
×
49
}));
1✔
50

51
const StyledTableCell = styled(TableCell)(() => ({
1✔
52
  borderTop: "1px solid #6B7294 !important",
×
53
  borderRight: "1px solid #6B7294 !important",
×
54
  borderBottom: "none!important",
×
55
  borderLeft: "none!important",
×
56
  padding: "0",
×
57
  "& .MuiStack-root": {
×
58
    width: "auto",
×
59
  },
×
60
}));
1✔
61

62
const StyledPaper = styled(Paper)(() => ({
1✔
63
  borderRadius: "8px",
×
64
  border: "1px solid #6B7294",
×
65
  marginTop: "2px",
×
66
  "& .MuiAutocomplete-listbox": {
×
67
    padding: 0,
×
68
  },
×
69
  "& .MuiAutocomplete-option[aria-selected='true']": {
×
70
    color: "#083A50",
×
71
    background: "#FFFFFF",
×
72
  },
×
73
  "& .MuiAutocomplete-option": {
×
74
    padding: "0 10px",
×
75
    height: "35px",
×
76
    color: "#083A50",
×
77
    background: "#FFFFFF",
×
78
  },
×
79
  "& .MuiAutocomplete-option:hover": {
×
80
    backgroundColor: "#3E7E6D",
×
81
    color: "#FFFFFF",
×
82
  },
×
83
  "& .MuiAutocomplete-option.Mui-focused": {
×
84
    backgroundColor: "#3E7E6D !important",
×
85
    color: "#FFFFFF",
×
86
  },
×
87
}));
1✔
88

89
const StyledAutocomplete = styled(Autocomplete)(({ readOnly }: { readOnly?: boolean }) => ({
1✔
90
  "& .MuiInputBase-root": {
×
91
    backgroundColor: readOnly ? "#E5EEF4" : "#FFFFFF",
×
92
    "&.MuiAutocomplete-inputRoot.MuiInputBase-root": {
×
93
      display: "flex",
×
94
      alignItems: "center",
×
95
      padding: 0,
×
96
    },
×
97
    "& .MuiInputBase-input": {
×
98
      fontWeight: 400,
×
99
      fontSize: "16px",
×
100
      fontFamily: "'Nunito', 'Rubik', sans-serif",
×
101
      padding: "10px 12px 10px 12px !important",
×
102
      color: readOnly ? "#083A50" : "initial",
×
103
      cursor: readOnly ? "not-allowed !important" : "initial",
×
104
    },
×
105
    "& ::placeholder": {
×
106
      color: "#87878C",
×
107
      fontWeight: 400,
×
108
      opacity: 1,
×
109
    },
×
110
    "& .MuiAutocomplete-endAdornment": {
×
111
      right: "8px",
×
112
    },
×
113
    "&.Mui-focused": {
×
114
      boxShadow:
×
115
        "2px 2px 2px 1px rgba(38, 184, 147, 0.10), -2px -2px 2px 1px rgba(38, 184, 147, 0.20)",
×
116
    },
×
117
  },
×
118
}));
1✔
119

120
/**
121
 * Generates a generic autocomplete select box with a label and help text
122
 *
123
 * @param {Props} props
124
 * @returns {JSX.Element}
125
 */
126
const TableAutocompleteInput: FC<Props> = ({
1✔
127
  inputID,
×
128
  typeValue,
×
129
  extensionValue,
×
130
  name,
×
131
  required = false,
×
132
  helpText,
×
133
  onChange,
×
134
  ...rest
×
135
}) => {
×
136
  const { readOnlyInputs } = useFormMode();
×
137

138
  const [typeVal, setTypeVal] = useState(typeValue);
×
139
  const [extensionVal, setExtensionVal] = useState(extensionValue);
×
140
  const fileTypeRef = useRef<HTMLInputElement>(null);
×
141
  const fileExtensionRef = useRef<HTMLInputElement>(null);
×
142
  const [showFileTypeEror, setShowFileTypeError] = useState<boolean>(false);
×
143
  const [showFileExtensionError, setShowFileExtensionError] = useState<boolean>(false);
×
144

145
  useEffect(() => {
×
146
    const invalid = () => {
×
147
      setShowFileTypeError(true);
×
148
    };
×
149
    fileTypeRef.current?.addEventListener("invalid", invalid);
×
150
    return () => {
×
151
      fileTypeRef.current?.removeEventListener("invalid", invalid);
×
152
    };
×
153
  }, [fileTypeRef]);
×
154

155
  useEffect(() => {
×
156
    const invalid = () => {
×
157
      setShowFileExtensionError(true);
×
158
    };
×
159

160
    fileExtensionRef.current?.addEventListener("invalid", invalid);
×
161
    return () => {
×
162
      fileExtensionRef.current?.removeEventListener("invalid", invalid);
×
163
    };
×
164
  }, [fileExtensionRef]);
×
165

166
  const onTypeValChangeWrapper = (e, v, r) => {
×
167
    setShowFileTypeError(false);
×
168
    v = v || "";
×
169
    if (v === "") {
×
170
      fileTypeRef.current.setCustomValidity("Please specify a file type");
×
171
    } else {
×
172
      fileTypeRef.current.setCustomValidity("");
×
173
    }
×
174
    if (typeof onChange === "function") {
×
175
      onChange(e, v, r);
×
176
    }
×
177

178
    setTypeVal(v);
×
179
  };
×
180
  const onExtensionValChangeWrapper = (e, v, r) => {
×
181
    setShowFileExtensionError(false);
×
182
    v = v || "";
×
183
    if (v === "") {
×
184
      fileExtensionRef.current.setCustomValidity("Please specify a file extension type");
×
185
    } else {
×
186
      fileExtensionRef.current.setCustomValidity("");
×
187
    }
×
188
    if (typeof onChange === "function") {
×
189
      onChange(e, v, r);
×
190
    }
×
191

192
    setExtensionVal(v);
×
193
  };
×
194
  const typeTextInputOnChange = (r) => {
×
195
    onTypeValChangeWrapper(null, r.target.value, null);
×
196
  };
×
197
  const extensionTextInputOnChange = (r) => {
×
198
    onExtensionValChangeWrapper(null, r.target.value, null);
×
199
  };
×
200

201
  useEffect(() => {
×
202
    onTypeValChangeWrapper(null, typeValue, null);
×
203
  }, [typeValue]);
×
204

205
  useEffect(() => {
×
206
    onExtensionValChangeWrapper(null, extensionValue, null);
×
207
  }, [extensionValue]);
×
208

209
  return (
×
210
    <>
×
211
      <StyledTableCell>
×
212
        <StyledAutocomplete
×
213
          sx={{
×
214
            "& .MuiInputBase-input": {
×
215
              fontWeight: 400,
×
216
              fontSize: "16px",
×
217
              fontFamily: "'Nunito', 'Rubik', sans-serif",
×
218
              padding: "0 !important",
×
219
              height: "20px",
×
220
            },
×
221
          }}
×
222
          id={inputID.concat("-type")}
×
223
          size="small"
×
224
          value={typeVal || ""}
×
225
          onChange={onTypeValChangeWrapper}
×
226
          popupIcon={<DropdownArrowsIcon />}
×
227
          readOnly={readOnlyInputs}
×
228
          freeSolo
×
229
          PaperComponent={StyledPaper}
×
230
          slotProps={{
×
231
            popper: {
×
232
              disablePortal: true,
×
233
              sx: {
×
234
                top: "-2px !important",
×
NEW
235
                zIndex: 700,
×
236
              },
×
237
              modifiers: [
×
238
                {
×
239
                  // disables popper from flipping above the input when out of screen room
240
                  name: "flip",
×
241
                  enabled: false,
×
242
                  options: {
×
243
                    fallbackPlacements: [],
×
244
                  },
×
245
                },
×
246
              ],
247
            },
×
248
          }}
×
249
          renderInput={(p) => (
×
250
            <StyledTooltip
×
251
              title="Missing required field"
×
252
              arrow
×
253
              disableHoverListener
×
254
              disableFocusListener
×
255
              disableTouchListener
×
256
              open={showFileTypeEror}
×
257
              slotProps={{
×
258
                tooltip: { style: { marginTop: "4px !important" } },
×
259
              }}
×
260
            >
261
              <TextField
×
262
                {...p}
×
263
                onChange={typeTextInputOnChange}
×
264
                name={name.concat("[type]")}
×
265
                required={required}
×
266
                placeholder={rest.placeholder || "Enter or select a type"}
×
267
                variant="standard"
×
268
                inputRef={fileTypeRef}
×
269
                InputProps={{ ...p.InputProps, disableUnderline: true }}
×
270
                // eslint-disable-next-line react/jsx-no-duplicate-props
271
                inputProps={{
×
272
                  ...p.inputProps,
×
273
                  maxLength: 30,
×
274
                  "aria-label": "File type",
×
275
                }}
×
276
              />
277
            </StyledTooltip>
×
278
          )}
279
          {...rest}
×
280
        />
281
      </StyledTableCell>
×
282
      <StyledTableCell>
×
283
        <StyledAutocomplete
×
284
          sx={{
×
285
            "& .MuiInputBase-input": {
×
286
              fontWeight: 400,
×
287
              fontSize: "16px",
×
288
              fontFamily: "'Nunito', 'Rubik', sans-serif",
×
289
              padding: "0 !important",
×
290
              height: "20px",
×
291
            },
×
292
          }}
×
293
          id={inputID.concat("-extension")}
×
294
          size="small"
×
295
          value={extensionVal || ""}
×
296
          onChange={onExtensionValChangeWrapper}
×
297
          popupIcon={<DropdownArrowsIcon />}
×
298
          readOnly={readOnlyInputs}
×
299
          freeSolo
×
300
          PaperComponent={StyledPaper}
×
301
          slotProps={{
×
302
            popper: {
×
303
              disablePortal: true,
×
304
              sx: {
×
305
                top: "-2px !important",
×
NEW
306
                zIndex: 700,
×
307
              },
×
308
              modifiers: [
×
309
                {
×
310
                  // disables popper from flipping above the input when out of screen room
311
                  name: "flip",
×
312
                  enabled: false,
×
313
                  options: {
×
314
                    fallbackPlacements: [],
×
315
                  },
×
316
                },
×
317
              ],
318
            },
×
319
          }}
×
320
          renderInput={(p) => (
×
321
            <StyledTooltip
×
322
              title="Missing required field"
×
323
              arrow
×
324
              disableHoverListener
×
325
              disableFocusListener
×
326
              disableTouchListener
×
327
              open={showFileExtensionError}
×
328
            >
329
              <TextField
×
330
                {...p}
×
331
                onChange={extensionTextInputOnChange}
×
332
                name={name.concat("[extension]")}
×
333
                required={required}
×
334
                placeholder={rest.placeholder || "Enter or select an extension"}
×
335
                variant="standard"
×
336
                inputRef={fileExtensionRef}
×
337
                InputProps={{ ...p.InputProps, disableUnderline: true }}
×
338
                // eslint-disable-next-line react/jsx-no-duplicate-props
339
                inputProps={{
×
340
                  ...p.inputProps,
×
341
                  maxLength: 10,
×
342
                  "aria-label": "File extension",
×
343
                }}
×
344
              />
345
            </StyledTooltip>
×
346
          )}
347
          options={fileTypeExtensions[typeVal] || []}
×
348
        />
349
      </StyledTableCell>
×
350
    </>
×
351
  );
352
};
×
353

354
export default TableAutocompleteInput;
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