Skip to content

Commit 20d6b44

Browse files
honzajerabekmartijnrusschen
authored andcommitted
Calculate scroll of TimeSelect (Hacker0x01#1331)
Changed hardcoded 30px-per-item scroll in order to have selectedDate/currH scrolled down in the middle of the TimeSelect. Now it saves a reference of list-item of selectedDate (with fallback to currH when selectedDate not provided) and calculates list's default scroll based on list's height and the referenced item's offset. Add tests
1 parent 0bced9c commit 20d6b44

File tree

3 files changed

+115
-14
lines changed

3 files changed

+115
-14
lines changed

src/calendar.jsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export default class Calendar extends React.Component {
132132
this.state = {
133133
date: this.localizeDate(this.getDateInView()),
134134
selectingDate: null,
135-
monthContainer: this.monthContainer
135+
monthContainer: null
136136
};
137137
}
138138

@@ -607,7 +607,10 @@ export default class Calendar extends React.Component {
607607
};
608608

609609
renderTimeSection = () => {
610-
if (this.props.showTimeSelect) {
610+
if (
611+
this.props.showTimeSelect &&
612+
(this.state.monthContainer || this.props.showTimeSelectOnly)
613+
) {
611614
return (
612615
<Time
613616
selected={this.props.selected}

src/time.jsx

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,20 @@ export default class Time extends React.Component {
3838
};
3939
}
4040

41+
static calcCenterPosition = (listHeight, centerLiRef) => {
42+
return (
43+
centerLiRef.offsetTop - (listHeight / 2 - centerLiRef.clientHeight / 2)
44+
);
45+
};
46+
4147
componentDidMount() {
4248
// code to ensure selected time will always be in focus within time window when it first appears
43-
const multiplier = 60 / this.props.intervals;
44-
const currH = this.props.selected
45-
? getHour(this.props.selected)
46-
: getHour(newDate());
47-
this.list.scrollTop = 30 * (multiplier * currH);
49+
this.list.scrollTop = Time.calcCenterPosition(
50+
this.props.monthRef
51+
? this.props.monthRef.clientHeight - this.header.clientHeight
52+
: this.list.clientHeight,
53+
this.centerLi
54+
);
4855
}
4956

5057
handleClick = time => {
@@ -123,6 +130,14 @@ export default class Time extends React.Component {
123130
key={i}
124131
onClick={this.handleClick.bind(this, time)}
125132
className={this.liClasses(time, currH, currM)}
133+
ref={li => {
134+
if (
135+
(currH === getHour(time) && currM === getMinute(time)) ||
136+
(currH === getHour(time) && !this.centerLi)
137+
) {
138+
this.centerLi = li;
139+
}
140+
}}
126141
>
127142
{formatDate(time, format)}
128143
</li>
@@ -131,8 +146,8 @@ export default class Time extends React.Component {
131146

132147
render() {
133148
let height = null;
134-
if (this.props.monthRef) {
135-
height = this.props.monthRef.clientHeight - 39;
149+
if (this.props.monthRef && this.header) {
150+
height = this.props.monthRef.clientHeight - this.header.clientHeight;
136151
}
137152

138153
return (
@@ -143,7 +158,12 @@ export default class Time extends React.Component {
143158
: ""
144159
}`}
145160
>
146-
<div className="react-datepicker__header react-datepicker__header--time">
161+
<div
162+
className="react-datepicker__header react-datepicker__header--time"
163+
ref={header => {
164+
this.header = header;
165+
}}
166+
>
147167
<div className="react-datepicker-time__header">
148168
{this.props.timeCaption}
149169
</div>

test/time_format_test.js

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,100 @@
11
import React from "react";
22
import { mount } from "enzyme";
33
import TimeComponent from "../src/time";
4+
import moment from "moment";
45

56
describe("TimeComponent", () => {
67
let sandbox;
78

89
beforeEach(() => {
910
sandbox = sinon.sandbox.create();
11+
// mock global time to June 14, 1990 13:28:12, so test results will be constant
12+
sandbox.useFakeTimers({
13+
now: moment("1990-06-14 13:28").valueOf(),
14+
toFake: ["Date"]
15+
});
1016
});
1117

1218
afterEach(() => {
1319
sandbox.restore();
1420
});
1521

16-
it("should forward the time format provided in timeFormat props", () => {
17-
var timeComponent = mount(<TimeComponent format="HH:mm" />);
22+
describe("Format", () => {
23+
it("should forward the time format provided in timeFormat props", () => {
24+
var timeComponent = mount(<TimeComponent format="HH:mm" />);
1825

19-
var timeListItem = timeComponent.find(".react-datepicker__time-list-item");
20-
expect(timeListItem.at(0).text()).to.eq("00:00");
26+
var timeListItem = timeComponent.find(
27+
".react-datepicker__time-list-item"
28+
);
29+
expect(timeListItem.at(0).text()).to.eq("00:00");
30+
});
31+
});
32+
33+
describe("Initial position", () => {
34+
let spy;
35+
beforeEach(() => {
36+
spy = sandbox.spy(TimeComponent, "calcCenterPosition");
37+
});
38+
39+
it("should call calcCenterPosition once", () => {
40+
mount(<TimeComponent format="HH:mm" />);
41+
expect(spy.calledOnce).to.eq(true);
42+
});
43+
44+
it("should call calcCenterPosition with centerLi ref, closest to the current time", () => {
45+
mount(<TimeComponent format="HH:mm" />);
46+
expect(spy.args[0][1].innerHTML).to.eq("13:00");
47+
});
48+
49+
it("should call calcCenterPosition with centerLi ref, closest to the selected time", () => {
50+
mount(
51+
<TimeComponent format="HH:mm" selected={moment("1990-06-14 08:11")} />
52+
);
53+
expect(spy.args[0][1].innerHTML).to.eq("08:00");
54+
});
55+
56+
it("should call calcCenterPosition with centerLi ref, which is selected", () => {
57+
mount(
58+
<TimeComponent format="HH:mm" selected={moment("1990-06-14 08:00")} />
59+
);
60+
expect(
61+
spy.args[0][1].classList.contains(
62+
"react-datepicker__time-list-item--selected"
63+
)
64+
).to.be.true;
65+
});
66+
67+
it("should calculate scroll for the first item of 4 (even) items list", () => {
68+
expect(
69+
TimeComponent.calcCenterPosition(200, {
70+
offsetTop: 0,
71+
clientHeight: 50
72+
})
73+
).to.be.eq(-75);
74+
});
75+
76+
it("should calculate scroll for the last item of 4 (even) items list", () => {
77+
expect(
78+
TimeComponent.calcCenterPosition(200, {
79+
offsetTop: 150,
80+
clientHeight: 50
81+
})
82+
).to.be.eq(75);
83+
});
84+
85+
it("should calculate scroll for the first item of 3 (odd) items list", () => {
86+
expect(
87+
TimeComponent.calcCenterPosition(90, { offsetTop: 0, clientHeight: 30 })
88+
).to.be.eq(-30);
89+
});
90+
91+
it("should calculate scroll for the last item of 3 (odd) items list", () => {
92+
expect(
93+
TimeComponent.calcCenterPosition(90, {
94+
offsetTop: 60,
95+
clientHeight: 30
96+
})
97+
).to.be.eq(30);
98+
});
2199
});
22100
});

0 commit comments

Comments
 (0)