Skip to content

Commit 3bf48e0

Browse files
authored
feature: Add modal to the FileViewer components (#1126)
[CLNP-3150](https://sendbird.atlassian.net/browse/CLNP-3150) ### Feature Request & Change Log * Add a modal to the `FileViewer` components for using `onMounted` event handler [CLNP-3150]: https://sendbird.atlassian.net/browse/CLNP-3150?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
1 parent 732db95 commit 3bf48e0

File tree

4 files changed

+249
-302
lines changed

4 files changed

+249
-302
lines changed

src/modules/GroupChannel/components/FileViewer/FileViewerView.tsx

Lines changed: 61 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Label, { LabelColors, LabelTypography, LabelStringSet } from '../../../..
1111
import { isImage, isSupportedFileView, isVideo } from '../../../../utils';
1212
import { MODAL_ROOT } from '../../../../hooks/useModal';
1313
import useSendbirdStateContext from '../../../../hooks/useSendbirdStateContext';
14+
import Modal from '../../../../ui/Modal';
1415

1516
type DeleteMessageTypeLegacy = (message: CoreMessageType) => Promise<void>;
1617
export interface FileViewerViewProps extends FileViewerProps {
@@ -77,68 +78,70 @@ export const FileViewerComponent = ({
7778
disableDelete,
7879
onDownloadClick,
7980
}: FileViewerUIProps) => (
80-
<div className="sendbird-fileviewer" data-testid="sendbird-fileviewer">
81-
<div className="sendbird-fileviewer__header">
82-
<div className="sendbird-fileviewer__header__left">
83-
<div className="sendbird-fileviewer__header__left__avatar">
84-
<Avatar height="32px" width="32px" src={profileUrl} />
81+
<Modal>
82+
<div className="sendbird-fileviewer" data-testid="sendbird-fileviewer">
83+
<div className="sendbird-fileviewer__header">
84+
<div className="sendbird-fileviewer__header__left">
85+
<div className="sendbird-fileviewer__header__left__avatar">
86+
<Avatar height="32px" width="32px" src={profileUrl} />
87+
</div>
88+
<Label className="sendbird-fileviewer__header__left__filename" type={LabelTypography.H_2} color={LabelColors.ONBACKGROUND_1}>
89+
{name}
90+
</Label>
91+
<Label className="sendbird-fileviewer__header__left__sender-name" type={LabelTypography.BODY_1} color={LabelColors.ONBACKGROUND_2}>
92+
{nickname}
93+
</Label>
94+
</div>
95+
<div className="sendbird-fileviewer__header__right">
96+
{isSupportedFileView(type) && (
97+
<div className="sendbird-fileviewer__header__right__actions">
98+
<a
99+
className="sendbird-fileviewer__header__right__actions__download"
100+
rel="noopener noreferrer"
101+
href={url}
102+
target="_blank"
103+
onClick={onDownloadClick}
104+
>
105+
<Icon type={IconTypes.DOWNLOAD} fillColor={IconColors.ON_BACKGROUND_1} height="24px" width="24px" />
106+
</a>
107+
{onDelete && isByMe && (
108+
<div className="sendbird-fileviewer__header__right__actions__delete">
109+
<Icon
110+
className={disableDelete ? 'disabled' : ''}
111+
type={IconTypes.DELETE}
112+
fillColor={disableDelete ? IconColors.GRAY : IconColors.ON_BACKGROUND_1}
113+
height="24px"
114+
width="24px"
115+
onClick={() => {
116+
if (!disableDelete) {
117+
onDelete();
118+
}
119+
}}
120+
/>
121+
</div>
122+
)}
123+
</div>
124+
)}
125+
<div className="sendbird-fileviewer__header__right__actions__close">
126+
<Icon type={IconTypes.CLOSE} fillColor={IconColors.ON_BACKGROUND_1} height="24px" width="24px" onClick={onCancel} />
127+
</div>
85128
</div>
86-
<Label className="sendbird-fileviewer__header__left__filename" type={LabelTypography.H_2} color={LabelColors.ONBACKGROUND_1}>
87-
{name}
88-
</Label>
89-
<Label className="sendbird-fileviewer__header__left__sender-name" type={LabelTypography.BODY_1} color={LabelColors.ONBACKGROUND_2}>
90-
{nickname}
91-
</Label>
92129
</div>
93-
<div className="sendbird-fileviewer__header__right">
94-
{isSupportedFileView(type) && (
95-
<div className="sendbird-fileviewer__header__right__actions">
96-
<a
97-
className="sendbird-fileviewer__header__right__actions__download"
98-
rel="noopener noreferrer"
99-
href={url}
100-
target="_blank"
101-
onClick={onDownloadClick}
102-
>
103-
<Icon type={IconTypes.DOWNLOAD} fillColor={IconColors.ON_BACKGROUND_1} height="24px" width="24px" />
104-
</a>
105-
{onDelete && isByMe && (
106-
<div className="sendbird-fileviewer__header__right__actions__delete">
107-
<Icon
108-
className={disableDelete ? 'disabled' : ''}
109-
type={IconTypes.DELETE}
110-
fillColor={disableDelete ? IconColors.GRAY : IconColors.ON_BACKGROUND_1}
111-
height="24px"
112-
width="24px"
113-
onClick={() => {
114-
if (!disableDelete) {
115-
onDelete();
116-
}
117-
}}
118-
/>
119-
</div>
120-
)}
130+
<div className="sendbird-fileviewer__content">
131+
{isVideo(type) && (
132+
<video controls className="sendbird-fileviewer__content__video">
133+
<source src={url} type={type} />
134+
</video>
135+
)}
136+
{isImage(type) && <img src={url} alt={name} className="sendbird-fileviewer__content__img" />}
137+
{!isSupportedFileView(type) && (
138+
<div className="sendbird-fileviewer__content__unsupported">
139+
<Label type={LabelTypography.H_1} color={LabelColors.ONBACKGROUND_1}>
140+
{LabelStringSet.UI__FILE_VIEWER__UNSUPPORT}
141+
</Label>
121142
</div>
122143
)}
123-
<div className="sendbird-fileviewer__header__right__actions__close">
124-
<Icon type={IconTypes.CLOSE} fillColor={IconColors.ON_BACKGROUND_1} height="24px" width="24px" onClick={onCancel} />
125-
</div>
126144
</div>
127145
</div>
128-
<div className="sendbird-fileviewer__content">
129-
{isVideo(type) && (
130-
<video controls className="sendbird-fileviewer__content__video">
131-
<source src={url} type={type} />
132-
</video>
133-
)}
134-
{isImage(type) && <img src={url} alt={name} className="sendbird-fileviewer__content__img" />}
135-
{!isSupportedFileView(type) && (
136-
<div className="sendbird-fileviewer__content__unsupported">
137-
<Label type={LabelTypography.H_1} color={LabelColors.ONBACKGROUND_1}>
138-
{LabelStringSet.UI__FILE_VIEWER__UNSUPPORT}
139-
</Label>
140-
</div>
141-
)}
142-
</div>
143-
</div>
146+
</Modal>
144147
);

src/ui/FileViewer/__tests__/FileViewer.spec.js

Lines changed: 92 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,25 @@ import { render, screen } from '@testing-library/react';
33

44
import { FileViewerComponent as FileViewer } from "../index";
55
import { msg0, msg1 } from '../data.mock';
6+
import { SendbirdSdkContext } from '../../../lib/SendbirdSdkContext';
7+
import { MODAL_ROOT } from '../../../hooks/useModal';
68

79
describe('ui/FileViewer', () => {
8-
it('should display image', function() {
10+
let modalRoot;
11+
12+
beforeAll(() => {
13+
// Create a modal root element and append it to the body
14+
modalRoot = document.createElement('div');
15+
modalRoot.setAttribute('id', MODAL_ROOT);
16+
document.body.appendChild(modalRoot);
17+
});
18+
19+
afterAll(() => {
20+
// Remove the modal root element after tests
21+
document.body.removeChild(modalRoot);
22+
});
23+
24+
it('should display image', function () {
925
const {
1026
sender,
1127
type,
@@ -14,15 +30,17 @@ describe('ui/FileViewer', () => {
1430
} = msg0;
1531
const { profileUrl, nickname = '' } = sender;
1632
render(
17-
<FileViewer
18-
profileUrl={profileUrl}
19-
nickname={nickname}
20-
type={type}
21-
url={url}
22-
name={name}
23-
onClose={() => {}}
24-
onDelete={() => {}}
33+
<SendbirdSdkContext.Provider value={{}}>
34+
<FileViewer
35+
profileUrl={profileUrl}
36+
nickname={nickname}
37+
type={type}
38+
url={url}
39+
name={name}
40+
onClose={() => { }}
41+
onDelete={() => { }}
2542
/>
43+
</SendbirdSdkContext.Provider>
2644
);
2745
expect(
2846
screen.getByAltText(msg0.name).className
@@ -35,7 +53,7 @@ describe('ui/FileViewer', () => {
3553
).toEqual(msg0.url);
3654
});
3755

38-
it('should display video', function() {
56+
it('should display video', function () {
3957
const {
4058
sender,
4159
type,
@@ -44,28 +62,33 @@ describe('ui/FileViewer', () => {
4462
} = msg1;
4563
const { profileUrl, nickname = '' } = sender;
4664
const { container } = render(
47-
<FileViewer
48-
profileUrl={profileUrl}
49-
nickname={nickname}
50-
type={type}
51-
url={url}
52-
name={name}
53-
onClose={() => {}}
54-
onDelete={() => {}}
55-
/>
65+
<SendbirdSdkContext.Provider value={{}}>
66+
<FileViewer
67+
profileUrl={profileUrl}
68+
nickname={nickname}
69+
type={type}
70+
url={url}
71+
name={name}
72+
onClose={() => { }}
73+
onDelete={() => { }}
74+
/>
75+
</SendbirdSdkContext.Provider>
5676
);
57-
expect(
58-
container.getElementsByClassName('sendbird-fileviewer__content__video')[0].className
59-
).not.toContain('sendbird-fileviewer__content__img');
60-
expect(
61-
container.getElementsByClassName('sendbird-fileviewer__content__video')[0].className
62-
).toBe('sendbird-fileviewer__content__video');
63-
expect(
64-
container.getElementsByClassName('sendbird-fileviewer__content__video')[0].children[0].src
65-
).toEqual(url);
77+
78+
// Use document to search for the element inside the modal root
79+
const videoElement = document.querySelector(`#${MODAL_ROOT} .sendbird-fileviewer__content__video`);
80+
if (!videoElement) {
81+
throw new Error('Video element not found');
82+
}
83+
84+
expect(videoElement.className).not.toContain('sendbird-fileviewer__content__img');
85+
expect(videoElement.className).toBe('sendbird-fileviewer__content__video');
86+
87+
const videoChild = videoElement.children[0];
88+
expect(videoChild.src).toEqual(url);
6689
});
6790

68-
it('should handle unsupported msg', function() {
91+
it('should handle unsupported msg', function () {
6992
const unsupportedMsg = { sender: {} };
7093
const profileUrl = '';
7194
const nickname = '';
@@ -75,25 +98,37 @@ describe('ui/FileViewer', () => {
7598
name = '',
7699
} = unsupportedMsg;
77100
const { container } = render(
78-
<FileViewer
79-
profileUrl={profileUrl}
80-
nickname={nickname}
81-
type={type}
82-
url={url}
83-
name={name}
84-
onClose={() => {}}
85-
onDelete={() => {}}
86-
/>
101+
<SendbirdSdkContext.Provider value={{}}>
102+
<FileViewer
103+
profileUrl={profileUrl}
104+
nickname={nickname}
105+
type={type}
106+
url={url}
107+
name={name}
108+
onClose={() => { }}
109+
onDelete={() => { }}
110+
/>
111+
</SendbirdSdkContext.Provider>
87112
);
88-
expect(
89-
container.getElementsByClassName('sendbird-fileviewer__content__unsupported')[0].className
90-
).toBe('sendbird-fileviewer__content__unsupported');
91-
expect(
92-
container.getElementsByClassName('sendbird-fileviewer__header__right')[0].children[0].className
93-
).not.toBe('sendbird-fileviewer__header__right__actions');
113+
114+
// Use document to search for the element inside the modal root
115+
const unsupportedElement = document.querySelector(`#${MODAL_ROOT} .sendbird-fileviewer__content__unsupported`);
116+
if (!unsupportedElement) {
117+
throw new Error('Unsupported element not found');
118+
}
119+
120+
expect(unsupportedElement.className).toBe('sendbird-fileviewer__content__unsupported');
121+
122+
const headerRightElement = document.querySelector(`#${MODAL_ROOT} .sendbird-fileviewer__header__right`);
123+
if (!headerRightElement) {
124+
throw new Error('Header right element not found');
125+
}
126+
127+
const headerRightActionElement = headerRightElement.children[0];
128+
expect(headerRightActionElement.className).not.toBe('sendbird-fileviewer__header__right__actions');
94129
});
95130

96-
it('should do a snapshot test of the FileViewer DOM', function() {
131+
it('should do a snapshot test of the FileViewer DOM', function () {
97132
const {
98133
sender,
99134
type,
@@ -102,16 +137,18 @@ describe('ui/FileViewer', () => {
102137
} = msg0;
103138
const { profileUrl, nickname = '' } = sender;
104139
const { asFragment } = render(
105-
<FileViewer
106-
profileUrl={profileUrl}
107-
nickname={nickname}
108-
type={type}
109-
url={url}
110-
name={name}
111-
onClose={() => {}}
112-
onDelete={() => {}}
113-
message={msg0}
114-
/>
140+
<SendbirdSdkContext.Provider value={{}}>
141+
<FileViewer
142+
profileUrl={profileUrl}
143+
nickname={nickname}
144+
type={type}
145+
url={url}
146+
name={name}
147+
onClose={() => { }}
148+
onDelete={() => { }}
149+
message={msg0}
150+
/>
151+
</SendbirdSdkContext.Provider>
115152
);
116153
expect(asFragment()).toMatchSnapshot();
117154
});

0 commit comments

Comments
 (0)