Skip to content

Commit 7b9d3d5

Browse files
authored
Merge pull request #2042 from devtron-labs/feat/toast-manager
feat: add generic service for managing toasts
2 parents 3c57518 + e9e37db commit 7b9d3d5

File tree

141 files changed

+1690
-977
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+1690
-977
lines changed

.eslintignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,6 @@ src/components/common/HiddenInput/HiddenInput.tsx
281281
src/components/common/List/List.tsx
282282
src/components/common/MultiSelect/MultiSelect.tsx
283283
src/components/common/Select/Select.tsx
284-
src/components/common/ToastBody.tsx
285284
src/components/common/ValidateForm/ValidateForm.tsx
286285
src/components/common/eaEmptyState/EAEmptyState.tsx
287286
src/components/common/edge/rectangularEdge.tsx
@@ -462,7 +461,7 @@ src/components/v2/common/ReactSelectCustomization.tsx
462461
src/components/v2/common/message.ui.tsx
463462
src/components/v2/devtronStackManager/AboutDevtronView.tsx
464463
src/components/v2/devtronStackManager/DevtronStackManager.component.tsx
465-
src/components/v2/devtronStackManager/DevtronStackManager.service.ts
464+
src/components/v2/devtronStackManager/DevtronStackManager.service.tsx
466465
src/components/v2/devtronStackManager/DevtronStackManager.tsx
467466
src/components/v2/devtronStackManager/DevtronStackManager.utils.ts
468467
src/components/v2/devtronStackManager/SuccessModalComponent.tsx

.storybook/preview.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react'
22
import type { Preview } from '@storybook/react'
33
import '../src/css/application.scss'
44
import { BrowserRouter } from 'react-router-dom'
5+
import { ToastManagerContainer } from '@devtron-labs/devtron-fe-common-lib'
56

67
const preview: Preview = {
78
parameters: {
@@ -13,7 +14,14 @@ const preview: Preview = {
1314
},
1415
},
1516
tags: ['autodocs'],
16-
decorators: (Story) => <BrowserRouter><Story /></BrowserRouter>
17+
decorators: (Story) => (
18+
<>
19+
<BrowserRouter>
20+
<Story />
21+
</BrowserRouter>
22+
<ToastManagerContainer />
23+
</>
24+
),
1725
}
1826

1927
export default preview

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "0.3.1",
7+
"@devtron-labs/devtron-fe-common-lib": "0.3.3",
88
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
99
"@rjsf/core": "^5.13.3",
1010
"@rjsf/utils": "^5.13.3",
@@ -40,7 +40,6 @@
4040
"react-monaco-editor": "^0.55.0",
4141
"react-router-dom": "^5.3.4",
4242
"react-select": "5.8.0",
43-
"react-toastify": "^8.2.0",
4443
"react-virtualized": "^9.22.5",
4544
"recharts": "^2.1.9",
4645
"rollup-plugin-node-polyfills": "0.2.1",

src/App.tsx

Lines changed: 64 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import { lazy, Suspense, useRef, useState, useEffect } from 'react'
1818
import { Route, Switch, Redirect, useHistory, useLocation } from 'react-router-dom'
19-
import { toast } from 'react-toastify'
2019
import './css/application.scss';
2120
import {
2221
showError,
@@ -25,36 +24,27 @@ import {
2524
DevtronProgressing,
2625
APPROVAL_MODAL_TYPE,
2726
useUserEmail,
28-
URLS as CommonURLS
27+
URLS as CommonURLS,
28+
ToastManager,
29+
ToastVariantType
2930
} from '@devtron-labs/devtron-fe-common-lib'
31+
import { ReactComponent as ICSparkles } from '@Icons/ic-sparkles.svg'
3032
import { useRegisterSW } from 'virtual:pwa-register/react'
3133
import {
3234
useOnline,
33-
ToastBody,
34-
ToastBody3 as UpdateToast,
3535
ErrorBoundary,
3636
importComponentFromFELibrary,
3737
getApprovalModalTypeFromURL,
38+
reloadLocation,
3839
} from './components/common'
39-
import { URLS } from './config'
40+
import { UPDATE_AVAILABLE_TOAST_PROGRESS_BG, URLS } from './config'
4041
import Hotjar from './components/Hotjar/Hotjar'
4142
import { validateToken } from './services/service'
4243

4344
const NavigationRoutes = lazy(() => import('./components/common/navigation/NavigationRoutes'))
4445
const Login = lazy(() => import('./components/login/Login'))
4546
const GenericDirectApprovalModal = importComponentFromFELibrary('GenericDirectApprovalModal')
4647

47-
toast.configure({
48-
autoClose: 3000,
49-
hideProgressBar: true,
50-
pauseOnHover: true,
51-
pauseOnFocusLoss: true,
52-
closeOnClick: false,
53-
newestOnTop: true,
54-
toastClassName: 'devtron-toast',
55-
bodyClassName: 'devtron-toast__body',
56-
})
57-
5848
export default function App() {
5949
const onlineToastRef = useRef(null)
6050
const updateToastRef = useRef(null)
@@ -78,27 +68,29 @@ export default function App() {
7868
? 'custom-theme-override'
7969
: ''
8070

81-
function onlineToast(toastBody: JSX.Element, options) {
82-
if (onlineToastRef.current && toast.isActive(onlineToastRef.current)) {
83-
toast.update(onlineToastRef.current, { render: toastBody, ...options })
84-
} else {
85-
onlineToastRef.current = toast[options.type](toastBody, options)
71+
function onlineToast(...showToastParams: Parameters<typeof ToastManager.showToast>) {
72+
if (onlineToastRef.current && ToastManager.isToastActive(onlineToastRef.current)) {
73+
ToastManager.dismissToast(onlineToastRef.current)
8674
}
75+
onlineToastRef.current = ToastManager.showToast(...showToastParams)
8776
}
8877

8978
useEffect(() => {
9079
if (didMountRef.current) {
9180
if (!isOnline) {
92-
const toastBody = (
93-
<ToastBody
94-
title="You are offline!"
95-
subtitle="You are not seeing real-time data and any changes you make will not be saved."
96-
/>
97-
)
98-
onlineToast(toastBody, { type: toast.TYPE.ERROR, autoClose: false, closeButton: false })
81+
onlineToast({
82+
variant: ToastVariantType.error,
83+
title: 'You are offline!',
84+
description: 'You are not seeing real-time data and any changes you make will not be saved.',
85+
}, {
86+
autoClose: false
87+
})
9988
} else {
100-
const toastBody = <ToastBody title="Connected!" subtitle="You're back online." />
101-
onlineToast(toastBody, { type: toast.TYPE.SUCCESS, autoClose: 3000, closeButton: true })
89+
onlineToast({
90+
variant: ToastVariantType.success,
91+
title: 'Connected!',
92+
description: "You're back online.",
93+
})
10294
}
10395
} else {
10496
didMountRef.current = true
@@ -233,25 +225,34 @@ export default function App() {
233225
navigator.serviceWorker.getRegistrations().then((regs) => regs.forEach((reg) => reg.update()))
234226
if (needRefresh) {
235227
update()
236-
} else if (toast.isActive(updateToastRef.current)) {
237-
toast.dismiss(updateToastRef.current)
228+
} else if (ToastManager.isToastActive(updateToastRef.current)) {
229+
ToastManager.dismissToast(updateToastRef.current)
238230
}
239231
}
240232
}, [location])
241233

242234
function onUpdate() {
243-
const updateToastBody = (
244-
<UpdateToast
245-
onClick={update}
246-
text="You are viewing an outdated version of Devtron UI."
247-
buttonText="Reload"
248-
/>
249-
)
250-
if (toast.isActive(updateToastRef.current)) {
251-
toast.update(updateToastRef.current, { render: updateToastBody })
252-
} else {
253-
updateToastRef.current = toast.info(updateToastBody, { autoClose: false, closeButton: false })
235+
if (ToastManager.isToastActive(updateToastRef.current)) {
236+
ToastManager.dismissToast(updateToastRef.current)
254237
}
238+
239+
updateToastRef.current = ToastManager.showToast(
240+
{
241+
variant: ToastVariantType.info,
242+
title: 'Update available',
243+
description: 'You are viewing an outdated version of Devtron UI.',
244+
buttonProps: {
245+
text: 'Reload',
246+
dataTestId: 'reload-btn',
247+
onClick: update,
248+
},
249+
icon: <ICSparkles />,
250+
progressBarBg: UPDATE_AVAILABLE_TOAST_PROGRESS_BG,
251+
},
252+
{
253+
autoClose: false,
254+
},
255+
)
255256
if (typeof Storage !== 'undefined') {
256257
localStorage.removeItem('serverInfo')
257258
}
@@ -267,18 +268,27 @@ export default function App() {
267268
if (!bgUpdated) {
268269
return
269270
}
270-
const bgUpdatedToastBody = (
271-
<UpdateToast
272-
onClick={() => window.location.reload()}
273-
text="This page has been updated. Please save any unsaved changes and refresh."
274-
buttonText="Reload"
275-
/>
276-
)
277-
if (toast.isActive(updateToastRef.current)) {
278-
toast.update(updateToastRef.current, { render: bgUpdatedToastBody })
279-
} else {
280-
updateToastRef.current = toast.info(bgUpdatedToastBody, { autoClose: false, closeButton: false })
271+
if (ToastManager.isToastActive(updateToastRef.current)) {
272+
ToastManager.dismissToast(updateToastRef.current)
281273
}
274+
275+
updateToastRef.current = ToastManager.showToast(
276+
{
277+
variant: ToastVariantType.info,
278+
title: 'Update available',
279+
description: 'This page has been updated. Please save any unsaved changes and refresh.',
280+
buttonProps: {
281+
text: 'Reload',
282+
dataTestId: 'reload-btn',
283+
onClick: reloadLocation,
284+
},
285+
icon: <ICSparkles />,
286+
progressBarBg: UPDATE_AVAILABLE_TOAST_PROGRESS_BG,
287+
},
288+
{
289+
autoClose: false,
290+
},
291+
)
282292
}, [bgUpdated])
283293

284294
return (

src/Pages/Applications/DevtronApps/Details/AppConfigurations/AppConfig.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import { useState, useEffect } from 'react'
1818
import { useParams, useLocation, useRouteMatch, useHistory, Link } from 'react-router-dom'
19-
import { toast } from 'react-toastify'
2019
import {
2120
showError,
2221
Progressing,
@@ -25,6 +24,8 @@ import {
2524
ConfirmationDialog,
2625
useAsync,
2726
ResourceKindType,
27+
ToastManager,
28+
ToastVariantType,
2829
} from '@devtron-labs/devtron-fe-common-lib'
2930
import { URLS, getAppComposeURL, APP_COMPOSE_STAGE, ViewType } from '../../../../../config'
3031
import { importComponentFromFELibrary } from '../../../../../components/common'
@@ -345,10 +346,16 @@ export const AppConfig = ({ appName, resourceKind, filteredEnvIds }: AppConfigPr
345346
.then((response) => {
346347
if (response.code === 200) {
347348
if (isJob) {
348-
toast.success('Job Deleted!')
349+
ToastManager.showToast({
350+
variant: ToastVariantType.success,
351+
description: 'Job Deleted!',
352+
})
349353
history.push(`${URLS.JOB}/${URLS.APP_LIST}`)
350354
} else {
351-
toast.success('Application Deleted!')
355+
ToastManager.showToast({
356+
variant: ToastVariantType.success,
357+
description: 'Application Deleted!',
358+
})
352359
history.push(`${URLS.APP}/${URLS.APP_LIST}`)
353360
}
354361
}

src/Pages/Applications/DevtronApps/Details/AppConfigurations/Navigation/EnvironmentOverrideRouter.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ import {
2525
PopupMenu,
2626
Progressing,
2727
getEnvironmentListMinPublic,
28+
ToastVariantType,
29+
ToastManager,
2830
} from '@devtron-labs/devtron-fe-common-lib'
29-
import { toast } from 'react-toastify'
3031
import ReactSelect, { ValueContainerProps, components } from 'react-select'
3132
import { URLS, DOCUMENTATION } from '../../../../../../config'
3233
import { usePrevious, createClusterEnvGroup } from '../../../../../../components/common'
@@ -71,7 +72,10 @@ const JobEnvOverrideRoute = ({ envOverride, ciPipelines, reload, isEnvProtected
7172
const requestBody = { envId: envOverride.environmentId, appId }
7273
deleteJobEnvironment(requestBody)
7374
.then(() => {
74-
toast.success('Deleted Successfully')
75+
ToastManager.showToast({
76+
variant: ToastVariantType.success,
77+
description: 'Deleted Successfully',
78+
})
7579
reload()
7680
setDeleteView(false)
7781
})
@@ -290,7 +294,10 @@ const EnvironmentOverrideRouter = () => {
290294
setEnvironmentView(!addEnvironment)
291295
const requestBody = { envId: selection.id, appId }
292296
await addJobEnvironment(requestBody)
293-
toast.success('Saved Successfully')
297+
ToastManager.showToast({
298+
variant: ToastVariantType.success,
299+
description: 'Saved Successfully',
300+
})
294301
reloadEnvData()
295302
} catch (error) {
296303
showError(error)

src/Pages/GlobalConfigurations/Authorization/APITokens/CreateAPIToken.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@
2020
import { useEffect, useState } from 'react'
2121
import { useHistory, useRouteMatch } from 'react-router-dom'
2222
import { Moment } from 'moment'
23-
import { toast } from 'react-toastify'
2423
import {
2524
ServerErrors,
2625
showError,
2726
CustomInput,
2827
ResizableTextarea,
2928
InfoIconTippy,
3029
useMainContext,
30+
ToastVariantType,
31+
ToastManager,
3132
} from '@devtron-labs/devtron-fe-common-lib'
3233
import { FormType, GenerateTokenType } from './apiToken.type'
3334
import { createGeneratedAPIToken } from './service'
@@ -177,7 +178,10 @@ const CreateAPIToken = ({
177178
invalidDescription: !descriptionValidation.isValid,
178179
invalidDescriptionMessage: descriptionValidation.message,
179180
})
180-
toast.error(REQUIRED_FIELDS_MISSING)
181+
ToastManager.showToast({
182+
variant: ToastVariantType.error,
183+
description: REQUIRED_FIELDS_MISSING,
184+
})
181185

182186
return
183187
}

src/Pages/GlobalConfigurations/Authorization/APITokens/DeleteAPITokenModal.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616

1717
import { useState } from 'react'
1818
import { useHistory, useRouteMatch } from 'react-router-dom'
19-
import { toast } from 'react-toastify'
20-
import { showError, DeleteDialog } from '@devtron-labs/devtron-fe-common-lib'
19+
import { showError, DeleteDialog, ToastManager, ToastVariantType } from '@devtron-labs/devtron-fe-common-lib'
2120
import { deleteGeneratedAPIToken } from './service'
2221

2322
const DeleteAPITokenModal = ({
@@ -42,7 +41,10 @@ const DeleteAPITokenModal = ({
4241
const { result } = await deleteGeneratedAPIToken(tokenData.id)
4342

4443
if (result) {
45-
toast.success('Deleted successfully')
44+
ToastManager.showToast({
45+
variant: ToastVariantType.success,
46+
description: 'Deleted successfully',
47+
})
4648
reload()
4749

4850
if (isEditView) {

src/Pages/GlobalConfigurations/Authorization/APITokens/EditAPIToken.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ import {
2727
ResizableTextarea,
2828
useMainContext,
2929
ButtonWithLoader,
30+
ToastVariantType,
31+
ToastManager,
3032
} from '@devtron-labs/devtron-fe-common-lib'
3133
import { useHistory, useRouteMatch, useParams } from 'react-router-dom'
3234
import moment from 'moment'
33-
import { toast } from 'react-toastify'
3435
import { ReactComponent as InfoIcon } from '../../../../assets/icons/info-filled.svg'
3536
import RegeneratedModal from './RegenerateModal'
3637
import { EditDataType, EditTokenType } from './apiToken.type'
@@ -139,7 +140,10 @@ const EditAPIToken = ({
139140

140141
const { result: userPermissionResponse } = await createOrUpdateUser(userPermissionPayload)
141142
if (userPermissionResponse) {
142-
toast.success('Changes saved')
143+
ToastManager.showToast({
144+
variant: ToastVariantType.success,
145+
description: 'Changes saved',
146+
})
143147
reload()
144148
redirectToTokenList()
145149
}

0 commit comments

Comments
 (0)