There are no notes for this item.
Prop | Required? | Type | Default | Description |
---|---|---|---|---|
required | No | bool | ||
validation | No | { valid: bool, message: string } |
||
label | No | string | ||
name | Yes | string | ||
date | Yes | { day: { value: string, dirty: bool }, month: { value: string, dirty: bool }, year: { value: string, dirty: bool } } |
||
onValueChange | Yes | func | ||
toolTipText | No | string | ||
requiredMessage | No | string | 'Please provide a response' | |
invalidMessage | No | string | 'Please provide a valid date' |
<div id="reactMount" data-tpl="errorabledate">
<div class="false"><label>Please tell us a date<span class="form-required-span">*</span></label>
<div>
<div class="usa-date-of-birth row">
<div class="form-datefield-month">
<div><label for="errorable-select-31">Month</label><select id="errorable-select-31" name="Errorable DateMonth" autocomplete="false"><option value=""></option><option value="1">Jan</option><option value="2">Feb</option><option value="3">Mar</option><option selected="" value="4">Apr</option><option value="5">May</option><option value="6">Jun</option><option value="7">Jul</option><option value="8">Aug</option><option value="9">Sep</option><option value="10">Oct</option><option value="11">Nov</option><option value="12">Dec</option></select></div>
</div>
<div class="form-datefield-day">
<div><label for="errorable-select-32">Day</label><select id="errorable-select-32" name="Errorable DateDay" autocomplete="false"><option value=""></option><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option><option value="6">6</option><option value="7">7</option><option value="8">8</option><option value="9">9</option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option selected="" value="14">14</option><option value="15">15</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="21">21</option><option value="22">22</option><option value="23">23</option><option value="24">24</option><option value="25">25</option><option value="26">26</option><option value="27">27</option><option value="28">28</option><option value="29">29</option><option value="30">30</option></select></div>
</div>
<div class="usa-datefield usa-form-group usa-form-group-year">
<div><label for="errorable-number-input-33">Year</label><input type="number" min="1900" max="2119" autocomplete="false" id="errorable-number-input-33" name="Errorable DateYear" pattern="[0-9]{4}" value="1983" /></div>
</div>
</div>
<div><button>More Info</button>
<div aria-hidden="true">This is a tool tip<br/><button>Close</button></div>
</div>
</div>
</div>
</div>
<script>
window.currentProps = {
"package": {
"name": "department-of-veteran-affairs/jean-pants",
"version": "0.1.0"
},
"assetPath": "/design-system/",
"isProduction": true,
"componentSourcePath": "./ErrorableDate.jsx",
"required": true,
"label": "Please tell us a date",
"name": "Errorable Date",
"date": {
"day": {
"value": "14",
"dirty": null
},
"month": {
"value": "4",
"dirty": null
},
"year": {
"value": "1983",
"dirty": null
}
},
"toolTipText": "This is a tool tip",
"requiredMessage": "Please provide a response",
"invalidMessage": "Please provide a valid date"
}
</script>
import React from 'react';
import ErrorableDate from './ErrorableDate';
export default function ErrorableDateExample(props) {
return (
<ErrorableDate
label={props.label}
onValueChange={(e) => {e.target.checked}}
{...props}
/>);
}
package:
name: department-of-veteran-affairs/jean-pants
version: 0.1.0
assetPath: /design-system/
isProduction: true
componentSourcePath: ./ErrorableDate.jsx
required: true
label: Please tell us a date
name: Errorable Date
date:
day:
value: '14'
dirty: null
month:
value: '4'
dirty: null
year:
value: '1983'
dirty: null
toolTipText: This is a tool tip
requiredMessage: Please provide a response
invalidMessage: Please provide a valid date
/* eslint-disable jsx-a11y/label-has-for */
import PropTypes from 'prop-types';
import React from 'react';
import _ from 'lodash';
import { set } from 'lodash/fp';
import moment from 'moment';
import ErrorableSelect from '../ErrorableSelect/ErrorableSelect';
import ErrorableNumberInput from '../ErrorableNumberInput/ErrorableNumberInput';
import ToolTip from '../../../Tooltip/Tooltip';
import {
isDirtyDate,
isValidPartialDate,
isNotBlankDateField,
validateCustomFormComponent
} from '../../../../utils/validations';
import { months, days } from '../../../../utils/options-for-select.js';
/**
* A date input field that accepts values for month and year
*/
class ErrorableDate extends React.Component {
constructor() {
super();
this.handleChange = this.handleChange.bind(this);
}
componentWillMount() {
this.id = _.uniqueId('date-input-');
}
handleChange(path, update) {
let date = set(path, update, this.props.date);
if (!date.month.value) {
date = set('day.value', '', date);
}
this.props.onValueChange(date);
}
render() {
const { day, month, year } = this.props.date;
let daysForSelectedMonth = [];
if (month.value) {
daysForSelectedMonth = days[month.value];
}
// we want to do validations in a specific order, so we show the message
// that makes the most sense to the user
let isValid = true;
let errorMessage;
if (isDirtyDate(this.props.date)) {
// make sure the user enters a full date first, if required
if (this.props.required && !isNotBlankDateField(this.props.date)) {
isValid = false;
errorMessage = this.props.requiredMessage;
// make sure the user has entered a minimally valid date
} else if (!isValidPartialDate(day.value, month.value, year.value)) {
isValid = false;
errorMessage = this.props.invalidMessage;
} else {
const validationResult = validateCustomFormComponent(
this.props.validation
);
isValid = validationResult.valid;
errorMessage = validationResult.message;
}
}
let errorSpanId;
let errorSpan = '';
if (!isValid) {
errorSpanId = `${this.inputId}-error-message`;
errorSpan = (
<span className="usa-input-error-message" role="alert" id={errorSpanId}>
<span className="sr-only">Error</span> {errorMessage}
</span>
);
}
// Adds ToolTip if text is provided.
let toolTip;
if (this.props.toolTipText) {
toolTip = (
<ToolTip
tabIndex={this.props.tabIndex}
toolTipText={this.props.toolTipText}/>
);
}
return (
<div className={!isValid && 'input-error-date'}>
<label>
{this.props.label ? this.props.label : 'Date of birth'}
{this.props.required && <span className="form-required-span">*</span>}
</label>
{errorSpan}
<div
className={isValid ? undefined : 'usa-input-error form-error-date'}>
<div className="usa-date-of-birth row">
<div className="form-datefield-month">
<ErrorableSelect
errorMessage={isValid ? undefined : ''}
autocomplete="false"
label="Month"
name={`${this.props.name}Month`}
options={months}
value={month}
onValueChange={update => {
this.handleChange('month', update);
}}/>
</div>
<div className="form-datefield-day">
<ErrorableSelect
errorMessage={isValid ? undefined : ''}
autocomplete="false"
label="Day"
name={`${this.props.name}Day`}
options={daysForSelectedMonth}
value={day}
onValueChange={update => {
this.handleChange('day', update);
}}/>
</div>
<div className="usa-datefield usa-form-group usa-form-group-year">
<ErrorableNumberInput
errorMessage={isValid ? undefined : ''}
autocomplete="false"
label="Year"
name={`${this.props.name}Year`}
max={moment()
.add(100, 'year')
.year()}
min="1900"
pattern="[0-9]{4}"
field={year}
onValueChange={update => {
this.handleChange('year', update);
}}/>
</div>
</div>
{toolTip}
</div>
</div>
);
}
}
ErrorableDate.propTypes = {
/* Render marker indicating field is required. */
required: PropTypes.bool,
/* object or array. Result of custom validation. Should include a valid prop and a message prop */
validation: PropTypes.shape({
valid: PropTypes.bool,
message: PropTypes.string
}),
/* Label for entire question. */
label: PropTypes.string,
/* Used to create unique name attributes for each input. */
name: PropTypes.string.isRequired,
/* Date value. Should have month, day, and year props */
date: PropTypes.shape({
day: PropTypes.shape({
value: PropTypes.string,
dirty: PropTypes.bool
}),
month: PropTypes.shape({
value: PropTypes.string,
dirty: PropTypes.bool
}),
year: PropTypes.shape({
value: PropTypes.string,
dirty: PropTypes.bool
})
}).isRequired,
/* a function with this prototype: (newValue) */
onValueChange: PropTypes.func.isRequired,
/* String with help text for user. */
toolTipText: PropTypes.string,
requiredMessage: PropTypes.string,
invalidMessage: PropTypes.string
};
ErrorableDate.defaultProps = {
requiredMessage: 'Please provide a response',
invalidMessage: 'Please provide a valid date'
};
export default ErrorableDate;
import React from 'react';
// import SkinDeep from 'skin-deep';
import { expect } from 'chai';
import { shallow } from 'enzyme';
import { axeCheck } from '../../../../../lib/testing/helpers';
import ErrorableDate from './ErrorableDate';
import { makeField } from '../../../../helpers/fields.js';
describe('<ErrorableDate>', () => {
it('renders input elements', () => {
const date = {
day: makeField(1),
month: makeField(12),
year: makeField(2010)
};
const tree = shallow(
<ErrorableDate date={date} onValueChange={(_update) => {}}/>);
expect(tree.find('ErrorableNumberInput')).to.have.lengthOf(1);
expect(tree.find('ErrorableSelect')).to.have.lengthOf(2);
});
it('displays required message', () => {
const date = {
day: makeField(''),
month: makeField(''),
year: makeField('')
};
date.year.dirty = true;
date.month.dirty = true;
date.day.dirty = true;
const tree = shallow(
<ErrorableDate required date={date} onValueChange={(_update) => {}}/>);
expect(tree.find('.usa-input-error')).not.to.be.empty;
expect(tree.find('.usa-input-error-message').text()).to.equal('Error Please provide a response');
});
it('displays invalid message', () => {
const date = {
day: makeField(''),
month: makeField(''),
year: makeField('1890')
};
date.year.dirty = true;
date.month.dirty = true;
date.day.dirty = true;
const tree = shallow(
<ErrorableDate date={date} onValueChange={(_update) => {}}/>);
expect(tree.find('.usa-input-error')).not.to.be.empty;
expect(tree.find('.usa-input-error-message').text()).to.equal('Error Please provide a valid date');
});
it('does not show invalid message for month year date', () => {
const date = {
day: makeField(''),
month: makeField('12'),
year: makeField('2003')
};
date.year.dirty = true;
date.month.dirty = true;
date.day.dirty = true;
const tree = shallow(
<ErrorableDate date={date} onValueChange={(_update) => {}}/>);
expect(tree.find('.usa-input-error')).to.have.length(0);
});
it('displays custom message', () => {
const date = {
day: makeField('3'),
month: makeField(''),
year: makeField('2010')
};
date.year.dirty = true;
date.month.dirty = true;
date.day.dirty = true;
const tree = shallow(
<ErrorableDate date={date} validation={{ valid: false, message: 'Test' }} onValueChange={(_update) => {}}/>);
expect(tree.find('.usa-input-error')).not.to.be.empty;
expect(tree.find('.usa-input-error-message').text()).to.equal('Error Test');
});
it('displays custom message from array', () => {
const date = {
day: makeField('3'),
month: makeField(''),
year: makeField('2010')
};
date.year.dirty = true;
date.month.dirty = true;
date.day.dirty = true;
const tree = shallow(
<ErrorableDate
date={date}
validation={[
{ valid: true, message: 'NotShownMessage' },
{ valid: false, message: 'Test' }
]}
onValueChange={(_update) => {}}/>
);
expect(tree.find('.usa-input-error')).not.to.be.empty;
expect(tree.find('.usa-input-error-message').text()).to.equal('Error Test');
});
it('should pass aXe check', () => {
const date = {
day: makeField(''),
month: makeField(''),
year: makeField('1890')
};
return axeCheck(<ErrorableDate date={date} onValueChange={(_update) => {}}/>);
});
it('should pass aXe check when errorMessage is set', () => {
const date = {
day: makeField(''),
month: makeField(''),
year: makeField('1890')
};
date.year.dirty = true;
date.month.dirty = true;
date.day.dirty = true;
return axeCheck(<ErrorableDate date={date} onValueChange={(_update) => {}}/>);
});
});