-
Notifications
You must be signed in to change notification settings - Fork 300
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ameerul / P2PS-1732 / Refactor FileUploaderComponent (#10650)
* chore: migrate FileUploaderComponent to typescript * fix: failing test cases * chore: added suggestions * chore: moved file-dropzone to p2p package * chore: moved truncateFileName from shared to utils file and added test cases * fix: regex potential DDos issue * chore: added suggestions * Update packages/p2p/src/components/file-dropzone/file-dropzone.tsx Co-authored-by: Niloofar Sadeghi <93518187+niloofar-deriv@users.noreply.github.com> * fix: import for types from file-dropzone * fix: flickering Icon on tab switch --------- Co-authored-by: Niloofar Sadeghi <93518187+niloofar-deriv@users.noreply.github.com>
- Loading branch information
1 parent
76d67b8
commit a8451d5
Showing
21 changed files
with
659 additions
and
134 deletions.
There are no files selected for viewing
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
30 changes: 30 additions & 0 deletions
30
packages/p2p/src/components/file-dropzone/fade-in-message/fade-in-message.scss
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,30 @@ | ||
@import '../file-dropzone.scss'; | ||
|
||
.fade-in-message { | ||
@include file-dropzone-message; | ||
|
||
&--enter-done { | ||
opacity: 1; | ||
transform: translate3d(0, 0, 0); | ||
} | ||
|
||
&--enter { | ||
opacity: 0; | ||
transform: translate3d(0, -16px, 0); | ||
} | ||
|
||
&--enter-active { | ||
opacity: 1; | ||
transform: translate3d(0, 0, 0); | ||
} | ||
|
||
&--exit { | ||
opacity: 1; | ||
transform: translate3d(0, 0, 0); | ||
} | ||
|
||
&--exit-active { | ||
opacity: 0; | ||
transform: translate3d(0, -16px, 0); | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
packages/p2p/src/components/file-dropzone/fade-in-message/fade-in-message.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,46 @@ | ||
import React from 'react'; | ||
import { CSSTransition } from 'react-transition-group'; | ||
import { Text } from '@deriv/components'; | ||
|
||
type TFadeInMessage = { | ||
is_visible: boolean; | ||
color?: string; | ||
key?: string; | ||
timeout: number; | ||
no_text?: boolean; | ||
}; | ||
|
||
const FadeInMessage = ({ | ||
children, | ||
color, | ||
is_visible, | ||
key, | ||
no_text, | ||
timeout, | ||
}: React.PropsWithChildren<TFadeInMessage>) => ( | ||
<CSSTransition | ||
appear | ||
classNames={{ | ||
appear: 'fade-in-message--enter', | ||
enter: 'fade-in-message--enter', | ||
enterActive: 'fade-in-message--enter-active', | ||
enterDone: 'fade-in-message--enter-done', | ||
exit: 'fade-in-message--exit', | ||
exitActive: 'fade-in-message--exit-active', | ||
}} | ||
in={is_visible} | ||
key={key} | ||
timeout={timeout} | ||
unmountOnExit | ||
> | ||
{no_text ? ( | ||
<div className='fade-in-message'>{children}</div> | ||
) : ( | ||
<Text align='center' className='fade-in-message' color={color || 'general'} size='xxs'> | ||
{children} | ||
</Text> | ||
)} | ||
</CSSTransition> | ||
); | ||
|
||
export default FadeInMessage; |
4 changes: 4 additions & 0 deletions
4
packages/p2p/src/components/file-dropzone/fade-in-message/index.ts
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,4 @@ | ||
import FadeInMessage from './fade-in-message'; | ||
import './fade-in-message.scss'; | ||
|
||
export default FadeInMessage; |
74 changes: 74 additions & 0 deletions
74
packages/p2p/src/components/file-dropzone/file-dropzone.scss
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,74 @@ | ||
@mixin file-dropzone-message { | ||
display: block; | ||
max-width: 168px; | ||
opacity: 1; | ||
pointer-events: none; | ||
position: absolute; | ||
transform: translate3d(0, 0, 0); | ||
transition: transform 0.25s ease, opacity 0.15s linear; | ||
|
||
@include mobile { | ||
max-width: 26rem; | ||
} | ||
} | ||
|
||
.file-dropzone { | ||
border-radius: $BORDER_RADIUS; | ||
border: 1px dashed var(--border-normal); | ||
color: var(--text-prominent); | ||
cursor: pointer; | ||
font-size: 1.25rem; | ||
font-weight: bold; | ||
height: 14rem; | ||
padding: 2rem; | ||
position: relative; | ||
text-align: center; | ||
width: 100%; | ||
|
||
&__content { | ||
align-items: center; | ||
display: flex; | ||
height: 100%; | ||
justify-content: center; | ||
left: 0; | ||
position: absolute; | ||
top: 0; | ||
width: 100%; | ||
} | ||
|
||
&__filename { | ||
width: 100%; | ||
} | ||
|
||
&__message { | ||
@include file-dropzone-message; | ||
} | ||
|
||
&--has-file { | ||
border-style: solid; | ||
border-color: var(--status-success); | ||
background-color: var(--general-section-1); | ||
} | ||
|
||
&--has-error { | ||
border-style: solid; | ||
border-color: var(--status-danger); | ||
|
||
.dc-file-dropzone__filename { | ||
margin-top: -3em; | ||
} | ||
} | ||
|
||
&--is-noclick { | ||
cursor: auto; | ||
} | ||
|
||
&:hover, | ||
&:focus { | ||
outline: 0; | ||
} | ||
|
||
&:hover { | ||
background-color: rgba(0, 0, 0, 0.025); | ||
} | ||
} |
126 changes: 126 additions & 0 deletions
126
packages/p2p/src/components/file-dropzone/file-dropzone.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,126 @@ | ||
import React from 'react'; | ||
import classNames from 'classnames'; | ||
import Dropzone, { DropzoneRef } from 'react-dropzone'; | ||
import { Text } from '@deriv/components'; | ||
import { TFileDropzone } from 'Types'; | ||
import { truncateFileName } from 'Utils/file-uploader'; | ||
import FadeInMessage from './fade-in-message'; | ||
import PreviewSingle from './preview-single'; | ||
|
||
const FileDropzone = ({ className, noClick = false, ...props }: TFileDropzone) => { | ||
const { | ||
accept, | ||
error_message, | ||
filename_limit, | ||
hover_message, | ||
max_size, | ||
message, | ||
multiple, | ||
onDropAccepted, | ||
onDropRejected, | ||
validation_error_message, | ||
value, | ||
} = props; | ||
|
||
const RenderErrorMessage = React.useCallback( | ||
({ open }: DropzoneRef) => { | ||
if (noClick && typeof message === 'function') return <>{message(open)}</>; | ||
|
||
return <>{message}</>; | ||
}, | ||
[message, noClick] | ||
); | ||
|
||
const RenderValidationErrorMessage = React.useCallback( | ||
({ open }: DropzoneRef) => { | ||
if (typeof validation_error_message === 'function') return <>{validation_error_message(open)}</>; | ||
|
||
return <>{validation_error_message}</>; | ||
}, | ||
[validation_error_message] | ||
); | ||
|
||
const dropzone_ref = React.useRef(null); | ||
|
||
return ( | ||
<Dropzone | ||
// accept prop is same as native HTML5 input accept - e.g - 'image/png' | ||
accept={accept} | ||
// set maximum size limit for file, in bytes (binary) | ||
maxSize={max_size} | ||
// allow multiple uploads | ||
multiple={multiple || false} | ||
// sends back accepted files array | ||
onDropAccepted={onDropAccepted} | ||
// sends back rejected files array | ||
onDropRejected={onDropRejected} | ||
noClick={noClick} | ||
> | ||
{({ getRootProps, getInputProps, isDragAccept, isDragActive, isDragReject, open }) => ( | ||
<div | ||
{...getRootProps()} | ||
className={classNames('file-dropzone', className, { | ||
'file-dropzone--is-active': isDragActive, | ||
'file-dropzone--has-file': isDragActive || value.length > 0, | ||
'file-dropzone--has-error': (isDragReject || !!validation_error_message) && !isDragAccept, | ||
'file-dropzone--is-noclick': noClick, | ||
})} | ||
ref={dropzone_ref} | ||
> | ||
<input {...getInputProps()} data-testid='dt_file_upload_input' /> | ||
<div className='file-dropzone__content'> | ||
<FadeInMessage | ||
// default message when not on hover or onDrag | ||
is_visible={!isDragActive && !!message && value.length < 1 && !validation_error_message} | ||
timeout={150} | ||
no_text={noClick} | ||
> | ||
<RenderErrorMessage open={open} /> | ||
</FadeInMessage> | ||
<FadeInMessage | ||
// message shown on hover if files are accepted onDrag | ||
is_visible={isDragActive && !isDragReject} | ||
timeout={150} | ||
> | ||
{hover_message} | ||
</FadeInMessage> | ||
{/* Handle cases for displaying multiple files and single filenames */} | ||
{multiple && value.length > 0 && !validation_error_message | ||
? value.map((file, idx) => ( | ||
<Text | ||
size='xxs' | ||
weight='bold' | ||
align='center' | ||
key={file.name} | ||
className='file-dropzone__filename' | ||
> | ||
{filename_limit ? truncateFileName(file, filename_limit) : file.name} | ||
</Text> | ||
)) | ||
: value[0] && | ||
!isDragActive && | ||
!validation_error_message && <PreviewSingle dropzone_ref={dropzone_ref} {...props} />} | ||
<FadeInMessage | ||
// message shown if there are errors with the dragged file | ||
is_visible={isDragReject} | ||
timeout={150} | ||
color='loss-danger' | ||
> | ||
{error_message} | ||
</FadeInMessage> | ||
<FadeInMessage | ||
// message shown on if there are validation errors with file uploaded | ||
is_visible={!!validation_error_message && !isDragActive} | ||
timeout={150} | ||
color='loss-danger' | ||
> | ||
<RenderValidationErrorMessage open={open} /> | ||
</FadeInMessage> | ||
</div> | ||
</div> | ||
)} | ||
</Dropzone> | ||
); | ||
}; | ||
|
||
export default FileDropzone; |
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,4 @@ | ||
import FileDropzoneComponent from './file-dropzone'; | ||
import './file-dropzone.scss'; | ||
|
||
export default FileDropzoneComponent; |
30 changes: 30 additions & 0 deletions
30
packages/p2p/src/components/file-dropzone/preview-single/__tests__/preview-single.spec.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,30 @@ | ||
import React from 'react'; | ||
import { render, screen } from '@testing-library/react'; | ||
import PreviewSingle from '../preview-single'; | ||
|
||
describe('<PreviewSingle />', () => { | ||
const props = { | ||
dropzone_ref: React.createRef(), | ||
error_message: '', | ||
hover_message: '', | ||
onClickClose: jest.fn(), | ||
value: [] as File[], | ||
}; | ||
|
||
it('should render the Text component if preview_single is false', () => { | ||
const file: File = new File(['hello'], 'hello.png', { type: 'image/png' }); | ||
props.value = [file]; | ||
|
||
render(<PreviewSingle {...props} />); | ||
|
||
expect(screen.getByText('hello.png')).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render the Image component if preview_single is true', () => { | ||
const preview_single = <img data-testid='dt_image' src='hello.png' />; | ||
|
||
render(<PreviewSingle {...props} preview_single={preview_single} />); | ||
|
||
expect(screen.getByTestId('dt_image')).toBeInTheDocument(); | ||
}); | ||
}); |
4 changes: 4 additions & 0 deletions
4
packages/p2p/src/components/file-dropzone/preview-single/index.ts
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,4 @@ | ||
import PreviewSingle from './preview-single'; | ||
import './preview-single.scss'; | ||
|
||
export default PreviewSingle; |
11 changes: 11 additions & 0 deletions
11
packages/p2p/src/components/file-dropzone/preview-single/preview-single.scss
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,11 @@ | ||
@import '../file-dropzone.scss'; | ||
|
||
.preview-single { | ||
&__filename { | ||
width: 100%; | ||
} | ||
|
||
&__message { | ||
@include file-dropzone-message; | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
packages/p2p/src/components/file-dropzone/preview-single/preview-single.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,32 @@ | ||
import React, { RefObject } from 'react'; | ||
import { Text } from '@deriv/components'; | ||
import { TFileDropzone } from 'Types'; | ||
import { truncateFileName } from 'Utils/file-uploader'; | ||
|
||
type TPreviewSingle = { | ||
dropzone_ref: RefObject<HTMLElement>; | ||
} & TFileDropzone; | ||
|
||
const PreviewSingle = (props: TPreviewSingle) => { | ||
const { dropzone_ref, filename_limit, preview_single, value } = props; | ||
|
||
if (preview_single) { | ||
return <div className='preview-single__message'>{preview_single}</div>; | ||
} | ||
|
||
return ( | ||
<Text | ||
align='center' | ||
className='preview-single__filename' | ||
size='xxs' | ||
styles={{ | ||
maxWidth: `calc(${dropzone_ref.current?.offsetWidth || 365}px - 3.2rem)`, | ||
}} | ||
weight='bold' | ||
> | ||
{filename_limit ? truncateFileName(value[0], filename_limit) : value[0].name} | ||
</Text> | ||
); | ||
}; | ||
|
||
export default PreviewSingle; |
Oops, something went wrong.