From eb9b102a80e86d37cf18500d368b7317198284ac Mon Sep 17 00:00:00 2001 From: DomW Date: Wed, 18 Dec 2024 15:20:45 +0000 Subject: [PATCH] feat(attachment-preview-modal): add attachment preview modal component (#2528) --- .../attachment-preview-modal/index.test.tsx | 71 +++++++++++++++++++ .../attachment-preview-modal/index.tsx | 48 +++++++++++++ .../attachment-preview-modal/styles.scss | 28 ++++++++ 3 files changed, 147 insertions(+) create mode 100644 src/components/attachment-preview-modal/index.test.tsx create mode 100644 src/components/attachment-preview-modal/index.tsx create mode 100644 src/components/attachment-preview-modal/styles.scss diff --git a/src/components/attachment-preview-modal/index.test.tsx b/src/components/attachment-preview-modal/index.test.tsx new file mode 100644 index 000000000..f39fe41c4 --- /dev/null +++ b/src/components/attachment-preview-modal/index.test.tsx @@ -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(); + }; + + 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(); + }); +}); diff --git a/src/components/attachment-preview-modal/index.tsx b/src/components/attachment-preview-modal/index.tsx new file mode 100644 index 000000000..0c9aafc49 --- /dev/null +++ b/src/components/attachment-preview-modal/index.tsx @@ -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 = ({ attachment, onClose }) => { + const fileType = attachment.mimetype; + const canPreview = PREVIEWABLE_TYPES.includes(fileType); + + return ( + +
+ {canPreview ? ( +