Skip to content

Commit 4b03e5a

Browse files
authored
Merge pull request #1976 from nickmelnikov82/fix-is-outside-range-function
Removed unnecessary recalculation of dates
2 parents 5ca0339 + 595ef5f commit 4b03e5a

File tree

4 files changed

+113
-17
lines changed

4 files changed

+113
-17
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
88

99
- [#2015](https://github.com/plotly/dash/pull/2015) Fix bug [#1854](https://github.com/plotly/dash/issues/1854) in which the combination of row_selectable="single or multi" and filter_action="native" caused the JS error.
1010

11+
- [#1976](https://github.com/plotly/dash/pull/1976) Fix [#1962](https://github.com/plotly/dash/issues/1962) in which DatePickerSingle and DatePickerRange are extremely slow when provided a long list of disabled_days.
12+
1113
### Changed
1214

1315
- [#2016](https://github.com/plotly/dash/pull/2016) Drop the 375px width from default percy_snapshot calls, keep only 1280px

components/dash-core-components/src/fragments/DatePickerRange.react.js

+30-10
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,30 @@ export default class DatePickerRange extends Component {
3030
state.end_date = newProps.end_date;
3131
}
3232

33+
if (
34+
force ||
35+
newProps.max_date_allowed !== this.props.max_date_allowed
36+
) {
37+
state.max_date_allowed = convertToMoment(newProps, [
38+
'max_date_allowed',
39+
]).max_date_allowed;
40+
}
41+
42+
if (
43+
force ||
44+
newProps.min_date_allowed !== this.props.min_date_allowed
45+
) {
46+
state.min_date_allowed = convertToMoment(newProps, [
47+
'min_date_allowed',
48+
]).min_date_allowed;
49+
}
50+
51+
if (force || newProps.disabled_days !== this.props.disabled_days) {
52+
state.disabled_days = convertToMoment(newProps, [
53+
'disabled_days',
54+
]).disabled_days;
55+
}
56+
3357
if (Object.keys(state).length) {
3458
this.setState(state);
3559
}
@@ -82,17 +106,13 @@ export default class DatePickerRange extends Component {
82106
}
83107

84108
isOutsideRange(date) {
85-
const {max_date_allowed, min_date_allowed, disabled_days} =
86-
convertToMoment(this.props, [
87-
'max_date_allowed',
88-
'min_date_allowed',
89-
'disabled_days',
90-
]);
91-
92109
return (
93-
(min_date_allowed && date.isBefore(min_date_allowed)) ||
94-
(max_date_allowed && date.isAfter(max_date_allowed)) ||
95-
(disabled_days && disabled_days.some(d => date.isSame(d, 'day')))
110+
(this.state.min_date_allowed &&
111+
date.isBefore(this.state.min_date_allowed)) ||
112+
(this.state.max_date_allowed &&
113+
date.isAfter(this.state.max_date_allowed)) ||
114+
(this.state.disabled_days &&
115+
this.state.disabled_days.some(d => date.isSame(d, 'day')))
96116
);
97117
}
98118

components/dash-core-components/src/fragments/DatePickerSingle.react.js

+44-7
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,60 @@ import convertToMoment from '../utils/convertToMoment';
1010
export default class DatePickerSingle extends Component {
1111
constructor() {
1212
super();
13+
this.propsToState = this.propsToState.bind(this);
1314
this.isOutsideRange = this.isOutsideRange.bind(this);
1415
this.onDateChange = this.onDateChange.bind(this);
1516
this.state = {focused: false};
1617
}
1718

18-
isOutsideRange(date) {
19-
const {max_date_allowed, min_date_allowed, disabled_days} =
20-
convertToMoment(this.props, [
19+
propsToState(newProps, force = false) {
20+
const state = {};
21+
22+
if (
23+
force ||
24+
newProps.max_date_allowed !== this.props.max_date_allowed
25+
) {
26+
state.max_date_allowed = convertToMoment(newProps, [
2127
'max_date_allowed',
28+
]).max_date_allowed;
29+
}
30+
31+
if (
32+
force ||
33+
newProps.min_date_allowed !== this.props.min_date_allowed
34+
) {
35+
state.min_date_allowed = convertToMoment(newProps, [
2236
'min_date_allowed',
37+
]).min_date_allowed;
38+
}
39+
40+
if (force || newProps.disabled_days !== this.props.disabled_days) {
41+
state.disabled_days = convertToMoment(newProps, [
2342
'disabled_days',
24-
]);
43+
]).disabled_days;
44+
}
45+
46+
if (Object.keys(state).length) {
47+
this.setState(state);
48+
}
49+
}
2550

51+
UNSAFE_componentWillReceiveProps(newProps) {
52+
this.propsToState(newProps);
53+
}
54+
55+
UNSAFE_componentWillMount() {
56+
this.propsToState(this.props, true);
57+
}
58+
59+
isOutsideRange(date) {
2660
return (
27-
(min_date_allowed && date.isBefore(min_date_allowed)) ||
28-
(max_date_allowed && date.isAfter(max_date_allowed)) ||
29-
(disabled_days && disabled_days.some(d => date.isSame(d, 'day')))
61+
(this.state.min_date_allowed &&
62+
date.isBefore(this.state.min_date_allowed)) ||
63+
(this.state.max_date_allowed &&
64+
date.isAfter(this.state.max_date_allowed)) ||
65+
(this.state.disabled_days &&
66+
this.state.disabled_days.some(d => date.isSame(d, 'day')))
3067
);
3168
}
3269

components/dash-core-components/tests/integration/calendar/test_date_picker_single.py

+37
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from datetime import datetime, timedelta
2+
import pandas as pd
3+
import time
24

35
import pytest
46
import werkzeug
@@ -186,3 +188,38 @@ def test_dtps013_disabled_days_arent_clickable(dash_dcc):
186188
# open datepicker to take snapshot
187189
date.click()
188190
dash_dcc.percy_snapshot("dtps013 - disabled days")
191+
192+
193+
def test_dtps0014_disabed_days_timeout(dash_dcc):
194+
app = Dash(__name__)
195+
196+
min_date = pd.to_datetime("2010-01-01")
197+
max_date = pd.to_datetime("2099-01-01")
198+
disabled_days = [
199+
x for x in pd.date_range(min_date, max_date, freq="D") if x.day != 1
200+
]
201+
202+
app.layout = html.Div(
203+
[
204+
html.Label("Operating Date"),
205+
dcc.DatePickerSingle(
206+
id="dps",
207+
min_date_allowed=min_date,
208+
max_date_allowed=max_date,
209+
disabled_days=disabled_days,
210+
),
211+
]
212+
)
213+
dash_dcc.start_server(app)
214+
date = dash_dcc.wait_for_element("#dps", timeout=5)
215+
216+
"""
217+
WebDriver click() function hangs at the time of the react code
218+
execution, so it necessary to check execution time.
219+
"""
220+
start_time = time.time()
221+
date.click()
222+
assert time.time() - start_time < 5
223+
224+
dash_dcc.wait_for_element(".SingleDatePicker_picker", timeout=5)
225+
assert dash_dcc.get_logs() == []

0 commit comments

Comments
 (0)