Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing date_input | min and max selectable date issues #1426

Merged
merged 6 commits into from
May 8, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion e2e/scripts/st_date_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from datetime import datetime
from datetime import date

w1 = st.date_input("Label 1", date(1970, 1, 1))
w1 = st.date_input("Label 1", date(1970, 1, 1), min_value=date(1970, 1, 1))
st.write("Value 1:", w1)

w2 = st.date_input("Label 2", datetime(2019, 7, 6, 21, 15))
Expand Down
22 changes: 21 additions & 1 deletion frontend/src/components/widgets/DateInput/DateInput.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import React from "react"
import { shallow } from "enzyme"
import { fromJS } from "immutable"
import { WidgetStateManager } from "lib/WidgetStateManager"
import { DateInput as DateInputProto } from "autogen/proto"

import DateInput, { Props } from "./DateInput"
import { Datepicker as UIDatePicker } from "baseui/datepicker"
Expand All @@ -27,11 +28,12 @@ jest.mock("lib/WidgetStateManager")

const sendBackMsg = jest.fn()

const getProps = (elementProps: object = {}): Props => ({
const getProps = (elementProps: Partial<DateInputProto> = {}): Props => ({
element: fromJS({
id: 1,
label: "Label",
default: "1970/01/01",
min: "1970/1/1",
...elementProps,
}),
width: 0,
Expand Down Expand Up @@ -102,4 +104,22 @@ describe("DateInput widget", () => {
{ fromUi: true }
)
})

it("should have a minDate", () => {
expect(wrapper.find(UIDatePicker).prop("minDate")).toStrictEqual(
new Date("1970/1/1")
)
expect(wrapper.find(UIDatePicker).prop("maxDate")).toBeUndefined()
})

it("should have a maxDate if it is passed", () => {
const props = getProps({
max: "2030/02/06",
})
const wrapper = shallow(<DateInput {...props} />)

expect(wrapper.find(UIDatePicker).prop("maxDate")).toStrictEqual(
new Date("2030/02/06")
)
})
})
33 changes: 20 additions & 13 deletions frontend/src/components/widgets/DateInput/DateInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,35 +52,42 @@ class DateInput extends React.PureComponent<Props, State> {
}

private handleChange = ({ date }: { date: Date | Date[] }): void => {
const value = dateToString(date as Date)
const value = moment(date as Date).format("YYYY/MM/DD")

this.setState({ value }, () => this.setWidgetValue({ fromUi: true }))
}

private getMaxDate = (): Date | undefined => {
const { element } = this.props
const maxDate = element.get("max")

return maxDate && maxDate.length > 0 ? new Date(maxDate) : undefined
}

public render = (): React.ReactNode => {
const style = { width: this.props.width }
const label = this.props.element.get("label")
const { width, element, disabled } = this.props
const { value } = this.state

const style = { width }
const label = element.get("label")
const minDate = new Date(element.get("min"))
const maxDate = this.getMaxDate()

return (
<div className="Widget stDateInput" style={style}>
<label>{label}</label>
<UIDatePicker
formatString="yyyy/MM/dd"
disabled={this.props.disabled}
disabled={disabled}
onChange={this.handleChange}
overrides={datePickerOverrides}
value={stringToDate(this.state.value)}
value={new Date(value)}
minDate={minDate}
maxDate={maxDate}
/>
</div>
)
}
}

function dateToString(date: Date): string {
return moment(date).format("YYYY/MM/DD")
}

function stringToDate(value: string): Date {
return moment(value, "YYYY/MM/DD").toDate()
}

export default DateInput
19 changes: 17 additions & 2 deletions lib/streamlit/DeltaGenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2351,16 +2351,20 @@ def time_input(self, element, label, value=None, key=None):
return current_value

@_with_element
def date_input(self, element, label, value=None, key=None):
def date_input(self, element, label, value=None, min_value=datetime.min, max_value=None, key=None):
"""Display a date input widget.

Parameters
----------
label : str
A short label explaining to the user what this date input is for.
value : datetime.date/datetime.datetime
value : datetime.date or datetime.datetime
The value of this widget when it first renders. This will be
cast to str internally. Defaults to today.
min_value : datetime.date or datetime.datetime
The minimum selectable date. Defaults to datetime.min.
max_value : datetime.date or datetime.datetime
The maximum selectable date. Defaults to 2030/12.
arraydude marked this conversation as resolved.
Show resolved Hide resolved
key : str
An optional string to use as the unique key for the widget.
If this is omitted, a key will be generated for the widget
Expand Down Expand Up @@ -2397,6 +2401,17 @@ def date_input(self, element, label, value=None, key=None):
element.date_input.label = label
element.date_input.default = date.strftime(value, "%Y/%m/%d")

if isinstance(min_value, datetime):
min_value = min_value.date()

element.date_input.min = date.strftime(min_value, "%Y/%m/%d")

if max_value is not None:
if isinstance(max_value, datetime):
max_value = max_value.date()

element.date_input.max = date.strftime(max_value, "%Y/%m/%d")

arraydude marked this conversation as resolved.
Show resolved Hide resolved
ui_value = _get_widget_ui_value("date_input", element, user_key=key)
current_value = (
datetime.strptime(ui_value, "%Y/%m/%d").date()
Expand Down
2 changes: 2 additions & 0 deletions proto/streamlit/proto/DateInput.proto
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ message DateInput {
string id = 1;
string label = 2;
string default = 3;
string min = 4;
string max = 5;
}