Skip to content

Commit 8bec2a3

Browse files
committed
fix: DeleteConfirmation close on cannot delete modal open
1 parent 62ec46e commit 8bec2a3

File tree

6 files changed

+75
-56
lines changed

6 files changed

+75
-56
lines changed

src/Shared/Components/ConfirmationModal/ConfirmationModal.tsx

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
import { ButtonHTMLAttributes, ChangeEvent, cloneElement, useCallback, useEffect, useState } from 'react'
1818
import { AnimatePresence, motion } from 'framer-motion'
1919
import { CustomInput, noop, stopPropagation, useRegisterShortcut, UseRegisterShortcutProvider } from '@Common/index'
20-
import { ComponentSizeType } from '@Shared/constants'
20+
import { ComponentSizeType, DEFAULT_ROUTE_PROMPT_MESSAGE } from '@Shared/constants'
21+
import { usePrompt } from '@Shared/Hooks'
22+
import { Prompt } from 'react-router-dom'
2123
import { ConfirmationModalBodyProps, ConfirmationModalProps } from './types'
2224
import { getPrimaryButtonStyleFromVariant, getConfirmationLabel, getIconFromVariant } from './utils'
2325
import { Button, ButtonStyleType, ButtonVariantType } from '../Button'
2426
import { Backdrop } from '../Backdrop'
25-
import { useConfirmationModalContext } from './ConfirmationModalContext'
2627
import './confirmationModal.scss'
28+
import { useConfirmationModalContext } from './ConfirmationModalContext'
2729

2830
const ConfirmationModalBody = ({
2931
title,
@@ -167,30 +169,48 @@ const ConfirmationModalBody = ({
167169
* Please see NodeActionMenu.tsx as an example of why this is required
168170
*/
169171
export const BaseConfirmationModal = () => {
170-
const { props } = useConfirmationModalContext()
172+
const { modalKey, settersRef } = useConfirmationModalContext()
173+
const [confirmationProps, setConfirmationProps] = useState<ConfirmationModalProps | null>(null)
174+
const apiCallInProgress = modalKey && (confirmationProps?.buttonConfig.primaryButtonConfig.isLoading ?? false)
175+
176+
usePrompt({ shouldPrompt: apiCallInProgress })
177+
178+
useEffect(() => {
179+
settersRef.current = {
180+
setProps: setConfirmationProps,
181+
}
182+
}, [])
171183

172184
return (
173185
<UseRegisterShortcutProvider ignoreTags={['button']}>
174-
<AnimatePresence>{props && <ConfirmationModalBody {...props} />}</AnimatePresence>
186+
<Prompt when={apiCallInProgress} message={DEFAULT_ROUTE_PROMPT_MESSAGE} />
187+
<AnimatePresence>{!!modalKey && <ConfirmationModalBody {...confirmationProps} />}</AnimatePresence>
175188
</UseRegisterShortcutProvider>
176189
)
177190
}
178191

179192
const ConfirmationModal = (props: ConfirmationModalProps) => {
180-
const { props: currentProps, setProps } = useConfirmationModalContext()
193+
const { setModalKey, settersRef } = useConfirmationModalContext()
181194

182195
useEffect(() => {
183-
if (currentProps) {
184-
throw new Error('Only one ConfirmationModal can be rendered at a time')
185-
}
186-
187-
setProps(props)
196+
const dateString = new Date().toISOString()
197+
setModalKey(dateString)
188198

189199
return () => {
190-
setProps(null)
200+
setModalKey((prev) => {
201+
if (prev === dateString) {
202+
return ''
203+
}
204+
205+
return prev
206+
})
191207
}
192208
}, [])
193209

210+
useEffect(() => {
211+
settersRef.current.setProps(props)
212+
})
213+
194214
return null
195215
}
196216

src/Shared/Components/ConfirmationModal/ConfirmationModalContext.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import { createContext, PropsWithChildren, useContext, useMemo, useState } from 'react'
1+
import { createContext, PropsWithChildren, useContext, useMemo, useRef, useState } from 'react'
22
import { ConfirmationModalContextType } from './types'
33

44
export const ConfirmationModalContext = createContext<ConfirmationModalContextType>(null)
55

66
export const ConfirmationModalProvider = ({ children }: PropsWithChildren<{}>) => {
7-
const [props, setProps] = useState<ConfirmationModalContextType['props']>(null)
7+
const settersRef = useRef<ConfirmationModalContextType['settersRef']['current']>(null)
8+
const [modalKey, setModalKey] = useState('')
89

9-
const value = useMemo(() => ({ props, setProps }), [props])
10+
const value = useMemo(() => ({ modalKey, setModalKey, settersRef }), [modalKey])
1011

1112
return <ConfirmationModalContext.Provider value={value}>{children}</ConfirmationModalContext.Provider>
1213
}

src/Shared/Components/ConfirmationModal/DeleteConfirmationModal.tsx

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { showError, stopPropagation } from '@Common/Helper'
2121
import { ConfirmationModalVariantType, DeleteConfirmationModalProps } from './types'
2222
import ConfirmationModal from './ConfirmationModal'
2323
import { CannotDeleteModal } from './CannotDeleteModal'
24-
import { ForceDeleteConfirmationModal } from './ForceDeleteConfirmationModal'
2524

2625
export const DeleteConfirmationModal: React.FC<DeleteConfirmationModalProps> = ({
2726
title,
@@ -41,7 +40,6 @@ export const DeleteConfirmationModal: React.FC<DeleteConfirmationModalProps> = (
4140
children,
4241
}: DeleteConfirmationModalProps) => {
4342
const [showCannotDeleteDialogModal, setCannotDeleteDialogModal] = useState(false)
44-
const [showForceDeleteModal, setForceDeleteModal] = useState(false)
4543
const [cannotDeleteText, setCannotDeleteText] = useState(renderCannotDeleteConfirmationSubTitle)
4644
const [isLoading, setLoading] = useState(isDeleting)
4745

@@ -69,7 +67,6 @@ export const DeleteConfirmationModal: React.FC<DeleteConfirmationModalProps> = (
6967
) {
7068
setCannotDeleteText(serverError.errors[0].userMessage as string)
7169
}
72-
closeConfirmationModal()
7370
} else if (typeof onError === 'function') {
7471
onError(serverError)
7572
} else {
@@ -79,9 +76,10 @@ export const DeleteConfirmationModal: React.FC<DeleteConfirmationModalProps> = (
7976
}
8077
}
8178

82-
const handleCloseCannotDeleteModal = () => setCannotDeleteDialogModal(false)
83-
84-
const handleCloseForceDeleteModal = () => setForceDeleteModal(false)
79+
const handleCloseCannotDeleteModal = () => {
80+
setCannotDeleteDialogModal(false)
81+
closeConfirmationModal()
82+
}
8583

8684
const renderCannotDeleteDialogModal = () => (
8785
<CannotDeleteModal
@@ -92,15 +90,6 @@ export const DeleteConfirmationModal: React.FC<DeleteConfirmationModalProps> = (
9290
/>
9391
)
9492

95-
const renderForceDeleteModal = () => (
96-
<ForceDeleteConfirmationModal
97-
title={title}
98-
subtitle={subtitle}
99-
onDelete={onDelete}
100-
closeConfirmationModal={handleCloseForceDeleteModal}
101-
/>
102-
)
103-
10493
const renderDeleteModal = () => (
10594
<ConfirmationModal
10695
variant={ConfirmationModalVariantType.delete}
@@ -110,12 +99,13 @@ export const DeleteConfirmationModal: React.FC<DeleteConfirmationModalProps> = (
11099
secondaryButtonConfig: {
111100
text: 'Cancel',
112101
onClick: closeConfirmationModal,
102+
disabled: isDeleting || isLoading,
113103
},
114104
primaryButtonConfig: {
115105
text: primaryButtonText,
116106
onClick: handleDelete,
117107
isLoading: isDeleting || isLoading,
118-
disabled: isLoading || disabled,
108+
disabled,
119109
},
120110
}}
121111
handleClose={closeConfirmationModal}
@@ -127,9 +117,8 @@ export const DeleteConfirmationModal: React.FC<DeleteConfirmationModalProps> = (
127117

128118
return (
129119
<>
130-
{renderDeleteModal()}
120+
{!showCannotDeleteDialogModal && renderDeleteModal()}
131121
{showCannotDeleteDialogModal && renderCannotDeleteDialogModal()}
132-
{showForceDeleteModal && renderForceDeleteModal()}
133122
</>
134123
)
135124
}

src/Shared/Components/ConfirmationModal/types.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,15 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { Dispatch, PropsWithChildren, ReactElement, ReactNode, SetStateAction, SyntheticEvent } from 'react'
17+
import {
18+
Dispatch,
19+
MutableRefObject,
20+
PropsWithChildren,
21+
ReactElement,
22+
ReactNode,
23+
SetStateAction,
24+
SyntheticEvent,
25+
} from 'react'
1826
import { ButtonProps } from '../Button'
1927

2028
export enum ConfirmationModalVariantType {
@@ -173,6 +181,9 @@ export interface ForceDeleteConfirmationProps
173181
Partial<Pick<ConfirmationModalProps, 'title' | 'subtitle'>> {}
174182

175183
export interface ConfirmationModalContextType {
176-
props: ConfirmationModalProps | null
177-
setProps: Dispatch<SetStateAction<ConfirmationModalContextType['props']>>
184+
settersRef: MutableRefObject<{
185+
setProps: Dispatch<SetStateAction<ConfirmationModalProps>>
186+
}>
187+
modalKey: string
188+
setModalKey: Dispatch<SetStateAction<ConfirmationModalContextType['modalKey']>>
178189
}

src/Shared/Components/UnsavedChangesDialog/UnsavedChangesDialog.component.tsx

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,24 @@
1717
import { ConfirmationModal, ConfirmationModalVariantType } from '../ConfirmationModal'
1818
import { UnsavedChangesDialogProps } from './types'
1919

20-
const UnsavedChangesDialog = ({ showUnsavedChangesDialog, handleClose, handleProceed }: UnsavedChangesDialogProps) =>
21-
showUnsavedChangesDialog && (
22-
<ConfirmationModal
23-
handleClose={handleClose}
24-
variant={ConfirmationModalVariantType.warning}
25-
shouldCloseOnEscape={false}
26-
title="Unsaved changes available"
27-
subtitle="Unsaved changes will be lost, are you sure you want to close the window?"
28-
buttonConfig={{
29-
primaryButtonConfig: {
30-
onClick: handleProceed,
31-
text: 'Discard changes',
32-
},
33-
secondaryButtonConfig: {
34-
onClick: handleClose,
35-
text: 'Cancel',
36-
},
37-
}}
38-
/>
39-
)
20+
const UnsavedChangesDialog = ({ handleClose, handleProceed }: UnsavedChangesDialogProps) => (
21+
<ConfirmationModal
22+
handleClose={handleClose}
23+
variant={ConfirmationModalVariantType.warning}
24+
shouldCloseOnEscape={false}
25+
title="Unsaved changes available"
26+
subtitle="Unsaved changes will be lost, are you sure you want to close the window?"
27+
buttonConfig={{
28+
primaryButtonConfig: {
29+
onClick: handleProceed,
30+
text: 'Discard changes',
31+
},
32+
secondaryButtonConfig: {
33+
onClick: handleClose,
34+
text: 'Cancel',
35+
},
36+
}}
37+
/>
38+
)
4039

4140
export default UnsavedChangesDialog

src/Shared/Components/UnsavedChangesDialog/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616

1717
export interface UnsavedChangesDialogProps {
18-
showUnsavedChangesDialog: boolean
1918
handleProceed: () => void
2019
handleClose: () => void
2120
}

0 commit comments

Comments
 (0)