Skip to content

Commit

Permalink
refactor: convert FilePicker into functional component
Browse files Browse the repository at this point in the history
  • Loading branch information
chaofan232 committed Oct 22, 2021
1 parent 616b6fc commit f7107de
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 119 deletions.
201 changes: 94 additions & 107 deletions src/components/FilePicker/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,122 +6,109 @@ import './styles.scss';

const baseClass = 'filepicker-component';

class FilePickerComponent extends React.PureComponent {
static propTypes = {
/**
* determines if the filePicker is disabled
*/
disabled: PropTypes.bool,
/**
* data-test-selector of the filePicker
*/
dts: PropTypes.string,
/**
* determines what file types the user can pick from the file input dialog box
*/
filter: PropTypes.string,
/**
* determines if the filePicker is highlighted or not
*/
isHighlighted: PropTypes.bool,
/**
* the label to be displayed
*/
label: PropTypes.string,
/**
* function called when onRemove event is fired
*/
onRemove: PropTypes.func,
/**
* function called when onSelect event is fired
*/
onSelect: PropTypes.func.isRequired,
/**
* determines the placeholder when no date is selected
*/
placeholder: PropTypes.string,
};

static defaultProps = {
isHighlighted: false,
label: 'Select',
placeholder: 'No file selected',
disabled: false,
};

constructor(props) {
super(props);

this.fileInput = React.createRef();
}
const FilePicker = ({ filter, dts, placeholder, onSelect, onRemove, isHighlighted, disabled, label }) => {
const fileInputRef = React.useRef();
const [fileName, setFileName] = React.useState('');

state = {
isFileSelected: false,
fileName: '',
};

onChange = (changeEvent) => {
if (!this.state.isFileSelected) {
this.setState({ isFileSelected: true, fileName: changeEvent.target.files[0].name });
this.props.onSelect(changeEvent.target.files[0]);
const onChange = (event) => {
if (!fileName) {
setFileName(event.target.files[0].name);
onSelect(event.target.files[0]);
}
};

onUploadBtnClick = () => {
this.fileInput.current.click();
const onUploadBtnClick = () => {
fileInputRef.current.click();
};

removeFile = () => {
this.fileInput.current.value = null;
this.setState({ isFileSelected: false, fileName: '' });
if (this.props.onRemove) {
this.props.onRemove();
}
const removeFile = () => {
fileInputRef.current.value = null;
setFileName('');
onRemove?.();
};

render() {
const mainClass = classNames({ [`${baseClass}-highlight`]: this.props.isHighlighted }, baseClass, 'input-group');
const { isFileSelected, fileName } = this.state;
const mainClass = classNames({ [`${baseClass}-highlight`]: isHighlighted }, baseClass, 'input-group');

return (
<div data-testid="file-picker-wrapper" className={mainClass}>
<input
data-testid="file-picker-form-control"
className="form-control"
type="text"
disabled
placeholder={this.props.placeholder}
readOnly="readonly"
value={fileName}
title={fileName}
/>
<div className="input-group-btn">
{isFileSelected ? (
<Button data-testid="file-picker-remove-button" className="remove-file" onClick={this.removeFile}>
×
</Button>
) : null}
<Button
data-testid="file-picker-input-button"
inverse
onClick={this.onUploadBtnClick}
disabled={this.props.disabled || isFileSelected}
>
<span data-testid="file-picker-input-button-label">{this.props.label}</span>
<input
data-testid="file-picker-input-button-input"
className="file-input"
ref={this.fileInput}
type="file"
onChange={this.onChange}
accept={this.props.filter}
data-test-selector={this.props.dts}
/>
return (
<div data-testid="file-picker-wrapper" className={mainClass}>
<input
data-testid="file-picker-form-control"
className="form-control"
type="text"
disabled
placeholder={placeholder}
readOnly="readonly"
value={fileName}
title={fileName}
/>
<div className="input-group-btn">
{!!fileName ? (
<Button data-testid="file-picker-remove-button" className="remove-file" onClick={removeFile}>
×
</Button>
</div>
) : null}
<Button
data-testid="file-picker-input-button"
inverse
onClick={onUploadBtnClick}
disabled={disabled || !!fileName}
>
<span data-testid="file-picker-input-button-label">{label}</span>
<input
data-testid="file-picker-input-button-input"
className="file-input"
ref={fileInputRef}
type="file"
onChange={onChange}
accept={filter}
data-test-selector={dts}
/>
</Button>
</div>
);
}
}
</div>
);
};

FilePicker.propTypes = {
/**
* determines if the filePicker is disabled
*/
disabled: PropTypes.bool,
/**
* data-test-selector of the filePicker
*/
dts: PropTypes.string,
/**
* determines what file types the user can pick from the file input dialog box
*/
filter: PropTypes.string,
/**
* determines if the filePicker is highlighted or not
*/
isHighlighted: PropTypes.bool,
/**
* the label to be displayed
*/
label: PropTypes.string,
/**
* function called when onRemove event is fired
*/
onRemove: PropTypes.func,
/**
* function called when onSelect event is fired
*/
onSelect: PropTypes.func.isRequired,
/**
* determines the placeholder when no date is selected
*/
placeholder: PropTypes.string,
};

FilePicker.defaultProps = {
isHighlighted: false,
label: 'Select',
placeholder: 'No file selected',
disabled: false,
};

export default FilePickerComponent;
export default FilePicker;
14 changes: 6 additions & 8 deletions src/components/FilePicker/index.spec.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react';
import { render, cleanup, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import FilePickerComponent from '.';
import FilePicker from '.';

afterEach(cleanup);

describe('<FilePicker />', () => {
it('should render with defaults', () => {
const { getByTestId } = render(<FilePickerComponent onSelect={jest.fn()} />);
const { getByTestId } = render(<FilePicker onSelect={jest.fn()} />);
expect(getByTestId('file-picker-wrapper')).toHaveClass('filepicker-component input-group');

expect(getByTestId('file-picker-form-control')).toHaveClass('form-control');
Expand All @@ -20,9 +20,7 @@ describe('<FilePicker />', () => {

it('should show remove button and call `onSelect` when file selected', () => {
const onSelect = jest.fn();
const { getByTestId, queryByTestId } = render(
<FilePickerComponent onSelect={onSelect} dts="test-file-picker-input" />
);
const { getByTestId, queryByTestId } = render(<FilePicker onSelect={onSelect} dts="test-file-picker-input" />);

expect(getByTestId('file-picker-form-control')).toHaveAttribute('title', '');
expect(getByTestId('file-picker-form-control')).toHaveAttribute('placeholder', 'No file selected');
Expand Down Expand Up @@ -52,7 +50,7 @@ describe('<FilePicker />', () => {
it('should upload a file when input-button is clicked', () => {
const onSelect = jest.fn();
const file = new File(['test'], 'test.png', { type: 'image/png' });
const { getByTestId } = render(<FilePickerComponent onSelect={onSelect} />);
const { getByTestId } = render(<FilePicker onSelect={onSelect} />);

userEvent.upload(getByTestId('file-picker-input-button-input'), file);
expect(onSelect).toHaveBeenCalledTimes(1);
Expand All @@ -68,7 +66,7 @@ describe('<FilePicker />', () => {
it('should remove file selected when remove file button is clicked', () => {
const onSelect = jest.fn();
const file = new File(['test'], 'test.png', { type: 'image/png' });
const { getByTestId } = render(<FilePickerComponent onSelect={onSelect} />);
const { getByTestId } = render(<FilePicker onSelect={onSelect} />);

userEvent.upload(getByTestId('file-picker-input-button-input'), file);
expect(onSelect).toHaveBeenCalledTimes(1);
Expand All @@ -84,7 +82,7 @@ describe('<FilePicker />', () => {
const onSelect = jest.fn();
const onRemove = jest.fn();
const file = new File(['test'], 'test.png', { type: 'image/png' });
const { getByTestId } = render(<FilePickerComponent onSelect={onSelect} onRemove={onRemove} />);
const { getByTestId } = render(<FilePicker onSelect={onSelect} onRemove={onRemove} />);

userEvent.upload(getByTestId('file-picker-input-button-input'), file);
expect(getByTestId('file-picker-form-control')).toHaveAttribute('title', 'test.png');
Expand Down
3 changes: 1 addition & 2 deletions src/components/FilePicker/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
.remove-file {
background-color: $color-disabled;
border: 0;
box-shadow: none;
height: 28px;
margin-right: 0;
}

Expand All @@ -21,6 +19,7 @@

.aui--button {
box-shadow: none;
height: 28px;

&:focus {
background-color: inherit;
Expand Down
4 changes: 2 additions & 2 deletions www/containers/props.json
Original file line number Diff line number Diff line change
Expand Up @@ -1455,15 +1455,15 @@
"src/components/FilePicker/index.jsx": [
{
"description": "",
"displayName": "FilePickerComponent",
"displayName": "FilePicker",
"methods": [
{
"name": "onChange",
"docblock": null,
"modifiers": [],
"params": [
{
"name": "changeEvent",
"name": "event",
"type": null
}
],
Expand Down

0 comments on commit f7107de

Please sign in to comment.