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

mendersoftware / gui / 897326496

pending completion
897326496

Pull #3752

gitlab-ci

mzedel
chore(e2e): made use of shared timeout & login checking values to remove code duplication

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #3752: chore(e2e-tests): slightly simplified log in test + separated log out test

4395 of 6392 branches covered (68.76%)

8060 of 9780 relevant lines covered (82.41%)

126.17 hits per line

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

63.24
/src/js/components/search-result.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, { useEffect, useState } from 'react';
15
import { connect } from 'react-redux';
16
import { useNavigate } from 'react-router-dom';
17

18
import { Close as CloseIcon } from '@mui/icons-material';
19
// material ui
20
import { Drawer, IconButton, Typography } from '@mui/material';
21
import { makeStyles } from 'tss-react/mui';
22

23
import pluralize from 'pluralize';
24

25
import { setSearchState, setSnackbar } from '../actions/appActions';
26
import { setDeviceListState } from '../actions/deviceActions';
27
import { SORTING_OPTIONS, TIMEOUTS } from '../constants/appConstants';
28
import { getIdAttribute, getMappedDevicesList, getOnboardingState, getUserSettings } from '../selectors';
29
import { getHeaders } from './devices/authorized-devices';
30
import { routes } from './devices/base-devices';
31
import Devicelist from './devices/devicelist';
32

33
const useStyles = makeStyles()(theme => ({
50✔
34
  drawerOffset: {
35
    top: theme.mixins.toolbar.minHeight + 1,
36
    left: 200
37
  },
38
  paper: {
39
    maxWidth: '100vw',
40
    minHeight: '20vh',
41
    boxShadow: 'none'
42
  }
43
}));
44

45
const leftNavOffset = 500;
3✔
46
const ResultTitle = ({ onClick, term, total }) => {
3✔
47
  const content = `${total ? total : 'No'} ${pluralize('device', total)} found for "${term}"`;
1!
48
  let props = { className: 'bold' };
1✔
49
  let style = {};
1✔
50
  if (!total) {
1!
51
    props = { className: 'info' };
1✔
52
    style = { width: `calc(100% - ${leftNavOffset}px)` };
1✔
53
  }
54
  return (
1✔
55
    <div className={`flexbox ${total ? 'center-aligned' : 'centered'}`} style={style}>
1!
56
      <Typography variant="body2" {...props}>
57
        {content}
58
      </Typography>
59
      <a className="margin-left-large" onClick={onClick}>
60
        clear search
61
      </a>
62
    </div>
63
  );
64
};
65

66
export const SearchResult = ({
3✔
67
  columnSelection,
68
  customColumnSizes,
69
  devices,
70
  idAttribute,
71
  onboardingState,
72
  onToggleSearchResult,
73
  open = true,
×
74
  searchState,
75
  setDeviceListState,
76
  setSearchState,
77
  setSnackbar
78
}) => {
79
  const navigate = useNavigate();
561✔
80

81
  const { classes } = useStyles();
561✔
82

83
  const [columnHeaders, setColumnHeaders] = useState(getHeaders(columnSelection, routes.devices.defaultHeaders, idAttribute));
561✔
84

85
  const { isSearching, searchTerm, searchTotal, sort = {} } = searchState;
561!
86
  const { direction: sortDown = SORTING_OPTIONS.desc, key: sortCol } = sort;
561✔
87

88
  useEffect(() => {
561✔
89
    const columnHeaders = getHeaders(columnSelection, routes.devices.defaultHeaders, idAttribute);
13✔
90
    setColumnHeaders(columnHeaders);
13✔
91
  }, [columnSelection, idAttribute.attribute]);
92

93
  useEffect(() => {
561✔
94
    if (!open && isSearching) {
7!
95
      onToggleSearchResult();
×
96
    }
97
  }, [open, isSearching]);
98

99
  useEffect(() => {
561✔
100
    if (open && !searchTerm) {
7!
101
      onToggleSearchResult();
×
102
    }
103
  }, [open, searchTerm]);
104

105
  const onDeviceSelect = device => {
561✔
106
    setDeviceListState({ selectedId: device.id });
×
107
    onToggleSearchResult();
×
108
    setTimeout(() => navigate(`/devices/${device.status}?id=${device.id}`), TIMEOUTS.debounceShort);
×
109
  };
110

111
  const handlePageChange = page => {
561✔
112
    setSearchState({ page });
×
113
  };
114

115
  const onSortChange = attribute => {
561✔
116
    let changedSortCol = attribute.name;
×
117
    let changedSortDown = sortDown === SORTING_OPTIONS.desc ? SORTING_OPTIONS.asc : SORTING_OPTIONS.desc;
×
118
    if (changedSortCol !== sortCol) {
×
119
      changedSortDown = SORTING_OPTIONS.desc;
×
120
    }
121
    setSearchState({ page: 1, sort: { direction: changedSortDown, key: changedSortCol, scope: attribute.scope } });
×
122
  };
123

124
  const onClearClick = () => {
561✔
125
    setSearchState({ searchTerm: '' });
×
126
    onToggleSearchResult();
×
127
  };
128

129
  return (
561✔
130
    <Drawer
131
      anchor="top"
132
      classes={classes}
133
      disableEnforceFocus
134
      open={open}
135
      ModalProps={{ className: classes.drawerOffset, BackdropProps: { className: classes.drawerOffset } }}
136
      PaperProps={{ className: `${classes.drawerOffset} ${classes.paper}` }}
137
      SlideProps={{ direction: 'left' }}
138
    >
139
      <div className="flexbox center-aligned margin-bottom-small space-between">
140
        <ResultTitle onClick={onClearClick} term={searchTerm} total={searchTotal} />
141
        <IconButton onClick={onToggleSearchResult} aria-label="close" size="large">
142
          <CloseIcon />
143
        </IconButton>
144
      </div>
145
      {!!searchTotal && (
561!
146
        <Devicelist
147
          className=""
148
          columnHeaders={columnHeaders}
149
          customColumnSizes={customColumnSizes}
150
          deviceListState={{ perPage: 10, sort: {} }}
151
          devices={devices}
152
          idAttribute={idAttribute}
153
          onboardingState={onboardingState}
154
          onSort={onSortChange}
155
          PaginationProps={{ rowsPerPageOptions: [10] }}
156
          pageTotal={searchTotal}
157
          onPageChange={handlePageChange}
158
          pageLoading={isSearching}
159
          onExpandClick={onDeviceSelect}
160
          setSnackbar={setSnackbar}
161
        />
162
      )}
163
    </Drawer>
164
  );
165
};
166

167
const actionCreators = { setDeviceListState, setSearchState, setSnackbar };
3✔
168

169
const mapStateToProps = state => {
3✔
170
  const { columnSelection } = getUserSettings(state);
432✔
171
  return {
432✔
172
    columnSelection,
173
    customColumnSizes: state.users.customColumns,
174
    devices: getMappedDevicesList(state, 'search'),
175
    idAttribute: getIdAttribute(state),
176
    onboardingState: getOnboardingState(state),
177
    searchState: state.app.searchState
178
  };
179
};
180

181
export default connect(mapStateToProps, actionCreators)(SearchResult);
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