From c1afabc0a04c0ba3bd334fe2bd70ed74031ee7fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 14 Oct 2022 12:11:30 +0200 Subject: [PATCH] Add option to clear value of DatetimePicker (#3990) --- panel/models/datetime_picker.py | 2 +- panel/models/datetime_picker.ts | 13 +++++-- panel/tests/ui/widgets/test_input.py | 56 +++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/panel/models/datetime_picker.py b/panel/models/datetime_picker.py index c73fc7fe79..529fb94ecf 100644 --- a/panel/models/datetime_picker.py +++ b/panel/models/datetime_picker.py @@ -10,7 +10,7 @@ class DatetimePicker(InputWidget): ''' - value = String(help=""" + value = Nullable(String, help=""" The initial or picked date. """) diff --git a/panel/models/datetime_picker.ts b/panel/models/datetime_picker.ts index 944422dd43..8815167fd8 100644 --- a/panel/models/datetime_picker.ts +++ b/panel/models/datetime_picker.ts @@ -36,7 +36,7 @@ export class DatetimePickerView extends InputWidgetView { const {value, min_date, max_date, disabled_dates, enabled_dates, position, inline, enable_time, enable_seconds, military_time, date_format, mode} = this.model.properties - this.connect(value.change, () => this._picker?.setDate(this.model.value)) + this.connect(value.change, () => this.model.value ? this._picker?.setDate(this.model.value) : this._clear()) this.connect(min_date.change, () => this._picker?.set("minDate", this.model.min_date)) this.connect(max_date.change, () => this._picker?.set("maxDate", this.model.max_date)) this.connect(disabled_dates.change, () => this._picker?.set("disable", this.model.disabled_dates)) @@ -68,7 +68,7 @@ export class DatetimePickerView extends InputWidgetView { this.input_el = input({type: "text", class: inputs.input, disabled: this.model.disabled}) this.group_el.appendChild(this.input_el) this._picker = flatpickr(this.input_el, { - defaultDate: this.model.value, + defaultDate: this.model.value!, minDate: this.model.min_date ? new Date(this.model.min_date) : undefined, maxDate: this.model.max_date ? new Date(this.model.max_date) : undefined, inline: this.model.inline, @@ -86,6 +86,11 @@ export class DatetimePickerView extends InputWidgetView { this._picker.minDateHasTime = true } + protected _clear() { + this._picker?.clear() + this.model.value = null + } + protected _on_close(_selected_dates: Date[], date_string: string, _instance: flatpickr.Instance): void { if (this.model.mode == "range" && !date_string.includes("to")) return @@ -98,7 +103,7 @@ export namespace DatetimePicker { export type Attrs = p.AttrsOf export type Props = InputWidget.Props & { - value: p.Property + value: p.Property min_date: p.Property max_date: p.Property disabled_dates: p.Property @@ -132,7 +137,7 @@ export class DatetimePicker extends InputWidget { const DateStr = String const DatesList = Array(Or(DateStr, Tuple(DateStr, DateStr))) return { - value: [ String ], + value: [ Nullable(String), null ], min_date: [ Nullable(String), null ], max_date: [ Nullable(String), null ], disabled_dates: [ DatesList, [] ], diff --git a/panel/tests/ui/widgets/test_input.py b/panel/tests/ui/widgets/test_input.py index 726a2273ca..bf41cba845 100644 --- a/panel/tests/ui/widgets/test_input.py +++ b/panel/tests/ui/widgets/test_input.py @@ -4,7 +4,8 @@ import pytest from panel.io.server import serve -from panel.widgets import DatetimePicker +from panel.tests.util import wait_until +from panel.widgets import DatetimePicker, DatetimeRangePicker try: from playwright.sync_api import expect @@ -566,3 +567,56 @@ def test_datetimepicker_name(page, port): datetime_picker_with_name = page.locator('.datetimepicker-with-name') expect(datetime_picker_with_name).to_have_text(name) + + +def test_datetimepicker_no_value(page, port, datetime_start_end): + datetime_picker_widget = DatetimePicker() + + serve(datetime_picker_widget, port=port, threaded=True, show=False) + time.sleep(0.2) + page.goto(f"http://localhost:{port}") + + datetime_picker = page.locator('.flatpickr-input') + assert datetime_picker.input_value() == "" + + datetime_picker_widget.value = datetime_start_end[0] + wait_until(lambda: datetime_picker.input_value() == '2021-03-02 00:00:00', page) + + datetime_picker_widget.value = None + wait_until(lambda: datetime_picker.input_value() == '', page) + + +def test_datetimerangepicker_no_value(page, port, datetime_start_end): + datetime_picker_widget = DatetimeRangePicker() + + serve(datetime_picker_widget, port=port, threaded=True, show=False) + time.sleep(0.2) + page.goto(f"http://localhost:{port}") + + datetime_picker = page.locator('.flatpickr-input') + assert datetime_picker.input_value() == "" + + datetime_picker_widget.value = datetime_start_end[:2] + expected = '2021-03-02 00:00:00 to 2021-03-03 00:00:00' + wait_until(lambda: datetime_picker.input_value() == expected, page) + + datetime_picker_widget.value = None + wait_until(lambda: datetime_picker.input_value() == '', page) + + +def test_datetimepicker_remove_value(page, port, datetime_start_end): + datetime_picker_widget = DatetimePicker(value=datetime_start_end[0]) + + serve(datetime_picker_widget, port=port, threaded=True, show=False) + time.sleep(0.2) + page.goto(f"http://localhost:{port}") + + datetime_picker = page.locator('.flatpickr-input') + assert datetime_picker.input_value() == "2021-03-02 00:00:00" + + # Remove values from the browser + datetime_picker.dblclick() + datetime_picker.press("Backspace") + datetime_picker.press("Escape") + + wait_until(lambda: datetime_picker_widget.value is None, page)