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

CBIIT / crdc-datahub-ui / 17135186002

21 Aug 2025 06:09PM UTC coverage: 77.612% (+1.7%) from 75.941%
17135186002

Pull #806

github

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

4850 of 5333 branches covered (90.94%)

Branch coverage included in aggregate %.

3174 of 3450 new or added lines in 33 files covered. (92.0%)

7 existing lines in 3 files now uncovered.

29048 of 38343 relevant lines covered (75.76%)

175.51 hits per line

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

6.64
/src/components/Questionnaire/SwitchInput.tsx
1
import { SwitchProps, Grid, Switch, FormHelperText, styled, SxProps } from "@mui/material";
1✔
2
import React, {
1✔
3
  FC,
4
  HTMLProps,
5
  ReactElement,
6
  useEffect,
7
  useId,
8
  useMemo,
9
  useRef,
10
  useState,
11
} from "react";
12

13
import { updateInputValidity } from "../../utils";
1✔
14
import Tooltip from "../Tooltip";
1✔
15

16
const GridStyled = styled(Grid, { shouldForwardProp: (p) => p !== "switchSx" })<{
1✔
17
  switchSx: SxProps;
18
}>(({ theme, switchSx }) => ({
1✔
19
  "& .switchRoot": {
×
20
    width: "65px",
×
21
    height: "35px",
×
22
    padding: 0,
×
23
  },
×
24
  "& .switchBase": {
×
25
    paddingTop: "5px",
×
26
    paddingLeft: "7px",
×
27
    transitionDuration: "300ms",
×
28
    "&.Mui-checked": {
×
29
      transform: "translateX(26px)",
×
30
      "& + .MuiSwitch-track": {
×
31
        border: "1.25px solid #6A6A6A",
×
32
      },
×
33
    },
×
34
    "&.focusVisible, &:focus, &:active": {
×
35
      "& + .track": {
×
36
        border: "1.25px solid #6A6A6A !important",
×
37
      },
×
38
    },
×
39
  },
×
40
  "& .thumb": {
×
41
    color: "#08A0B4",
×
42
    width: "25px",
×
43
    height: "25px",
×
44
    boxShadow: "none",
×
45
    transition: "color 300ms ease",
×
46
  },
×
47
  "& .Mui-checked .thumb": {
×
48
    color: "#FFFFFF",
×
49
  },
×
50
  "& .MuiSwitch-track.track": {
×
51
    position: "relative",
×
52
    borderRadius: "60px",
×
53
    backgroundColor: "#FFFFFF",
×
54
    border: "1px solid #DBDBDB",
×
55
    opacity: 1,
×
56
    overflow: "hidden",
×
57
    transition: theme.transitions.create(["border"], {
×
58
      duration: 300,
×
59
    }),
×
60
    "&::before": {
×
61
      position: "absolute",
×
62
      content: "''",
×
63
      inset: 0,
×
64
      backgroundImage: "linear-gradient(270deg, #08A0B4 49.23%, #05595E 115.38%)",
×
65
      zIndex: 1,
×
66
      opacity: 0,
×
67
      transition: "opacity 0.3s linear",
×
68
    },
×
69
  },
×
70
  "& .Mui-checked+.MuiSwitch-track.track": {
×
71
    borderRadius: "60px",
×
72
    border: "1.25px solid #6A6A6A",
×
73
    opacity: 1,
×
74
  },
×
75
  "& .Mui-checked+.MuiSwitch-track.track::before": {
×
76
    opacity: 1,
×
77
  },
×
78
  "& .readOnly .Mui-checked+.MuiSwitch-track.track::before": {
×
79
    opacity: 0.5,
×
80
  },
×
81
  "& .readOnly .MuiSwitch-track": {
×
82
    backgroundColor: "#E5EEF4 !important",
×
83
  },
×
84
  "& .readOnly .MuiSwitch-input": {
×
85
    cursor: "not-allowed",
×
86
  },
×
87
  "& .text": {
×
88
    display: "inline",
×
89
    fontFamily: "Lato",
×
90
    fontStyle: "normal",
×
91
    fontWeight: 600,
×
92
    fontSize: "16px",
×
93
    lineHeight: "22px",
×
94
    color: "#5D7B7E",
×
95
    marginLeft: "6px",
×
96
    marginRight: "6px",
×
97
  },
×
98
  "& .textChecked": {
×
99
    display: "inline",
×
100
    fontFamily: "Lato",
×
101
    fontStyle: "normal",
×
102
    fontWeight: 600,
×
103
    fontSize: "16px",
×
104
    lineHeight: "22px",
×
105
    color: "#4E5F63",
×
106
    marginLeft: "6px",
×
107
    marginRight: "6px",
×
108
  },
×
109
  "& .input": {
×
110
    display: "none",
×
111
  },
×
112
  "& .asterisk": {
×
113
    color: "#C93F08",
×
114
    marginLeft: "2px",
×
115
  },
×
116
  "& .labelContainer": {
×
117
    color: "#083A50",
×
118
    display: "flex",
×
119
    alignItems: "center",
×
120
    height: "20px",
×
121
  },
×
122
  "& .switchYesNoContainer": {
×
123
    display: "flex",
×
124
    alignItems: "center",
×
125
    marginRight: "28px",
×
126
    marginLeft: "auto",
×
127
    minHeight: "50px",
×
128
    ...switchSx,
×
129
  },
×
130
  "& .tooltip": {
×
131
    alignSelf: "start",
×
132
    marginLeft: "6px",
×
133
  },
×
134
  "& .errorMessage": {
×
135
    color: "#D54309 !important",
×
136
    marginTop: "44px",
×
137
    marginLeft: "8px",
×
138
    minHeight: "20px",
×
139
    width: "fit-content",
×
140
    position: "absolute",
×
141
  },
×
142
  "& .switchErrorContainer": {
×
143
    display: "flex",
×
144
    flexDirection: "column",
×
145
  },
×
146
}));
1✔
147

148
const Container = styled("div", {
1✔
149
  shouldForwardProp: (prop) => prop !== "containerWidth",
1✔
150
})<HTMLProps<HTMLDivElement> & { containerWidth?: string }>(({ containerWidth }) => ({
1✔
151
  display: "flex",
×
152
  flexDirection: "row",
×
153
  justifyContent: "space-between",
×
154
  alignItems: "center",
×
155
  fontSize: "16px",
×
156
  fontFamily: "'Nunito', 'Rubik', sans-serif",
×
157
  fontWeight: 700,
×
158
  lineHeight: "19.6px",
×
159
  minHeight: "50px",
×
160
  flexWrap: "wrap",
×
161
  width: containerWidth,
×
162
}));
1✔
163

164
const HideContentWrapper = styled("div")({
1✔
165
  display: "none !important",
1✔
166
});
1✔
167

168
type Props = {
169
  label: string;
170
  name: string;
171
  tooltipText?: string;
172
  required?: boolean;
173
  gridWidth?: 2 | 4 | 6 | 8 | 10 | 12;
174
  errorText?: string;
175
  value: boolean;
176
  toggleContent?: ReactElement;
177
  isBoolean?: boolean;
178
  touchRequired?: boolean;
179
  graphQLValue?: string;
180
  containerWidth?: string;
181
  /**
182
   * Provides styling override for the switch container
183
   */
184
  switchSx?: SxProps;
185
} & Omit<SwitchProps, "color">;
186

187
const CustomSwitch: FC<Props> = ({
1✔
188
  classes,
×
189
  label,
×
190
  required,
×
191
  value,
×
192
  onChange,
×
193
  name,
×
194
  tooltipText,
×
195
  gridWidth,
×
196
  errorText,
×
197
  toggleContent,
×
198
  graphQLValue = "",
×
199
  isBoolean = false,
×
200
  switchSx = {},
×
201
  containerWidth = "auto",
×
202
  touchRequired,
×
203
  readOnly,
×
204
  sx,
×
205
  ...rest
×
206
}) => {
×
207
  const id = rest.id || useId();
×
208

209
  const [val, setVal] = useState<boolean | null>(value);
×
210
  const [touched, setTouched] = useState(value?.toString()?.length > 0);
×
211
  const [error, setError] = useState(false);
×
212
  const errorMsg = errorText || (required ? "This field is required" : null);
×
213
  const inputRef = useRef<HTMLInputElement>(null);
×
214

215
  const proxyValue = useMemo(() => {
×
216
    if (isBoolean) {
×
217
      return touchRequired && !touched ? undefined : val?.toString();
×
218
    }
×
219
    return val ? graphQLValue : "";
×
220
  }, [isBoolean, val, graphQLValue, touched, touchRequired]);
×
221

UNCOV
222
  const onChangeWrapper = (event: React.ChangeEvent<HTMLInputElement>, checked: boolean) => {
×
223
    if (readOnly) {
×
224
      return;
×
225
    }
×
226
    if (typeof onChange === "function") {
×
227
      onChange(event, checked);
×
228
    }
×
229
    if (!touched) {
×
230
      setTouched(true);
×
231
    }
×
232

233
    setVal(checked);
×
234
    setError(false);
×
235
  };
×
236

237
  useEffect(() => {
×
238
    const invalid = () => setError(true);
×
239

240
    inputRef.current?.addEventListener("invalid", invalid);
×
241
    return () => {
×
242
      inputRef.current?.removeEventListener("invalid", invalid);
×
243
    };
×
244
  }, [inputRef]);
×
245

NEW
246
  useEffect(() => {
×
NEW
247
    if (!touchRequired || readOnly) {
×
NEW
248
      return;
×
NEW
249
    }
×
NEW
250
    if (!touched) {
×
NEW
251
      updateInputValidity(inputRef, errorMsg);
×
NEW
252
      return;
×
NEW
253
    }
×
254

NEW
255
    updateInputValidity(inputRef);
×
NEW
256
  }, [touched, touchRequired]);
×
257

NEW
258
  useEffect(() => {
×
NEW
259
    setVal(value);
×
NEW
260
  }, [value]);
×
261

262
  return (
×
263
    <GridStyled md={gridWidth || 6} xs={12} item sx={sx} switchSx={switchSx}>
×
264
      <Container containerWidth={containerWidth}>
×
265
        <div className="labelContainer">
×
266
          {label && (
×
267
            <label htmlFor={id} id={`${id}-label`}>
×
268
              {label}
×
269
            </label>
×
270
          )}
271
          {required ? <span className="asterisk">*</span> : ""}
×
272
          {tooltipText && <Tooltip placement="right" className="tooltip" title={tooltipText} />}
×
273
        </div>
×
274
        <div className="switchErrorContainer">
×
275
          <div className="switchYesNoContainer">
×
276
            <div className={val ? "text" : "textChecked"}>No</div>
×
277
            <Switch
×
278
              inputRef={inputRef}
×
279
              inputProps={{ datatype: "boolean" }}
×
280
              focusVisibleClassName="focusVisible"
×
281
              checked={val || false}
×
282
              onChange={onChangeWrapper}
×
283
              readOnly={readOnly}
×
284
              disableRipple
×
285
              className={readOnly ? "readOnly" : ""}
×
286
              classes={{
×
287
                root: "switchRoot",
×
288
                switchBase: "switchBase",
×
289
                thumb: "thumb",
×
290
                track: "track",
×
291
                checked: "checked",
×
292
              }}
×
293
              {...rest}
×
294
              id={id}
×
295
            />
296
            {/* To satisfy the form parser. The mui switch value is not good for the form parser */}
297
            <input
×
298
              onChange={() => {}}
×
299
              className="input"
×
300
              name={name}
×
301
              type="checkbox"
×
302
              data-type={isBoolean ? "boolean" : "auto"}
×
303
              value={proxyValue}
×
304
              aria-labelledby={`${id}-label`}
×
305
              checked
×
306
            />
307
            <div className={val ? "textChecked" : "text"}>Yes</div>
×
308
          </div>
×
309
          <FormHelperText className="errorMessage">
×
310
            {!readOnly && error ? errorMsg : " "}
×
311
          </FormHelperText>
×
312
        </div>
×
313
      </Container>
×
314
      {val ? toggleContent : <HideContentWrapper>{toggleContent}</HideContentWrapper>}
×
315
    </GridStyled>
×
316
  );
317
};
×
318

319
export default CustomSwitch;
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