-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(upload): add upload button functionality (#176)
* feat(upload): add upload button functionality * fix(rename): rename upload file * feat(dnd): super simple drag and drop been added * fix(test): hook test * fix(refactor): move UploadZone into a separated file * fix(refactor): remove older version dataTransfer support
- Loading branch information
1 parent
2ca6cce
commit 6f5565e
Showing
6 changed files
with
282 additions
and
74 deletions.
There are no files selected for viewing
13 changes: 13 additions & 0 deletions
13
...ctcore/frontend/src/components/atoms/sharedStyledComponents/VisuallyHiddenInput/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { styled } from "@mui/material"; | ||
|
||
export const VisuallyHiddenInput = styled('input')({ | ||
clip: 'rect(0 0 0 0)', | ||
clipPath: 'inset(50%)', | ||
height: 1, | ||
overflow: 'hidden', | ||
position: 'absolute', | ||
bottom: 0, | ||
left: 0, | ||
whiteSpace: 'nowrap', | ||
width: 1, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
aqueductcore/frontend/src/components/templates/UploadFileZone/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { DragEvent, PropsWithChildren } from "react"; | ||
import { | ||
useTheme, | ||
} from "@mui/material"; | ||
|
||
|
||
function DrawerLayout({ children, handleUpload }: PropsWithChildren<{ handleUpload?: (file: File) => void }>) { | ||
const theme = useTheme(); | ||
function dropHandler(e: DragEvent<HTMLDivElement>) { | ||
if (!handleUpload) return; | ||
dragLeaveHandler(e) | ||
e.preventDefault(); | ||
if (e?.dataTransfer) { | ||
if (e.dataTransfer?.items) { | ||
[...e.dataTransfer.items].forEach((item) => { | ||
if (item.kind === "file") { | ||
const file = item.getAsFile(); | ||
if (file) { | ||
handleUpload(file) | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
|
||
function dragOverHandler(e: DragEvent<HTMLDivElement>) { | ||
if (!handleUpload) return; | ||
e.preventDefault(); | ||
e.currentTarget.style.background = theme.palette.action.selected | ||
} | ||
|
||
function dragLeaveHandler(e: DragEvent<HTMLDivElement>) { | ||
e.currentTarget.style.background = "inherit" | ||
} | ||
return ( | ||
<div | ||
onDrop={dropHandler} | ||
onDragOver={dragOverHandler} | ||
onDragLeave={dragLeaveHandler} | ||
> | ||
{children} | ||
</div>) | ||
} | ||
export default DrawerLayout; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { renderHook, act } from '@testing-library/react'; | ||
import toast from 'react-hot-toast'; | ||
import { useContext } from 'react'; | ||
|
||
import { client } from 'API/apolloClientConfig'; | ||
import { AQD_FILE_URI } from 'constants/api'; | ||
import useFileUpload from './useUploadFile'; | ||
|
||
jest.mock('react-hot-toast'); | ||
|
||
jest.mock('react', () => ({ | ||
...jest.requireActual('react'), | ||
useContext: jest.fn(), | ||
})); | ||
|
||
jest.mock('API/apolloClientConfig', () => ({ | ||
client: { | ||
refetchQueries: jest.fn(), | ||
}, | ||
})); | ||
|
||
global.fetch = jest.fn(); | ||
|
||
let setSelectedFileMock: jest.Mock; | ||
|
||
beforeEach(() => { | ||
setSelectedFileMock = jest.fn(); | ||
(useContext as jest.Mock).mockReturnValue({ | ||
setSelectedFile: setSelectedFileMock, | ||
}); | ||
(fetch as jest.Mock).mockClear(); | ||
(client.refetchQueries as jest.Mock).mockClear(); | ||
}); | ||
|
||
test('should handle successful file upload', async () => { | ||
const mockResponse = { | ||
status: 200, | ||
json: jest.fn().mockResolvedValue({ result: 'Upload success!' }), | ||
}; | ||
(fetch as jest.Mock).mockResolvedValue(mockResponse); | ||
|
||
const { result } = renderHook(() => useFileUpload('test-uuid')); | ||
|
||
await act(async () => { | ||
result.current.handleExperimentFileUpload(new File(['dummy content'], 'test.txt', { type: 'text/plain' })); | ||
}); | ||
|
||
expect(fetch).toHaveBeenCalledWith(`${AQD_FILE_URI}/api/files/test-uuid`, expect.any(Object)); | ||
expect(client.refetchQueries).toHaveBeenCalledWith({ | ||
include: 'active', | ||
}); | ||
expect(setSelectedFileMock).toHaveBeenCalledWith('test.txt'); | ||
expect(toast.success).toHaveBeenCalledWith('Upload success!', { | ||
id: 'upload_success', | ||
}); | ||
}); | ||
|
||
test('should handle failed file upload with error message', async () => { | ||
const mockResponse = { | ||
status: 400, | ||
statusText: 'Bad Request', | ||
json: jest.fn().mockResolvedValue({ detail: 'Upload failed!' }), | ||
}; | ||
(fetch as jest.Mock).mockResolvedValue(mockResponse); | ||
|
||
const { result } = renderHook(() => useFileUpload('test-uuid')); | ||
|
||
await act(async () => { | ||
result.current.handleExperimentFileUpload(new File(['dummy content'], 'test.txt', { type: 'text/plain' })); | ||
}); | ||
|
||
expect(toast.error).toHaveBeenCalledWith('Upload failed!', { | ||
id: 'upload_failed', | ||
}); | ||
}); | ||
|
||
test('should handle failed file upload without error message', async () => { | ||
const mockResponse = { | ||
status: 400, | ||
statusText: 'Bad Request', | ||
json: jest.fn().mockRejectedValue(new Error('Failed to parse JSON')), | ||
}; | ||
(fetch as jest.Mock).mockResolvedValue(mockResponse); | ||
|
||
const { result } = renderHook(() => useFileUpload('test-uuid')); | ||
|
||
await act(async () => { | ||
result.current.handleExperimentFileUpload(new File(['dummy content'], 'test.txt', { type: 'text/plain' })); | ||
}); | ||
|
||
expect(toast.error).toHaveBeenCalledWith('Bad Request', { | ||
id: 'upload_catch', | ||
}); | ||
}); |
Oops, something went wrong.
6f5565e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coverage Report
6f5565e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coverage Report