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

mendersoftware / mender-server / 10423

11 Nov 2025 04:53PM UTC coverage: 74.435% (-0.1%) from 74.562%
10423

push

gitlab-ci

web-flow
Merge pull request #1071 from mendersoftware/dependabot/npm_and_yarn/frontend/main/development-dependencies-92732187be

3868 of 5393 branches covered (71.72%)

Branch coverage included in aggregate %.

5 of 5 new or added lines in 2 files covered. (100.0%)

176 existing lines in 95 files now uncovered.

64605 of 86597 relevant lines covered (74.6%)

7.74 hits per line

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

96.72
/frontend/src/js/common-ui/ChipSelect.tsx
1
// Copyright 2023 Northern.tech AS
2✔
2
//
2✔
3
//    Licensed under the Apache License, Version 2.0 (the "License");
2✔
4
//    you may not use this file except in compliance with the License.
2✔
5
//    You may obtain a copy of the License at
2✔
6
//
2✔
7
//        http://www.apache.org/licenses/LICENSE-2.0
2✔
8
//
2✔
9
//    Unless required by applicable law or agreed to in writing, software
2✔
10
//    distributed under the License is distributed on an "AS IS" BASIS,
2✔
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2✔
12
//    See the License for the specific language governing permissions and
2✔
13
//    limitations under the License.
2✔
14
import { useState } from 'react';
2✔
15
import { Controller, useFormContext } from 'react-hook-form';
2✔
16

2✔
17
import { Cancel as CancelIcon } from '@mui/icons-material';
2✔
18
import { Autocomplete, Chip, TextField } from '@mui/material';
2✔
19

2✔
20
import { duplicateFilter, unionizeStrings } from '@northern.tech/utils/helpers';
2✔
21

2✔
22
export const ChipSelect = ({ className = '', name, disabled = false, helperText, inputRef, label = '', options = [], placeholder = '' }) => {
12✔
23
  const [value, setValue] = useState('');
185✔
24

2✔
25
  const { control, getValues } = useFormContext();
185✔
26

2✔
27
  // to allow device types to automatically be selected on entered ',' we have to filter the input and transform any completed device types (followed by a ',')
2✔
28
  // while also checking for duplicates and allowing complete resets of the input
2✔
29
  const onTextInputChange = (inputValue, reason, setCurrentSelection) => {
185✔
30
    const value = inputValue || '';
20✔
31
    if (reason === 'clear') {
20!
32
      setValue('');
2✔
33
      return setCurrentSelection([]);
2✔
34
    } else if (reason === 'reset') {
20!
35
      return setValue('');
2✔
36
    }
2✔
37
    const lastIndex = value.lastIndexOf(',');
20✔
38
    const possibleSelection = value.substring(0, lastIndex).split(',').filter(duplicateFilter);
20✔
39
    const currentValue = value.substring(lastIndex + 1);
20✔
40
    const selection = getValues(name);
20✔
41
    const nextSelection = unionizeStrings(selection, possibleSelection);
20✔
42
    setValue(currentValue);
20✔
43
    setCurrentSelection(nextSelection);
20✔
44
  };
2✔
45

2✔
46
  const onTextInputLeave = (value, setCurrentSelection) => {
185✔
47
    const selection = getValues(name);
3✔
48
    const nextSelection = unionizeStrings(selection, [value]);
3✔
49
    setCurrentSelection(nextSelection);
3✔
50
    setValue('');
3✔
51
  };
2✔
52

2✔
53
  return (
185✔
54
    <Controller
2✔
55
      control={control}
2✔
56
      name={name}
2✔
57
      render={({ field: { onChange: formOnChange, value: currentSelection, ref, ...props } }) => (
2✔
58
        <Autocomplete
195✔
59
          id={`${name}-chip-select`}
2✔
60
          value={currentSelection}
2✔
61
          className={className}
2✔
62
          filterSelectedOptions
2✔
63
          freeSolo={true}
2✔
64
          includeInputInList={true}
2✔
65
          multiple
2✔
66
          // allow edits to the textinput without deleting existing device types by ignoring backspace
2✔
UNCOV
67
          onChange={(e, value) => (e.key !== 'Backspace' ? formOnChange(value) : null)}
2!
68
          onInputChange={(e, v, reason) => onTextInputChange(null, reason, formOnChange)}
11✔
69
          options={options}
2✔
70
          readOnly={disabled}
2✔
71
          ref={ref}
2✔
72
          renderTags={(values, getTagProps) =>
2✔
73
            values.map((option, index) => {
29✔
74
              const { key, onDelete, ...tagProps } = getTagProps({ index });
29✔
75
              return (
29✔
76
                <Chip label={option} key={key} onDelete={onDelete} deleteIcon={<CancelIcon onClick={onDelete} aria-label={`${name}-delete`} />} {...tagProps} />
2✔
77
              );
2✔
78
            })
2✔
79
          }
2✔
80
          renderInput={params => (
2✔
81
            <TextField
198✔
82
              {...params}
2✔
83
              fullWidth
2✔
84
              slotProps={{
2✔
85
                htmlInput: { ...params.inputProps, value },
2✔
86
                input: params.InputProps
2✔
87
              }}
2✔
88
              key={`${name}-input`}
2✔
89
              label={label}
2✔
90
              variant={disabled ? 'standard' : 'outlined'}
2✔
91
              onBlur={e => onTextInputLeave(e.target.value, formOnChange)}
3✔
92
              onChange={e => onTextInputChange(e.target.value, 'input', formOnChange)}
11✔
93
              placeholder={currentSelection.length ? '' : placeholder}
2✔
94
              helperText={helperText}
2✔
95
              inputRef={inputRef}
2✔
96
            />
2✔
97
          )}
2✔
98
          {...props}
2✔
99
        />
2✔
100
      )}
2✔
101
    />
2✔
102
  );
2✔
103
};
2✔
104

2✔
105
export default ChipSelect;
2✔
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