Skip to content

Commit

Permalink
add download method example
Browse files Browse the repository at this point in the history
  • Loading branch information
mollpo committed Sep 3, 2024
1 parent 4217a1c commit 7c7e4e6
Show file tree
Hide file tree
Showing 14 changed files with 180 additions and 118 deletions.
4 changes: 0 additions & 4 deletions src/components/button/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,6 @@ export default {
},
},
},
buttonIconResponsive: {
button: 'hidden md:flex',
buttonIcon: 'md:hidden',
},
modeVariantTone: {
[Mode.Light]: {
[ButtonVariant.Solid]: {
Expand Down
38 changes: 18 additions & 20 deletions src/components/files/DeleteFileAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import IconDelete from '@aboutbits/react-material-icons/dist/IconDelete'
import { useState } from 'react'
import { useInternationalization } from '../../framework'
import { ButtonVariant } from '../button'
import { ResponsiveButtonIcon } from '../button/ResponsiveButtonIcon'
import { IconSpinner } from '../loading/IconSpinner'
import { Tone } from '../types'
import { FileUploadObject } from './FileUploadState'
import { ResponsiveButtonIcon } from './ResponsiveButtonIcon'

type DeleteFileActionProps<TRemoteFile> = {
fileUploadObject: FileUploadObject<TRemoteFile>
Expand All @@ -21,24 +21,22 @@ export function DeleteFileAction<TRemoteFile>({
const [isDeleting, setIsDeleting] = useState(false)
const { messages } = useInternationalization()
return (
<>
<ResponsiveButtonIcon
variant={ButtonVariant.Transparent}
tone={Tone.Neutral}
disabled={isDeleting}
onClick={() => {
setIsDeleting(true)
Promise.resolve(onDelete(fileUploadObject))
.then(() => {
setIsDeleting(false)
})
.catch(() => {
setIsDeleting(false)
})
}}
icon={isDeleting ? IconSpinner : IconDelete}
label={messages['files.action.delete']}
/>
</>
<ResponsiveButtonIcon
variant={ButtonVariant.Transparent}
tone={Tone.Neutral}
disabled={isDeleting}
onClick={() => {
setIsDeleting(true)
Promise.resolve(onDelete(fileUploadObject))
.then(() => {
setIsDeleting(false)
})
.catch(() => {
setIsDeleting(false)
})
}}
icon={isDeleting ? IconSpinner : IconDelete}
label={messages['files.action.delete']}
/>
)
}
37 changes: 28 additions & 9 deletions src/components/files/DownloadFileAction.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,41 @@
import IconDownload from '@aboutbits/react-material-icons/dist/IconDownload'
import { useState } from 'react'
import { useInternationalization } from '../../framework'
import { ButtonVariant } from '../button'
import {
ResponsiveButtonIcon,
ResponsiveButtonIconProps,
} from '../button/ResponsiveButtonIcon'
import { IconSpinner } from '../loading/IconSpinner'
import { Tone } from '../types'
import { FileUploadObject } from './FileUploadState'
import { ResponsiveButtonIcon } from './ResponsiveButtonIcon'

export function DownloadFileAction({
onClick,
}: Pick<ResponsiveButtonIconProps, 'onClick'>) {
type DownloadFileActionProps<TRemoteFile> = {
fileUploadObject: FileUploadObject<TRemoteFile>
onDownload: (
fileUploadObject: FileUploadObject<TRemoteFile>,
) => void | Promise<void>
}

export function DownloadFileAction<TRemoteFile>({
onDownload,
fileUploadObject,
}: DownloadFileActionProps<TRemoteFile>) {
const { messages } = useInternationalization()
const [isDownloading, setIsDownloading] = useState(false)
return (
<ResponsiveButtonIcon
variant={ButtonVariant.Transparent}
tone={Tone.Neutral}
icon={IconDownload}
onClick={onClick}
disabled={isDownloading}
icon={isDownloading ? IconSpinner : IconDownload}
onClick={() => {
setIsDownloading(true)
Promise.resolve(onDownload(fileUploadObject))
.then(() => {
setIsDownloading(false)
})
.catch(() => {
setIsDownloading(false)
})
}}
label={messages['files.action.download']}
/>
)
Expand Down
42 changes: 6 additions & 36 deletions src/components/files/FileListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function FileListItem<TRemoteFile>({
}: FileListItemProps<TRemoteFile>) {
const [recentlyUploaded, setRecentlyUploaded] = useState<boolean>()
const formatFileSize = useHumanReadableFileSize()
const { messages } = useInternationalization()

const fileSize =
fileUploadObject.space === FileSpace.Remote
Expand All @@ -36,6 +37,8 @@ export function FileListItem<TRemoteFile>({

const formattedFileSize = fileSize ? formatFileSize(fileSize) : undefined

const fileState = fileUploadObject.state

const name =
fileUploadObject.space === FileSpace.Remote
? renderRemoteFileName(fileUploadObject.file)
Expand All @@ -57,39 +60,6 @@ export function FileListItem<TRemoteFile>({

return (
<div className={classNames(files.fileList.item.container)}>
<FileListItemContent
fileName={name}
fileSize={formattedFileSize}
fileUploadObject={fileUploadObject}
recentlyUploaded={recentlyUploaded}
disabled={disabled}
fileActions={fileActions}
/>
</div>
)
}

function FileListItemContent<TRemoteFile>({
fileName,
fileSize,
fileUploadObject,
recentlyUploaded,
disabled,
fileActions,
}: {
fileName: string
fileSize?: string
fileUploadObject: FileUploadObject<TRemoteFile>
recentlyUploaded?: boolean
disabled?: boolean
fileActions?: ReactNode
}) {
const { files } = useTheme()
const { messages } = useInternationalization()

const fileState = fileUploadObject.state
return (
<>
<div className={files.fileList.item.content}>
{fileUploadObject.state === FileState.Uploading ? (
<div
Expand Down Expand Up @@ -132,7 +102,7 @@ function FileListItemContent<TRemoteFile>({
</div>
)}
<div className={files.fileList.item.textContainer}>
<div className={classNames(files.text.bold)}>{fileName}</div>
<div className={classNames(files.text.bold)}>{name}</div>
<div
className={
fileUploadObject.state === FileState.Failed
Expand All @@ -145,7 +115,7 @@ function FileListItemContent<TRemoteFile>({
: fileState === FileState.Uploading
? messages['files.item.uploading']
: fileState === FileState.Uploaded && fileSize
? fileSize
? formattedFileSize
: ''}
</div>
</div>
Expand All @@ -161,6 +131,6 @@ function FileListItemContent<TRemoteFile>({
style={{ width: `${fileUploadObject.progress * 100}%` }}
/>
)}
</>
</div>
)
}
43 changes: 37 additions & 6 deletions src/components/files/FileUpload.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Title,
} from '@storybook/blocks'
import { Meta, StoryObj } from '@storybook/react'
import { AxiosResponse } from 'axios'
import { FC, useCallback } from 'react'
import { Button } from '../button'
import { Size } from '../types'
Expand All @@ -17,6 +18,7 @@ import { DownloadFileAction } from './DownloadFileAction'
import { FileDropZone } from './FileDropZone'
import { FileList } from './FileList'
import { FileListItem } from './FileListItem'
import { FileUploadContainer } from './FileUploadContainer'
import { FileSpace, FileState, FileUploadObject } from './FileUploadState'
import {
FileUploadOnUploadMulitple,
Expand Down Expand Up @@ -131,7 +133,7 @@ export const Single: Story = {
}

return (
<div className="flex flex-col gap-4">
<FileUploadContainer>
{fileUploadObjects.length === 0 && (
<FileDropZone
onSelect={addFilesToUpload}
Expand Down Expand Up @@ -165,7 +167,7 @@ export const Single: Story = {
Upload
</Button>
)}
</div>
</FileUploadContainer>
)
},
}
Expand Down Expand Up @@ -222,15 +224,41 @@ export const Multiple: Story = {
fileUploadObject: FileUploadObject<CustomRemoteFile>,
) => {
if (fileUploadObject.space === FileSpace.Local) {
removeFile(fileUploadObject.file) // Move this logic into FileListItem
removeFile(fileUploadObject.file)
} else {
await axiosInstance.post('/delete')
removeFile(fileUploadObject.file)
}
}

const handleDownload = async (
fileUploadObject: FileUploadObject<CustomRemoteFile>,
): Promise<void> => {
try {
const fileName: string = fileUploadObject.file.name
const response: AxiosResponse<Blob> = await axiosInstance.get(
'/download',
{
params: { fileName },
responseType: 'blob',
},
)

const downloadUrl: string = URL.createObjectURL(response.data)
const a: HTMLAnchorElement = document.createElement('a')
a.href = downloadUrl
a.download = fileName
document.body.appendChild(a)
a.click()
a.remove()
} catch (error) {
// Handle error here
console.error('Download failed:', error)
}
}

return (
<div className="flex flex-col gap-4">
<FileUploadContainer>
<FileDropZone
multipleFiles
onSelect={addFilesToUpload}
Expand All @@ -246,7 +274,10 @@ export const Multiple: Story = {
renderRemoteFileSize={(remoteFile) => remoteFile.size}
fileActions={
<>
<DownloadFileAction onClick={() => void triggerUpload()} />
<DownloadFileAction
fileUploadObject={fileUploadObject}
onDownload={handleDownload}
/>
<DeleteFileAction
fileUploadObject={fileUploadObject}
onDelete={handleDelete}
Expand All @@ -266,7 +297,7 @@ export const Multiple: Story = {
Upload
</Button>
)}
</div>
</FileUploadContainer>
)
},
}
18 changes: 18 additions & 0 deletions src/components/files/FileUploadContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import classNames from 'classnames'
import { ComponentProps, PropsWithChildren } from 'react'
import { useTheme } from '../../framework'

type FileUploadContainerProps = PropsWithChildren<ComponentProps<'div'>>

export function FileUploadContainer({
className,
children,
...props
}: FileUploadContainerProps) {
const { files } = useTheme()
return (
<div className={classNames(files.container, className)} {...props}>
{children}
</div>
)
}
18 changes: 12 additions & 6 deletions src/components/files/FileUploadWithDeleteDialogAction.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DeleteFileAction } from './DeleteFileAction'
import { FileDropZone } from './FileDropZone'
import { FileList } from './FileList'
import { FileListItem } from './FileListItem'
import { FileUploadContainer } from './FileUploadContainer'
import { FileSpace, FileState, FileUploadObject } from './FileUploadState'
import { FileUploadOnUploadSingle, useFileUpload } from './useFileUpload'
import { useMockedUploadApi } from './useMockedUploadApi'
Expand Down Expand Up @@ -110,10 +111,15 @@ export const WithDeleteDialog: Story = {
fileUploadObject: FileUploadObject<CustomRemoteFile>,
) => {
setIsDeleting(true)
await axiosInstance.post('/delete')
removeFile(fileUploadObject.file)
setIsDeleting(false)
setDeleteDialogState({ isOpen: false })
try {
await axiosInstance.post('/delete')
removeFile(fileUploadObject.file)
} catch {
// handle error here
} finally {
setIsDeleting(false)
setDeleteDialogState({ isOpen: false })
}
}

const [deleteDialogState, setDeleteDialogState] =
Expand All @@ -122,7 +128,7 @@ export const WithDeleteDialog: Story = {
const [isDeleting, setIsDeleting] = useState(false)

return (
<div className="flex flex-col gap-4">
<FileUploadContainer>
{fileUploadObjects.length === 0 && (
<FileDropZone
onSelect={addFilesToUpload}
Expand Down Expand Up @@ -166,7 +172,7 @@ export const WithDeleteDialog: Story = {
dismissButtonText="Cancel"
/>
)}
</div>
</FileUploadContainer>
)
},
}
Loading

0 comments on commit 7c7e4e6

Please sign in to comment.