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

CSCfi / metadata-submitter-frontend / 20331691855

18 Dec 2025 09:07AM UTC coverage: 55.065% (-3.3%) from 58.346%
20331691855

push

github

Hang Le
Update dependency @vitest/coverage-v8 to v4 (merge commit)

Merge branch 'renovate/vitest-coverage-v8-4.x' into 'main'
* Update dependency @vitest/coverage-v8 to v4

See merge request https://gitlab.ci.csc.fi/sds-dev/sd-submit/metadata-submitter-frontend/-/merge_requests/1182

Approved-by: Liisa Lado-Villar <145-lilado@users.noreply.gitlab.ci.csc.fi>
Co-authored-by: renovate-bot <group_183_bot_aa67d732ac40e4c253df6728543b928a@noreply.gitlab.ci.csc.fi>
Merged by Hang Le <lhang@csc.fi>

476 of 1055 branches covered (45.12%)

Branch coverage included in aggregate %.

1307 of 2183 relevant lines covered (59.87%)

8.04 hits per line

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

76.34
/src/components/StatusMessageHandler.tsx
1
import React, { useState } from "react"
2

3
import CancelIcon from "@mui/icons-material/Cancel"
4
import CheckCircleIcon from "@mui/icons-material/CheckCircle"
5
import CloseIcon from "@mui/icons-material/Close"
6
import WarningIcon from "@mui/icons-material/Warning"
7
import Alert from "@mui/material/Alert"
8
import Box from "@mui/material/Box"
9
import Snackbar from "@mui/material/Snackbar"
10
import { styled, useTheme } from "@mui/material/styles"
11
import Typography from "@mui/material/Typography"
12
import { useTranslation } from "react-i18next"
13

14
import { ResponseStatus } from "constants/responseStatus"
15
import { resetStatusDetails } from "features/statusMessageSlice"
16
import { useAppDispatch, useAppSelector } from "hooks"
17
import { APIResponse } from "types"
18

19
type MessageHandlerProps = {
20
  response?: APIResponse
21
  helperText?: string
22
  handleClose: (status: boolean) => void
23
}
24

25
const CustomAlert = styled(Alert, {
5✔
26
  shouldForwardProp: prop => prop !== "severity",
17✔
27
})(({ theme, severity }) => ({
3✔
28
  backgroundColor: theme.palette.background.paper,
29
  borderLeft: `1.25rem solid ${
30
    severity === "error"
3✔
31
      ? theme.palette.error.main
32
      : severity === "warning"
2✔
33
        ? theme.palette.warning.main
34
        : theme.palette.success.main
35
  }`,
36
  borderTop: `0.25rem solid ${
37
    severity === "error"
3✔
38
      ? theme.palette.error.main
39
      : severity === "warning"
2✔
40
        ? theme.palette.warning.main
41
        : theme.palette.success.main
42
  }`,
43
  borderRight: `0.25rem solid ${
44
    severity === "error"
3✔
45
      ? theme.palette.error.main
46
      : severity === "warning"
2✔
47
        ? theme.palette.warning.main
48
        : theme.palette.success.main
49
  }`,
50
  borderBottom: `0.25rem solid ${
51
    severity === "error"
3✔
52
      ? theme.palette.error.main
53
      : severity === "warning"
2✔
54
        ? theme.palette.warning.main
55
        : theme.palette.success.main
56
  }`,
57
  lineHeight: "1.75",
58
  boxShadow: "0 0.25rem 0.625rem rgba(0, 0, 0, 0.2)",
59
  position: "relative",
60
  padding: "1rem",
61
  display: "flex",
62
  justifyContent: "space-between",
63
  alignItems: "center",
64
}))
65

66
const AlertWrap = styled(Box)(() => ({
5✔
67
  display: "flex",
68
  justifyContent: "space-between",
69
  alignItems: "center",
70
}))
71

72
const MessageContainer = styled(Typography)(() => ({
5✔
73
  fontSize: "1.5rem !important",
74
  fontWeight: "bold",
75
}))
76

77
const CustomIconButton = styled("div")(({ theme }) => ({
5✔
78
  display: "flex",
79
  alignItems: "center",
80
  cursor: "pointer",
81
  color: theme.palette.primary.main,
82
}))
83

84
const CustomCloseIcon = styled(CloseIcon)(({ theme }) => ({
5✔
85
  color: theme.palette.primary.main,
86
  fontSize: "2.25rem",
87
  marginLeft: "1rem",
88
}))
89

90
const ClosingLink = styled(Typography)(() => ({
5✔
91
  marginLeft: "0.75rem",
92
  fontSize: "1.5rem !important",
93
  fontWeight: "bold",
94
}))
95

96
const getSeverityIcon = (severity, theme) => {
5✔
97
  const iconStyle = {
3✔
98
    fontSize: "2rem",
99
    color:
100
      severity === "error"
3✔
101
        ? theme.palette.error.main
102
        : severity === "warning"
2✔
103
          ? theme.palette.warning.main
104
          : theme.palette.success.main,
105
  }
106

107
  switch (severity) {
3!
108
    case "error":
109
      return <CancelIcon style={iconStyle} />
1✔
110
    case "warning":
111
      return <WarningIcon style={iconStyle} />
1✔
112
    case "success":
113
      return <CheckCircleIcon style={iconStyle} />
1✔
114
    default:
115
      return null
×
116
  }
117
}
118

119
const ErrorHandler = (props: MessageHandlerProps) => {
5✔
120
  const { t } = useTranslation()
1✔
121
  const { response, helperText, handleClose } = props
1✔
122
  const theme = useTheme()
1✔
123
  let message: string
124

125
  switch (response?.status) {
1!
126
    case 504:
127
      message = t("snackbarMessages.error.504")
×
128
      break
×
129
    case 400:
130
      message = t("snackbarMessages.error.400", {
×
131
        helperText,
132
        responseDetail: response.data.detail,
133
      })
134
      break
×
135
    default:
136
      message = t("snackbarMessages.error.default", { helperText: helperText ? t(helperText) : "" })
1!
137
  }
138

139
  const closeMessage = t("close")
1✔
140

141
  return (
1✔
142
    <CustomAlert severity="error" icon={getSeverityIcon("error", theme)} data-testid="error-alert">
143
      <AlertWrap>
144
        <MessageContainer>{message}</MessageContainer>
145
        <CustomIconButton onClick={() => handleClose(false)}>
×
146
          <CustomCloseIcon />
147
          <ClosingLink>{closeMessage}</ClosingLink>
148
        </CustomIconButton>
149
      </AlertWrap>
150
    </CustomAlert>
151
  )
152
}
153

154
const InfoHandler = (props: MessageHandlerProps) => {
5✔
155
  const { t } = useTranslation()
1✔
156
  const { helperText, handleClose } = props
1✔
157
  const theme = useTheme()
1✔
158
  const defaultMessage = t("snackbarMessages.info.default")
1✔
159
  const closeMessage = t("close")
1✔
160

161
  const messageTemplate = (helperText?: string) => {
1✔
162
    return helperText?.length ? helperText : defaultMessage
1!
163
  }
164

165
  return (
1✔
166
    <CustomAlert
167
      severity="warning"
168
      icon={getSeverityIcon("warning", theme)}
169
      data-testid="warning-alert"
170
    >
171
      <AlertWrap>
172
        <MessageContainer>{messageTemplate(helperText)}</MessageContainer>
173
        <CustomIconButton onClick={() => handleClose(false)}>
×
174
          <CustomCloseIcon />
175
          <ClosingLink>{closeMessage}</ClosingLink>
176
        </CustomIconButton>
177
      </AlertWrap>
178
    </CustomAlert>
179
  )
180
}
181

182
const SuccessHandler = (props: MessageHandlerProps) => {
5✔
183
  const { t } = useTranslation()
1✔
184
  const { response, helperText, handleClose } = props
1✔
185
  const theme = useTheme()
1✔
186
  let message = ""
1✔
187
  if (response) {
1!
188
    switch (response?.config?.baseURL) {
1✔
189
      case "/v1/objects": {
190
        switch (response.config.method) {
1!
191
          case "patch": {
192
            message = t("snackbarMessages.success.objects.updated")
×
193
            break
×
194
          }
195
          case "put": {
196
            message = t("snackbarMessages.success.objects.replaced")
×
197
            break
×
198
          }
199
          default: {
200
            message = t("snackbarMessages.success.objects.submitted")
1✔
201
          }
202
        }
203
        break
1✔
204
      }
205
    }
206
  } else {
207
    message = helperText ? (t(helperText) as string) : ""
×
208
  }
209

210
  const closeMessage = t("close")
1✔
211

212
  return (
1✔
213
    <CustomAlert
214
      severity="success"
215
      icon={getSeverityIcon("success", theme)}
216
      data-testid="success-alert"
217
    >
218
      <AlertWrap>
219
        <MessageContainer>{message}</MessageContainer>
220
        <CustomIconButton onClick={() => handleClose(false)}>
×
221
          <CustomCloseIcon />
222
          <ClosingLink>{closeMessage}</ClosingLink>
223
        </CustomIconButton>
224
      </AlertWrap>
225
    </CustomAlert>
226
  )
227
}
228

229
type StatusMessageProps = {
230
  status: string
231
  response?: APIResponse
232
  helperText?: string
233
}
234

235
const Message = (props: StatusMessageProps) => {
5✔
236
  const { status, response, helperText } = props
3✔
237
  const [open, setOpen] = useState(true)
3✔
238
  const autoHideDuration = 20000
3✔
239
  const dispatch = useAppDispatch()
3✔
240

241
  const messageTemplate = (status: string) => {
3✔
242
    switch (status) {
3!
243
      case ResponseStatus.success:
244
        return (
1✔
245
          <SuccessHandler handleClose={handleClose} response={response} helperText={helperText} />
246
        )
247
      case ResponseStatus.info:
248
        return <InfoHandler handleClose={handleClose} helperText={helperText} />
1✔
249
      case ResponseStatus.error:
250
        return (
1✔
251
          <ErrorHandler handleClose={handleClose} response={response} helperText={helperText} />
252
        )
253
      default:
254
        return null
×
255
    }
256
  }
257

258
  const handleClose = (status = false) => {
3!
259
    setOpen(status)
×
260
    dispatch(resetStatusDetails())
×
261
  }
262

263
  const messageElement = messageTemplate(status)
3✔
264

265
  return typeof response !== "undefined" && response.status === 404
3!
266
    ? null
267
    : messageElement && (
6✔
268
        <Snackbar
269
          autoHideDuration={autoHideDuration}
270
          open={open}
271
          onClose={() => handleClose()}
×
272
          anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
273
          sx={{
274
            position: "fixed",
275
            left: "50%",
276
            transform: "translateX(-50%)",
277
            width: "auto",
278
            marginBottom: "4.375rem",
279
          }}
280
        >
281
          <Box>{messageElement}</Box>
282
        </Snackbar>
283
      )
284
}
285

286
/*
287
 * Render a pop-up notification indicates whether an action is done with success/failure/warning
288
 */
289
const StatusMessageHandler: React.FC = () => {
5✔
290
  const statusDetails = useAppSelector(state => state.statusDetails)
16✔
291
  return (
7✔
292
    <React.Fragment>
293
      {statusDetails?.status && !Array.isArray(statusDetails.response) && (
13✔
294
        <Message
295
          status={statusDetails.status}
296
          response={
297
            typeof statusDetails.response === "string"
3!
298
              ? JSON.parse(statusDetails.response)
299
              : statusDetails.response
300
          }
301
          helperText={statusDetails.helperText}
302
        />
303
      )}
304
    </React.Fragment>
305
  )
306
}
307

308
export default StatusMessageHandler
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