UNPKG

@availity/reactstrap-validation-date

Version:

Wrapper for react-date-range to work with availity-reactstrap-validation

212 lines (175 loc) 6.58 kB
/* eslint-disable react/default-props-match-prop-types */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { SingleDatePicker } from '@availity/react-dates'; import { InputGroup } from 'reactstrap'; import classNames from 'classnames'; import moment from 'moment'; import '@availity/react-dates/initialize'; import '../polyfills'; import { inputType, isoDateFormat } from 'availity-reactstrap-validation/lib/AvValidator/utils'; import { AvInput } from 'availity-reactstrap-validation'; import { isOutsideRange, limitPropType } from './utils'; class AvDate extends Component { static getDerivedStateFromProps({ value }, prevState) { if (value !== undefined && value !== prevState.value) { return { value }; } return null; } getDateValue = () => { const { value, format } = this.state; const date = moment(value, [isoDateFormat, format, 'MMDDYYYY', 'YYYYMMDD'], true); if (date.isValid()) return date; return null; }; constructor(props, context) { super(props); this.state = {}; // eslint-disable-next-line unicorn/prefer-ternary if (props.type.toLowerCase() === 'date' && inputType.date) { this.state.format = isoDateFormat; } else { this.state.format = props.validate?.date?.format || 'MM/DD/YYYY'; } this.state.focused = false; const { getDefaultValue } = context.FormCtrl; if (getDefaultValue(props.name)) { this.state.value = getDefaultValue(props.name); } } onFocusChange = ({ focused }) => { const { onPickerFocusChange, name } = this.props; const touched = this.context.FormCtrl.isTouched(name); if (!touched && !focused) { this.context.FormCtrl.setTouched(name); } this.setState({ focused }); if (onPickerFocusChange) onPickerFocusChange({ focused }); }; // For updating when we delete the current input onInputChange = async (value) => { const { name, onChange } = this.props; const date = moment(value, [isoDateFormat, this.state.format, 'MMDDYYYY', 'YYYYMMDD'], true); this.context.FormCtrl.getInput(name).getValidatorProps().onChange(value); const isoFormatted = date.format(isoDateFormat); this.setState({ value }, () => { if (date.isValid()) { this.setState({ focused: false, }); this.context.FormCtrl.setTouched(name); if (onChange) onChange(isoFormatted); } }); }; onPickerChange = async (value) => { if (value === null) return; const { format } = this.state; const { name, onChange } = this.props; if (value === null) return; let val = value; if (val instanceof Object && val.isValid()) { val = val.format(format); } this.context.FormCtrl.getInput(name).getValidatorProps().onChange(val); this.setState({ value: val }, () => { if (onChange) onChange(val); }); }; onClose = ({ date }) => { const { format } = this.state; const { onBlur } = this.context.FormCtrl.getInput(this.props.name).getValidatorProps(); onBlur(date && date.format(format)); }; valueParser = (value) => { if (this.state.format === isoDateFormat) return value; const date = moment(value, [this.state.format, 'MMDDYYYY', 'YYYYMMDD'], true); if (date.isValid()) return date.format(isoDateFormat); return value; }; valueFormatter = (value) => { const date = moment(value, [isoDateFormat, this.state.format, 'MMDDYYYY', 'YYYYMMDD'], true); if (date.isValid()) return date.format(this.state.format); return value; }; render() { const { className, datePickerProps, hideIcon, max, min, name, type, validate, ...attributes } = this.props; const { focused, format, value } = this.state; const { FormCtrl } = this.context; const minDate = validate && validate.min ? validate.min.value : min; const maxDate = validate && validate.max ? validate.max.value : max; const pickerId = `${(this.props.id || name).replaceAll(/[^\da-z]/gi, '')}-btn`; const touched = FormCtrl.isTouched(name); const hasError = FormCtrl.hasError(name); const classes = classNames( className, touched ? 'is-touched' : 'is-untouched', FormCtrl.isDirty(name) ? 'is-dirty' : 'is-pristine', FormCtrl.isBad(name) ? 'is-bad-input' : null, hasError ? 'av-invalid' : 'av-valid', touched && hasError && 'is-invalid', !value && 'current-day-highlight' ); const input = ( <AvInput name={name} placeholder={format.toLowerCase()} {...attributes} type="text" min={minDate} max={maxDate} style={{ display: 'none' }} value={value || ''} valueFormatter={this.valueFormatter} valueParser={this.valueParser} validate={{ date: true, ...validate }} /> ); return ( <> {input} <InputGroup disabled={attributes.disabled} className={classes} onChange={({ target }) => target.id === pickerId && this.onInputChange(target.value)} data-testid={`date-input-group-${name}`} > <SingleDatePicker {...datePickerProps} placeholder={format.toLowerCase()} id={pickerId} disabled={attributes.disabled} date={this.getDateValue()} onDateChange={this.onPickerChange} focused={focused} onFocusChange={this.onFocusChange} numberOfMonths={1} isOutsideRange={isOutsideRange(minDate, maxDate, format)} onClose={this.onClose} autoComplete="date" /> </InputGroup> </> ); } } AvDate.propTypes = { ...AvInput.propTypes, /** Minimum date to allow the datepicker and input to take. You can either pass the min here or in the validate object if you want a custom error message with it. */ min: limitPropType, /** Max date to allow the datepicker and input to take. You can either pass the max here or in the validate object if you want a custom error message with it. */ max: limitPropType, /** Function to be run when focus on the input changes. */ onPickerFocusChange: PropTypes.func, /** Props to be spread onto the datepicker component from [react-dates](https://github.com/react-dates/react-dates#singledatepicker). */ datePickerProps: PropTypes.object, }; AvDate.contextTypes = { FormCtrl: PropTypes.object.isRequired, }; AvDate.defaultProps = { type: 'text', datePickerProps: {}, }; export default AvDate;