Skip to content

Commit ed55d2b

Browse files
jimmynguycmartijnrusschen
authored andcommitted
Ability to inject times outside of default time interval (Hacker0x01#1329)
* Allow specific times outside of intervals to be injected into time list * Update docs-site * Update docs * Add tests * More specs
1 parent f715f15 commit ed55d2b

File tree

10 files changed

+440
-257
lines changed

10 files changed

+440
-257
lines changed

docs-site/src/example_components.jsx

Lines changed: 261 additions & 254 deletions
Large diffs are not rendered by default.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React from "react";
2+
import DatePicker from "react-datepicker";
3+
import moment from "moment";
4+
5+
export default class InjectTimes extends React.Component {
6+
state = {
7+
startDate: moment()
8+
.hours(16)
9+
.minutes(30)
10+
};
11+
12+
handleChange = date => {
13+
this.setState({
14+
startDate: date
15+
});
16+
};
17+
18+
render() {
19+
return (
20+
<div className="row">
21+
<pre className="column example__code">
22+
<code className="jsx">
23+
{`
24+
<DatePicker
25+
selected={this.state.startDate}
26+
onChange={this.handleChange}`}
27+
<br />
28+
<strong>{` showTimeSelect
29+
timeFormat="HH:mm"
30+
injectTimes={[moment().hours(0).minutes(1), moment().hours(12).minutes(5), moment().hours(23).minutes(59)]}
31+
dateFormat="LLL"
32+
/>
33+
`}</strong>
34+
</code>
35+
</pre>
36+
<div className="column">
37+
<DatePicker
38+
selected={this.state.startDate}
39+
onChange={this.handleChange}
40+
showTimeSelect
41+
timeFormat="HH:mm"
42+
injectTimes={[
43+
moment()
44+
.hours(0)
45+
.minutes(1),
46+
moment()
47+
.hours(12)
48+
.minutes(5),
49+
moment()
50+
.hours(23)
51+
.minutes(59)
52+
]}
53+
dateFormat="LLL"
54+
/>
55+
</div>
56+
</div>
57+
);
58+
}
59+
}

docs/datepicker.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ General datepicker component.
2929
| `id` | `string` | | |
3030
| `includeDates` | `array` | | |
3131
| `includeTimes` | `array` | | |
32+
| `injectTimes` | `array` | | |
3233
| `inline` | `bool` | | |
3334
| `isClearable` | `bool` | | |
3435
| `locale` | `string` | | |

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ General datepicker component.
3030
| `id` | `string` | | |
3131
| `includeDates` | `array` | | |
3232
| `includeTimes` | `array` | | |
33+
| `injectTimes` | `array` | | |
3334
| `inline` | `bool` | | |
3435
| `isClearable` | `bool` | | |
3536
| `locale` | `string` | | |

docs/time.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
| `excludeTimes` | `array` | | |
66
| `format` | `string` | | |
77
| `includeTimes` | `array` | | |
8+
| `injectTimes` | `array` | | |
89
| `intervals` | `number` | `30` | |
910
| `maxTime` | `object` | | |
1011
| `minTime` | `object` | | |

src/calendar.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export default class Calendar extends React.Component {
6262
highlightDates: PropTypes.instanceOf(Map),
6363
includeDates: PropTypes.array,
6464
includeTimes: PropTypes.array,
65+
injectTimes: PropTypes.array,
6566
inline: PropTypes.bool,
6667
locale: PropTypes.string,
6768
maxDate: PropTypes.object,
@@ -545,6 +546,7 @@ export default class Calendar extends React.Component {
545546
showYearDropdown={this.props.showYearDropdown}
546547
withPortal={this.props.withPortal}
547548
monthRef={this.state.monthContainer}
549+
injectTimes={this.props.injectTimes}
548550
/>
549551
);
550552
}

src/date_utils.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ export function addMinutes(date, amount) {
197197
return add(date, amount, "minutes");
198198
}
199199

200+
export function addHours(date, amount) {
201+
return add(date, amount, "hours");
202+
}
203+
200204
export function addDays(date, amount) {
201205
return add(date, amount, "days");
202206
}
@@ -490,3 +494,29 @@ export function getHightLightDaysMap(
490494

491495
return dateClasses;
492496
}
497+
498+
export function timeToInjectAfter(
499+
startOfDay,
500+
currentTime,
501+
currentMultiplier,
502+
intervals,
503+
injectedTimes
504+
) {
505+
const l = injectedTimes.length;
506+
for (let i = 0; i < l; i++) {
507+
const injectedTime = addMinutes(
508+
addHours(cloneDate(startOfDay), getHour(injectedTimes[i])),
509+
getMinute(injectedTimes[i])
510+
);
511+
const nextTime = addMinutes(
512+
cloneDate(startOfDay),
513+
(currentMultiplier + 1) * intervals
514+
);
515+
516+
if (injectedTime.isBetween(currentTime, nextTime)) {
517+
return injectedTimes[i];
518+
}
519+
}
520+
521+
return false;
522+
}

src/index.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export default class DatePicker extends React.Component {
6969
id: PropTypes.string,
7070
includeDates: PropTypes.array,
7171
includeTimes: PropTypes.array,
72+
injectTimes: PropTypes.array,
7273
inline: PropTypes.bool,
7374
isClearable: PropTypes.bool,
7475
locale: PropTypes.string,
@@ -506,6 +507,7 @@ export default class DatePicker extends React.Component {
506507
highlightDates={this.state.highlightDates}
507508
includeDates={this.props.includeDates}
508509
includeTimes={this.props.includeTimes}
510+
injectTimes={this.props.injectTimes}
509511
inline={this.props.inline}
510512
peekNextMonth={this.props.peekNextMonth}
511513
showMonthDropdown={this.props.showMonthDropdown}

src/time.jsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import {
99
cloneDate,
1010
formatDate,
1111
isTimeInDisabledRange,
12-
isTimeDisabled
12+
isTimeDisabled,
13+
timeToInjectAfter
1314
} from "./date_utils";
1415

1516
export default class Time extends React.Component {
@@ -24,7 +25,8 @@ export default class Time extends React.Component {
2425
maxTime: PropTypes.object,
2526
excludeTimes: PropTypes.array,
2627
monthRef: PropTypes.object,
27-
timeCaption: PropTypes.string
28+
timeCaption: PropTypes.string,
29+
injectTimes: PropTypes.array
2830
};
2931

3032
static get defaultProps() {
@@ -76,6 +78,12 @@ export default class Time extends React.Component {
7678
) {
7779
classes.push("react-datepicker__time-list-item--disabled");
7880
}
81+
if (
82+
this.props.injectTimes &&
83+
(getHour(time) * 60 + getMinute(time)) % this.props.intervals !== 0
84+
) {
85+
classes.push("react-datepicker__time-list-item--injected");
86+
}
7987

8088
return classes.join(" ");
8189
};
@@ -90,7 +98,21 @@ export default class Time extends React.Component {
9098
let base = getStartOfDay(newDate());
9199
const multiplier = 1440 / intervals;
92100
for (let i = 0; i < multiplier; i++) {
93-
times.push(addMinutes(cloneDate(base), i * intervals));
101+
const currentTime = addMinutes(cloneDate(base), i * intervals);
102+
times.push(currentTime);
103+
104+
if (this.props.injectTimes) {
105+
const timeToInject = timeToInjectAfter(
106+
base,
107+
currentTime,
108+
i,
109+
intervals,
110+
this.props.injectTimes
111+
);
112+
if (timeToInject) {
113+
times.push(timeToInject);
114+
}
115+
}
94116
}
95117

96118
return times.map((time, i) => (

test/inject_times_test.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from "react";
2+
import { mount } from "enzyme";
3+
import * as utils from "../src/date_utils";
4+
import { setTime, cloneDate, newDate } from "../src/date_utils";
5+
import TimeComponent from "../src/time";
6+
7+
function cloneDateWithTime(date, time) {
8+
return setTime(cloneDate(date), time);
9+
}
10+
11+
describe("TimeComponent", () => {
12+
let sandbox;
13+
14+
beforeEach(() => {
15+
sandbox = sinon.sandbox.create();
16+
});
17+
18+
afterEach(() => {
19+
sandbox.restore();
20+
});
21+
22+
it("should show times specified in injectTimes props", () => {
23+
const today = utils.getStartOfDay(utils.newDate());
24+
const timeComponent = mount(
25+
<TimeComponent
26+
injectTimes={[
27+
utils.addMinutes(cloneDate(today), 1),
28+
utils.addMinutes(cloneDate(today), 725),
29+
utils.addMinutes(cloneDate(today), 1439)
30+
]}
31+
/>
32+
);
33+
34+
const disabledItems = timeComponent.find(
35+
".react-datepicker__time-list-item--injected"
36+
);
37+
expect(disabledItems).to.have.length(3);
38+
});
39+
40+
it("should not affect existing time intervals", () => {
41+
const today = utils.getStartOfDay(utils.newDate());
42+
const timeComponent = mount(
43+
<TimeComponent
44+
timeIntervals={60}
45+
injectTimes={[
46+
utils.addMinutes(cloneDate(today), 0),
47+
utils.addMinutes(cloneDate(today), 60),
48+
utils.addMinutes(cloneDate(today), 1440)
49+
]}
50+
/>
51+
);
52+
53+
const disabledItems = timeComponent.find(
54+
".react-datepicker__time-list-item--injected"
55+
);
56+
expect(disabledItems).to.have.length(0);
57+
});
58+
});

0 commit comments

Comments
 (0)