diff --git a/CHANGELOG.md b/CHANGELOG.md index 53f1b2fa2..56fcf614c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Fixed +- [#854](https://github.com/plotly/dash-core-components/pull/854) Used `persistenceTransforms` to strip the time part of the datetime in the persited props of DatePickerSingle (date) and DatePickerRange (end_date, start_date), fixing [dcc#700](https://github.com/plotly/dash-core-components/issues/700). + ### Added - [#850](https://github.com/plotly/dash-core-components/pull/850) Add property `prependData` to `Graph` to support `Plotly.prependTraces` + refactored the existing `extendTraces` API to be a single `mergeTraces` API that can handle both `prepend` as well as `extend`. diff --git a/src/components/DatePickerRange.react.js b/src/components/DatePickerRange.react.js index 58560c6c9..b0352850d 100644 --- a/src/components/DatePickerRange.react.js +++ b/src/components/DatePickerRange.react.js @@ -1,6 +1,7 @@ import PropTypes from 'prop-types'; import React, {Component, lazy, Suspense} from 'react'; import datePickerRange from '../utils/LazyLoader/datePickerRange'; +import transformDate from '../utils/DatePickerPersistence'; const RealDatePickerRange = lazy(datePickerRange); @@ -263,6 +264,11 @@ DatePickerRange.propTypes = { persistence_type: PropTypes.oneOf(['local', 'session', 'memory']), }; +DatePickerRange.persistenceTransforms = { + end_date: transformDate, + start_date: transformDate, +}; + DatePickerRange.defaultProps = { calendar_orientation: 'horizontal', is_RTL: false, diff --git a/src/components/DatePickerSingle.react.js b/src/components/DatePickerSingle.react.js index 3391e430f..11c3cbfcb 100644 --- a/src/components/DatePickerSingle.react.js +++ b/src/components/DatePickerSingle.react.js @@ -1,6 +1,7 @@ import PropTypes from 'prop-types'; import React, {Component, lazy, Suspense} from 'react'; import datePickerSingle from '../utils/LazyLoader/datePickerSingle'; +import transformDate from '../utils/DatePickerPersistence'; const RealDateSingleRange = lazy(datePickerSingle); @@ -220,6 +221,10 @@ DatePickerSingle.propTypes = { persistence_type: PropTypes.oneOf(['local', 'session', 'memory']), }; +DatePickerSingle.persistenceTransforms = { + date: transformDate, +}; + DatePickerSingle.defaultProps = { calendar_orientation: 'horizontal', is_RTL: false, diff --git a/src/utils/DatePickerPersistence.js b/src/utils/DatePickerPersistence.js new file mode 100644 index 000000000..4a18a04f5 --- /dev/null +++ b/src/utils/DatePickerPersistence.js @@ -0,0 +1,14 @@ +import moment from 'moment'; +import {isNil} from 'ramda'; + +export default { + extract: propValue => { + if (!isNil(propValue)) { + return moment(propValue) + .startOf('day') + .format('YYYY-MM-DD'); + } + return propValue; + }, + apply: storedValue => storedValue, +}; diff --git a/tests/integration/calendar/test_date_picker_persistence.py b/tests/integration/calendar/test_date_picker_persistence.py new file mode 100644 index 000000000..f05d8847c --- /dev/null +++ b/tests/integration/calendar/test_date_picker_persistence.py @@ -0,0 +1,87 @@ +from datetime import datetime + +import dash +import dash_html_components as html +import dash_core_components as dcc +from dash.dependencies import Input, Output + + +def test_rdpr001_persisted_dps(dash_dcc): + app = dash.Dash(__name__) + app.layout = html.Div( + [ + html.Button("fire callback", id="btn", n_clicks=1), + html.Div(children=[html.Div(id="container"), html.P("dps", id="dps-p")]), + ] + ) + + # changing value of date with each callback to verify + # persistenceTransforms is stripping the time-part from the date-time + @app.callback(Output("container", "children"), [Input("btn", "n_clicks")]) + def update_output(value): + return dcc.DatePickerSingle( + id="dps", + min_date_allowed=datetime(2020, 1, 1), + max_date_allowed=datetime(2020, 1, 7), + date=datetime(2020, 1, 3, 1, 1, 1, value), + persistence=True, + persistence_type="session", + ) + + @app.callback(Output("dps-p", "children"), [Input("dps", "date")]) + def display_dps(value): + return value + + dash_dcc.start_server(app) + + dash_dcc.select_date_single("dps", day="2") + dash_dcc.wait_for_text_to_equal("#dps-p", "2020-01-02") + dash_dcc.find_element("#btn").click() + dash_dcc.wait_for_text_to_equal("#dps-p", "2020-01-02") + + +def test_rdpr002_persisted_dpr(dash_dcc): + app = dash.Dash(__name__) + app.layout = html.Div( + [ + html.Button("fire callback", id="btn", n_clicks=1), + html.Div( + children=[ + html.Div(id="container"), + html.P("dpr", id="dpr-p-start"), + html.P("dpr", id="dpr-p-end"), + ] + ), + ] + ) + + # changing value of start_date and end_date with each callback to verify + # persistenceTransforms is stripping the time-part from the date-time + @app.callback(Output("container", "children"), [Input("btn", "n_clicks")]) + def update_output(value): + return dcc.DatePickerRange( + id="dpr", + min_date_allowed=datetime(2020, 1, 1), + max_date_allowed=datetime(2020, 1, 7), + start_date=datetime(2020, 1, 3, 1, 1, 1, value), + end_date=datetime(2020, 1, 4, 1, 1, 1, value), + persistence=True, + persistence_type="session", + ) + + @app.callback(Output("dpr-p-start", "children"), [Input("dpr", "start_date")]) + def display_dpr_start(value): + return value + + @app.callback(Output("dpr-p-end", "children"), [Input("dpr", "end_date")]) + def display_dpr_end(value): + return value + + dash_dcc.start_server(app) + + dash_dcc.select_date_range("dpr", (2, 5)) + dash_dcc.wait_for_text_to_equal("#dpr-p-start", "2020-01-02") + dash_dcc.wait_for_text_to_equal("#dpr-p-end", "2020-01-05") + dash_dcc.find_element("#btn").click() + dash_dcc.wait_for_text_to_equal("#dpr-p-start", "2020-01-02") + dash_dcc.wait_for_text_to_equal("#dpr-p-end", "2020-01-05")