UNPKG

@navinc/base-react-components

Version:
183 lines (167 loc) 4.7 kB
import React from 'react' import Input from './input.js' import Select from './select.js' import Copy from './copy.js' import { Errors, Err } from './form-elements/shared.js' import VMasker from 'vanilla-masker' import styled from 'styled-components' import PropTypes from 'prop-types' import { noop } from '@navinc/utils' export const DatePickerWrapper = styled.div` div > ${Copy} { text-align: left; } ` const StyledCopy = styled(Copy)` margin-bottom: 8px; ` const InputWrapper = styled.div` display: grid; grid-gap: 12px; grid-template-columns: ${({ fieldsToShow = [] }) => { if (fieldsToShow.length === 3) { return '5fr 2fr 2.5fr' } else if (fieldsToShow.length === 2) { return '1fr 1fr' } else { return '1fr' } }}; ${Errors} { display: none; } ` const months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ] export function isDateValid(dateIn) { if (!dateIn || !dateIn.year || !dateIn.month || !dateIn.day) return false const { year, month, day } = dateIn if (year.toString().length !== 4) { return false } const date = new Date(year, month - 1, day) return date.getFullYear() === Number(year) && date.getMonth() + 1 === Number(month) && date.getDate() === Number(day) } export const DatePicker = ({ errors = [], fieldsToShow = ['day', 'month', 'year'], hasSpaceForErrors, isInvalid, label, name, onBlur, onChange = noop, value = {}, ...rest }) => { const onChangeWrapper = (key, val) => { onChange({ target: { value: { ...value, [key]: val, }, }, }) } const handleMonthChange = (event) => { const monthNumber = months.indexOf(event.target.value) + 1 const val = monthNumber < 10 ? `0${monthNumber}` : `${monthNumber}` onChangeWrapper('month', val) } const handleDayChange = (event) => { onChangeWrapper('day', VMasker.toPattern(event.target.value, '99')) } const handleYearChange = (event) => { onChangeWrapper('year', VMasker.toPattern(event.target.value, '9999')) } const isInvalidOrHasErrors = isInvalid || errors.length return ( <DatePickerWrapper {...rest}> {label && ( <div> <StyledCopy light size="sm"> {label} </StyledCopy> </div> )} <InputWrapper fieldsToShow={fieldsToShow}> {fieldsToShow.includes('month') && ( <Select data-testid="date_picker:month" isInvalid={isInvalidOrHasErrors} label="Month" name={name} options={months} onBlur={onBlur} onChange={handleMonthChange} value={months[parseInt(value.month, 10) - 1] ?? 0} /> )} {fieldsToShow.includes('day') && ( <Input data-testid="date_picker:day" isInvalid={isInvalidOrHasErrors} label="Day" onBlur={onBlur} onChange={handleDayChange} // chrome 73 onWheel events became passive onWheel={(e) => { e.preventDefault() e.target.blur() }} type="number" value={value.day || ''} /> )} {fieldsToShow.includes('year') && ( <Input data-testid="date_picker:year" isInvalid={isInvalidOrHasErrors} label="Year" onBlur={onBlur} onChange={handleYearChange} // chrome 73 onWheel events became passive onWheel={(e) => { e.preventDefault() e.target.blur() }} type="number" value={value.year || ''} /> )} </InputWrapper> <Errors hasSpaceForErrors={hasSpaceForErrors} data-testid="date_picker:errors"> {!!errors.length && errors.map((err, i) => <Err key={`err-${i}`}>{err}</Err>)} </Errors> </DatePickerWrapper> ) } DatePicker.propTypes = { errors: PropTypes.arrayOf(PropTypes.string), fieldsToShow: PropTypes.arrayOf(PropTypes.oneOf(['day', 'month', 'year'])), hasSpaceForErrors: PropTypes.bool, isInvalid: PropTypes.bool, label: PropTypes.string, onBlur: PropTypes.func, onChange: PropTypes.func, value: PropTypes.shape({ day: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), month: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), year: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), }), } const StyledDatePicker = styled(DatePicker)`` export default StyledDatePicker