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

CBIIT / crdc-datahub-ui / 17132131774

21 Aug 2025 03:52PM UTC coverage: 77.592% (+1.7%) from 75.941%
17132131774

Pull #806

github

web-flow
Merge 6b88b37d9 into c10ceac73
Pull Request #806: Submission Request Excel Import & Export CRDCDH-3033, CRDCDH-3045, CRDCDH-3063

4841 of 5322 branches covered (90.96%)

Branch coverage included in aggregate %.

3122 of 3394 new or added lines in 32 files covered. (91.99%)

7 existing lines in 3 files now uncovered.

28996 of 38287 relevant lines covered (75.73%)

1856.98 hits per line

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

23.48
/src/content/questionnaire/sections/D.tsx
1
import { parseForm } from "@jalik/form-parser";
1✔
2
import AddCircleIcon from "@mui/icons-material/AddCircle";
1✔
3
import RemoveCircleIcon from "@mui/icons-material/RemoveCircle";
1✔
4
import { Table, TableBody, TableCell, TableHead, TableRow, styled } from "@mui/material";
1✔
5
import dayjs from "dayjs";
1✔
6
import React, { FC, useEffect, useRef, useState } from "react";
1✔
7

8
import AddRemoveButton from "../../../components/AddRemoveButton";
1✔
9
import { Status as FormStatus, useFormContext } from "../../../components/Contexts/FormContext";
1✔
10
import DatePickerInput from "../../../components/Questionnaire/DatePickerInput";
1✔
11
import FormContainer from "../../../components/Questionnaire/FormContainer";
1✔
12
import FormGroupCheckbox from "../../../components/Questionnaire/FormGroupCheckbox";
1✔
13
import RadioYesNoInput from "../../../components/Questionnaire/RadioYesNoInput";
1✔
14
import SectionGroup, { StyledDescription } from "../../../components/Questionnaire/SectionGroup";
1✔
15
import SwitchInput from "../../../components/Questionnaire/SwitchInput";
1✔
16
import TableFileTypeAndExtensionInput from "../../../components/Questionnaire/TableFileTypeAndExtensionInput";
1✔
17
import TableTextInput from "../../../components/Questionnaire/TableTextInput";
1✔
18
import TextInput from "../../../components/Questionnaire/TextInput";
1✔
19
import cellLineModelSystemOptions from "../../../config/CellLineModelSystemConfig";
1✔
20
import { fileTypeOptions } from "../../../config/FileTypeConfig";
1✔
21
import { InitialQuestionnaire } from "../../../config/InitialValues";
1✔
22
import SectionMetadata from "../../../config/SectionMetadata";
1✔
23
import useFormMode from "../../../hooks/useFormMode";
1✔
24
import {
1✔
25
  mapObjectWithKey,
26
  filterPositiveIntegerString,
27
  reshapeCheckboxGroupOptions,
28
  combineQuestionnaireData,
29
} from "../../../utils";
30

31
export type KeyedFileTypeData = {
32
  key: string;
33
} & FileInfo;
34

35
const TableContainer = styled("div")({
1✔
36
  marginLeft: "12px",
1✔
37
  marginBottom: "24px",
1✔
38
  display: "flex",
1✔
39
  width: "100%",
1✔
40
  border: "1px solid #6B7294",
1✔
41
  borderRadius: "10px",
1✔
42
  overflow: "hidden",
1✔
43
  "& .readOnly": {
1✔
44
    backgroundColor: "#E5EEF4",
1✔
45
    color: "#083A50",
1✔
46
    cursor: "not-allowed",
1✔
47
  },
1✔
48
  "& .MuiTableContainer-root": {
1✔
49
    width: "100%",
1✔
50
    marginLeft: "12px",
1✔
51
    overflowY: "visible",
1✔
52
    height: "200px",
1✔
53
  },
1✔
54
  "& th": {
1✔
55
    color: "#083A50",
1✔
56
    fontSize: "16px",
1✔
57
    fontFamily: "'Nunito', 'Rubik', sans-serif",
1✔
58
    fontWeight: 700,
1✔
59
    lineHeight: "19.6px",
1✔
60
  },
1✔
61
  "& table": {
1✔
62
    overflowY: "visible",
1✔
63
  },
1✔
64
  "& .noBorder": {
1✔
65
    border: "none",
1✔
66
  },
1✔
67
  "& .topRowLast": {
1✔
68
    border: "none",
1✔
69
    padding: "10px 8px",
1✔
70
    textAlign: "center",
1✔
71
  },
1✔
72
  "& .fileTypeTableCell": {
1✔
73
    borderTop: "none",
1✔
74
    borderRight: "1px solid #6B7294",
1✔
75
    borderBottom: "none",
1✔
76
    borderLeft: "none",
1✔
77
    padding: "10px 20px",
1✔
78
    textAlign: "center",
1✔
79
  },
1✔
80
  "& .tableTopRowMiddle": {
1✔
81
    borderTop: "none",
1✔
82
    borderRight: "1px solid #6B7294",
1✔
83
    borderBottom: "none",
1✔
84
    borderLeft: "none",
1✔
85
    padding: "10px 10px",
1✔
86
    textAlign: "center",
1✔
87
  },
1✔
88
  "& .bottomRowMiddle": {
1✔
89
    borderTop: "1px solid #6B7294",
1✔
90
    borderRight: "1px solid #6B7294",
1✔
91
    borderBottom: "none",
1✔
92
    borderLeft: "none",
1✔
93
    padding: "10px 10px",
1✔
94
  },
1✔
95
  "& .bottomRowLast": {
1✔
96
    borderTop: "1px solid #6B7294",
1✔
97
    borderRight: "none",
1✔
98
    borderBottom: "none",
1✔
99
    borderLeft: "none",
1✔
100
    textAlign: "center",
1✔
101
    padding: "10px",
1✔
102
    width: "20px",
1✔
103
    minWidth: "0",
1✔
104
  },
1✔
105
  "& .autoComplete": {
1✔
106
    borderTop: "1px solid #6B7294 !important",
1✔
107
    borderRight: "1px solid #6B7294 !important",
1✔
108
    borderBottom: "none !important",
1✔
109
    borderLeft: "none !important",
1✔
110
    padding: "10px 12px 10px 15px",
1✔
111
    "& .MuiStack-root": {
1✔
112
      width: "auto",
1✔
113
    },
1✔
114
  },
1✔
115
  "& .removeButtonContainer": {
1✔
116
    margin: "auto",
1✔
117
    width: "23px",
1✔
118
    "& .MuiStack-root": {
1✔
119
      width: "auto",
1✔
120
    },
1✔
121
  },
1✔
122
  "& .asterisk": {
1✔
123
    color: "#C93F08",
1✔
124
    marginLeft: "2px",
1✔
125
  },
1✔
126
  "& .MuiButton-startIcon": {
1✔
127
    margin: "0 !important",
1✔
128
  },
1✔
129
});
1✔
130

131
const InvisibleInput = styled("input")({
1✔
132
  height: 0,
1✔
133
  width: 0,
1✔
134
  padding: 0,
1✔
135
  border: 0,
1✔
136
  display: "block",
1✔
137
});
1✔
138

139
const FormSectionD: FC<FormSectionProps> = ({ SectionOption, refs }: FormSectionProps) => {
1✔
140
  const {
×
141
    status,
×
142
    data: { questionnaireData: data },
×
143
  } = useFormContext();
×
144
  const { readOnlyInputs } = useFormMode();
×
145
  const { D: SectionDMetadata } = SectionMetadata;
×
146

147
  const [dataTypes, setDataTypes] = useState<string[]>(data.dataTypes);
×
148
  const formContainerRef = useRef<HTMLDivElement>();
×
149
  const formRef = useRef<HTMLFormElement>();
×
150
  const [dataTypesErrorMsg, setDataTypesErrorMsg] = useState<string>("");
×
151
  const [clinicalDataTypesErrorMsg, setClinicalDataTypesErrorMsg] = useState<string>("");
×
152
  const dataTypesInputRef = useRef<HTMLInputElement>(null);
×
153
  const clinicalDataTypesInputRef = useRef<HTMLInputElement>(null);
×
154
  const { getFormObjectRef } = refs;
×
155
  const [fileTypeData, setFileTypeData] = useState<KeyedFileTypeData[]>(
×
156
    data.files?.map(mapObjectWithKey) || []
×
157
  );
×
158
  const [cellLineModelSystemCheckboxes, setCellLineModelSystemCheckboxes] = useState<string[]>(
×
159
    reshapeCheckboxGroupOptions(cellLineModelSystemOptions, data)
×
160
  );
×
NEW
161
  const isClinical = dataTypes?.includes("clinicalTrial");
×
162

163
  const getFormObject = (): FormObject | null => {
×
164
    if (!formRef.current) {
×
165
      return null;
×
166
    }
×
167

168
    const formObject = parseForm(formRef.current, { nullify: false });
×
NEW
169
    const combinedData = combineQuestionnaireData(data, formObject);
×
170
    // Remove empty strings from dataType arrays
NEW
171
    combinedData.dataTypes = combinedData.dataTypes.filter((str: string) => str !== "");
×
172
    // Handle validity for at dataTypes section
173
    if (combinedData.dataTypes.length !== 0 || combinedData.otherDataTypes !== "") {
×
174
      setDataTypesErrorMsg("");
×
175
      dataTypesInputRef.current.setCustomValidity("");
×
176
    } else {
×
177
      setDataTypesErrorMsg("At least one data type is required");
×
178
      dataTypesInputRef.current.setCustomValidity("At least one data type is required");
×
179
    }
×
180

181
    if (!combinedData.dataTypes.includes("clinicalTrial")) {
×
182
      combinedData.clinicalData = InitialQuestionnaire.clinicalData;
×
183
    } else {
×
184
      combinedData.clinicalData.dataTypes = combinedData.clinicalData.dataTypes.filter(
×
NEW
185
        (str: string) => str !== ""
×
186
      );
×
187
    }
×
188

189
    // Handle validity for at clinical data types section
190
    if (
×
191
      combinedData.dataTypes.includes("clinicalTrial") &&
×
192
      (combinedData.clinicalData?.dataTypes?.length !== 0 ||
×
193
        combinedData.clinicalData?.otherDataTypes !== "")
×
194
    ) {
×
195
      setClinicalDataTypesErrorMsg("");
×
NEW
196
      clinicalDataTypesInputRef.current?.setCustomValidity("");
×
197
    } else if (combinedData.dataTypes.includes("clinicalTrial")) {
×
198
      setClinicalDataTypesErrorMsg("At least one clinical data type is required");
×
199
      clinicalDataTypesInputRef.current?.setCustomValidity(
×
200
        "At least one clinical data type is required"
×
201
      );
×
202
    }
×
203

204
    combinedData.targetedReleaseDate = dayjs(formObject.targetedReleaseDate).isValid()
×
205
      ? formObject.targetedReleaseDate
×
206
      : "";
×
207
    combinedData.targetedSubmissionDate = dayjs(formObject.targetedSubmissionDate).isValid()
×
208
      ? formObject.targetedSubmissionDate
×
209
      : "";
×
210
    if (formObject.imagingDataDeIdentified === "true") {
×
211
      combinedData.imagingDataDeIdentified = true;
×
212
    } else if (formObject.imagingDataDeIdentified === "false") {
×
213
      combinedData.imagingDataDeIdentified = false;
×
214
    }
×
215
    // Override empty file array
216
    combinedData.files = formObject.files;
×
217
    // Overwrite number type. If empty string, convert to null.
218
    combinedData.files.map((file) => {
×
219
      file.count = parseInt(file.count.toString(), 10) || null;
×
220

221
      return file;
×
222
    });
×
223

224
    return { ref: formRef, data: combinedData };
×
225
  };
×
226

227
  const addFileDataType = () => {
×
NEW
228
    setFileTypeData((prev) => [
×
NEW
229
      ...prev,
×
230
      {
×
231
        key: `${fileTypeData.length}_${new Date().getTime()}`,
×
232
        type: ``,
×
233
        count: null,
×
234
        amount: "",
×
235
        extension: "",
×
236
      },
×
237
    ]);
×
238
  };
×
239

240
  const removeFileDataType = (key: string) => {
×
NEW
241
    setFileTypeData((prev) => prev.filter((c) => c.key !== key));
×
242
  };
×
243

244
  const handleDataTypesChange = (checked: boolean, value: string) => {
×
245
    const updatedDataTypes = checked
×
246
      ? [...dataTypes, value]
×
247
      : dataTypes.filter((dt) => dt !== value);
×
248

249
    setDataTypes(updatedDataTypes);
×
250
  };
×
251

252
  useEffect(() => {
×
253
    getFormObjectRef.current = getFormObject;
×
254
  }, [refs]);
×
255

256
  useEffect(() => {
×
257
    formContainerRef.current?.scrollIntoView({ block: "start" });
×
258
  }, []);
×
259

NEW
260
  useEffect(() => {
×
NEW
261
    setDataTypes(data?.dataTypes || []);
×
NEW
262
  }, [data?.dataTypes]);
×
263

NEW
264
  useEffect(() => {
×
NEW
265
    const incoming = data?.files ?? [];
×
NEW
266
    setFileTypeData((prev) =>
×
NEW
267
      incoming.map((c, i) => ({
×
NEW
268
        ...c,
×
NEW
269
        key: prev[i]?.key ?? mapObjectWithKey(c, i).key,
×
NEW
270
      }))
×
NEW
271
    );
×
NEW
272
  }, [data?.files]);
×
273

NEW
274
  useEffect(() => {
×
NEW
275
    setCellLineModelSystemCheckboxes(reshapeCheckboxGroupOptions(cellLineModelSystemOptions, data));
×
NEW
276
  }, [data?.cellLines, data?.modelSystems]);
×
277

278
  return (
×
279
    <FormContainer ref={formContainerRef} formRef={formRef} description={SectionOption.title}>
×
280
      {/* Data Delivery and Release Dates Section */}
281
      <SectionGroup title={SectionDMetadata.sections.DATA_DELIVERY_AND_RELEASE_DATES.title}>
×
282
        <DatePickerInput
×
283
          inputID="section-d-targeted-data-submission-delivery-date"
×
284
          label="Targeted Data Submission Delivery Date"
×
285
          name="targetedSubmissionDate"
×
286
          tooltipText="The date that transfer of data from the submitter to CRDC Submission Portal is expected to begin."
×
287
          initialValue={data.targetedSubmissionDate}
×
288
          gridWidth={6}
×
289
          disablePast
×
290
          required
×
291
          readOnly={readOnlyInputs}
×
292
        />
293
        <DatePickerInput
×
294
          inputID="section-d-expected-publication-date"
×
295
          label="Expected Publication Date"
×
296
          name="targetedReleaseDate"
×
297
          tooltipText="The date that submitters expect any paper using this data will be published."
×
298
          initialValue={data.targetedReleaseDate}
×
299
          gridWidth={6}
×
300
          disablePast
×
301
          required
×
302
          readOnly={readOnlyInputs}
×
303
        />
304
      </SectionGroup>
×
305
      {/* Data Types Section */}
306
      <SectionGroup
×
307
        title={SectionDMetadata.sections.DATA_TYPES.title}
×
308
        description={SectionDMetadata.sections.DATA_TYPES.description}
×
309
        required
×
310
        error={dataTypesErrorMsg}
×
311
      >
312
        <InvisibleInput
×
313
          ref={dataTypesInputRef}
×
314
          aria-label={SectionDMetadata.sections.DATA_TYPES.title}
×
315
        />
316
        <SwitchInput
×
317
          id="section-d-clinical-trial"
×
318
          label="Clinical"
×
319
          name="dataTypes[]"
×
320
          graphQLValue="clinicalTrial"
×
321
          value={dataTypes.includes("clinicalTrial")}
×
NEW
322
          onChange={(_e, checked) => handleDataTypesChange(checked, "clinicalTrial")}
×
323
          tooltipText="A research study in which one or more subjects are prospectively assigned to one or more interventions (which may include placebo or other control) to evaluate the effects of those interventions on health-related biomedical outcomes."
×
324
          readOnly={readOnlyInputs}
×
325
        />
326
        <SwitchInput
×
327
          id="section-d-genomics"
×
328
          label="Genomics"
×
329
          name="dataTypes[]"
×
330
          graphQLValue="genomics"
×
331
          value={dataTypes.includes("genomics")}
×
332
          tooltipText="The branch of molecular biology concerned with the structure, function, evolution, and mapping of genomes.  Includes data from DNA sequencing, RNA sequencing, mutational analysis, and other experiments focused on genomes."
×
333
          readOnly={readOnlyInputs}
×
334
          switchSx={{ marginRight: "0px" }}
×
335
        />
336
        <SwitchInput
×
337
          id="section-d-imaging"
×
338
          label="Imaging"
×
339
          name="dataTypes[]"
×
340
          graphQLValue="imaging"
×
341
          value={dataTypes.includes("imaging")}
×
342
          onChange={(e, checked) => handleDataTypesChange(checked, "imaging")}
×
343
          toggleContent={
×
344
            <RadioYesNoInput
×
345
              id="section-d-imaging-de-identified"
×
346
              value={data.imagingDataDeIdentified}
×
347
              containerWidth="1100px"
×
348
              gridWidth={12}
×
349
              label="Confirm the imaging data you plan to submit are de-identified"
×
350
              name="imagingDataDeIdentified"
×
351
              row
×
352
              required={dataTypes.includes("imaging")}
×
353
              readOnly={readOnlyInputs}
×
354
            />
355
          }
356
          tooltipText="Medical and experimental images from disciplines such as radiology, pathology, and microscopy."
×
357
          readOnly={readOnlyInputs}
×
358
        />
359
        <SwitchInput
×
360
          id="section-d-proteomics"
×
361
          label="Proteomics"
×
362
          graphQLValue="proteomics"
×
363
          name="dataTypes[]"
×
364
          value={dataTypes.includes("proteomics")}
×
365
          tooltipText="Data from the study of the large scale expression and use of proteins."
×
366
          readOnly={readOnlyInputs}
×
367
          sx={{ paddingBottom: "8px" }}
×
368
          switchSx={{ marginRight: "0px" }}
×
369
        />
370
        <TextInput
×
371
          id="section-d-other-data-types"
×
372
          label="Other Data Type(s)"
×
373
          name="otherDataTypes"
×
374
          value={data.otherDataTypes}
×
375
          placeholder="Other Data Types (Specify)"
×
376
          gridWidth={12}
×
377
          tooltipText='Data that do not fit in any of the other categories. Enter additional Data Types, separated by pipes ("|").'
×
378
          readOnly={readOnlyInputs}
×
379
          maxLength={200}
×
380
        />
381
      </SectionGroup>
×
382

383
      {/* Clinical Data Types Section */}
384
      {isClinical && (
×
385
        <SectionGroup
×
386
          title={SectionDMetadata.sections.CLINICAL_DATA_TYPES.title}
×
387
          description={SectionDMetadata.sections.CLINICAL_DATA_TYPES.description}
×
388
          required
×
389
          error={clinicalDataTypesErrorMsg}
×
390
        >
391
          <InvisibleInput
×
392
            ref={clinicalDataTypesInputRef}
×
393
            aria-label={SectionDMetadata.sections.CLINICAL_DATA_TYPES.title}
×
394
          />
395
          <SwitchInput
×
396
            id="section-d-demographic-data"
×
397
            label="Demographic Data"
×
398
            name="clinicalData[dataTypes][]"
×
399
            graphQLValue="demographicData"
×
400
            value={data.clinicalData.dataTypes.includes("demographicData")}
×
401
            tooltipText="Indicate whether demographics information is available for the study (such as age or gender)."
×
402
            readOnly={readOnlyInputs}
×
403
          />
404
          <SwitchInput
×
405
            id="section-d-relapse-recurrence-data"
×
406
            label="Relapse/Recurrence Data"
×
407
            name="clinicalData[dataTypes][]"
×
408
            graphQLValue="relapseRecurrenceData"
×
409
            value={data.clinicalData.dataTypes.includes("relapseRecurrenceData")}
×
410
            tooltipText="Relapse/recurrence data refers to information associated with the return of a disease after a period of remission. Indicate whether relapse/recurrence data is available for the study."
×
411
            readOnly={readOnlyInputs}
×
412
            switchSx={{ marginRight: "0px" }}
×
413
          />
414
          <SwitchInput
×
415
            id="section-d-diagnosis-data"
×
416
            label="Diagnosis Data"
×
417
            name="clinicalData[dataTypes][]"
×
418
            graphQLValue="diagnosisData"
×
419
            value={data.clinicalData.dataTypes.includes("diagnosisData")}
×
420
            tooltipText="Indicate whether diagnosis information is available for the study."
×
421
            readOnly={readOnlyInputs}
×
422
          />
423
          <SwitchInput
×
424
            id="section-d-outcome-data"
×
425
            label="Outcome Data"
×
426
            name="clinicalData[dataTypes][]"
×
427
            graphQLValue="outcomeData"
×
428
            value={data.clinicalData.dataTypes.includes("outcomeData")}
×
429
            tooltipText="Outcome data refers to information on a specific result or effect that can be measured. Examples of outcomes include decreased pain, reduced tumor size, and improvement of disease. Indicate whether outcome data is available for the study."
×
430
            readOnly={readOnlyInputs}
×
431
            switchSx={{ marginRight: "0px" }}
×
432
          />
433
          <SwitchInput
×
434
            id="section-d-treatment-data"
×
435
            label="Treatment Data"
×
436
            name="clinicalData[dataTypes][]"
×
437
            graphQLValue="treatmentData"
×
438
            value={data.clinicalData.dataTypes.includes("treatmentData")}
×
439
            tooltipText="Treatment data refers to information on the action or administration of therapeutic agents to produce an effect that is intended to alter the course of a pathological process. Indicate whether treatment data is available for the study."
×
440
            readOnly={readOnlyInputs}
×
441
            sx={{ paddingBottom: "8px" }}
×
442
          />
443
          <SwitchInput
×
444
            id="section-d-biospecimen-data"
×
445
            label="Biospecimen Data"
×
446
            name="clinicalData[dataTypes][]"
×
447
            graphQLValue="biospecimenData"
×
448
            value={data.clinicalData.dataTypes.includes("biospecimenData")}
×
449
            tooltipText="Biospecimen data refers to information associated with the biological sample, portion, analyte, or aliquot. Indicate whether biospecimen data is available for the study."
×
450
            readOnly={readOnlyInputs}
×
451
            switchSx={{ marginRight: "0px" }}
×
452
          />
453
          <TextInput
×
454
            id="section-d-clinical-data-other-data-types"
×
455
            label="Other Clinical Data Types"
×
456
            name="clinicalData[otherDataTypes]"
×
457
            value={data.clinicalData.otherDataTypes}
×
458
            placeholder="Other clinical data types (Specify)"
×
459
            gridWidth={12}
×
460
            tooltipText='If there are any additional types of data included with the study not already specified above, describe here. Enter additional Clinical Data Types, separated by pipes ("|").'
×
461
            readOnly={readOnlyInputs}
×
462
            maxLength={200}
×
463
          />
464
          <SwitchInput
×
465
            id="section-d-additional-data-in-future"
×
466
            label="Additional Data Types with a future submission?"
×
467
            name="clinicalData[futureDataTypes]"
×
468
            value={data.clinicalData.futureDataTypes}
×
469
            gridWidth={8}
×
470
            isBoolean
×
471
            readOnly={readOnlyInputs}
×
472
            tooltipText="Indicate if there will be additional types of data included with a future submission."
×
473
            switchSx={{ marginRight: "72px" }}
×
474
          />
475
        </SectionGroup>
×
476
      )}
477

478
      {/* File Types Section */}
479
      <SectionGroup
×
480
        title={SectionDMetadata.sections.FILE_TYPES.title}
×
481
        description={SectionDMetadata.sections.FILE_TYPES.description}
×
482
        beginButton={
×
483
          <AddRemoveButton
×
484
            id="section-d-add-file-type-button"
×
485
            label="Add File Type"
×
486
            startIcon={<AddCircleIcon />}
×
487
            onClick={addFileDataType}
×
488
            disabled={readOnlyInputs || status === FormStatus.SAVING}
×
489
          />
490
        }
491
      >
492
        <TableContainer>
×
493
          <Table className="noBorder">
×
494
            <TableHead className="noBorder">
×
495
              <TableRow className="noBorder">
×
496
                <TableCell width="25%" className="fileTypeTableCell">
×
497
                  File Type
498
                  <span className="asterisk">*</span>
×
499
                </TableCell>
×
500
                <TableCell width="24%" className="fileTypeTableCell">
×
501
                  File Extension
502
                  <span className="asterisk">*</span>
×
503
                </TableCell>
×
504
                <TableCell width="15%" className="tableTopRowMiddle">
×
505
                  Number of files
506
                  <span className="asterisk">*</span>
×
507
                </TableCell>
×
508
                <TableCell width="20%" className="tableTopRowMiddle">
×
509
                  Estimated data size
510
                  <span className="asterisk">*</span>
×
511
                </TableCell>
×
512
                <TableCell width="5%" className="topRowLast">
×
513
                  Remove
514
                </TableCell>
×
515
              </TableRow>
×
516
            </TableHead>
×
517
            <TableBody>
×
518
              {fileTypeData.map((fileData: KeyedFileTypeData, idx: number) => (
×
519
                <TableRow key={fileData.key} className={`${readOnlyInputs ? "readOnly" : ""}`}>
×
520
                  <TableFileTypeAndExtensionInput
×
521
                    inputID={`section-d-file-type-${idx}-file`}
×
522
                    typeValue={fileData.type}
×
523
                    extensionValue={fileData.extension}
×
524
                    name={`files[${idx}]`}
×
525
                    options={fileTypeOptions.map((fileType) => fileType)}
×
526
                  />
527
                  <TableCell className="bottomRowMiddle">
×
NEW
528
                    <input
×
NEW
529
                      type="hidden"
×
NEW
530
                      name={`files[${idx}][key]`}
×
NEW
531
                      value={fileData.key}
×
NEW
532
                      readOnly
×
533
                    />
534
                    <TableTextInput
×
535
                      id={`section-d-file-type-${idx}-number-of-files`}
×
536
                      name={`files[${idx}][count]`}
×
537
                      value={fileData.count ?? ""}
×
538
                      placeholder="Enter file count"
×
539
                      inputProps={{ "aria-label": "File count" }}
×
540
                      pattern="^[1-9]\d*$"
×
541
                      filter={filterPositiveIntegerString}
×
542
                      patternValidityMessage="Please enter a whole number greater than 0"
×
543
                      maxLength={10}
×
544
                      required
×
545
                    />
546
                  </TableCell>
×
547
                  <TableCell className="bottomRowMiddle">
×
548
                    <TableTextInput
×
549
                      id={`section-d-file-type-${idx}-amount-of-data`}
×
550
                      name={`files[${idx}][amount]`}
×
551
                      value={fileData.amount}
×
552
                      placeholder="E.g. 500 GB"
×
553
                      inputProps={{ "aria-label": "File size" }}
×
554
                      maxLength={50}
×
555
                      required
×
556
                    />
557
                  </TableCell>
×
558
                  <TableCell className="bottomRowLast">
×
559
                    {idx !== 0 ? (
×
560
                      <div className="removeButtonContainer">
×
561
                        <AddRemoveButton
×
562
                          id={`section-d-file-type-${idx}-remove-file-type-button`}
×
563
                          placement="start"
×
564
                          onClick={() => removeFileDataType(fileData.key)}
×
565
                          startIcon={<RemoveCircleIcon />}
×
566
                          iconColor="#E74040"
×
567
                          disabled={readOnlyInputs || status === FormStatus.SAVING}
×
568
                          aria-label="Remove File Type"
×
569
                          sx={{ minWidth: "0px !important" }}
×
570
                        />
571
                      </div>
×
572
                    ) : null}
×
573
                  </TableCell>
×
574
                </TableRow>
×
575
              ))}
×
576
            </TableBody>
×
577
          </Table>
×
578
        </TableContainer>
×
579
        <RadioYesNoInput
×
580
          id="section-d-data-de-identified"
×
581
          name="dataDeIdentified"
×
582
          label="Confirm the data you plan to submit are de-identified"
×
583
          value={data.dataDeIdentified}
×
584
          gridWidth={12}
×
585
          row
×
586
          required
×
587
          readOnly={readOnlyInputs}
×
588
        />
589
      </SectionGroup>
×
590

591
      {/* Additional Information Section */}
592
      <SectionGroup title={SectionDMetadata.sections.ADDITIONAL_COMMENTS.title} description={" "}>
×
593
        <FormGroupCheckbox
×
594
          idPrefix="section-c-"
×
595
          label="Cell lines, model systems (select all that apply or neither)"
×
596
          options={cellLineModelSystemOptions}
×
597
          value={cellLineModelSystemCheckboxes}
×
598
          onChange={(val: string[]) => setCellLineModelSystemCheckboxes(val)}
×
599
          orientation="horizontal"
×
600
          gridWidth={12}
×
601
          readOnly={readOnlyInputs}
×
602
        />
603
        <TextInput
×
604
          name="submitterComment"
×
605
          label={
×
606
            <StyledDescription variant="body1">
×
607
              {SectionDMetadata.sections.ADDITIONAL_COMMENTS.description}
×
608
            </StyledDescription>
×
609
          }
610
          value={data.submitterComment}
×
611
          gridWidth={12}
×
612
          maxLength={500}
×
613
          placeholder="500 characters allowed"
×
614
          minRows={5}
×
615
          multiline
×
616
          readOnly={readOnlyInputs}
×
617
          inputProps={{
×
618
            "aria-label": SectionDMetadata.sections.ADDITIONAL_COMMENTS.title,
×
619
          }}
×
620
        />
621
      </SectionGroup>
×
622
    </FormContainer>
×
623
  );
624
};
×
625

626
export default FormSectionD;
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

© 2025 Coveralls, Inc