Skip to content

Commit 8681b0d

Browse files
authored
feat: added modal.onMounted handler (#1082)
- Added `modal.onMounted` handler to global eventHandlers. This event handler is triggered within the `useEffect` of the Modal component at mounting time. --- Example usage ```tsx const App = () => { return ( <SendbirdProvider eventHandlers={{ modal: { onMounted({ id, close }) { // Push the state to prevent the browser's back button press window.history.pushState({ id }, ''); // Close the modal if the back button is pressed const onPopState = (e: PopStateEvent) => { e.preventDefault(); // Prevents the default back action close(); // Closes the modal }; window.addEventListener('popstate', onPopState); // Cleanup function return () => { // Clear the current state when the modal is closed without using the back button if (window.history.state?.id === id) window.history.back(); window.removeEventListener('popstate', onPopState); }; } } }} /> ); }; ``` ticket: [CLNP-3057] [CLNP-3057]: https://sendbird.atlassian.net/browse/CLNP-3057?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
1 parent 69a2cbd commit 8681b0d

File tree

3 files changed

+45
-62
lines changed

3 files changed

+45
-62
lines changed

src/lib/types.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import type { User, SendbirdChatParams, SendbirdError } from '@sendbird/chat';
44

55
import type {
66
GroupChannel,
7-
GroupChannelCreateParams, GroupChannelModule,
7+
GroupChannelCreateParams,
8+
GroupChannelModule,
89
} from '@sendbird/chat/groupChannel';
910
import type {
1011
OpenChannel,
11-
OpenChannelCreateParams, OpenChannelModule,
12+
OpenChannelCreateParams,
13+
OpenChannelModule,
1214
} from '@sendbird/chat/openChannel';
1315
import type {
1416
FileMessage,
@@ -53,7 +55,10 @@ export interface SBUEventHandlers {
5355
},
5456
connection?: {
5557
onFailed?(error: SendbirdError): void;
56-
}
58+
},
59+
modal?: {
60+
onMounted?(params: { id: string; close(): void; }): void | (() => void);
61+
};
5762
}
5863

5964
export interface SendBirdStateConfig {

src/ui/Modal/index.tsx

Lines changed: 32 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import React, { ReactElement, ReactNode, useContext, MouseEvent } from 'react';
1+
import React, { ReactElement, ReactNode, useContext, MouseEvent, useState, useEffect } from 'react';
22
import { createPortal } from 'react-dom';
33

44
import './index.scss';
55

6-
import { noop } from '../../utils/utils';
6+
import { classnames, noop } from '../../utils/utils';
77
import { MODAL_ROOT } from '../../hooks/useModal/ModalRoot';
88
import { LocalizationContext } from '../../lib/LocalizationContext';
99
import { useMediaQueryContext } from '../../lib/MediaQueryContext';
@@ -12,6 +12,8 @@ import IconButton from '../IconButton';
1212
import Button, { ButtonTypes } from '../Button';
1313
import Icon, { IconTypes, IconColors } from '../Icon';
1414
import Label, { LabelTypography, LabelColors } from '../Label';
15+
import { useSendbirdStateContext } from '../../lib/Sendbird';
16+
import uuidv4 from '../../utils/uuid';
1517

1618
export interface ModalHeaderProps {
1719
titleText: string;
@@ -23,17 +25,8 @@ export const ModalHeader = ({ titleText, onCloseClick }: ModalHeaderProps): Reac
2325
{titleText}
2426
</Label>
2527
<div className="sendbird-modal__close">
26-
<IconButton
27-
width="32px"
28-
height="32px"
29-
onClick={onCloseClick}
30-
>
31-
<Icon
32-
type={IconTypes.CLOSE}
33-
fillColor={IconColors.DEFAULT}
34-
width="24px"
35-
height="24px"
36-
/>
28+
<IconButton width="32px" height="32px" onClick={onCloseClick}>
29+
<Icon type={IconTypes.CLOSE} fillColor={IconColors.DEFAULT} width="24px" height="24px" />
3730
</IconButton>
3831
</div>
3932
</div>
@@ -44,10 +37,7 @@ export interface ModalBodyProps {
4437
}
4538
export const ModalBody = ({ children }: ModalBodyProps): ReactElement => (
4639
<div className="sendbird-modal__body">
47-
<Label
48-
type={LabelTypography.SUBTITLE_1}
49-
color={LabelColors.ONBACKGROUND_2}
50-
>
40+
<Label type={LabelTypography.SUBTITLE_1} color={LabelColors.ONBACKGROUND_2}>
5141
{children}
5242
</Label>
5343
</div>
@@ -97,14 +87,12 @@ export interface ModalProps {
9787
disabled?: boolean;
9888
hideFooter?: boolean;
9989
type?: ButtonTypes;
100-
/**
101-
* Do not use this! We will deprecate onCancel in v4.
102-
*/
103-
onCancel?: () => void;
10490
onClose?: () => void;
10591
onSubmit?: (...args: any[]) => void;
10692
renderHeader?: () => ReactElement;
10793
customFooter?: ReactNode;
94+
/** @deprecated Please use `onClose` instead, we will remove `onCancel` in v4. * */
95+
onCancel?: () => void;
10896
}
10997
export function Modal(props: ModalProps): ReactElement {
11098
const {
@@ -118,58 +106,43 @@ export function Modal(props: ModalProps): ReactElement {
118106
disabled = false,
119107
hideFooter = false,
120108
type = ButtonTypes.DANGER,
121-
/**
122-
* Do not use this! We will deprecate onCancel in v4.
123-
*/
124-
onCancel = noop,
125-
onClose,
126-
onSubmit = noop,
127109
renderHeader,
110+
onSubmit = noop,
111+
onClose,
112+
onCancel,
128113
customFooter,
129114
} = props;
130-
const handleClose = onClose ?? onCancel;
115+
const handleClose = onClose ?? onCancel ?? noop;
116+
const { eventHandlers } = useSendbirdStateContext();
117+
118+
const [id] = useState(() => `sbu-modal-${uuidv4()}`);
119+
120+
useEffect(() => {
121+
return eventHandlers?.modal?.onMounted?.({ close: handleClose, id });
122+
}, []);
131123

132124
const { isMobile } = useMediaQueryContext();
133-
return createPortal((
134-
<div className={`
135-
sendbird-modal ${className}
136-
${(isFullScreenOnMobile && isMobile) ? 'sendbird-modal--full-mobile' : ''}
137-
`}>
138-
<div
139-
className={[
140-
'sendbird-modal__content',
141-
...(Array.isArray(contentClassName) ? contentClassName : [contentClassName]),
142-
].join(' ')}
143-
>
144-
{renderHeader?.() || (
145-
<ModalHeader titleText={titleText ?? ''} onCloseClick={handleClose} />
146-
)}
125+
return createPortal(
126+
<div className={classnames('sendbird-modal', className, isFullScreenOnMobile && isMobile && 'sendbird-modal--full-mobile')}>
127+
<div className={classnames('sendbird-modal__content', ...(Array.isArray(contentClassName) ? contentClassName : [contentClassName]))}>
128+
{renderHeader?.() || <ModalHeader titleText={titleText ?? ''} onCloseClick={handleClose} />}
147129
<ModalBody>{children}</ModalBody>
148-
{
149-
!hideFooter && (customFooter ?? (
150-
<ModalFooter
151-
disabled={disabled}
152-
onCancel={handleClose}
153-
onSubmit={onSubmit}
154-
submitText={submitText ?? ''}
155-
type={type}
156-
/>
157-
))
158-
}
130+
{!hideFooter
131+
&& (customFooter ?? (
132+
<ModalFooter disabled={disabled} onCancel={handleClose} onSubmit={onSubmit} submitText={submitText ?? ''} type={type} />
133+
))}
159134
</div>
160135
<div
161-
className={`
162-
sendbird-modal__backdrop
163-
${isCloseOnClickOutside && 'sendbird-modal__backdrop--clickoutside'}
164-
`}
136+
className={classnames('sendbird-modal__backdrop', isCloseOnClickOutside && 'sendbird-modal__backdrop--clickoutside')}
165137
onClick={(e) => {
166138
e?.stopPropagation();
167139
if (isCloseOnClickOutside) {
168140
handleClose();
169141
}
170142
}}
171143
/>
172-
</div>
173-
), document.getElementById(MODAL_ROOT) as HTMLElement);
144+
</div>,
145+
document.getElementById(MODAL_ROOT) as HTMLElement,
146+
);
174147
}
175148
export default Modal;

src/utils/utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ export function openURL(url?: string | null) {
3434
}
3535
}
3636

37+
type Falsy = undefined | null | false | 0 | '';
38+
export function classnames(...args: (string | Falsy)[]) {
39+
return args.filter(Boolean).join(' ');
40+
}
41+
3742
export default {
3843
getSenderName,
3944
getSenderProfileUrl,

0 commit comments

Comments
 (0)