Skip to content

Commit

Permalink
Create CustomFileInput based on existing CustomInput.
Browse files Browse the repository at this point in the history
Fix failing tests due to conversion from a React
functional component to a React Class component.

This fixes reactstrap#1460.
  • Loading branch information
David Ellingsworth authored and David Ellingsworth committed Apr 15, 2019
1 parent 3df7c03 commit c93dab0
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 6 deletions.
112 changes: 112 additions & 0 deletions src/CustomFileInput.js
@@ -0,0 +1,112 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules } from './utils';

const propTypes = {
className: PropTypes.string,
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
label: PropTypes.node,
valid: PropTypes.bool,
invalid: PropTypes.bool,
bsSize: PropTypes.string,
htmlFor: PropTypes.string,
cssModule: PropTypes.object,
onChange: PropTypes.func,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.array, PropTypes.func]),
innerRef: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string,
PropTypes.func,
])
};

class CustomFileInput extends React.Component {
constructor(props) {
super(props);

this.state = {
files:null,
};

this.onChange = this.onChange.bind(this);
}

onChange(e) {
let input = e.target;
let {onChange} = this.props;
let files = this.getSelectedFiles(input);

if (typeof(onChange) === 'function') {
onChange(...arguments);
}

this.setState({files})
}

getSelectedFiles(input) {
let {multiple} = this.props;

if (multiple && input.files) {
let files = [].slice.call(input.files);

return files.map(file => file.name).join(', ');
}

if (input.value.indexOf('fakepath') !== -1) {
let parts = input.value.split('\\');

return parts[parts.length - 1];
}

return input.value;
}

render() {
const {
className,
label,
valid,
invalid,
cssModule,
children,
bsSize,
innerRef,
htmlFor,
type,
onChange,
...attributes
} = this.props;

const customClass = mapToCssModules(
classNames(
className,
`custom-file`,
bsSize ? `custom-file-${bsSize}` : false,
),
cssModule
);

const validationClassNames = mapToCssModules(
classNames(
invalid && 'is-invalid',
valid && 'is-valid',
),
cssModule
);

const labelHtmlFor = htmlFor || attributes.id;
const {files} = this.state;

return (
<div className={customClass}>
<input type="file" {...attributes} ref={innerRef} className={classNames(validationClassNames, mapToCssModules('custom-file-input', cssModule))} onChange={this.onChange}/>
<label className={mapToCssModules('custom-file-label', cssModule)} htmlFor={labelHtmlFor}>{files || label || 'Choose file'}</label>
</div>
);
}
}

CustomFileInput.propTypes = propTypes;

export default CustomFileInput;
6 changes: 2 additions & 4 deletions src/CustomInput.js
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules } from './utils';
import CustomFileInput from './CustomFileInput';

const propTypes = {
className: PropTypes.string,
Expand Down Expand Up @@ -58,10 +59,7 @@ function CustomInput(props) {

if (type === 'file') {
return (
<div className={customClass}>
<input {...attributes} ref={innerRef} className={classNames(validationClassNames, mapToCssModules('custom-file-input', cssModule))} />
<label className={mapToCssModules('custom-file-label', cssModule)} htmlFor={labelHtmlFor}>{label || 'Choose file'}</label>
</div>
<CustomFileInput {...props}/>
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/CustomInput.spec.js
Expand Up @@ -212,8 +212,8 @@ describe('Custom Inputs', () => {

describe('CustomFile', () => {
it('should render children in the outer div', () => {
const file = shallow(<CustomInput type="file" />);
expect(file.type()).toBe('div');
const file = mount(<CustomInput type="file" />);
expect(file.find('.custom-file').first().type()).toBe('div');
});

it('should add class is-invalid when invalid is true', () => {
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Expand Up @@ -38,6 +38,7 @@ export CarouselCaption from './CarouselCaption';
export CardSubtitle from './CardSubtitle';
export CardText from './CardText';
export CardTitle from './CardTitle';
export CustomFileInput from './CustomFileInput';
export CustomInput from './CustomInput';
export PopperContent from './PopperContent';
export PopperTargetHelper from './PopperTargetHelper';
Expand Down

0 comments on commit c93dab0

Please sign in to comment.