• 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

8.82
/src/components/Questionnaire/AutocompleteInput.tsx
1
import {
2
  Autocomplete,
3
  AutocompleteChangeReason,
4
  AutocompleteProps,
5
  AutocompleteValue,
6
  FormControl,
7
  FormHelperText,
8
  Grid,
9
  TextField,
10
  styled,
11
} from "@mui/material";
12
import { ReactNode, SyntheticEvent, useEffect, useId, useRef, useState } from "react";
13
import { ReactComponent as DropdownArrowsIconSvg } from "../../assets/icons/dropdown_arrows.svg";
14
import Tooltip from "../Tooltip";
15
import { updateInputValidity } from "../../utils";
16

17
const StyledFormControl = styled(FormControl)(() => ({
2✔
18
  height: "100%",
19
  justifyContent: "end",
20
  "& .MuiFormHelperText-root.Mui-error": {
21
    color: "#D54309 !important",
22
  },
23
  "& .MuiOutlinedInput-notchedOutline": {
24
    borderRadius: "8px",
25
    borderColor: "#6B7294",
26
  },
27
  "& .Mui-focused .MuiOutlinedInput-notchedOutline": {
28
    border: "1px solid #209D7D !important",
29
    boxShadow:
30
      "2px 2px 4px 0px rgba(38, 184, 147, 0.10), -1px -1px 6px 0px rgba(38, 184, 147, 0.20)",
31
  },
32
  "& .Mui-error fieldset": {
33
    borderColor: "#D54309 !important",
34
  },
35
  "& .MuiInputBase-input::placeholder": {
36
    color: "#87878C",
37
    fontWeight: 400,
38
    opacity: 1,
39
  },
40
  "& .MuiAutocomplete-input": {
41
    color: "#083A50",
42
  },
43
  "& .MuiAutocomplete-root .MuiAutocomplete-endAdornment": {
44
    top: "50%",
45
    transform: "translateY(-50%)",
46
    right: "12px",
47
  },
48
  "& .MuiAutocomplete-popupIndicator": {
49
    marginRight: "1px",
50
  },
51
  "& .MuiAutocomplete-popupIndicatorOpen": {
52
    transform: "none",
53
  },
54

55
  "& .MuiPaper-root": {
56
    borderRadius: "8px",
57
    border: "1px solid #6B7294",
58
    marginTop: "2px",
59
    "& .MuiAutocomplete-listbox": {
60
      padding: 0,
61
      overflow: "auto",
62
      maxHeight: "300px",
63
    },
64
    "& .MuiAutocomplete-option[aria-selected='true']": {
65
      color: "#083A50",
66
      background: "#FFFFFF",
67
    },
68
    "& .MuiAutocomplete-option": {
69
      padding: "7.5px 10px",
70
      minHeight: "35px",
71
      color: "#083A50",
72
      background: "#FFFFFF",
73
    },
74
    "& .MuiAutocomplete-option:hover": {
75
      backgroundColor: "#3E7E6D",
76
      color: "#FFFFFF",
77
    },
78
    "& .MuiAutocomplete-option.Mui-focused": {
79
      backgroundColor: "#3E7E6D !important",
80
      color: "#FFFFFF",
81
    },
82
  },
83
}));
84

85
const StyledFormLabel = styled("label")(() => ({
2✔
86
  fontWeight: 700,
87
  fontSize: "16px",
88
  lineHeight: "19.6px",
89
  minHeight: "20px",
90
  color: "#083A50",
91
  marginBottom: "4px",
92
}));
93

94
const StyledAsterisk = styled("span")(() => ({
2✔
95
  color: "#C93F08",
96
  marginLeft: "2px",
97
}));
98

99
const StyledAutocomplete = styled(Autocomplete)(({ readOnly }: { readOnly?: boolean }) => ({
2✔
100
  "& .MuiInputBase-root": {
101
    "&.MuiAutocomplete-inputRoot.MuiInputBase-root": {
102
      display: "flex",
103
      alignItems: "center",
104
      padding: 0,
105
    },
106
    "& .MuiOutlinedInput-input:read-only": {
107
      backgroundColor: "#E5EEF4",
108
      color: "#083A50",
109
      cursor: "not-allowed",
110
      borderRadius: "8px",
111
    },
112
    "& .MuiInputBase-input": {
113
      fontWeight: 400,
114
      fontSize: "16px",
115
      fontFamily: "'Nunito', 'Rubik', sans-serif",
116
      padding: "12px 30px 12px 12px !important",
117
      height: "20px",
118
      cursor: readOnly ? "not-allowed !important" : "initial",
×
119
    },
120
    "& .MuiAutocomplete-clearIndicator": {
121
      visibility: "hidden !important",
122
      position: "absolute",
123
    },
124
  },
125
}));
126

127
const StyledFormHelperText = styled(FormHelperText)(() => ({
2✔
128
  marginLeft: 0,
129
  marginTop: "4px",
130
  minHeight: "20px",
131
}));
132

133
type Props<T> = {
134
  name?: string;
135
  label?: string;
136
  value?: T;
137
  options?: T[];
138
  gridWidth?: 2 | 4 | 6 | 8 | 10 | 12;
139
  helpText?: string;
140
  tooltipText?: string | ReactNode;
141
  required?: boolean;
142
  validate?: (input: T) => boolean;
143
} & Omit<AutocompleteProps<T, false, true, true, "div">, "renderInput">;
144

145
const AutocompleteInput = <T,>({
2✔
146
  name,
147
  label,
148
  gridWidth,
149
  helpText,
150
  tooltipText,
151
  required,
152
  value,
153
  onChange,
154
  options,
155
  validate,
156
  placeholder,
157
  freeSolo,
158
  readOnly,
159
  ...rest
160
}: Props<T>) => {
161
  const id = rest.id || useId();
×
162

163
  const [val, setVal] = useState<T>(value);
×
164
  const [error, setError] = useState<boolean>(false);
×
165
  const helperText = helpText || (required ? "This field is required" : " ");
×
166
  const inputRef = useRef<HTMLInputElement>(null);
×
167

168
  const processValue = (newValue: T) => {
×
169
    if (typeof validate === "function") {
×
170
      const customIsValid = validate(newValue);
×
171
      updateInputValidity(inputRef, !customIsValid ? helpText : "");
×
172
    } else if (required) {
×
173
      updateInputValidity(inputRef, !newValue ? helperText : "");
×
174
    }
175

176
    setVal(newValue);
×
177
  };
178

179
  const onChangeWrapper = (
×
180
    event: SyntheticEvent,
181
    newValue: AutocompleteValue<T, false, false, false>,
182
    reason: AutocompleteChangeReason
183
  ): void => {
184
    if (typeof onChange === "function") {
×
185
      onChange(event, newValue, reason);
×
186
    }
187

188
    processValue(newValue);
×
189
    setError(false);
×
190
  };
191

192
  const onInputChangeWrapper = (event: SyntheticEvent, newValue: string): void => {
×
193
    processValue(newValue as unknown as T);
×
194
    setError(false);
×
195
  };
196

197
  useEffect(() => {
×
198
    const invalid = () => setError(true);
×
199

200
    inputRef.current?.addEventListener("invalid", invalid);
×
201
    return () => {
×
202
      inputRef.current?.removeEventListener("invalid", invalid);
×
203
    };
204
  }, [inputRef]);
205

206
  useEffect(() => {
×
207
    processValue(value);
×
208
  }, [value]);
209

210
  return (
×
211
    <Grid md={gridWidth || 6} xs={12} item>
×
212
      <StyledFormControl fullWidth error={error}>
213
        <StyledFormLabel htmlFor={id}>
214
          {label}
215
          {required ? <StyledAsterisk>*</StyledAsterisk> : ""}
×
216
          {tooltipText && <Tooltip placement="right" title={tooltipText} />}
×
217
        </StyledFormLabel>
218
        <StyledAutocomplete
219
          value={val}
220
          onChange={onChangeWrapper}
221
          onInputChange={onInputChangeWrapper}
222
          options={options}
223
          readOnly={readOnly}
224
          forcePopupIcon
225
          popupIcon={<DropdownArrowsIconSvg />}
226
          freeSolo={freeSolo}
227
          slotProps={{
228
            popper: {
229
              disablePortal: true,
230
              modifiers: [
231
                {
232
                  // disables popper from flipping above the input when out of screen room
233
                  name: "flip",
234
                  enabled: false,
235
                  options: {
236
                    fallbackPlacements: [],
237
                  },
238
                },
239
              ],
240
            },
241
          }}
242
          renderInput={(params) => (
243
            <TextField
×
244
              {...params}
245
              inputRef={inputRef}
246
              name={name}
247
              required={required}
248
              placeholder={placeholder}
249
              id={id}
250
            />
251
          )}
252
          {...rest}
253
        />
254
        <StyledFormHelperText>{!readOnly && error ? helperText : " "}</StyledFormHelperText>
×
255
      </StyledFormControl>
256
    </Grid>
257
  );
258
};
259

260
export default AutocompleteInput;
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