Skip to content

Commit a9b5cf7

Browse files
committed
MM/DD/YYYY, DD/MM/YYYY, and YYYY/MM/DD formats. Closes pushtell#3. React 15 compatibility. Remove valueLink as it's been depreciated.
Test of pre and post-commit hooks. Test of pre and post-commit hooks. Document date format property.
1 parent 2eb2aba commit a9b5cf7

File tree

6 files changed

+288
-214
lines changed

6 files changed

+288
-214
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Please [★ on GitHub](https://github.com/pushtell/react-bootstrap-date-picker)!
2828

2929
## Installation
3030

31-
`react-bootstrap-date-picker` is compatible with React 0.14.x.
31+
`react-bootstrap-date-picker` is compatible with React 0.14.x and 0.15.x.
3232

3333
```bash
3434
npm install react-bootstrap-date-picker
@@ -71,6 +71,10 @@ DatePicker component. Renders as a [react-bootstrap input element](https://react
7171
[Input element](https://react-bootstrap.github.io/components.html#forms) properties are passed through to the input element.
7272

7373
* **Properties:**
74+
* `dateFormat` - Date format. `"MM/DD/YYYY"` or `"DD/MM/YYYY"` or `"YYYY/MM/DD"`
75+
* **Optional**
76+
* **Type:** `string`
77+
* **Example:** `"MM/DD/YYYY"`
7478
* `clearButtonElement` - Character or component to use for the clear button.
7579
* **Optional**
7680
* **Type:** `string` or `ReactClass`

example/app.jsx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,12 @@ import NavBar from "react-bootstrap/lib/NavBar";
77
import Nav from "react-bootstrap/lib/Nav";
88
import NavItem from "react-bootstrap/lib/NavItem";
99
import DatePicker from "../src/index.jsx";
10-
import LinkedStateMixin from 'react-addons-linked-state-mixin';
1110
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
1211

1312
const spanishDayLabels = ['Dom', 'Lu', 'Ma', 'Mx', 'Ju', 'Vi', 'Sab'];
1413
const spanishMonthLabels = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'];
1514

1615
const App = React.createClass({
17-
mixins: [LinkedStateMixin],
1816
getInitialState() {
1917
return {
2018
date: new Date().toISOString(),
@@ -59,47 +57,57 @@ const App = React.createClass({
5957
</Row>
6058
<Row>
6159
<Col xs={12}>
62-
<h2>LinkState</h2>
60+
<h2>Blur and Focus Events</h2>
6361
</Col>
6462
</Row>
6563
<Row>
6664
<Col sm={6}>
6765
<form>
68-
<DatePicker placeholder="Placeholder" valueLink={this.linkState('date')} />
66+
<DatePicker placeholder="Placeholder" help="Open console to see focus/blur logging." value={this.state.date} onFocus={() => {console.log("Focus")}} onBlur={() => {console.log("Blur")}}/>
6967
</form>
7068
</Col>
7169
</Row>
7270
<Row>
7371
<Col xs={12}>
74-
<h2>Blur and Focus Events</h2>
72+
<h2>Styles</h2>
7573
</Col>
7674
</Row>
7775
<Row>
78-
<Col sm={6}>
76+
<Col sm={4}>
7977
<form>
80-
<DatePicker placeholder="Placeholder" help="Open console to see focus/blur logging." value={this.state.date} onFocus={() => {console.log("Focus")}} onBlur={() => {console.log("Blur")}}/>
78+
<DatePicker bsStyle="success" label="Success" placeholder="Placeholder" help="Help" />
79+
</form>
80+
</Col>
81+
<Col sm={4}>
82+
<form>
83+
<DatePicker bsStyle="warning" label="Warning" placeholder="Placeholder" value={this.state.date} help="Help" />
84+
</form>
85+
</Col>
86+
<Col sm={4}>
87+
<form>
88+
<DatePicker bsStyle="error" label="Error" placeholder="Placeholder" value={this.state.date} help="Help" />
8189
</form>
8290
</Col>
8391
</Row>
8492
<Row>
8593
<Col xs={12}>
86-
<h2>Styles</h2>
94+
<h2>Date Format</h2>
8795
</Col>
8896
</Row>
8997
<Row>
9098
<Col sm={4}>
9199
<form>
92-
<DatePicker bsStyle="success" label="Success" placeholder="Placeholder" help="Help" />
100+
<DatePicker label="MM/DD/YYYY" dateFormat="MM/DD/YYYY" onChange={this.handleChange} value={this.state.date} help="Help" />
93101
</form>
94102
</Col>
95103
<Col sm={4}>
96104
<form>
97-
<DatePicker bsStyle="warning" label="Warning" placeholder="Placeholder" value={this.state.date} help="Help" />
105+
<DatePicker label="DD/MM/YYYY" dateFormat="DD/MM/YYYY" onChange={this.handleChange} value={this.state.date} help="Help" />
98106
</form>
99107
</Col>
100108
<Col sm={4}>
101109
<form>
102-
<DatePicker bsStyle="error" label="Error" placeholder="Placeholder" value={this.state.date} help="Help" />
110+
<DatePicker label="YYYY/MM/DD" dateFormat="YYYY/MM/DD" onChange={this.handleChange} value={this.state.date} help="Help" />
103111
</form>
104112
</Col>
105113
</Row>

lib/index.js

Lines changed: 81 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use strict';
22

3-
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; // See http://jszen.blogspot.com/2007/03/how-to-build-simple-calendar-with.html for calendar logic.
4-
53
Object.defineProperty(exports, "__esModule", {
64
value: true
75
});
86

7+
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; // See http://jszen.blogspot.com/2007/03/how-to-build-simple-calendar-with.html for calendar logic.
8+
99
var _react = require('react');
1010

1111
var _react2 = _interopRequireDefault(_react);
@@ -28,19 +28,12 @@ var _OverlayTrigger2 = _interopRequireDefault(_OverlayTrigger);
2828

2929
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
3030

31-
var makeInputValueString = function makeInputValueString(date) {
32-
var month = date.getMonth() + 1;
33-
var day = date.getDate();
34-
return (month > 9 ? month : "0" + month) + "/" + (day > 9 ? day : "0" + day) + "/" + date.getFullYear();
35-
};
36-
3731
var CalendarHeader = _react2.default.createClass({
3832
displayName: "DatePickerHeader",
3933
propTypes: {
4034
displayDate: _react2.default.PropTypes.object.isRequired,
4135
onChange: _react2.default.PropTypes.func.isRequired,
4236
monthLabels: _react2.default.PropTypes.array.isRequired,
43-
onDateClick: _react2.default.PropTypes.func.isRequired,
4437
previousButtonElement: _react2.default.PropTypes.oneOfType([_react2.default.PropTypes.string, _react2.default.PropTypes.object]).isRequired,
4538
nextButtonElement: _react2.default.PropTypes.oneOfType([_react2.default.PropTypes.string, _react2.default.PropTypes.object]).isRequired
4639
},
@@ -65,7 +58,7 @@ var CalendarHeader = _react2.default.createClass({
6558
),
6659
_react2.default.createElement(
6760
'span',
68-
{ onClick: this.props.onDateClick },
61+
null,
6962
this.props.monthLabels[this.props.displayDate.getMonth()],
7063
' ',
7164
this.props.displayDate.getFullYear()
@@ -182,35 +175,40 @@ exports.default = _react2.default.createClass({
182175
propTypes: {
183176
value: _react2.default.PropTypes.string,
184177
cellPadding: _react2.default.PropTypes.string,
178+
placeholder: _react2.default.PropTypes.string,
185179
dayLabels: _react2.default.PropTypes.array,
186180
monthLabels: _react2.default.PropTypes.array,
187181
onChange: _react2.default.PropTypes.func,
188182
clearButtonElement: _react2.default.PropTypes.oneOfType([_react2.default.PropTypes.string, _react2.default.PropTypes.object]),
189183
previousButtonElement: _react2.default.PropTypes.oneOfType([_react2.default.PropTypes.string, _react2.default.PropTypes.object]),
190184
nextButtonElement: _react2.default.PropTypes.oneOfType([_react2.default.PropTypes.string, _react2.default.PropTypes.object]),
191-
calendarPlacement: _react2.default.PropTypes.string
185+
calendarPlacement: _react2.default.PropTypes.string,
186+
dateFormat: _react2.default.PropTypes.oneOf(['MM/DD/YYYY', 'DD/MM/YYYY', 'YYYY/MM/DD'])
192187
},
193188
getDefaultProps: function getDefaultProps() {
189+
var language = (window.navigator.userLanguage || window.navigator.language || '').toLowerCase();
190+
var dateFormat = !language || language === "en-us" ? 'MM/DD/YYYY' : 'DD/MM/YYYY';
194191
return {
195192
cellPadding: "5px",
196193
dayLabels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
197194
monthLabels: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
198-
placeholder: "MM/DD/YYYY",
199195
clearButtonElement: "×",
200196
previousButtonElement: "<",
201197
nextButtonElement: ">",
202-
calendarPlacement: "bottom"
198+
calendarPlacement: "bottom",
199+
dateFormat: dateFormat
203200
};
204201
},
205202
getInitialState: function getInitialState() {
206-
var state = this.makeDateValues(this.props.value ? this.props.value : this.props.valueLink ? this.props.valueLink.value : null);
203+
var state = this.makeDateValues(this.props.value);
207204
state.focused = false;
205+
state.placeholder = this.props.placeholder || this.props.dateFormat;
208206
return state;
209207
},
210208
makeDateValues: function makeDateValues(isoString) {
211-
var displayDate = undefined;
209+
var displayDate = void 0;
212210
var selectedDate = isoString ? new Date(isoString) : null;
213-
var inputValue = isoString ? makeInputValueString(selectedDate) : null;
211+
var inputValue = isoString ? this.makeInputValueString(selectedDate) : null;
214212
if (selectedDate) {
215213
displayDate = new Date(selectedDate);
216214
} else {
@@ -232,9 +230,6 @@ exports.default = _react2.default.createClass({
232230
if (this.props.onChange) {
233231
this.props.onChange(null);
234232
}
235-
if (this.props.valueLink && this.props.valueLink.requestChange) {
236-
this.props.valueLink.requestChange(null);
237-
}
238233
},
239234
handleHide: function handleHide(e) {
240235
if (document.activeElement === this.refs.input.getInputDOMNode()) {
@@ -269,24 +264,52 @@ exports.default = _react2.default.createClass({
269264
this.props.onBlur(e);
270265
}
271266
},
272-
handleHeaderDateClick: function handleHeaderDateClick(e) {},
273267
getValue: function getValue() {
274268
return this.state.selectedDate ? this.state.selectedDate.toISOString() : null;
275269
},
270+
makeInputValueString: function makeInputValueString(date) {
271+
var month = date.getMonth() + 1;
272+
var day = date.getDate();
273+
if (this.props.dateFormat === "MM/DD/YYYY") {
274+
return (month > 9 ? month : "0" + month) + "/" + (day > 9 ? day : "0" + day) + "/" + date.getFullYear();
275+
} else if (this.props.dateFormat === "DD/MM/YYYY") {
276+
return (day > 9 ? day : "0" + day) + "/" + (month > 9 ? month : "0" + month) + "/" + date.getFullYear();
277+
} else {
278+
return date.getFullYear() + "/" + (month > 9 ? month : "0" + month) + "/" + (day > 9 ? day : "0" + day);
279+
}
280+
},
276281
handleInputChange: function handleInputChange(e) {
277282
var inputValue = this.refs.input.getValue();
278283
inputValue = inputValue.replace(/(-|\/\/)/g, '/');
279-
var month = inputValue.slice(0, 2).replace(/[^0-9]/g, '');
280-
var day = inputValue.slice(3, 5).replace(/[^0-9]/g, '');
281-
var year = inputValue.slice(6, 10).replace(/[^0-9]/g, '');
284+
var month = void 0,
285+
day = void 0,
286+
year = void 0;
287+
if (this.props.dateFormat === "MM/DD/YYYY") {
288+
month = inputValue.slice(0, 2).replace(/[^0-9]/g, '');
289+
day = inputValue.slice(3, 5).replace(/[^0-9]/g, '');
290+
year = inputValue.slice(6, 10).replace(/[^0-9]/g, '');
291+
} else if (this.props.dateFormat === "DD/MM/YYYY") {
292+
day = inputValue.slice(0, 2).replace(/[^0-9]/g, '');
293+
month = inputValue.slice(3, 5).replace(/[^0-9]/g, '');
294+
year = inputValue.slice(6, 10).replace(/[^0-9]/g, '');
295+
} else {
296+
year = inputValue.slice(0, 4).replace(/[^0-9]/g, '');
297+
month = inputValue.slice(5, 7).replace(/[^0-9]/g, '');
298+
day = inputValue.slice(8, 10).replace(/[^0-9]/g, '');
299+
}
300+
282301
var monthInteger = parseInt(month, 10);
283302
var dayInteger = parseInt(day, 10);
284303
var yearInteger = parseInt(year, 10);
285304
if (!isNaN(monthInteger) && !isNaN(dayInteger) && !isNaN(yearInteger) && monthInteger <= 12 && dayInteger <= 31 && yearInteger > 999) {
286305
var selectedDate = new Date();
287-
selectedDate.setDate(dayInteger);
288-
selectedDate.setMonth(monthInteger - 1);
306+
selectedDate.setHours(12);
307+
selectedDate.setMinutes(0);
308+
selectedDate.setSeconds(0);
309+
selectedDate.setMilliseconds(0);
289310
selectedDate.setYear(yearInteger);
311+
selectedDate.setMonth(monthInteger - 1);
312+
selectedDate.setDate(dayInteger);
290313
this.setState({
291314
selectedDate: selectedDate,
292315
displayDate: selectedDate,
@@ -295,19 +318,34 @@ exports.default = _react2.default.createClass({
295318
if (this.props.onChange) {
296319
this.props.onChange(selectedDate.toISOString());
297320
}
298-
if (this.props.valueLink && this.props.valueLink.requestChange) {
299-
this.props.valueLink.requestChange(selectedDate.toISOString());
300-
}
301321
}
302-
inputValue = month + inputValue.slice(2, 3).replace(/[^\/]/g, '') + day + inputValue.slice(5, 6).replace(/[^\/]/g, '') + year;
303-
if (this.state.inputValue && inputValue.length > this.state.inputValue.length) {
304-
if (inputValue.length == 2) {
305-
inputValue += "/";
322+
if (this.props.dateFormat === "MM/DD/YYYY") {
323+
inputValue = month + inputValue.slice(2, 3).replace(/[^\/]/g, '') + day + inputValue.slice(5, 6).replace(/[^\/]/g, '') + year;
324+
} else if (this.props.dateFormat === "DD/MM/YYYY") {
325+
inputValue = day + inputValue.slice(2, 3).replace(/[^\/]/g, '') + month + inputValue.slice(5, 6).replace(/[^\/]/g, '') + year;
326+
} else {
327+
inputValue = year + inputValue.slice(4, 5).replace(/[^\/]/g, '') + month + inputValue.slice(7, 8).replace(/[^\/]/g, '');
328+
}
329+
if (this.props.dateFormat === "YYYY/MM/DD") {
330+
if (this.state.inputValue && inputValue.length > this.state.inputValue.length) {
331+
if (inputValue.length == 4) {
332+
inputValue += "/";
333+
}
334+
if (inputValue.length == 7) {
335+
inputValue += "/";
336+
}
337+
inputValue = inputValue.slice(0, 10);
306338
}
307-
if (inputValue.length == 5) {
308-
inputValue += "/";
339+
} else {
340+
if (this.state.inputValue && inputValue.length > this.state.inputValue.length) {
341+
if (inputValue.length == 2) {
342+
inputValue += "/";
343+
}
344+
if (inputValue.length == 5) {
345+
inputValue += "/";
346+
}
347+
inputValue = inputValue.slice(0, 10);
309348
}
310-
inputValue = inputValue.slice(0, 10);
311349
}
312350
this.setState({
313351
inputValue: inputValue
@@ -320,7 +358,7 @@ exports.default = _react2.default.createClass({
320358
},
321359
onChangeDate: function onChangeDate(newSelectedDate) {
322360
this.setState({
323-
inputValue: makeInputValueString(newSelectedDate),
361+
inputValue: this.makeInputValueString(newSelectedDate),
324362
selectedDate: newSelectedDate,
325363
displayDate: newSelectedDate,
326364
value: newSelectedDate.toISOString()
@@ -329,12 +367,9 @@ exports.default = _react2.default.createClass({
329367
if (this.props.onChange) {
330368
this.props.onChange(newSelectedDate.toISOString());
331369
}
332-
if (this.props.valueLink && this.props.valueLink.requestChange) {
333-
this.props.valueLink.requestChange(newSelectedDate.toISOString());
334-
}
335370
},
336371
componentWillReceiveProps: function componentWillReceiveProps(newProps) {
337-
var value = newProps.value ? newProps.value : newProps.valueLink ? newProps.valueLink.value : null;
372+
var value = newProps.value;
338373
if (this.getValue() !== value) {
339374
this.setState(this.makeDateValues(value));
340375
}
@@ -344,9 +379,9 @@ exports.default = _react2.default.createClass({
344379
previousButtonElement: this.props.previousButtonElement,
345380
nextButtonElement: this.props.nextButtonElement,
346381
displayDate: this.state.displayDate,
347-
onDateClick: this.handleHeaderDateClick,
348382
onChange: this.onChangeMonth,
349-
monthLabels: this.props.monthLabels });
383+
monthLabels: this.props.monthLabels,
384+
dateFormat: this.props.dateFormat });
350385
var popOver = _react2.default.createElement(
351386
_Popover2.default,
352387
{ id: 'calendar', title: calendarHeader },
@@ -360,16 +395,15 @@ exports.default = _react2.default.createClass({
360395
);
361396
return _react2.default.createElement(
362397
'div',
363-
null,
398+
{ id: this.props.id ? this.props.id + "_container" : null },
364399
_react2.default.createElement(
365400
_OverlayTrigger2.default,
366401
{ ref: 'overlay', trigger: 'click', rootClose: true, placement: this.props.calendarPlacement, overlay: popOver, delayHide: 100 },
367402
_react2.default.createElement(_Input2.default, _extends({}, this.props, {
368-
value: this.state.inputValue,
403+
value: this.state.inputValue || '',
369404
ref: 'input',
370405
type: 'text',
371-
valueLink: null,
372-
placeholder: this.state.focused ? "MM/DD/YYYY" : this.props.placeholder,
406+
placeholder: this.state.focused ? this.props.dateFormat : this.state.placeholder,
373407
onFocus: this.handleFocus,
374408
onBlur: this.handleBlur,
375409
onChange: this.handleInputChange,
@@ -378,7 +412,7 @@ exports.default = _react2.default.createClass({
378412
id: null
379413
}))
380414
),
381-
_react2.default.createElement('input', { type: 'hidden', id: this.props.id, name: this.props.name, value: this.state.value })
415+
_react2.default.createElement('input', { type: 'hidden', id: this.props.id, name: this.props.name, value: this.state.value || '' })
382416
);
383417
}
384418
});

0 commit comments

Comments
 (0)