There are no notes for this item.
Prop | Required? | Type | Default | Description |
---|---|---|---|---|
multiple | No | bool | false | |
buttonText | No | union | 'Add Files' | |
additionalClass | No | string | ||
additionalErrorClass | No | string | ||
triggerClass | No | string | ||
onChange | Yes | func | ||
accept | No | string | ||
name | Yes | string | ||
errorMessage | No | string | ||
mimeTypes | No | string | '' |
<div id="reactMount" data-tpl="errorablefileinput">
<div>
<div class="usa-input-error" role="alert"><label class="usa-input-error-label" for="errorable-file-input-35"></label><span class="usa-input-error-message undefined" role="alert" id="errorable-file-input-35-error-message"><span class="sr-only">Error</span> Error message</span><label role="button"
tabindex="0" for="errorable-file-input-35" class="usa-button usa-button-secondary">Upload some files</label><input type="file" multiple="" style="display:none;" accept="String" id="errorable-file-input-35" name="Name" /></div>
</div>
</div>
<script>
window.currentProps = {
"package": {
"name": "department-of-veteran-affairs/jean-pants",
"version": "0.1.0"
},
"assetPath": "/design-system/",
"isProduction": true,
"componentSourcePath": "./ErrorableFileInput.jsx",
"name": "Name",
"multiple": true,
"buttonText": "Upload some files",
"accept": "String",
"errorMessage": "Error message"
}
</script>
import React from 'react';
import ErrorableFileInput from './ErrorableFileInput';
export default function ErrorableFileInputExample(props) {
return (
<ErrorableFileInput
name={props.name}
multiple={props.multiple}
buttonText={props.buttonText}
accept={props.accept}
errorMessage={props.errorMessage}
onChange={(e) => e}/>
);
}
package:
name: department-of-veteran-affairs/jean-pants
version: 0.1.0
assetPath: /design-system/
isProduction: true
componentSourcePath: ./ErrorableFileInput.jsx
name: Name
multiple: true
buttonText: Upload some files
accept: String
errorMessage: Error message
/* eslint-disable jsx-a11y/no-noninteractive-element-to-interactive-role */
import PropTypes from 'prop-types';
import React from 'react';
import _ from 'lodash';
class ErrorableFileInput extends React.Component {
constructor() {
super();
this.handleChange = this.handleChange.bind(this);
}
componentWillMount() {
this.inputId = _.uniqueId('errorable-file-input-');
}
handleChange(domEvent) {
this.props.onChange(domEvent.target.files);
// clear the original input, otherwise events will be triggered
// with empty file arrays and sometimes uploading a file twice will
// not work
domEvent.target.value = null; // eslint-disable-line no-param-reassign
}
render() {
let errorSpan = '';
let errorSpanId = undefined;
let inputErrorClass = undefined;
let labelErrorClass = undefined;
if (this.props.errorMessage) {
errorSpanId = `${this.inputId}-error-message`;
errorSpan = (
<span className={`usa-input-error-message ${this.props.additionalErrorClass}`} role="alert" id={errorSpanId}>
<span className="sr-only">Error</span> {this.props.errorMessage}
</span>
);
inputErrorClass = 'usa-input-error';
labelErrorClass = 'usa-input-error-label';
}
// Calculate required
let requiredSpan = undefined;
if (this.props.required) {
requiredSpan = <span className="form-required-span">*</span>;
}
return (
<div className={this.props.additionalClass}>
<div className={inputErrorClass} role="alert">
<label
className={labelErrorClass}
htmlFor={this.inputId}>
{this.props.label}
{requiredSpan}
</label>
{errorSpan}
<label
role="button"
tabIndex="0"
htmlFor={this.inputId}
aria-describedby={this.props['aria-describedby']}
className={this.props.triggerClass || 'usa-button usa-button-secondary'}>
{this.props.buttonText}
</label>
<input
multiple={this.props.multiple}
style={{ display: 'none' }}
type="file"
accept={this.props.accept}
id={this.inputId}
name={this.props.name}
onChange={this.handleChange}/>
</div>
</div>
);
}
}
ErrorableFileInput.propTypes = {
/* accepts multiple files */
multiple: PropTypes.bool,
/* label for the button */
buttonText: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element]),
/* additional CSS classes */
additionalClass: PropTypes.string,
additionalErrorClass: PropTypes.string,
triggerClass: PropTypes.string,
/* function to handle interaction */
onChange: PropTypes.func.isRequired,
/* ? */
accept: PropTypes.string,
/* input name */
name: PropTypes.string.isRequired,
/* message for error state */
errorMessage: PropTypes.string,
/* file types */
mimeTypes: PropTypes.string,
};
ErrorableFileInput.defaultProps = {
buttonText: 'Add Files',
mimeTypes: '',
multiple: false
};
export default ErrorableFileInput;
import React from 'react';
import { shallow } from 'enzyme';
import { axeCheck } from '../../../../../lib/testing/helpers';
import { expect } from 'chai';
import sinon from 'sinon';
import ErrorableFileInput from './ErrorableFileInput';
describe('<ErrorableFileInput>', () => {
it('no error styles when errorMessage undefined', () => {
const tree = shallow(
<ErrorableFileInput buttonText="my label" onChange={(_update) => {}}/>
);
// No error classes.
expect(tree.find('.usa-input-error')).to.have.lengthOf(0);
expect(tree.find('.usa-input-error-label')).to.have.lengthOf(0);
expect(tree.find('.usa-input-error-message')).to.have.lengthOf(0);
});
it('has error styles when errorMessage is set', () => {
const tree = shallow(
<ErrorableFileInput buttonText="my label" errorMessage="error message" onChange={(_update) => {}}/>
);
expect(tree.find('.usa-input-error-message').text()).to.equal('Error error message');
});
it('onChange fires and clears input', () => {
const onChange = sinon.spy();
const tree = shallow(
<ErrorableFileInput buttonText="my label" onChange={onChange}/>
);
const event = {
target: {
files: [{}],
value: 'asdfasdf'
}
};
tree.instance().handleChange(event);
expect(onChange.called).to.be.true;
expect(event.target.value).to.be.null;
});
it('passes aXe check', () => {
const check = axeCheck(<ErrorableFileInput buttonText="my label"/>);
return check;
});
});