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

mendersoftware / gui / 963124858

pending completion
963124858

Pull #3870

gitlab-ci

mzedel
chore: cleaned up left over onboarding tooltips & aligned with updated design

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #3870: MEN-5413

4368 of 6355 branches covered (68.73%)

91 of 118 new or added lines in 22 files covered. (77.12%)

1753 existing lines in 162 files now uncovered.

8246 of 10042 relevant lines covered (82.12%)

193.52 hits per line

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

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

16
// material ui
17
import { Autocomplete, TextField, createFilterOptions } from '@mui/material';
18

19
import { TIMEOUTS } from '../../../constants/appConstants';
20
import { getFilterLabelByKey } from './filters';
21
import { defaultHeaders } from '../base-devices';
22

23
const textFieldStyle = { marginTop: 0, marginBottom: 15 };
13✔
24

25
const defaultScope = 'inventory';
13✔
26

27
export const getOptionLabel = option => {
13✔
28
  const header = Object.values(defaultHeaders).find(
287✔
29
    ({ attribute }) => attribute.scope === option.scope && (attribute.name === option.key || attribute.alternative === option.key)
1,596✔
30
  );
31
  return header?.title || option.title || option.value || option.key || option;
287✔
32
};
33

34
const FilterOption = (props, option) => {
13✔
35
  let content = getOptionLabel(option);
58✔
36
  if (option.category === 'recently used') {
58!
UNCOV
37
    content = (
×
38
      <div className="flexbox center-aligned space-between" style={{ width: '100%' }}>
39
        <div>{content}</div>
40
        <div className="muted slightly-smaller">({option.scope})</div>
41
      </div>
42
    );
43
  }
44
  return <li {...props}>{content}</li>;
58✔
45
};
46

47
const optionsFilter = createFilterOptions();
13✔
48

49
const filterOptions = (options, params) => {
13✔
50
  const filtered = optionsFilter(options, params);
9✔
51
  if (filtered.length !== 1 && params.inputValue !== '') {
9✔
52
    filtered.push({
5✔
53
      inputValue: params.inputValue,
54
      key: 'custom',
55
      value: `Use "${params.inputValue}"`,
56
      category: 'custom',
57
      priority: 99
58
    });
59
  }
60
  return filtered;
9✔
61
};
62

63
const defaultFilter = { key: '', scope: defaultScope };
13✔
64

65
export const AttributeAutoComplete = ({ attributes, disabled, filter = defaultFilter, label = 'Attribute', onRemove, onSelect }) => {
13!
66
  const [key, setKey] = useState(filter.key); // this refers to the selected filter with key as the id
14✔
67
  const [options, setOptions] = useState([]);
14✔
68
  const [reset, setReset] = useState(true);
14✔
69
  const [scope, setScope] = useState(filter.scope);
14✔
70
  const timer = useRef();
14✔
71

72
  useEffect(() => {
14✔
73
    return () => {
4✔
74
      clearTimeout(timer.current);
4✔
75
    };
76
  }, []);
77

78
  useEffect(() => {
14✔
79
    setKey('');
6✔
80
    setScope(defaultScope);
6✔
81
    setOptions(attributes.sort((a, b) => a.priority - b.priority));
26✔
82
    // eslint-disable-next-line react-hooks/exhaustive-deps
83
  }, [attributes.length, reset]);
84

85
  useEffect(() => {
14✔
86
    setKey(filter.key);
4✔
87
  }, [filter.key]);
88

89
  useEffect(() => {
14✔
90
    clearTimeout(timer.current);
9✔
91
    timer.current = setTimeout(() => onSelect({ key, scope }), TIMEOUTS.debounceDefault);
9✔
92
    return () => {
9✔
93
      clearTimeout(timer.current);
9✔
94
    };
95
  }, [key, onSelect, scope]);
96

97
  const updateFilterKey = (value, selectedScope) => {
14✔
UNCOV
98
    if (!value) {
×
UNCOV
99
      return removeFilter();
×
100
    }
UNCOV
101
    const { key = value, scope: fallbackScope } = attributes.find(filter => filter.key === value) ?? {};
×
UNCOV
102
    setKey(key);
×
UNCOV
103
    setScope(selectedScope || fallbackScope);
×
104
  };
105

106
  const removeFilter = useCallback(() => {
14✔
UNCOV
107
    if (key) {
×
UNCOV
108
      onRemove({ key, scope });
×
109
    }
UNCOV
110
    setReset(!reset);
×
111
  }, [key, onRemove, reset, setReset, scope]);
112

113
  return (
14✔
114
    <Autocomplete
115
      autoComplete
116
      autoHighlight
117
      autoSelect
118
      disabled={disabled}
119
      freeSolo
120
      filterSelectedOptions
121
      filterOptions={filterOptions}
122
      getOptionLabel={getOptionLabel}
123
      groupBy={option => option.category}
58✔
124
      renderOption={FilterOption}
125
      id="filter-selection"
126
      includeInputInList={true}
127
      onChange={(e, changedValue) => {
128
        const { inputValue, key = changedValue, scope } = changedValue || {};
1!
129
        if (inputValue) {
1!
130
          // only circumvent updateFilterKey if we deal with a custom attribute - those will be treated as inventory attributes
131
          setKey(inputValue);
1✔
132
          return setScope(defaultScope);
1✔
133
        }
UNCOV
134
        updateFilterKey(key, scope);
×
135
      }}
136
      options={options}
137
      renderInput={params => <TextField {...params} label={label} style={textFieldStyle} />}
25✔
138
      key={reset}
139
      value={getFilterLabelByKey(key, attributes)}
140
    />
141
  );
142
};
143

144
export default AttributeAutoComplete;
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