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

mendersoftware / gui / 1081664682

22 Nov 2023 02:11PM UTC coverage: 82.798% (-17.2%) from 99.964%
1081664682

Pull #4214

gitlab-ci

tranchitella
fix: Fixed the infinite page redirects when the back button is pressed

Remove the location and navigate from the useLocationParams.setValue callback
dependencies as they change the set function that is presented in other
useEffect dependencies. This happens when the back button is clicked, which
leads to the location changing infinitely.

Changelog: Title
Ticket: MEN-6847
Ticket: MEN-6796

Signed-off-by: Ihor Aleksandrychiev <ihor.aleksandrychiev@northern.tech>
Signed-off-by: Fabio Tranchitella <fabio.tranchitella@northern.tech>
Pull Request #4214: fix: Fixed the infinite page redirects when the back button is pressed

4319 of 6292 branches covered (0.0%)

8332 of 10063 relevant lines covered (82.8%)

191.0 hits per line

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

91.89
/src/js/components/devices/dialogs/monitordetailsdialog.js
1
// Copyright 2021 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, { useState } from 'react';
15
import CopyToClipboard from 'react-copy-to-clipboard';
16

17
import {
18
  ArrowDropDown as ArrowDropDownIcon,
19
  ArrowDropUp as ArrowDropUpIcon,
20
  FileCopy as CopyPasteIcon,
21
  ReportProblemOutlined as WarningIcon
22
} from '@mui/icons-material';
23
import { Accordion, AccordionDetails, AccordionSummary, Button, Collapse, Dialog, DialogActions, DialogContent, DialogTitle, IconButton } from '@mui/material';
24
import withStyles from '@mui/styles/withStyles';
25

26
import { TIMEOUTS } from '../../../constants/appConstants';
27
import { toggle } from '../../../helpers';
28

29
const CopyButton = ({ text, onCopy }) => (
10✔
30
  <CopyToClipboard text={text} onCopy={onCopy}>
×
31
    <IconButton size="small">
32
      <CopyPasteIcon fontSize="small" />
33
    </IconButton>
34
  </CopyToClipboard>
35
);
36

37
const LogLine = ({ beExplicit, line, prefix }) => {
10✔
38
  const [copied, setCopied] = useState(false);
9✔
39
  const [hovering, setHovering] = useState(false);
9✔
40

41
  const onCopied = () => {
9✔
42
    setCopied(true);
×
43
    setTimeout(() => setCopied(false), TIMEOUTS.threeSeconds);
×
44
  };
45

46
  const toggleHovering = () => setHovering(toggle);
9✔
47

48
  const { line_number, data } = line;
9✔
49

50
  return (
9✔
51
    <React.Fragment key={line_number}>
52
      <div className="log-line margin-right" onMouseLeave={toggleHovering} onMouseOver={setHovering}>
53
        {prefix ? prefix : <div />}
9✔
54
        {line_number !== undefined ? <code className={`align-right ${beExplicit ? 'red' : ''}`}>{line_number}</code> : <div />}
17✔
55
        <code className={beExplicit ? 'red' : ''}>{data}</code>
9✔
56
        {hovering && <CopyButton text={data} onCopy={onCopied} />}
9!
57
      </div>
58
      <Collapse in={copied}>
59
        <div className="margin-left-small green fadeIn">Copied to clipboard.</div>
60
      </Collapse>
61
    </React.Fragment>
62
  );
63
};
64

65
const CustomAccordion = withStyles({
10✔
66
  root: {
67
    '&:before': {
68
      display: 'none'
69
    },
70
    '&$expanded': {
71
      backgroundColor: 'transparent',
72
      margin: 0
73
    }
74
  },
75
  expanded: {}
76
})(Accordion);
77

78
const LogSection = ({ section = 'previous', lines }) => {
10!
79
  const [expanded, setExpanded] = useState(false);
5✔
80
  const onToggle = () => setExpanded(toggle);
5✔
81
  return (
5✔
82
    !!lines.length && (
8✔
83
      <CustomAccordion square expanded={expanded} onChange={onToggle}>
84
        <AccordionSummary style={{ paddingLeft: 0 }}>
85
          {expanded ? <ArrowDropUpIcon className="margin-right-small" /> : <ArrowDropDownIcon className="margin-right-small" />}
3✔
86
          <div>
87
            show {section} {lines.length} lines
88
          </div>
89
        </AccordionSummary>
90
        <AccordionDetails style={{ paddingLeft: 0, paddingRight: 0 }}>
91
          {lines.map(item => (
92
            <LogLine key={item.line_number} line={item} />
6✔
93
          ))}
94
        </AccordionDetails>
95
      </CustomAccordion>
96
    )
97
  );
98
};
99

100
const LogContent = ({ lines_before = [], lines_after = [], line_matching = '' }) => (
10!
101
  <>
2✔
102
    <LogSection section="previous" lines={lines_before} />
103
    <LogLine beExplicit line={line_matching} prefix={<WarningIcon fontSize="small" />} />
104
    <LogSection section="next" lines={lines_after} />
105
  </>
106
);
107

108
const DescriptionContent = ({ description }) => <LogLine line={{ data: description }} />;
10✔
109

110
const detailTypes = {
10✔
111
  log: {
112
    component: LogContent,
113
    title: 'Log excerpt'
114
  },
115
  description: {
116
    component: DescriptionContent,
117
    title: 'Details'
118
  }
119
};
120

121
const exportLog = (name, lines) => {
10✔
122
  const max = lines.reduce((accu, item) => Math.max(accu, item.line_number), 0);
3✔
123
  const length = `${max}`.length;
1✔
124
  const logData = lines
1✔
125
    .reduce((accu, item) => {
126
      const paddedLineNumber = `${item.line_number}`.padStart(length, '0');
3✔
127
      accu.push(`${paddedLineNumber}   ${item.data}`);
3✔
128
      return accu;
3✔
129
    }, [])
130
    .join('\n');
131
  const uriContent = `data:application/octet-stream,${encodeURIComponent(logData)}`;
1✔
132
  window.open(uriContent, `Mender-Monitor-${name.replace(/ /g, '_')}.log`);
1✔
133
};
134

135
export const MonitorDetailsDialog = ({ alert, onClose }) => {
10✔
136
  const { name, subject = { details: {} } } = alert ?? {};
4✔
137
  const {
138
    details: { lines_before = [], lines_after = [], line_matching = '' }
8✔
139
  } = subject;
4✔
140

141
  const lines = [...lines_before, line_matching, ...lines_after].filter(i => i);
8✔
142

143
  const { component: Component, title } = lines.length ? detailTypes.log : detailTypes.description;
4✔
144
  return (
4✔
145
    <Dialog open={!!alert} maxWidth="md">
146
      <DialogTitle>{`${title} for ${name}`}</DialogTitle>
147
      <DialogContent style={{ minWidth: 600 }}>
148
        <Component {...subject.details} />
149
      </DialogContent>
150
      <DialogActions>
151
        <Button onClick={onClose}>Close</Button>
152
        {!!lines.length && (
6✔
153
          <Button variant="contained" color="primary" onClick={() => exportLog(name, lines)}>
1✔
154
            Export log
155
          </Button>
156
        )}
157
      </DialogActions>
158
    </Dialog>
159
  );
160
};
161

162
export default MonitorDetailsDialog;
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