Skip to content
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ Displays an input field complete with custom inputs, native input, and a calenda
|clearAriaLabel|`aria-label` for the clear button.|n/a|`"Clear value"`|
|clearIcon|Content of the clear button. Setting the value explicitly to `null` will hide the icon.|(default icon)|<ul><li>String: `"Clear"`</li><li>React element: `<ClearIcon />`</li></ul>|
|closeCalendar|Whether to close the calendar on value selection.|`true`|`false`|
|customInput|Custom input component function. Note that many attributes will be overriden to provide react-date-picker functionality. See customInputOverrides to control this.|<input />|<input />|
|customInputStyle|Custom input style property. This will be merged with the existing default style.|{}|{ color: 'red' }|
|customInputOverrides|Tell react-date-picker to not override these attributes with its own custom attributes. Note this may result in loss of functionality.|[]|['aria-label']|
|dayAriaLabel|`aria-label` for the day input.|n/a|`"Day"`|
|dayPlaceholder|`placeholder` for the day input.|`"--"`|`"dd"`|
|disabled|Whether the date picker should be disabled.|`false`|`true`|
Expand Down
17 changes: 9 additions & 8 deletions src/DateInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -540,20 +540,18 @@ export default class DateInput extends PureComponent {
}

renderNativeInput() {
const {
disabled,
maxDate,
minDate,
name,
nativeInputAriaLabel,
required,
} = this.props;
const { disabled, maxDate, minDate } = this.props;
const { name, nativeInputAriaLabel, required } = this.props;
const { customInput, customInputOverrides, customInputStyle } = this.props;
const { value } = this.state;

return (
<NativeInput
key="date"
ariaLabel={nativeInputAriaLabel}
customInput={customInput}
customInputOverrides={customInputOverrides}
customInputStyle={customInputStyle}
disabled={disabled}
maxDate={maxDate || defaultMaxDate}
minDate={minDate || defaultMinDate}
Expand Down Expand Up @@ -597,6 +595,9 @@ const isValue = PropTypes.oneOfType([
DateInput.propTypes = {
autoFocus: PropTypes.bool,
className: PropTypes.string.isRequired,
customInput: PropTypes.element,
customInputOverrides: PropTypes.arrayOf(PropTypes.string),
customInputStyle: PropTypes.objectOf(PropTypes.any),
dayAriaLabel: PropTypes.string,
dayPlaceholder: PropTypes.string,
disabled: PropTypes.bool,
Expand Down
110 changes: 60 additions & 50 deletions src/DateInput/NativeInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,40 @@ import {

import { isMaxDate, isMinDate, isValueType } from '../shared/propTypes';

const nativeInputType = (valueType) => {
switch (valueType) {
case 'decade':
case 'year':
return 'number';
case 'month':
return 'month';
case 'day':
return 'date';
default:
throw new Error('Invalid valueType.');
}
};

const nativeValueParser = (valueType) => {
switch (valueType) {
case 'century':
case 'decade':
case 'year':
return getYear;
case 'month':
return getISOLocalMonth;
case 'day':
return getISOLocalDate;
default:
throw new Error('Invalid valueType.');
}
};

export default function NativeInput({
ariaLabel,
customInput = <input />,
customInputStyle = {},
customInputOverrides = [],
disabled,
maxDate,
minDate,
Expand All @@ -19,63 +51,41 @@ export default function NativeInput({
value,
valueType,
}) {
const nativeInputType = (() => {
switch (valueType) {
case 'decade':
case 'year':
return 'number';
case 'month':
return 'month';
case 'day':
return 'date';
default:
throw new Error('Invalid valueType.');
}
})();
const inputStyle = {
visibility: 'hidden',
position: 'absolute',
top: '-9999px',
left: '-9999px',
...customInputStyle,
};

const nativeValueParser = (() => {
switch (valueType) {
case 'century':
case 'decade':
case 'year':
return getYear;
case 'month':
return getISOLocalMonth;
case 'day':
return getISOLocalDate;
default:
throw new Error('Invalid valueType.');
}
})();
const inputProps = {
'aria-label': ariaLabel,
disabled,
max: maxDate ? nativeValueParser(valueType)(maxDate) : null,
min: minDate ? nativeValueParser(valueType)(minDate) : null,
name,
onChange,
onFocus: event => event.stopPropagation(),
required,
type: nativeInputType(valueType),
value: value ? nativeValueParser(valueType)(value) : '',
};

function stopPropagation(event) {
event.stopPropagation();
}
const filteredInputProps = Object.keys(inputProps)
.reduce((obj, key) => (customInputOverrides && customInputOverrides.includes(key)
? { ...obj }
: { ...obj, [key]: inputProps[key] }),
{});

return (
<input
aria-label={ariaLabel}
disabled={disabled}
max={maxDate ? nativeValueParser(maxDate) : null}
min={minDate ? nativeValueParser(minDate) : null}
name={name}
onChange={onChange}
onFocus={stopPropagation}
required={required}
style={{
visibility: 'hidden',
position: 'absolute',
top: '-9999px',
left: '-9999px',
}}
type={nativeInputType}
value={value ? nativeValueParser(value) : ''}
/>
);
return <>{React.cloneElement(customInput, { ...filteredInputProps, style: inputStyle })}</>;
}

NativeInput.propTypes = {
ariaLabel: PropTypes.string,
customInput: PropTypes.element,
customInputOverrides: PropTypes.arrayOf(PropTypes.string),
customInputStyle: PropTypes.objectOf(PropTypes.any),
disabled: PropTypes.bool,
maxDate: isMaxDate,
minDate: isMinDate,
Expand Down