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

mendersoftware / gui / 963002358

pending completion
963002358

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

4348 of 6319 branches covered (68.81%)

95 of 122 new or added lines in 24 files covered. (77.87%)

1734 existing lines in 160 files now uncovered.

8174 of 9951 relevant lines covered (82.14%)

178.12 hits per line

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

72.15
/src/js/components/devices/widgets/filteritem.js
1
// Copyright 2019 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
import { Help as HelpIcon, HighlightOff as HighlightOffIcon } from '@mui/icons-material';
17
// material ui
18
import { FormHelperText, IconButton, MenuItem, Select, TextField } from '@mui/material';
19

20
import { TIMEOUTS } from '../../../constants/appConstants';
21
import { DEVICE_FILTERING_OPTIONS, emptyFilter } from '../../../constants/deviceConstants';
22
import MenderTooltip from '../../common/mendertooltip';
23
import AttributeAutoComplete from './attribute-autocomplete';
24

25
const textFieldStyle = { marginTop: 0, marginBottom: 15 };
13✔
26

27
const filterOptionsByPlan = {
13✔
28
  os: { $eq: { title: 'equals' } },
29
  professional: DEVICE_FILTERING_OPTIONS,
30
  enterprise: DEVICE_FILTERING_OPTIONS
31
};
32

33
const defaultScope = 'inventory';
13✔
34

35
const filterNotifications = {
13✔
36
  name: (
37
    <MenderTooltip arrow placement="bottom" title="Filtering by name is limited to devices with a previously defined name.">
38
      <div className="tooltip help" style={{ top: 20, left: -12 }}>
39
        <HelpIcon />
40
      </div>
41
    </MenderTooltip>
42
  )
43
};
44

45
export const FilterItem = ({ attributes, filter, onRemove, onSelect, plan }) => {
13✔
46
  const [key, setKey] = useState(filter.key || ''); // this refers to the selected filter with key as the id
4✔
47
  const [value, setValue] = useState(filter.value || ''); // while this is the value that is applied with the filter
4✔
48
  const [operator, setOperator] = useState(filter.operator || '$eq');
4✔
49
  const [scope, setScope] = useState(filter.scope || defaultScope);
4✔
50
  const [reset, setReset] = useState(true);
4✔
51
  const timer = useRef();
4✔
52

53
  useEffect(() => {
4✔
54
    return () => {
2✔
55
      clearTimeout(timer.current);
2✔
56
    };
57
  }, []);
58

59
  useEffect(() => {
4✔
60
    setKey(emptyFilter.key);
2✔
61
    setValue(emptyFilter.value);
2✔
62
    setOperator(emptyFilter.operator);
2✔
63
    setScope(emptyFilter.scope);
2✔
64
  }, [attributes.length, reset]);
65

66
  useEffect(() => {
4✔
67
    setKey(filter.key);
2✔
68
    setValue(filter.value);
2✔
69
    setOperator(filter.operator);
2✔
70
    setScope(filter.scope);
2✔
71
  }, [filter.key, filter.operator, filter.scope, filter.value]);
72

73
  useEffect(() => {
4✔
74
    clearTimeout(timer.current);
4✔
75
    timer.current = setTimeout(
4✔
76
      () =>
UNCOV
77
        key && (value || operator.includes('exists'))
×
78
          ? onSelect({
79
              key,
80
              operator,
81
              scope,
82
              value
83
            })
84
          : null,
85
      TIMEOUTS.debounceDefault
86
    );
87
  }, [key, onSelect, operator, scope, value]);
88

89
  const updateFilterKey = ({ key, scope }) => {
4✔
UNCOV
90
    setKey(key);
×
UNCOV
91
    setScope(scope);
×
92
  };
93

94
  const updateFilterOperator = ({ target: { value: changedOperator } }) => {
4✔
UNCOV
95
    const operator = DEVICE_FILTERING_OPTIONS[changedOperator] || {};
×
UNCOV
96
    const opValue = operator.value ?? value ?? '';
×
UNCOV
97
    setOperator(changedOperator);
×
UNCOV
98
    setValue(opValue);
×
99
  };
100

101
  const updateFilterValue = ({ target: { value = '' } }) => {
4!
UNCOV
102
    setValue(value);
×
103
  };
104

105
  const removeFilter = useCallback(() => {
4✔
UNCOV
106
    onRemove({ key, operator, scope, value });
×
UNCOV
107
    setReset(!reset);
×
108
  }, [key, onRemove, operator, reset, scope, setReset, value]);
109

110
  const filterOptions = plan ? filterOptionsByPlan[plan] : DEVICE_FILTERING_OPTIONS;
4✔
111
  const operatorHelpMessage = (DEVICE_FILTERING_OPTIONS[operator] || {}).help || '';
4✔
112
  const showValue = typeof (filterOptions[operator] || {}).value === 'undefined';
4✔
113
  return (
4✔
114
    <>
115
      <div className="flexbox center-aligned relative">
116
        {filterNotifications[key]}
117
        <AttributeAutoComplete attributes={attributes} filter={filter} label="Attribute" onRemove={removeFilter} onSelect={updateFilterKey} />
118
        <Select className="margin-left-small margin-right-small" onChange={updateFilterOperator} value={operator}>
119
          {Object.entries(filterOptions).map(([optionKey, option]) => (
120
            <MenuItem key={optionKey} value={optionKey}>
24✔
121
              {option.title}
122
            </MenuItem>
123
          ))}
124
        </Select>
125
        {showValue && <TextField label="Value" value={value} onChange={updateFilterValue} InputLabelProps={{ shrink: !!value }} style={textFieldStyle} />}
8✔
126
        {!!key && (
6✔
127
          <IconButton className="margin-left" onClick={removeFilter} size="small">
128
            <HighlightOffIcon />
129
          </IconButton>
130
        )}
131
      </div>
132
      {operatorHelpMessage && (
4!
133
        <div className="margin-bottom-small">
134
          <FormHelperText>{operatorHelpMessage}</FormHelperText>
135
        </div>
136
      )}
137
    </>
138
  );
139
};
140

141
export default FilterItem;
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