diff --git a/.circleci/config.yml b/.circleci/config.yml index 5a03e95e0..76feecdba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,6 +12,7 @@ jobs: environment: PYTHON_VERSION: py27 PERCY_PARALLEL_TOTAL: '-1' + - image: percyio/agent steps: - checkout @@ -53,6 +54,11 @@ jobs: python --version npm run test + - run: + name: Percy TearDown + command: percy --finalize -all + when: on_fail + 'python-3.6': <<: *test-template docker: @@ -73,7 +79,7 @@ workflows: version: 2 build: jobs: - - 'python-2.7' + - python-2.7 - "percy-finalize": requires: - 'python-2.7' diff --git a/src/components/DatePickerRange.react.js b/src/components/DatePickerRange.react.js index 42c895fe0..2faa6dfee 100644 --- a/src/components/DatePickerRange.react.js +++ b/src/components/DatePickerRange.react.js @@ -46,7 +46,7 @@ export default class DatePickerRange extends Component { } onDatesChange({startDate: start_date, endDate: end_date}) { - const {setProps, updatemode} = this.props; + const {setProps, updatemode, clearable} = this.props; const oldMomentDates = convertToMoment(this.state, [ 'start_date', @@ -71,6 +71,16 @@ export default class DatePickerRange extends Component { }); } } + + if ( + clearable && + !start_date && + !end_date && + (oldMomentDates.start_date !== start_date || + oldMomentDates.end_date !== end_date) + ) { + setProps({start_date: null, end_date: null}); + } } isOutsideRange(date) { diff --git a/tests/integration/calendar/test_calendar_props.py b/tests/integration/calendar/test_calendar_props.py new file mode 100644 index 000000000..d74ffe50f --- /dev/null +++ b/tests/integration/calendar/test_calendar_props.py @@ -0,0 +1,110 @@ +import itertools +import pytest + +import dash_core_components as dcc +import dash_html_components as html +import dash +from dash.dependencies import Input, Output +import dash.testing.wait as wait + +DAY_SELECTOR = 'div[data-visible="true"] td.CalendarDay' + + +@pytest.mark.DCC594 +def test_cdpr001_date_clearable_true_works(dash_duo): + + app = dash.Dash(__name__) + app.layout = html.Div([dcc.DatePickerRange(id="dpr", clearable=True)]) + + dash_duo.start_server(app) + + start_date = dash_duo.find_element('input[aria-label="Start Date"]') + end_date = dash_duo.find_element('input[aria-label="End Date"]') + + start_date.click() + + dash_duo.find_elements(DAY_SELECTOR)[0].click() + dash_duo.find_elements(DAY_SELECTOR)[-1].click() + + close_btn = dash_duo.wait_for_element('button[aria-label="Clear Dates"]') + + assert start_date.get_attribute("value") and end_date.get_attribute( + "value" + ), "both start date and end date should get values" + + close_btn.click() + assert not start_date.get_attribute( + "value" + ) and not end_date.get_attribute( + "value" + ), "both start and end dates should be cleared" + + +def test_cdpr002_updatemodes(dash_duo): + app = dash.Dash(__name__) + + app.layout = html.Div( + [ + dcc.DatePickerRange( + id="date-picker-range", + start_date_id="startDate", + end_date_id="endDate", + start_date_placeholder_text="Select a start date!", + end_date_placeholder_text="Select an end date!", + updatemode="bothdates", + ), + html.Div(id="date-picker-range-output"), + ] + ) + + @app.callback( + Output("date-picker-range-output", "children"), + [ + Input("date-picker-range", "start_date"), + Input("date-picker-range", "end_date"), + ], + ) + def update_output(start_date, end_date): + return "{} - {}".format(start_date, end_date) + + dash_duo.start_server(app=app) + + start_date = dash_duo.find_element("#startDate") + start_date.click() + + end_date = dash_duo.find_element("#endDate") + end_date.click() + + assert ( + dash_duo.find_element("#date-picker-range-output").text + == "None - None" + ), "the output should not update when both clicked but no selection happen" + + start_date.click() + + dash_duo.find_elements(DAY_SELECTOR)[4].click() + assert ( + dash_duo.find_element("#date-picker-range-output").text + == "None - None" + ), "the output should not update when only one is selected" + + eday = dash_duo.find_elements(DAY_SELECTOR)[-4] + wait.until(lambda: eday.is_displayed() and eday.is_enabled(), timeout=2) + eday.click() + + date_tokens = set(start_date.get_attribute("value").split("/")) + date_tokens.update(end_date.get_attribute("value").split("/")) + + assert ( + set( + itertools.chain( + *[ + _.split("-") + for _ in dash_duo.find_element( + "#date-picker-range-output" + ).text.split(" - ") + ] + ) + ) + == date_tokens + ), "date should match the callback output" diff --git a/tests/test_integration.py b/tests/test_integration.py index 34584340f..0c26074a1 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1186,60 +1186,6 @@ def test_graphs_without_ids(self): self.assertNotEqual(graph_1.get_attribute('id'), graph_2.get_attribute('id')) - def test_datepickerrange_updatemodes(self): - app = dash.Dash(__name__) - - app.layout = html.Div([ - dcc.DatePickerRange( - id='date-picker-range', - start_date_id='startDate', - end_date_id='endDate', - start_date_placeholder_text='Select a start date!', - end_date_placeholder_text='Select an end date!', - updatemode='bothdates' - ), - html.Div(id='date-picker-range-output') - ]) - - @app.callback( - dash.dependencies.Output('date-picker-range-output', 'children'), - [dash.dependencies.Input('date-picker-range', 'start_date'), - dash.dependencies.Input('date-picker-range', 'end_date')]) - def update_output(start_date, end_date): - return '{} - {}'.format(start_date, end_date) - - self.startServer(app=app) - - start_date = self.wait_for_element_by_css_selector('#startDate') - start_date.click() - - end_date = self.wait_for_element_by_css_selector('#endDate') - end_date.click() - - self.wait_for_text_to_equal('#date-picker-range-output', 'None - None') - - # using mouse click with fixed day range, this can be improved - # once we start refactoring the test structure - start_date.click() - - sday = self.driver.find_element_by_xpath("//td[text()='1' and @tabindex='0']") - sday.click() - self.wait_for_text_to_equal('#date-picker-range-output', 'None - None') - - eday = self.driver.find_elements_by_xpath("//td[text()='28']")[1] - eday.click() - - date_tokens = set(start_date.get_attribute('value').split('/')) - date_tokens.update(end_date.get_attribute('value').split('/')) - - self.assertEqual( - set(itertools.chain(*[ - _.split('-') - for _ in self.driver.find_element_by_css_selector( - '#date-picker-range-output').text.split(' - ')])), - date_tokens, - "date should match the callback output") - def test_interval(self): app = dash.Dash(__name__) app.layout = html.Div([