Skip to content

Commit

Permalink
feat(attachment-preview-modal): add attachment preview modal component (
Browse files Browse the repository at this point in the history
  • Loading branch information
domw30 authored Dec 18, 2024
1 parent b2ac433 commit eb9b102
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 0 deletions.
71 changes: 71 additions & 0 deletions src/components/attachment-preview-modal/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import { shallow } from 'enzyme';
import { AttachmentPreviewModal } from '.';
import { Modal } from '../modal';
import { IconAlertCircle } from '@zero-tech/zui/icons';

describe(AttachmentPreviewModal, () => {
const defaultProps = {
attachment: {
name: 'test.pdf',
url: 'http://test.com/test.pdf',
mimetype: 'application/pdf',
},
onClose: jest.fn(),
};

const subject = (props = {}) => {
return shallow(<AttachmentPreviewModal {...defaultProps} {...props} />);
};

it('renders iframe for PDF files', () => {
const wrapper = subject();

expect(wrapper.find('iframe').exists()).toBe(true);
expect(wrapper.find('iframe').prop('src')).toBe('http://test.com/test.pdf');
});

it('shows alert for non-previewable files', () => {
const wrapper = subject({
attachment: {
...defaultProps.attachment,
mimetype: 'application/zip',
},
});

expect(wrapper.find('iframe').exists()).toBe(false);
expect(wrapper.find(IconAlertCircle).exists()).toBe(true);
});

it('displays correct modal title for previewable files', () => {
const wrapper = subject();

expect(wrapper.find(Modal).prop('title')).toBe('File Preview');
});

it('displays correct modal title for non-previewable files', () => {
const wrapper = subject({
attachment: {
...defaultProps.attachment,
mimetype: 'application/zip',
},
});

expect(wrapper.find(Modal).prop('title')).toBe('Download File');
});

it('displays file name', () => {
const wrapper = subject();

expect(wrapper.find('.attachment-preview-modal__info p').text()).toBe('test.pdf');
});

it('calls onClose when modal is closed', () => {
const onClose = jest.fn();
const wrapper = subject({ onClose });

wrapper.find(Modal).prop('onClose')();

expect(onClose).toHaveBeenCalled();
});
});
48 changes: 48 additions & 0 deletions src/components/attachment-preview-modal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import { Modal } from '../modal';
import { bemClassName } from '../../lib/bem';
import { IconAlertCircle } from '@zero-tech/zui/icons';

import './styles.scss';

const cn = bemClassName('attachment-preview-modal');

const PREVIEWABLE_TYPES = [
'application/pdf',
'text/plain',
'text/csv',
'text/markdown',
'application/json',
];

interface Props {
attachment: {
name: string;
url: string;
mimetype?: string;
};
onClose: () => void;
}

export const AttachmentPreviewModal: React.FC<Props> = ({ attachment, onClose }) => {
const fileType = attachment.mimetype;
const canPreview = PREVIEWABLE_TYPES.includes(fileType);

return (
<Modal onClose={onClose} title={canPreview ? 'File Preview' : 'Download File'}>
<div {...cn()}>
{canPreview ? (
<iframe src={attachment.url} {...cn('preview')} title={attachment.name} />
) : (
<div {...cn('alert')}>
<IconAlertCircle size={24} />
<p>Preview not available for this file type</p>
</div>
)}
<div {...cn('info')}>
<p>{attachment.name}</p>
</div>
</div>
</Modal>
);
};
28 changes: 28 additions & 0 deletions src/components/attachment-preview-modal/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@use '~@zero-tech/zui/styles/theme' as theme;

.attachment-preview-modal {
display: flex;
flex-direction: column;
width: 500px;

&__preview {
width: 100%;
height: 60vh;
border: none;
border-radius: 8px;
background: var(--color-background-secondary);
}

&__info {
display: flex;
justify-content: center;
color: theme.$color-greyscale-11;
}

&__alert {
display: flex;
align-items: center;
gap: 8px;
color: theme.$color-error-transparency-11;
}
}

0 comments on commit eb9b102

Please sign in to comment.