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

mendersoftware / gui / 1350829378

27 Jun 2024 01:46PM UTC coverage: 83.494% (-16.5%) from 99.965%
1350829378

Pull #4465

gitlab-ci

mzedel
chore: test fixes

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #4465: MEN-7169 - feat: added multi sorting capabilities to devices view

4506 of 6430 branches covered (70.08%)

81 of 100 new or added lines in 14 files covered. (81.0%)

1661 existing lines in 163 files now uncovered.

8574 of 10269 relevant lines covered (83.49%)

160.6 hits per line

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

92.11
/src/js/components/dashboard/widgets/chart-addition.js
1
// Copyright 2020 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, useState } from 'react';
15

16
import { Add as AddIcon } from '@mui/icons-material';
17
import { Button, FormControl, IconButton, InputLabel, ListSubheader, MenuItem, Select, iconButtonClasses, selectClasses } from '@mui/material';
18
import { makeStyles } from 'tss-react/mui';
19

20
import { BENEFITS, chartTypes, emptyChartSelection } from '../../../constants/appConstants';
21
import { toggle } from '../../../helpers';
22
import Confirm from '../../common/confirm';
23
import EnterpriseNotification from '../../common/enterpriseNotification';
24
import { InfoHintContainer } from '../../common/info-hint';
25
import { HELPTOOLTIPS, MenderHelpTooltip } from '../../helptips/helptooltips';
26
import { Header } from './distribution';
27

28
const fontSize = 'smaller';
6✔
29

30
const useStyles = makeStyles()(theme => ({
24✔
31
  additionButton: { fontSize: '1rem', cursor: 'pointer' },
32
  button: { marginLeft: theme.spacing(2), padding: '6px 8px', fontSize },
33
  buttonWrapper: { display: 'flex', justifyContent: 'flex-end', alignContent: 'center' },
34
  iconButton: {
35
    [`&.${iconButtonClasses.root}`]: {
36
      borderRadius: 5,
37
      border: `1px solid ${theme.palette.primary.main}`,
38
      marginRight: theme.spacing(),
39
      '&.selected': {
40
        background: theme.palette.primary.main,
41
        color: theme.palette.background.paper
42
      }
43
    }
44
  },
45
  formWrapper: {
46
    alignItems: 'baseline',
47
    columnGap: theme.spacing(3),
48
    display: 'grid',
49
    fontSize,
50
    gridTemplateColumns: 'max-content 1fr',
51
    gridTemplateRows: 'auto',
52
    rowGap: theme.spacing(0.5),
53
    marginTop: theme.spacing(),
54
    [`.${selectClasses.select}`]: { paddingBottom: theme.spacing(0.5), paddingTop: 0, fontSize }
55
  }
56
}));
57

58
const GroupSelect = ({ groups, selection, setSelection }) => (
6✔
59
  <FormControl className="margin-top-none">
2✔
60
    <InputLabel id="group-select-label">Device group</InputLabel>
61
    <Select labelId="group-select-label" value={selection.group || true} onChange={e => setSelection({ group: e.target.value })}>
1✔
62
      <MenuItem value={true}>
63
        <em>All Devices</em>
64
      </MenuItem>
65
      {Object.keys(groups).map(group => (
66
        <MenuItem key={group} value={group}>
4✔
67
          {group}
68
        </MenuItem>
69
      ))}
70
    </Select>
71
  </FormControl>
72
);
73

74
const themeSpacing = 8;
6✔
75
const basePadding = 2 * themeSpacing;
6✔
76
const getIndentation = level => ({ paddingLeft: basePadding + level * themeSpacing });
6✔
77

78
const SoftwareSelect = ({ selection, setSelection, software }) => (
6✔
79
  <FormControl className="margin-top-none">
2✔
80
    <InputLabel id="software-select-label">Software</InputLabel>
UNCOV
81
    <Select labelId="software-select-label" value={selection.software} onBlur={undefined} onChange={e => setSelection({ software: e.target.value })}>
×
82
      {software.map(({ subheader, title, value, nestingLevel }) =>
83
        subheader ? (
6✔
84
          <ListSubheader key={value} style={getIndentation(nestingLevel)}>
85
            {subheader}
86
          </ListSubheader>
87
        ) : (
88
          <MenuItem key={value} style={getIndentation(nestingLevel)} value={value}>
89
            {title}
90
          </MenuItem>
91
        )
92
      )}
93
    </Select>
94
  </FormControl>
95
);
96

97
const ChartSelect = ({ classes, selection, setSelection }) => (
6✔
98
  <div>
2✔
99
    {Object.values(chartTypes).map(type => {
100
      const { Icon, key } = type;
4✔
101
      return (
4✔
102
        <IconButton
103
          className={`${classes.iconButton} ${selection.chartType === key ? 'selected' : ''}`}
4✔
104
          key={key}
105
          size="small"
UNCOV
106
          onClick={() => setSelection({ chartType: key })}
×
107
        >
108
          <Icon fontSize="small" />
109
        </IconButton>
110
      );
111
    })}
112
  </div>
113
);
114

115
const chartOptions = [
6✔
116
  { key: 'software', title: 'Software', Selector: SoftwareSelect },
117
  { key: 'group', title: 'Device group', Selector: GroupSelect },
118
  { key: 'type', title: 'Display', Selector: ChartSelect }
119
];
120

121
export const ChartEditWidget = ({ groups, onSave, onCancel, selection: selectionProp = {}, software = [] }) => {
6!
122
  const [selection, setSelection] = useState({ ...emptyChartSelection, ...selectionProp });
2✔
123
  const { classes } = useStyles();
2✔
124

125
  const addCurrentSelection = useCallback(
2✔
126
    () => onSave({ ...emptyChartSelection, ...selection, group: typeof selection.group === 'string' ? selection.group : null }),
1!
127
    // eslint-disable-next-line react-hooks/exhaustive-deps
128
    [JSON.stringify(selection), onSave]
129
  );
130

131
  const onSelectionChange = changedSelection => setSelection(current => ({ ...current, ...changedSelection }));
2✔
132

133
  return (
2✔
134
    <div className="widget chart-widget">
135
      <Header chartType={selection.chartType} />
136
      <div className={classes.formWrapper}>
137
        {chartOptions.map(({ key, title, Selector }) => (
138
          <React.Fragment key={key}>
6✔
139
            <div>{title}</div>
140
            <Selector classes={classes} groups={groups} software={software} selection={selection} setSelection={onSelectionChange} />
141
          </React.Fragment>
142
        ))}
143
      </div>
144
      <div className={classes.buttonWrapper}>
145
        <Button className={classes.button} size="small" onClick={onCancel}>
146
          Cancel
147
        </Button>
148
        <Button className={classes.button} size="small" onClick={addCurrentSelection} variant="contained" disabled={!selection}>
149
          Save
150
        </Button>
151
      </div>
152
    </div>
153
  );
154
};
155

156
export const RemovalWidget = ({ onCancel, onClick }) => (
6✔
UNCOV
157
  <div className="widget chart-widget">
×
158
    <Confirm classes="flexbox centered confirmation-overlay" cancel={onCancel} action={onClick} style={{ justifyContent: 'center' }} type="chartRemoval" />
159
  </div>
160
);
161

162
export const WidgetAdditionWidget = ({ onAdditionClick, ...remainder }) => {
6✔
163
  const [adding, setAdding] = useState(false);
36✔
164
  const { classes } = useStyles();
36✔
165

166
  const addCurrentSelection = selection => {
36✔
167
    onAdditionClick(selection);
1✔
168
    setAdding(false);
1✔
169
  };
170

171
  const onCancelClick = () => setAdding(toggle);
36✔
172

173
  return adding ? (
36✔
174
    <ChartEditWidget {...remainder} onSave={addCurrentSelection} onCancel={onCancelClick} />
175
  ) : (
176
    <div className="widget">
177
      <InfoHintContainer className="" style={{ alignItems: 'end' }}>
178
        <EnterpriseNotification id={BENEFITS.dashboard.id} />
179
        <MenderHelpTooltip id={HELPTOOLTIPS.dashboardWidget.id} />
180
      </InfoHintContainer>
181
      <div className={`flexbox centered muted ${classes.additionButton}`} onClick={() => setAdding(true)}>
1✔
182
        <AddIcon />
183
        <span className={classes.additionButton}>add a widget</span>
184
      </div>
185
    </div>
186
  );
187
};
188

189
export default WidgetAdditionWidget;
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