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

CBIIT / crdc-datahub-ui / 11074479132

27 Sep 2024 04:44PM UTC coverage: 44.982% (+26.5%) from 18.435%
11074479132

Pull #479

github

web-flow
Merge a0867d25a into 3d8b55818
Pull Request #479: 3.0.0 Release

1727 of 4418 branches covered (39.09%)

Branch coverage included in aggregate %.

2612 of 5228 relevant lines covered (49.96%)

128.96 hits per line

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

3.96
/src/components/Questionnaire/FormGroupCheckbox.tsx
1
import { FormControl, FormGroup, FormHelperText, Grid, styled } from "@mui/material";
2
import { FC, useEffect, useId, useRef, useState } from "react";
3
import Tooltip from "../Tooltip";
4
import CheckboxInput from "./CheckboxInput";
5
import { updateInputValidity } from "../../utils";
6

7
const StyledFormLabel = styled("label")(({ theme }) => ({
2✔
8
  fontWeight: 700,
9
  fontSize: "16px",
10
  lineHeight: "19.6px",
11
  minHeight: "20px",
12
  color: "#083A50",
13
  marginBottom: "4px",
14
  [theme.breakpoints.up("lg")]: {
15
    whiteSpace: "nowrap",
16
  },
17
}));
18

19
const StyledAsterisk = styled("span")(() => ({
2✔
20
  color: "#C93F08",
21
  marginLeft: "2px",
22
}));
23

24
const StyledFormHelperText = styled(FormHelperText)(() => ({
2✔
25
  marginLeft: 0,
26
}));
27

28
type Props = {
29
  idPrefix?: string;
30
  label?: string | JSX.Element;
31
  hideLabel?: boolean;
32
  value: string[];
33
  name?: string;
34
  options: FormGroupCheckboxOption[];
35
  required?: boolean; // at least one checkbox needs to be checked
36
  allowMultipleChecked?: boolean;
37
  orientation?: "vertical" | "horizontal";
38
  helpText?: string;
39
  tooltipText?: string;
40
  gridWidth?: 2 | 4 | 6 | 8 | 10 | 12;
41
  readOnly?: boolean;
42
  onChange?: (values: string[]) => void;
43
};
44

45
const FormGroupCheckbox: FC<Props> = ({
2✔
46
  idPrefix = "",
×
47
  label,
48
  hideLabel,
49
  value,
50
  name,
51
  options,
52
  required = false,
×
53
  allowMultipleChecked = true,
×
54
  orientation = "vertical",
×
55
  helpText,
56
  tooltipText,
57
  gridWidth,
58
  readOnly,
59
  onChange,
60
}) => {
61
  const id = useId();
×
62

63
  const [val, setVal] = useState(value ?? []);
×
64
  const [error, setError] = useState(false);
×
65
  const helperText =
66
    helpText ||
×
67
    (required && !val?.length && "This field is required") ||
68
    (!allowMultipleChecked && val?.length > 1 ? "Please select only one option" : " ");
×
69
  const firstCheckboxInputRef = useRef<HTMLInputElement>(null);
×
70

71
  const onChangeWrapper = (newVal: string[]) => {
×
72
    if (typeof onChange === "function") {
×
73
      onChange(newVal);
×
74
    }
75

76
    setVal(newVal);
×
77
  };
78

79
  const handleChange = (selectedValue: string, checked: boolean) => {
×
80
    const currentVal = val || [];
×
81
    const updatedValues = checked
×
82
      ? [...currentVal, selectedValue]
83
      : currentVal?.filter((v) => v !== selectedValue);
×
84

85
    onChangeWrapper(updatedValues);
×
86
  };
87

88
  useEffect(() => {
×
89
    if (value) {
×
90
      onChangeWrapper(value);
×
91
    }
92
  }, [value]);
93

94
  useEffect(() => {
×
95
    const notSelectedAndRequired = required && !val?.length;
×
96
    const multipleChecked = val?.length > 1;
×
97

98
    if (notSelectedAndRequired) {
×
99
      updateInputValidity(firstCheckboxInputRef, "Please select at least one option");
×
100
      return;
×
101
    }
102

103
    if (!allowMultipleChecked && multipleChecked) {
×
104
      updateInputValidity(firstCheckboxInputRef, "Please select only one option");
×
105
      return;
×
106
    }
107

108
    updateInputValidity(firstCheckboxInputRef);
×
109
    setError(false);
×
110
  }, [val]);
111

112
  useEffect(() => {
×
113
    const invalid = () => setError(true);
×
114

115
    firstCheckboxInputRef.current?.addEventListener("invalid", invalid);
×
116
    return () => {
×
117
      firstCheckboxInputRef.current?.removeEventListener("invalid", invalid);
×
118
    };
119
  }, [firstCheckboxInputRef]);
120

121
  return (
×
122
    <Grid md={gridWidth || 6} xs={12} item>
×
123
      <FormControl fullWidth error={error}>
124
        {!hideLabel && (
×
125
          <StyledFormLabel htmlFor={id} id={`${id}-label`}>
126
            {label}
127
            {required ? <StyledAsterisk>*</StyledAsterisk> : ""}
×
128
            {tooltipText && <Tooltip title={tooltipText} />}
×
129
          </StyledFormLabel>
130
        )}
131
        <FormGroup row={orientation === "horizontal"}>
132
          {options.map((option, index) => {
133
            const isChecked = val?.includes(option.value);
×
134
            return (
×
135
              <CheckboxInput
136
                id={idPrefix.concat(`-${option.label.toLowerCase().replace(" ", "-")}-checkbox`)}
137
                aria-labelledby={`${id}-label`}
138
                key={option.value}
139
                name={name}
140
                checked={isChecked}
141
                value={option.value}
142
                inputLabel={option.label}
143
                inputLabelTooltipText={option.tooltipText}
144
                errorText={option.errorText}
145
                onChange={handleChange}
146
                readOnly={readOnly}
147
                inputRef={(ref) => {
148
                  if (index === 0) {
×
149
                    firstCheckboxInputRef.current = ref;
×
150
                  }
151
                }}
152
              />
153
            );
154
          })}
155
        </FormGroup>
156

157
        {/* NOTE: This is a proxy element for form parsing purposes.
158
          Also, if parent has shared name then it will use string[] as value,
159
          otherwise value will be of type boolean for the form parser */}
160
        {!name &&
×
161
          options.map((option) => {
162
            const isChecked = val?.includes(option.value);
×
163
            return (
×
164
              <input
165
                key={option.value}
166
                name={option.name}
167
                type="checkbox"
168
                data-type="boolean"
169
                value={isChecked ? "true" : "false"}
×
170
                onChange={() => {}}
171
                aria-labelledby={`${id}-label`}
172
                checked
173
                hidden
174
              />
175
            );
176
          })}
177
        <StyledFormHelperText>{error ? helperText : " "}</StyledFormHelperText>
×
178
      </FormControl>
179
    </Grid>
180
  );
181
};
182

183
export default FormGroupCheckbox;
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