Skip to content

Commit 7dc00b2

Browse files
committed
fix(CModal): remove event.stopPropagation to allow support components with a click outside listeners inside modal content
1 parent 50f8de3 commit 7dc00b2

File tree

3 files changed

+45
-30
lines changed

3 files changed

+45
-30
lines changed

packages/coreui-react/src/components/modal/CModal.tsx

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ export interface CModalProps extends HTMLAttributes<HTMLDivElement> {
7272
* Remove animation to create modal that simply appear rather than fade in to view.
7373
*/
7474
transition?: boolean
75+
/*
76+
* By default the component is unmounted after close animation, if you want to keep the component mounted set this property to false
77+
*/
78+
unmountOnClose?: boolean
7579
/**
7680
* Toggle the visibility of modal component.
7781
*/
@@ -102,11 +106,13 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
102106
scrollable,
103107
size,
104108
transition = true,
109+
unmountOnClose = true,
105110
visible,
106111
},
107112
ref,
108113
) => {
109114
const modalRef = useRef<HTMLDivElement>(null)
115+
const modalContentRef = useRef<HTMLDivElement>(null)
110116
const forkedRef = useForkedRef(ref, modalRef)
111117

112118
const [_visible, setVisible] = useState(visible)
@@ -121,6 +127,16 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
121127
setVisible,
122128
}
123129

130+
useEffect(() => {
131+
modalRef.current && modalRef.current.addEventListener('click', handleClickOutside)
132+
modalRef.current && modalRef.current.addEventListener('keyup', handleKeyDown)
133+
134+
return () => {
135+
modalRef.current && modalRef.current.removeEventListener('click', handleClickOutside)
136+
modalRef.current && modalRef.current.removeEventListener('keyup', handleKeyDown)
137+
}
138+
}, [_visible])
139+
124140
const handleDismiss = () => {
125141
if (backdrop === 'static') {
126142
return setStaticBackdrop(true)
@@ -167,6 +183,15 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
167183
return () => document.body.classList.remove('modal-open')
168184
}, [_visible])
169185

186+
const handleClickOutside = (event: Event) => {
187+
if (
188+
modalContentRef.current &&
189+
!modalContentRef.current.contains(event.target as HTMLElement)
190+
) {
191+
handleDismiss()
192+
}
193+
}
194+
170195
const handleKeyDown = useCallback(
171196
(event) => {
172197
if (event.key === 'Escape' && keyboard) {
@@ -190,9 +215,8 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
190215
fullscreen={fullscreen}
191216
scrollable={scrollable}
192217
size={size}
193-
onClick={(event) => event.stopPropagation()}
194218
>
195-
<CModalContent>{children}</CModalContent>
219+
<CModalContent ref={modalContentRef}>{children}</CModalContent>
196220
</CModalDialog>
197221
</div>
198222
</CModalContext.Provider>
@@ -201,23 +225,21 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
201225

202226
return (
203227
<>
204-
<div onClick={handleDismiss} onKeyDown={handleKeyDown}>
205-
<Transition
206-
in={_visible}
207-
mountOnEnter
208-
onEnter={onShow}
209-
onExit={onClose}
210-
unmountOnExit
211-
timeout={!transition ? 0 : duration}
212-
>
213-
{(state) => {
214-
const transitionClass = getTransitionClass(state)
215-
return typeof window !== 'undefined' && portal
216-
? createPortal(modal(forkedRef, transitionClass), document.body)
217-
: modal(forkedRef, transitionClass)
218-
}}
219-
</Transition>
220-
</div>
228+
<Transition
229+
in={_visible}
230+
mountOnEnter
231+
onEnter={onShow}
232+
onExit={onClose}
233+
unmountOnExit={unmountOnClose}
234+
timeout={!transition ? 0 : duration}
235+
>
236+
{(state) => {
237+
const transitionClass = getTransitionClass(state)
238+
return typeof window !== 'undefined' && portal
239+
? createPortal(modal(forkedRef, transitionClass), document.body)
240+
: modal(forkedRef, transitionClass)
241+
}}
242+
</Transition>
221243
{typeof window !== 'undefined' && portal
222244
? backdrop && createPortal(<CBackdrop visible={_visible} />, document.body)
223245
: backdrop && <CBackdrop visible={_visible} />}
@@ -244,6 +266,7 @@ CModal.propTypes = {
244266
scrollable: PropTypes.bool,
245267
size: PropTypes.oneOf(['sm', 'lg', 'xl']),
246268
transition: PropTypes.bool,
269+
unmountOnClose: PropTypes.bool,
247270
visible: PropTypes.bool,
248271
}
249272

packages/coreui-react/src/components/modal/__tests__/CModal.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ test('CModal dialog close on press ESC', async () => {
3636
expect(onClose).toHaveBeenCalledTimes(0)
3737
const modal = document.querySelector('.modal')
3838
if (modal !== null) {
39-
fireEvent.keyDown(modal, {
39+
fireEvent.keyUp(modal, {
4040
key: 'Escape',
4141
code: 'Escape',
4242
keyCode: 27,
Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`CModal customize 1`] = `
4-
<div>
5-
<div />
6-
</div>
7-
`;
3+
exports[`CModal customize 1`] = `<div />`;
84

9-
exports[`loads and displays CModal component 1`] = `
10-
<div>
11-
<div />
12-
</div>
13-
`;
5+
exports[`loads and displays CModal component 1`] = `<div />`;

0 commit comments

Comments
 (0)