Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@
"src/**/*.{js,jsx,mjs}"
],
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/src/__mocks__/fileMock.js",
"\\.(css|scss)$": "identity-obj-proxy"
},
"testMatch": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,51 @@ describe('UploadInputV3', () => {
});
});

describe('File Preview and Download', () => {
test('renders a preview image linked to the file', () => {
const files = [{ filename: 'image.png', size: 102400, public_url: 'https://cdn.example.com/image.png' }];
render(<UploadInputV3 {...defaultProps} value={files} />);
const img = screen.getByRole('img', { name: 'image.png' });
const previewLink = img.closest('a');
expect(previewLink).toHaveAttribute('href', 'https://cdn.example.com/image.png');
expect(previewLink).toHaveAttribute('target', '_blank');
expect(previewLink).not.toHaveAttribute('download');
});

test('prefers private_url and falls back to public_url when private_url is "#"', () => {
const { rerender, container } = render(<UploadInputV3 {...defaultProps} value={[{
filename: 'a.png', size: 1024,
private_url: 'https://private.example.com/a.png',
public_url: 'https://cdn.example.com/a.png',
}]} />);
expect(container.querySelector('a[download]')).toHaveAttribute('href', 'https://private.example.com/a.png');

rerender(<UploadInputV3 {...defaultProps} value={[{
filename: 'a.png', size: 1024,
private_url: '#',
public_url: 'https://cdn.example.com/a.png',
}]} />);
expect(container.querySelector('a[download]')).toHaveAttribute('href', 'https://cdn.example.com/a.png');
});

test('filename is a download link with correct href', () => {
const files = [{ filename: 'document.pdf', size: 102400, public_url: 'https://cdn.example.com/document.pdf' }];
const { container } = render(<UploadInputV3 {...defaultProps} value={files} />);
const downloadLink = container.querySelector('a[download]');
expect(downloadLink).toHaveAttribute('href', 'https://cdn.example.com/document.pdf');
expect(downloadLink).toHaveAttribute('target', '_blank');
expect(downloadLink).toHaveTextContent('document.pdf');
});

test('preview image falls back to file_icon on load error', () => {
const files = [{ filename: 'document.pdf', size: 102400, public_url: 'https://cdn.example.com/document.pdf' }];
render(<UploadInputV3 {...defaultProps} value={files} />);
const img = screen.getByRole('img', { name: 'document.pdf' });
fireEvent.error(img);
expect(img).not.toHaveAttribute('src', 'https://cdn.example.com/document.pdf');
});
});

describe('Edge Cases', () => {
test('handles empty value array', () => {
const { container } = render(<UploadInputV3 {...defaultProps} value={[]} />);
Expand Down
26 changes: 22 additions & 4 deletions src/components/inputs/upload-input-v3/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
import CloseIcon from "@mui/icons-material/Close";
import { DropzoneV3 } from './dropzone-v3';
import ProgressiveImg from '../../progressive-img';
import file_icon from '../upload-input/file.png';
import './index.less';

const UploadInputV3 = ({
Expand All @@ -41,7 +43,7 @@ const UploadInputV3 = ({
id,
parallelChunkUploads = false,
maxConcurrentChunks = 6,
onError = () => {},
onError = () => { },
getAllowedExtensions = null,
getMaxSize = null,
error,
Expand Down Expand Up @@ -371,21 +373,37 @@ const UploadInputV3 = ({
{value.map((file, index) => {
const filename = file.filename;
const fileSize = formatFileSize(file.size);
let src = file?.private_url || file?.public_url || file?.file_url;
if (src === '#') src = file?.public_url;
// custom replace for dropbox case ( download vs raw)
const previewSrc = src ? src.replace("?dl=0", "?raw=1") : filename;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomrndom this logic is not correct u are asumming that files are always an image, file can have any ext please do check v2 implementation that use https://github.com/OpenStackweb/openstack-uicore-foundation/blob/main/src/components/progressive-img/index.js


return (
<Box
key={`uploaded-${index}`}
sx={fileRowSx}
>
<Box sx={{ color: 'primary.main', display: 'flex', alignItems: 'center', mr: 2, minWidth: 32 }}>
<UploadFileIcon fontSize="medium" />
<Box sx={{ display: 'flex', alignItems: 'center', mr: 2, width: 64, height: 64, flexShrink: 0 }}>
<a href={src} target="_blank" title="See Preview">
<ProgressiveImg
alt={filename}
src={previewSrc}
placeholderSrc={file_icon}
/>
</a>
</Box>

<Box sx={{ flex: 1, minWidth: 0 }}>
<Typography
component="a"
href={src}
target="_blank"
rel="noreferrer"
title="Preview file"
download
variant="body2"
fontWeight={500}
sx={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
sx={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block', color: 'primary.main', textDecoration: 'none', '&:hover': { textDecoration: 'underline' } }}
>
{filename}
</Typography>
Expand Down
Loading