react-datetime-picker
Advanced tools
Comparing version 2.0.0 to 2.1.0
@@ -69,2 +69,4 @@ 'use strict'; | ||
var _utils = require('./shared/utils'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -80,2 +82,4 @@ | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
var defaultMinDate = new Date(-8.64e15); | ||
@@ -109,13 +113,19 @@ var defaultMaxDate = new Date(8.64e15); | ||
var removeUnwantedCharacters = function removeUnwantedCharacters(str) { | ||
return str.replace(/[年月日]/g, '/').split('').filter(function (a) { | ||
return ( | ||
// We don't want spaces in dates | ||
a.charCodeAt(0) !== 32 | ||
// Internet Explorer specific | ||
&& a.charCodeAt(0) !== 8206 | ||
// Remove non-ASCII characters | ||
&& /^[\x20-\x7F]*$/.test(a) | ||
var renderCustomInputs = function renderCustomInputs(placeholder, elementFunctions) { | ||
var pattern = new RegExp(Object.keys(elementFunctions).join('|'), 'gi'); | ||
var matches = placeholder.match(pattern); | ||
return placeholder.split(pattern).reduce(function (arr, element, index) { | ||
var divider = element && | ||
// eslint-disable-next-line react/no-array-index-key | ||
_react2.default.createElement( | ||
_Divider2.default, | ||
{ key: 'separator_' + index }, | ||
element | ||
); | ||
}).join(''); | ||
var res = [].concat(_toConsumableArray(arr), [divider]); | ||
if (matches[index]) { | ||
res.push(elementFunctions[matches[index]]()); | ||
} | ||
return res; | ||
}, []); | ||
}; | ||
@@ -145,2 +155,10 @@ | ||
second: null | ||
}, _this.onClick = function (event) { | ||
if (event.target === event.currentTarget) { | ||
// Wrapper was directly clicked | ||
var _event$target$childre = _slicedToArray(event.target.children, 2), | ||
firstInput = _event$target$childre[1]; | ||
focus(firstInput); | ||
} | ||
}, _this.onKeyDown = function (event) { | ||
@@ -238,3 +256,3 @@ switch (event.key) { | ||
onChange(processedValue); | ||
onChange(processedValue, false); | ||
}, _this.onChangeAmPm = function (event) { | ||
@@ -265,3 +283,3 @@ var value = event.target.value; | ||
})) { | ||
onChange(null); | ||
onChange(null, false); | ||
} else if (formElements.every(function (formElement) { | ||
@@ -273,22 +291,17 @@ return formElement.value && formElement.checkValidity(); | ||
var processedValue = proposedValue; | ||
onChange(processedValue); | ||
onChange(processedValue, false); | ||
} | ||
}, _temp), _possibleConstructorReturn(_this, _ret); | ||
} | ||
}, _this.renderDay = function () { | ||
var _this$props = _this.props, | ||
maxDetail = _this$props.maxDetail, | ||
showLeadingZeros = _this$props.showLeadingZeros; | ||
var _this$state = _this.state, | ||
day = _this$state.day, | ||
month = _this$state.month, | ||
year = _this$state.year; | ||
_createClass(DateTimeInput, [{ | ||
key: 'renderDay', | ||
value: function renderDay() { | ||
var _props = this.props, | ||
maxDetail = _props.maxDetail, | ||
showLeadingZeros = _props.showLeadingZeros; | ||
var _state = this.state, | ||
day = _state.day, | ||
month = _state.month, | ||
year = _state.year; | ||
return _react2.default.createElement(_DayInput2.default, _extends({ | ||
key: 'day' | ||
}, this.commonInputProps, { | ||
}, _this.commonInputProps, { | ||
maxDetail: maxDetail, | ||
@@ -300,10 +313,7 @@ month: month, | ||
})); | ||
} | ||
}, { | ||
key: 'renderMonth', | ||
value: function renderMonth() { | ||
var _props2 = this.props, | ||
maxDetail = _props2.maxDetail, | ||
showLeadingZeros = _props2.showLeadingZeros; | ||
var month = this.state.month; | ||
}, _this.renderMonth = function () { | ||
var _this$props2 = _this.props, | ||
maxDetail = _this$props2.maxDetail, | ||
showLeadingZeros = _this$props2.showLeadingZeros; | ||
var month = _this.state.month; | ||
@@ -313,3 +323,3 @@ | ||
key: 'month' | ||
}, this.commonInputProps, { | ||
}, _this.commonInputProps, { | ||
maxDetail: maxDetail, | ||
@@ -319,7 +329,4 @@ showLeadingZeros: showLeadingZeros, | ||
})); | ||
} | ||
}, { | ||
key: 'renderYear', | ||
value: function renderYear() { | ||
var year = this.state.year; | ||
}, _this.renderYear = function () { | ||
var year = _this.state.year; | ||
@@ -329,11 +336,8 @@ | ||
key: 'year' | ||
}, this.commonInputProps, { | ||
}, _this.commonInputProps, { | ||
value: year, | ||
valueType: 'day' | ||
})); | ||
} | ||
}, { | ||
key: 'renderHour12', | ||
value: function renderHour12() { | ||
var hour = this.state.hour; | ||
}, _this.renderHour12 = function () { | ||
var hour = _this.state.hour; | ||
@@ -343,10 +347,7 @@ | ||
key: 'hour12' | ||
}, this.commonInputProps, { | ||
}, _this.commonInputProps, { | ||
value: hour | ||
})); | ||
} | ||
}, { | ||
key: 'renderHour24', | ||
value: function renderHour24() { | ||
var hour = this.state.hour; | ||
}, _this.renderHour24 = function () { | ||
var hour = _this.state.hour; | ||
@@ -356,25 +357,15 @@ | ||
key: 'hour24' | ||
}, this.commonInputProps, { | ||
}, _this.commonInputProps, { | ||
value: hour | ||
})); | ||
} | ||
}, { | ||
key: 'renderMinute', | ||
value: function renderMinute() { | ||
var maxDetail = this.props.maxDetail; | ||
}, _this.renderMinute = function () { | ||
var maxDetail = _this.props.maxDetail; | ||
var _this$state2 = _this.state, | ||
hour = _this$state2.hour, | ||
minute = _this$state2.minute; | ||
// Do not display if maxDetail is "hour" or less | ||
if (allViews.indexOf(maxDetail) < 1) { | ||
return null; | ||
} | ||
var _state2 = this.state, | ||
hour = _state2.hour, | ||
minute = _state2.minute; | ||
return _react2.default.createElement(_MinuteInput2.default, _extends({ | ||
key: 'minute' | ||
}, this.commonInputProps, { | ||
}, _this.commonInputProps, { | ||
hour: hour, | ||
@@ -384,23 +375,13 @@ maxDetail: maxDetail, | ||
})); | ||
} | ||
}, { | ||
key: 'renderSecond', | ||
value: function renderSecond() { | ||
var maxDetail = this.props.maxDetail; | ||
}, _this.renderSecond = function () { | ||
var maxDetail = _this.props.maxDetail; | ||
var _this$state3 = _this.state, | ||
hour = _this$state3.hour, | ||
minute = _this$state3.minute, | ||
second = _this$state3.second; | ||
// Do not display if maxDetail is "minute" or less | ||
if (allViews.indexOf(maxDetail) < 2) { | ||
return null; | ||
} | ||
var _state3 = this.state, | ||
hour = _state3.hour, | ||
minute = _state3.minute, | ||
second = _state3.second; | ||
return _react2.default.createElement(_SecondInput2.default, _extends({ | ||
key: 'second' | ||
}, this.commonInputProps, { | ||
}, _this.commonInputProps, { | ||
hour: hour, | ||
@@ -411,7 +392,5 @@ maxDetail: maxDetail, | ||
})); | ||
} | ||
}, { | ||
key: 'renderAmPm', | ||
value: function renderAmPm() { | ||
var amPm = this.state.amPm; | ||
}, _this.renderAmPm = function () { | ||
var amPm = _this.state.amPm; | ||
var locale = _this.props.locale; | ||
@@ -421,42 +400,22 @@ | ||
key: 'ampm' | ||
}, this.commonInputProps, { | ||
value: amPm, | ||
onChange: this.onChangeAmPm | ||
}, _this.commonInputProps, { | ||
locale: locale, | ||
onChange: _this.onChangeAmPm, | ||
value: amPm | ||
})); | ||
} | ||
}, { | ||
}, _temp), _possibleConstructorReturn(_this, _ret); | ||
} | ||
_createClass(DateTimeInput, [{ | ||
key: 'renderCustomDateInputs', | ||
value: function renderCustomDateInputs() { | ||
var _this2 = this; | ||
var datePlaceholder = this.datePlaceholder; | ||
var dateDivider = this.dateDivider, | ||
datePlaceholder = this.datePlaceholder; | ||
var elementFunctions = { | ||
day: this.renderDay, | ||
month: this.renderMonth, | ||
year: this.renderYear | ||
}; | ||
return datePlaceholder.split(dateDivider).map(function (part) { | ||
switch (part) { | ||
case 'day': | ||
return _this2.renderDay(); | ||
case 'month': | ||
return _this2.renderMonth(); | ||
case 'year': | ||
return _this2.renderYear(); | ||
default: | ||
return null; | ||
} | ||
}).filter(Boolean).reduce(function (result, element, index) { | ||
if (index) { | ||
result.push( | ||
// eslint-disable-next-line react/no-array-index-key | ||
_react2.default.createElement( | ||
_Divider2.default, | ||
{ key: 'separator_' + index }, | ||
dateDivider | ||
)); | ||
} | ||
result.push(element); | ||
return result; | ||
}, []); | ||
return renderCustomInputs(datePlaceholder, elementFunctions); | ||
} | ||
@@ -466,38 +425,13 @@ }, { | ||
value: function renderCustomTimeInputs() { | ||
var _this3 = this; | ||
var timePlaceholder = this.timePlaceholder; | ||
var timeDivider = this.timeDivider, | ||
timePlaceholder = this.timePlaceholder; | ||
var elementFunctions = { | ||
'hour-12': this.renderHour12, | ||
'hour-24': this.renderHour24, | ||
minute: this.renderMinute, | ||
second: this.renderSecond, | ||
ampm: this.renderAmPm | ||
}; | ||
return timePlaceholder.split(timeDivider).map(function (part) { | ||
switch (part) { | ||
case 'hour-12': | ||
return _this3.renderHour12(); | ||
case 'hour-24': | ||
return _this3.renderHour24(); | ||
case 'minute': | ||
return _this3.renderMinute(); | ||
case 'second': | ||
return _this3.renderSecond(); | ||
case 'ampm': | ||
return _this3.renderAmPm(); | ||
default: | ||
return null; | ||
} | ||
}).filter(Boolean).reduce(function (result, element, index) { | ||
if (index && element.key !== 'ampm') { | ||
result.push( | ||
// eslint-disable-next-line react/no-array-index-key | ||
_react2.default.createElement( | ||
_Divider2.default, | ||
{ key: 'separator_' + index }, | ||
timeDivider | ||
)); | ||
} | ||
result.push(element); | ||
return result; | ||
}, []); | ||
return renderCustomInputs(timePlaceholder, elementFunctions); | ||
} | ||
@@ -507,9 +441,9 @@ }, { | ||
value: function renderNativeInput() { | ||
var _props3 = this.props, | ||
disabled = _props3.disabled, | ||
maxDate = _props3.maxDate, | ||
minDate = _props3.minDate, | ||
name = _props3.name, | ||
required = _props3.required, | ||
value = _props3.value; | ||
var _props = this.props, | ||
disabled = _props.disabled, | ||
maxDate = _props.maxDate, | ||
minDate = _props.minDate, | ||
name = _props.name, | ||
required = _props.required, | ||
value = _props.value; | ||
@@ -537,3 +471,7 @@ | ||
'div', | ||
{ className: className }, | ||
{ | ||
className: className, | ||
onClick: this.onClick, | ||
role: 'presentation' | ||
}, | ||
this.renderNativeInput(), | ||
@@ -550,6 +488,22 @@ this.renderCustomDateInputs(), | ||
}, { | ||
key: 'dateDivider', | ||
key: 'formatTime', | ||
get: function get() { | ||
var _props2 = this.props, | ||
locale = _props2.locale, | ||
maxDetail = _props2.maxDetail; | ||
// eslint-disable-next-line class-methods-use-this | ||
var options = { hour: 'numeric' }; | ||
var level = allViews.indexOf(maxDetail); | ||
if (level >= 1) { | ||
options.minute = 'numeric'; | ||
} | ||
if (level >= 2) { | ||
options.second = 'numeric'; | ||
} | ||
return (0, _dateFormatter.getFormatter)(options, locale); | ||
} | ||
}, { | ||
key: 'dateDivider', | ||
get: function get() { | ||
@@ -560,19 +514,11 @@ var locale = this.props.locale; | ||
return removeUnwantedCharacters((0, _dateFormatter.formatDate)(date, locale)).match(/[^0-9]/)[0]; | ||
return (0, _dateFormatter.formatDate)(date, locale).match(/[^0-9a-z]/i)[0]; | ||
} | ||
// eslint-disable-next-line class-methods-use-this | ||
}, { | ||
key: 'timeDivider', | ||
get: function get() { | ||
var locale = this.props.locale; | ||
var date = new Date(2017, 0, 1, 21, 12, 13); | ||
return removeUnwantedCharacters((0, _dateFormatter.formatTime)(date, locale)).match(/[^0-9]/)[0]; | ||
return this.formatTime(date).match(/[^0-9a-z]/i)[0]; | ||
} | ||
// eslint-disable-next-line class-methods-use-this | ||
}, { | ||
@@ -585,7 +531,4 @@ key: 'datePlaceholder', | ||
return removeUnwantedCharacters((0, _dateFormatter.formatDate)(date, locale)).replace('2017', 'year').replace('12', 'month').replace('11', 'day'); | ||
return (0, _dateFormatter.formatDate)(date, locale).replace('2017', 'year').replace('12', 'month').replace('11', 'day'); | ||
} | ||
// eslint-disable-next-line class-methods-use-this | ||
}, { | ||
@@ -598,3 +541,3 @@ key: 'timePlaceholder', | ||
return removeUnwantedCharacters((0, _dateFormatter.formatTime)(date, locale)).replace('21', 'hour-24').replace('9', 'hour-12').replace('13', 'minute').replace('14', 'second').replace(/AM|PM/i, this.timeDivider + 'ampm'); | ||
return this.formatTime(date).replace('21', 'hour-24').replace('9', 'hour-12').replace('13', 'minute').replace('14', 'second').replace(new RegExp((0, _utils.getAmPmLabels)(locale).join('|')), 'ampm'); | ||
} | ||
@@ -611,6 +554,6 @@ }, { | ||
var _state4 = this.state, | ||
year = _state4.year, | ||
month = _state4.month, | ||
day = _state4.day; | ||
var _state = this.state, | ||
year = _state.year, | ||
month = _state.month, | ||
day = _state.day; | ||
@@ -634,6 +577,6 @@ | ||
var _state5 = this.state, | ||
year = _state5.year, | ||
month = _state5.month, | ||
day = _state5.day; | ||
var _state2 = this.state, | ||
year = _state2.year, | ||
month = _state2.month, | ||
day = _state2.day; | ||
@@ -650,13 +593,13 @@ | ||
get: function get() { | ||
var _this4 = this; | ||
var _this2 = this; | ||
var maxTime = this.maxTime, | ||
minTime = this.minTime; | ||
var _props4 = this.props, | ||
className = _props4.className, | ||
disabled = _props4.disabled, | ||
isWidgetOpen = _props4.isWidgetOpen, | ||
maxDate = _props4.maxDate, | ||
minDate = _props4.minDate, | ||
required = _props4.required; | ||
var _props3 = this.props, | ||
className = _props3.className, | ||
disabled = _props3.disabled, | ||
isWidgetOpen = _props3.isWidgetOpen, | ||
maxDate = _props3.maxDate, | ||
minDate = _props3.minDate, | ||
required = _props3.required; | ||
@@ -678,3 +621,3 @@ | ||
// Save a reference to each input field | ||
_this4[name + 'Input'] = ref; | ||
_this2[name + 'Input'] = ref; | ||
} | ||
@@ -681,0 +624,0 @@ }; |
@@ -74,7 +74,3 @@ 'use strict'; | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = DateTimePicker.__proto__ || Object.getPrototypeOf(DateTimePicker)).call.apply(_ref, [this].concat(args))), _this), _this.state = {}, _this.onClick = function (event) { | ||
if (_this.wrapper && !_this.wrapper.contains(event.target)) { | ||
_this.closeWidgets(); | ||
} | ||
}, _this.onDateChange = function (value) { | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = DateTimePicker.__proto__ || Object.getPrototypeOf(DateTimePicker)).call.apply(_ref, [this].concat(args))), _this), _this.state = {}, _this.onDateChange = function (value) { | ||
var closeWidgets = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; | ||
@@ -140,2 +136,11 @@ var prevValue = _this.props.value; | ||
} | ||
}, _this.onBlur = function () { | ||
var onBlur = _this.props.onBlur; | ||
if (onBlur) { | ||
onBlur(event); | ||
} | ||
_this.closeWidgets(); | ||
}, _this.openClock = function () { | ||
@@ -177,12 +182,2 @@ _this.setState({ | ||
_createClass(DateTimePicker, [{ | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
document.addEventListener('mousedown', this.onClick); | ||
} | ||
}, { | ||
key: 'componentWillUnmount', | ||
value: function componentWillUnmount() { | ||
document.removeEventListener('mousedown', this.onClick); | ||
} | ||
}, { | ||
key: 'renderInputs', | ||
@@ -253,2 +248,4 @@ value: function renderInputs() { | ||
value: function renderCalendar() { | ||
var _this2 = this; | ||
var isCalendarOpen = this.state.isCalendarOpen; | ||
@@ -276,3 +273,3 @@ | ||
ref: function ref(_ref2) { | ||
if (!_ref2) { | ||
if (!_ref2 || !isCalendarOpen) { | ||
return; | ||
@@ -286,3 +283,8 @@ } | ||
if (collisions.collidedBottom) { | ||
_ref2.classList.add(className + '--above-label'); | ||
var overflowTopAfterChange = collisions.overflowTop + _ref2.clientHeight + _this2.wrapper.clientHeight; | ||
// If it's going to make situation any better, display the calendar above the input | ||
if (overflowTopAfterChange < collisions.overflowBottom) { | ||
_ref2.classList.add(className + '--above-label'); | ||
} | ||
} | ||
@@ -301,2 +303,4 @@ } | ||
value: function renderClock() { | ||
var _this3 = this; | ||
var disableClock = this.props.disableClock; | ||
@@ -326,3 +330,3 @@ var isClockOpen = this.state.isClockOpen; | ||
ref: function ref(_ref3) { | ||
if (!_ref3) { | ||
if (!_ref3 || !isClockOpen) { | ||
return; | ||
@@ -336,3 +340,8 @@ } | ||
if (collisions.collidedBottom) { | ||
_ref3.classList.add(className + '--above-label'); | ||
var overflowTopAfterChange = collisions.overflowTop + _ref3.clientHeight + _this3.wrapper.clientHeight; | ||
// If it's going to make situation any better, display the calendar above the input | ||
if (overflowTopAfterChange < collisions.overflowBottom) { | ||
_ref3.classList.add(className + '--above-label'); | ||
} | ||
} | ||
@@ -351,3 +360,3 @@ } | ||
value: function render() { | ||
var _this2 = this; | ||
var _this4 = this; | ||
@@ -368,4 +377,9 @@ var _props4 = this.props, | ||
onFocus: this.onFocus, | ||
onBlur: this.onBlur, | ||
ref: function ref(_ref4) { | ||
_this2.wrapper = _ref4; | ||
if (!_ref4) { | ||
return; | ||
} | ||
_this4.wrapper = _ref4; | ||
} | ||
@@ -372,0 +386,0 @@ }), |
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.formatTime = exports.formatDate = undefined; | ||
exports.formatDate = exports.getFormatter = undefined; | ||
@@ -21,3 +21,3 @@ var _getUserLocale = require('get-user-locale'); | ||
*/ | ||
var getFormatter = function getFormatter(options, locale) { | ||
var getFormatter = exports.getFormatter = function getFormatter(options, locale) { | ||
if (!locale) { | ||
@@ -44,6 +44,2 @@ // Default parameter is not enough as it does not protect us from null values | ||
return getFormatter({ day: 'numeric', month: 'numeric', year: 'numeric' }, locale)(date); | ||
}; | ||
var formatTime = exports.formatTime = function formatTime(date, locale) { | ||
return getFormatter({ hour: 'numeric', minute: 'numeric', second: 'numeric' }, locale)(date); | ||
}; |
@@ -26,2 +26,11 @@ 'use strict'; | ||
} | ||
}); | ||
var _utils2 = require('react-time-picker/dist/shared/utils'); | ||
Object.defineProperty(exports, 'getAmPmLabels', { | ||
enumerable: true, | ||
get: function get() { | ||
return _utils2.getAmPmLabels; | ||
} | ||
}); |
{ | ||
"name": "react-datetime-picker", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "A date range picker for your React app.", | ||
@@ -52,5 +52,5 @@ "main": "dist/entry.js", | ||
"react-clock": "^2.3.0", | ||
"react-date-picker": "^7.0.0", | ||
"react-date-picker": "^7.1.0", | ||
"react-lifecycles-compat": "^3.0.4", | ||
"react-time-picker": "^3.0.0" | ||
"react-time-picker": "^3.1.0" | ||
}, | ||
@@ -57,0 +57,0 @@ "devDependencies": { |
@@ -221,3 +221,3 @@ import React from 'react'; | ||
expect(separators).toHaveLength(4); | ||
expect(separators).toHaveLength(5); | ||
expect(separators.at(0).text()).toBe('/'); | ||
@@ -227,2 +227,3 @@ expect(separators.at(1).text()).toBe('/'); | ||
expect(separators.at(3).text()).toBe(':'); | ||
expect(separators.at(4).text()).toBe(' '); | ||
}); | ||
@@ -240,4 +241,5 @@ | ||
const customInputs = component.find('input[type="number"]'); | ||
const ampm = component.find('select'); | ||
expect(separators).toHaveLength(customInputs.length - 1); | ||
expect(separators).toHaveLength(customInputs.length + ampm.length - 1); | ||
}); | ||
@@ -288,12 +290,11 @@ | ||
const customInputs = component.find('input[type="number"]'); | ||
const minuteInput = customInputs.at(4); | ||
const select = component.find('select'); | ||
minuteInput.getDOMNode().focus(); | ||
select.getDOMNode().focus(); | ||
expect(document.activeElement).toBe(minuteInput.getDOMNode()); | ||
expect(document.activeElement).toBe(select.getDOMNode()); | ||
minuteInput.simulate('keydown', getKey('ArrowRight')); | ||
select.simulate('keydown', getKey('ArrowRight')); | ||
expect(document.activeElement).toBe(minuteInput.getDOMNode()); | ||
expect(document.activeElement).toBe(select.getDOMNode()); | ||
}); | ||
@@ -319,3 +320,3 @@ | ||
it('does not jump to the next field when right arrow is pressed when the last input is focused', () => { | ||
it('does not jump to the previous field when left arrow is pressed when the first input is focused', () => { | ||
const component = mount( | ||
@@ -355,3 +356,3 @@ <DateTimeInput {...defaultProps} /> | ||
expect(onChange).toHaveBeenCalled(); | ||
expect(onChange).toHaveBeenCalledWith(new Date(2017, 8, 30, 20, 17, 0)); | ||
expect(onChange).toHaveBeenCalledWith(new Date(2017, 8, 30, 20, 17, 0), false); | ||
}); | ||
@@ -380,3 +381,3 @@ | ||
expect(onChange).toHaveBeenCalledTimes(1); | ||
expect(onChange).toHaveBeenCalledWith(null); | ||
expect(onChange).toHaveBeenCalledWith(null, false); | ||
}); | ||
@@ -402,3 +403,3 @@ | ||
expect(onChange).toHaveBeenCalled(); | ||
expect(onChange).toHaveBeenCalledWith(new Date(2017, 8, 30, 20, 17, 0)); | ||
expect(onChange).toHaveBeenCalledWith(new Date(2017, 8, 30, 20, 17, 0), false); | ||
}); | ||
@@ -424,4 +425,4 @@ | ||
expect(onChange).toHaveBeenCalled(); | ||
expect(onChange).toHaveBeenCalledWith(null); | ||
expect(onChange).toHaveBeenCalledWith(null, false); | ||
}); | ||
}); |
@@ -8,18 +8,2 @@ import React from 'react'; | ||
const mockDocumentListeners = () => { | ||
const eventMap = {}; | ||
document.addEventListener = jest.fn((method, cb) => { | ||
if (!eventMap[method]) { | ||
eventMap[method] = []; | ||
} | ||
eventMap[method].push(cb); | ||
}); | ||
return { | ||
simulate: (method, args) => { | ||
eventMap[method].forEach(cb => cb(args)); | ||
}, | ||
}; | ||
}; | ||
describe('DateTimePicker', () => { | ||
@@ -246,5 +230,3 @@ it('passes default name to DateTimeInput', () => { | ||
it('closes Calendar and Clock component when clicked outside', () => { | ||
const { simulate } = mockDocumentListeners(); | ||
it('closes Calendar and Clock component when focused outside', () => { | ||
const component = mount( | ||
@@ -254,5 +236,6 @@ <DateTimePicker isCalendarOpen isClockOpen /> | ||
simulate('mousedown', { | ||
target: document, | ||
}); | ||
const customInputs = component.find('input[type="number"]'); | ||
const dayInput = customInputs.at(0); | ||
dayInput.simulate('blur'); | ||
component.update(); | ||
@@ -264,15 +247,33 @@ | ||
it('does not close Calendar and Clock component when clicked inside', () => { | ||
const { simulate } = mockDocumentListeners(); | ||
it('does not close Calendar component when focused within date inputs', () => { | ||
const component = mount( | ||
<DateTimePicker isCalendarOpen /> | ||
); | ||
const customInputs = component.find('input[type="number"]'); | ||
const dayInput = customInputs.at(0); | ||
const monthInput = customInputs.at(1); | ||
dayInput.simulate('blur'); | ||
monthInput.simulate('focus'); | ||
component.update(); | ||
expect(component.state('isCalendarOpen')).toBe(true); | ||
expect(component.state('isClockOpen')).toBe(false); | ||
}); | ||
it('does not close Clock component when focused within time inputs', () => { | ||
const component = mount( | ||
<DateTimePicker isCalendarOpen isClockOpen /> | ||
<DateTimePicker isClockOpen /> | ||
); | ||
simulate('mousedown', { | ||
target: component.getDOMNode(), | ||
}); | ||
const customInputs = component.find('input[type="number"]'); | ||
const hourInput = customInputs.at(3); | ||
const minuteInput = customInputs.at(4); | ||
hourInput.simulate('blur'); | ||
minuteInput.simulate('focus'); | ||
component.update(); | ||
expect(component.state('isCalendarOpen')).toBe(true); | ||
expect(component.state('isCalendarOpen')).toBe(false); | ||
expect(component.state('isClockOpen')).toBe(true); | ||
@@ -279,0 +280,0 @@ }); |
@@ -16,3 +16,3 @@ import React, { PureComponent } from 'react'; | ||
import { formatDate, formatTime } from './shared/dateFormatter'; | ||
import { getFormatter, formatDate } from './shared/dateFormatter'; | ||
import { | ||
@@ -30,2 +30,3 @@ getDay, | ||
import { isMaxDate, isMinDate } from './shared/propTypes'; | ||
import { getAmPmLabels } from './shared/utils'; | ||
@@ -60,14 +61,20 @@ const defaultMinDate = new Date(-8.64e15); | ||
const removeUnwantedCharacters = str => str | ||
.replace(/[年月日]/g, '/') | ||
.split('') | ||
.filter(a => ( | ||
// We don't want spaces in dates | ||
a.charCodeAt(0) !== 32 | ||
// Internet Explorer specific | ||
&& a.charCodeAt(0) !== 8206 | ||
// Remove non-ASCII characters | ||
&& /^[\x20-\x7F]*$/.test(a) | ||
)) | ||
.join(''); | ||
const renderCustomInputs = (placeholder, elementFunctions) => { | ||
const pattern = new RegExp(Object.keys(elementFunctions).join('|'), 'gi'); | ||
const matches = placeholder.match(pattern); | ||
return placeholder.split(pattern) | ||
.reduce((arr, element, index) => { | ||
const divider = element && ( | ||
// eslint-disable-next-line react/no-array-index-key | ||
<Divider key={`separator_${index}`}> | ||
{element} | ||
</Divider> | ||
); | ||
const res = [...arr, divider]; | ||
if (matches[index]) { | ||
res.push(elementFunctions[matches[index]]()); | ||
} | ||
return res; | ||
}, []); | ||
}; | ||
@@ -130,3 +137,17 @@ export default class DateTimeInput extends PureComponent { | ||
// eslint-disable-next-line class-methods-use-this | ||
get formatTime() { | ||
const { locale, maxDetail } = this.props; | ||
const options = { hour: 'numeric' }; | ||
const level = allViews.indexOf(maxDetail); | ||
if (level >= 1) { | ||
options.minute = 'numeric'; | ||
} | ||
if (level >= 2) { | ||
options.second = 'numeric'; | ||
} | ||
return getFormatter(options, locale); | ||
} | ||
get dateDivider() { | ||
@@ -136,20 +157,11 @@ const { locale } = this.props; | ||
return ( | ||
removeUnwantedCharacters(formatDate(date, locale)) | ||
.match(/[^0-9]/)[0] | ||
); | ||
return formatDate(date, locale).match(/[^0-9a-z]/i)[0]; | ||
} | ||
// eslint-disable-next-line class-methods-use-this | ||
get timeDivider() { | ||
const { locale } = this.props; | ||
const date = new Date(2017, 0, 1, 21, 12, 13); | ||
return ( | ||
removeUnwantedCharacters(formatTime(date, locale)) | ||
.match(/[^0-9]/)[0] | ||
); | ||
return this.formatTime(date).match(/[^0-9a-z]/i)[0]; | ||
} | ||
// eslint-disable-next-line class-methods-use-this | ||
get datePlaceholder() { | ||
@@ -160,3 +172,3 @@ const { locale } = this.props; | ||
return ( | ||
removeUnwantedCharacters(formatDate(date, locale)) | ||
formatDate(date, locale) | ||
.replace('2017', 'year') | ||
@@ -168,3 +180,2 @@ .replace('12', 'month') | ||
// eslint-disable-next-line class-methods-use-this | ||
get timePlaceholder() { | ||
@@ -175,3 +186,3 @@ const { locale } = this.props; | ||
return ( | ||
removeUnwantedCharacters(formatTime(date, locale)) | ||
this.formatTime(date) | ||
.replace('21', 'hour-24') | ||
@@ -181,3 +192,3 @@ .replace('9', 'hour-12') | ||
.replace('14', 'second') | ||
.replace(/AM|PM/i, `${this.timeDivider}ampm`) | ||
.replace(new RegExp(getAmPmLabels(locale).join('|')), 'ampm') | ||
); | ||
@@ -265,2 +276,10 @@ } | ||
onClick = (event) => { | ||
if (event.target === event.currentTarget) { | ||
// Wrapper was directly clicked | ||
const [/* nativeInput */, firstInput] = event.target.children; | ||
focus(firstInput); | ||
} | ||
} | ||
onKeyDown = (event) => { | ||
@@ -353,3 +372,3 @@ switch (event.key) { | ||
onChange(processedValue); | ||
onChange(processedValue, false); | ||
} | ||
@@ -396,3 +415,3 @@ | ||
if (formElementsWithoutSelect.every(formElement => !formElement.value)) { | ||
onChange(null); | ||
onChange(null, false); | ||
} else if ( | ||
@@ -411,7 +430,7 @@ formElements.every(formElement => formElement.value && formElement.checkValidity()) | ||
const processedValue = proposedValue; | ||
onChange(processedValue); | ||
onChange(processedValue, false); | ||
} | ||
} | ||
renderDay() { | ||
renderDay = () => { | ||
const { maxDetail, showLeadingZeros } = this.props; | ||
@@ -433,3 +452,3 @@ const { day, month, year } = this.state; | ||
renderMonth() { | ||
renderMonth = () => { | ||
const { maxDetail, showLeadingZeros } = this.props; | ||
@@ -449,3 +468,3 @@ const { month } = this.state; | ||
renderYear() { | ||
renderYear = () => { | ||
const { year } = this.state; | ||
@@ -463,3 +482,3 @@ | ||
renderHour12() { | ||
renderHour12 = () => { | ||
const { hour } = this.state; | ||
@@ -476,3 +495,3 @@ | ||
renderHour24() { | ||
renderHour24 = () => { | ||
const { hour } = this.state; | ||
@@ -489,10 +508,4 @@ | ||
renderMinute() { | ||
renderMinute = () => { | ||
const { maxDetail } = this.props; | ||
// Do not display if maxDetail is "hour" or less | ||
if (allViews.indexOf(maxDetail) < 1) { | ||
return null; | ||
} | ||
const { hour, minute } = this.state; | ||
@@ -511,10 +524,4 @@ | ||
renderSecond() { | ||
renderSecond = () => { | ||
const { maxDetail } = this.props; | ||
// Do not display if maxDetail is "minute" or less | ||
if (allViews.indexOf(maxDetail) < 2) { | ||
return null; | ||
} | ||
const { hour, minute, second } = this.state; | ||
@@ -534,4 +541,5 @@ | ||
renderAmPm() { | ||
renderAmPm = () => { | ||
const { amPm } = this.state; | ||
const { locale } = this.props; | ||
@@ -542,4 +550,5 @@ return ( | ||
{...this.commonInputProps} | ||
locale={locale} | ||
onChange={this.onChangeAmPm} | ||
value={amPm} | ||
onChange={this.onChangeAmPm} | ||
/> | ||
@@ -550,65 +559,23 @@ ); | ||
renderCustomDateInputs() { | ||
const { dateDivider, datePlaceholder } = this; | ||
const { datePlaceholder } = this; | ||
const elementFunctions = { | ||
day: this.renderDay, | ||
month: this.renderMonth, | ||
year: this.renderYear, | ||
}; | ||
return ( | ||
datePlaceholder | ||
.split(dateDivider) | ||
.map((part) => { | ||
switch (part) { | ||
case 'day': return this.renderDay(); | ||
case 'month': return this.renderMonth(); | ||
case 'year': return this.renderYear(); | ||
default: return null; | ||
} | ||
}) | ||
.filter(Boolean) | ||
.reduce((result, element, index) => { | ||
if (index) { | ||
result.push( | ||
// eslint-disable-next-line react/no-array-index-key | ||
<Divider key={`separator_${index}`}> | ||
{dateDivider} | ||
</Divider>, | ||
); | ||
} | ||
result.push(element); | ||
return result; | ||
}, []) | ||
); | ||
return renderCustomInputs(datePlaceholder, elementFunctions); | ||
} | ||
renderCustomTimeInputs() { | ||
const { timeDivider, timePlaceholder } = this; | ||
const { timePlaceholder } = this; | ||
const elementFunctions = { | ||
'hour-12': this.renderHour12, | ||
'hour-24': this.renderHour24, | ||
minute: this.renderMinute, | ||
second: this.renderSecond, | ||
ampm: this.renderAmPm, | ||
}; | ||
return ( | ||
timePlaceholder | ||
.split(timeDivider) | ||
.map((part) => { | ||
switch (part) { | ||
case 'hour-12': return this.renderHour12(); | ||
case 'hour-24': return this.renderHour24(); | ||
case 'minute': return this.renderMinute(); | ||
case 'second': return this.renderSecond(); | ||
case 'ampm': return this.renderAmPm(); | ||
default: return null; | ||
} | ||
}) | ||
.filter(Boolean) | ||
.reduce((result, element, index) => { | ||
if (index && element.key !== 'ampm') { | ||
result.push( | ||
// eslint-disable-next-line react/no-array-index-key | ||
<Divider key={`separator_${index}`}> | ||
{timeDivider} | ||
</Divider>, | ||
); | ||
} | ||
result.push(element); | ||
return result; | ||
}, []) | ||
); | ||
return renderCustomInputs(timePlaceholder, elementFunctions); | ||
} | ||
@@ -645,3 +612,7 @@ | ||
return ( | ||
<div className={className}> | ||
<div | ||
className={className} | ||
onClick={this.onClick} | ||
role="presentation" | ||
> | ||
{this.renderNativeInput()} | ||
@@ -648,0 +619,0 @@ {this.renderCustomDateInputs()} |
@@ -41,16 +41,2 @@ import React, { PureComponent } from 'react'; | ||
componentDidMount() { | ||
document.addEventListener('mousedown', this.onClick); | ||
} | ||
componentWillUnmount() { | ||
document.removeEventListener('mousedown', this.onClick); | ||
} | ||
onClick = (event) => { | ||
if (this.wrapper && !this.wrapper.contains(event.target)) { | ||
this.closeWidgets(); | ||
} | ||
} | ||
onDateChange = (value, closeWidgets = true) => { | ||
@@ -118,2 +104,12 @@ const { value: prevValue } = this.props; | ||
onBlur = () => { | ||
const { onBlur } = this.props; | ||
if (onBlur) { | ||
onBlur(event); | ||
} | ||
this.closeWidgets(); | ||
} | ||
openClock = () => { | ||
@@ -243,3 +239,3 @@ this.setState({ | ||
ref={(ref) => { | ||
if (!ref) { | ||
if (!ref || !isCalendarOpen) { | ||
return; | ||
@@ -253,3 +249,10 @@ } | ||
if (collisions.collidedBottom) { | ||
ref.classList.add(`${className}--above-label`); | ||
const overflowTopAfterChange = ( | ||
collisions.overflowTop + ref.clientHeight + this.wrapper.clientHeight | ||
); | ||
// If it's going to make situation any better, display the calendar above the input | ||
if (overflowTopAfterChange < collisions.overflowBottom) { | ||
ref.classList.add(`${className}--above-label`); | ||
} | ||
} | ||
@@ -295,3 +298,3 @@ }} | ||
ref={(ref) => { | ||
if (!ref) { | ||
if (!ref || !isClockOpen) { | ||
return; | ||
@@ -305,3 +308,10 @@ } | ||
if (collisions.collidedBottom) { | ||
ref.classList.add(`${className}--above-label`); | ||
const overflowTopAfterChange = ( | ||
collisions.overflowTop + ref.clientHeight + this.wrapper.clientHeight | ||
); | ||
// If it's going to make situation any better, display the calendar above the input | ||
if (overflowTopAfterChange < collisions.overflowBottom) { | ||
ref.classList.add(`${className}--above-label`); | ||
} | ||
} | ||
@@ -334,3 +344,10 @@ }} | ||
onFocus={this.onFocus} | ||
ref={(ref) => { this.wrapper = ref; }} | ||
onBlur={this.onBlur} | ||
ref={(ref) => { | ||
if (!ref) { | ||
return; | ||
} | ||
this.wrapper = ref; | ||
}} | ||
> | ||
@@ -337,0 +354,0 @@ {this.renderInputs()} |
@@ -1,18 +0,3 @@ | ||
import { | ||
formatDate, | ||
formatTime, | ||
} from '../dateFormatter'; | ||
import { formatDate } from '../dateFormatter'; | ||
const hasFullICU = (() => { | ||
try { | ||
const date = new Date(2018, 0, 1, 21); | ||
const formatter = new Intl.DateTimeFormat('de-DE', { hour: 'numeric' }); | ||
return formatter.format(date) === '21'; | ||
} catch (err) { | ||
return false; | ||
} | ||
})(); | ||
const itIfFullICU = hasFullICU ? it : it.skip; | ||
describe('formatDate', () => { | ||
@@ -27,19 +12,1 @@ it('returns proper full numeric date', () => { | ||
}); | ||
describe('formatTime', () => { | ||
it('returns proper full time (12-hour format)', () => { | ||
const date = new Date(2017, 1, 1, 13, 27); | ||
const formattedTime = formatTime(date, 'en-US'); | ||
expect(formattedTime).toBe('1:27:00 PM'); | ||
}); | ||
itIfFullICU('returns proper full time (24-hour format)', () => { | ||
const date = new Date(2017, 1, 1, 13, 27); | ||
const formattedTime = formatTime(date, 'de-DE'); | ||
expect(formattedTime).toBe('13:27:00'); | ||
}); | ||
}); |
@@ -9,3 +9,3 @@ import getUserLocale from 'get-user-locale'; | ||
*/ | ||
const getFormatter = (options, locale) => { | ||
export const getFormatter = (options, locale) => { | ||
if (!locale) { | ||
@@ -34,6 +34,1 @@ // Default parameter is not enough as it does not protect us from null values | ||
)(date); | ||
export const formatTime = (date, locale) => getFormatter( | ||
{ hour: 'numeric', minute: 'numeric', second: 'numeric' }, | ||
locale, | ||
)(date); |
@@ -6,1 +6,5 @@ export { | ||
} from 'react-date-picker/dist/shared/utils'; | ||
export { | ||
getAmPmLabels, | ||
} from 'react-time-picker/dist/shared/utils'; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
125890
3140
Updatedreact-date-picker@^7.1.0
Updatedreact-time-picker@^3.1.0