import { Bin, CheckCircleFill, CrossCircleFill } from '@transferwise/icons'; import { clsx } from 'clsx'; import { forwardRef, useImperativeHandle, useRef } from 'react'; import { useIntl } from 'react-intl'; import Body from '../../body'; import { Size, Status, Typography } from '../../common'; import ProcessIndicator from '../../processIndicator/ProcessIndicator'; import { UploadedFile, UploadError } from '../types'; import MESSAGES from './UploadItem.messages'; import { UploadItemLink } from './UploadItemLink'; export type UploadItemProps = React.JSX.IntrinsicAttributes & { file: UploadedFile; /** * Is this Item part of a multiple- or single-file UploadInput */ singleFileUpload: boolean; canDelete: boolean; onDelete: () => void; onFocus: (target?: 'button' | 'link') => void; /** * Callback to be called when the file link is clicked. * When provided, you need to manually trigger actions to load/download the file. * * @param file */ onDownload?: (file: UploadedFile) => void; ref?: React.Ref; }; interface UploadItemRef { /** * A method to set focus on the upload item. * @returns {void} */ focus: () => void; /** * A required unique identifier for the upload item. */ id: string | number; /** * An optional status of the upload item. */ status?: string; } export enum TEST_IDS { uploadItem = 'uploadItem', mediaBody = 'mediaBody', link = 'link', action = 'action', } const UploadItem = forwardRef( ({ file, canDelete, onDelete, onDownload, singleFileUpload, onFocus: handleFocus }, ref) => { const { formatMessage } = useIntl(); const { status, filename, error, errors, url } = file; const linkRef = useRef(null); const buttonRef = useRef(null); useImperativeHandle(ref, () => ({ focus: (target?: 'button' | 'link'): void => { if (target === 'button' && buttonRef.current) { buttonRef.current.focus(); } else if (target === 'link' && linkRef.current) { linkRef.current.focus(); } else if (buttonRef.current) { buttonRef.current.focus(); } else if (linkRef.current) { linkRef.current.focus(); } }, id: file.id, status: file.status, })); const isSucceeded = [Status.SUCCEEDED, undefined].includes(status) && !!url; /** * We're temporarily reverting to the regular icon components, * until the StatusIcon receives 24px sizing. Some misalignment * to be expected. */ const getIcon = () => { if (error || errors?.length || status === Status.FAILED) { return ; } let processIndicator: React.ReactNode; switch (status) { case Status.PROCESSING: case Status.PENDING: processIndicator = ( ); break; case Status.SUCCEEDED: case Status.DONE: default: processIndicator = ; } return processIndicator; }; const getErrorMessage = (error?: UploadError) => typeof error === 'object' ? error.message : error || formatMessage(MESSAGES.uploadingFailed); const getMultipleErrors = (errors?: UploadError[]) => { if (!errors?.length) { return null; } if (errors?.length === 1) { return getErrorMessage(errors[0]); } return (
    {errors.map((error, index) => { // eslint-disable-next-line react/no-array-index-key return
  • {getErrorMessage(error)}
  • ; })}
); }; const getDescription = () => { if (error || errors?.length || status === Status.FAILED) { return ( {errors?.length ? getMultipleErrors(errors) : getErrorMessage(error)} ); } switch (status) { case Status.PENDING: return ( {formatMessage(MESSAGES.uploading)} ); case Status.PROCESSING: return {formatMessage(MESSAGES.deleting)}; case Status.SUCCEEDED: case Status.DONE: default: return ( {formatMessage(MESSAGES.uploaded)} ); } }; const getTitle = () => { return filename || formatMessage(MESSAGES.uploadedFile); }; const onDownloadFile = (event: React.MouseEvent): void => { if (onDownload) { event.preventDefault(); onDownload(file); } }; return (
{getIcon()}
{getTitle()} {getDescription()}
{canDelete && (
)}
); }, ); UploadItem.displayName = 'UploadItem'; export default UploadItem;