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

mendersoftware / gui / 901187442

pending completion
901187442

Pull #3795

gitlab-ci

mzedel
feat: increased chances of adopting our intended navigation patterns instead of unsupported browser navigation

Ticket: None
Changelog: None
Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #3795: feat: increased chances of adopting our intended navigation patterns instead of unsupported browser navigation

4389 of 6365 branches covered (68.96%)

5 of 5 new or added lines in 1 file covered. (100.0%)

1729 existing lines in 165 files now uncovered.

8274 of 10019 relevant lines covered (82.58%)

144.86 hits per line

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

71.79
/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, 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
let timer;
28
const filterOptionsByPlan = {
13✔
29
  os: { $eq: { title: 'equals' } },
30
  professional: DEVICE_FILTERING_OPTIONS,
31
  enterprise: DEVICE_FILTERING_OPTIONS
32
};
33

34
const defaultScope = 'inventory';
13✔
35

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

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

53
  useEffect(() => {
4✔
54
    return () => {
2✔
55
      clearTimeout(timer);
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]);
72

73
  useEffect(() => {
4✔
74
    clearTimeout(timer);
4✔
75
    timer = 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, 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