diff --git a/components/dash-core-components/src/components/Input.react.js b/components/dash-core-components/src/components/Input.react.js index 472a84fc1d..be020b7cbc 100644 --- a/components/dash-core-components/src/components/Input.react.js +++ b/components/dash-core-components/src/components/Input.react.js @@ -234,6 +234,8 @@ Input.propTypes = { 'tel', 'url', 'hidden', + 'time', + 'datetime-local', ]), /** diff --git a/components/dash-core-components/tests/integration/input/test_input_basics.py b/components/dash-core-components/tests/integration/input/test_input_basics.py index 2b8eaa1c85..903a9dad5d 100644 --- a/components/dash-core-components/tests/integration/input/test_input_basics.py +++ b/components/dash-core-components/tests/integration/input/test_input_basics.py @@ -13,6 +13,8 @@ "tel", "url", "hidden", + "time", + "datetime-local", ) @@ -46,10 +48,16 @@ def cb_render(*vals): dash_dcc.find_element("#input_hidden").get_attribute("type") == "hidden" ), "hidden input element should present with hidden type" - for atype in ALLOWED_TYPES[:-1]: - dash_dcc.find_element(f"#input_{atype}").send_keys( - f"test intp001 - input[{atype}]" - ) + # Test all types except hidden (which should not accept user input) + testable_types = [t for t in ALLOWED_TYPES if t != 'hidden'] + for atype in testable_types: + element = dash_dcc.find_element(f"#input_{atype}") + if atype == 'time': + element.send_keys("12:30:45") + elif atype == 'datetime-local': + element.send_keys("2023-12-31T23:59:59") + else: + element.send_keys(f"test intp001 - input[{atype}]") with pytest.raises(WebDriverException): dash_dcc.find_element("#input_hidden").send_keys("no interaction") diff --git a/components/dash-core-components/tests/integration/input/test_time_datetime_inputs.py b/components/dash-core-components/tests/integration/input/test_time_datetime_inputs.py new file mode 100644 index 0000000000..fb45bfdd41 --- /dev/null +++ b/components/dash-core-components/tests/integration/input/test_time_datetime_inputs.py @@ -0,0 +1,151 @@ +""" +Tests for time and datetime-local input types in dcc.Input component. + +This module tests the newly added support for 'time' and 'datetime-local' +input types that were previously unsupported in dcc.Input. +""" + +from dash import Dash, Input, Output, dcc, html + + +def test_time_input_functionality(dash_dcc): + """Test that time input type works correctly.""" + app = Dash(__name__) + app.layout = html.Div([ + dcc.Input( + id='time-input', + type='time', + step=1, + value='14:30:15' + ), + html.Div(id='time-output') + ]) + + @app.callback( + Output('time-output', 'children'), + Input('time-input', 'value') + ) + def update_time_output(time_value): + return f"Time: {time_value}" + + dash_dcc.start_server(app) + + # Check that the input has the correct type + time_input = dash_dcc.find_element('#time-input') + assert time_input.get_attribute('type') == 'time' + assert time_input.get_attribute('value') == '14:30:15' + + # Test updating the time value + time_input.clear() + time_input.send_keys('09:45:30') + + # Wait for callback to update + dash_dcc.wait_for_text_to_equal('#time-output', 'Time: 09:45:30') + + assert dash_dcc.get_logs() == [] + + +def test_datetime_local_input_functionality(dash_dcc): + """Test that datetime-local input type works correctly.""" + app = Dash(__name__) + app.layout = html.Div([ + dcc.Input( + id='datetime-input', + type='datetime-local', + step=1, + value='2023-12-25T18:30:00' + ), + html.Div(id='datetime-output') + ]) + + @app.callback( + Output('datetime-output', 'children'), + Input('datetime-input', 'value') + ) + def update_datetime_output(datetime_value): + return f"DateTime: {datetime_value}" + + dash_dcc.start_server(app) + + # Check that the input has the correct type + datetime_input = dash_dcc.find_element('#datetime-input') + assert datetime_input.get_attribute('type') == 'datetime-local' + assert datetime_input.get_attribute('value') == '2023-12-25T18:30:00' + + # Test updating the datetime value + datetime_input.clear() + datetime_input.send_keys('2024-01-01T12:00:00') + + # Wait for callback to update + dash_dcc.wait_for_text_to_equal('#datetime-output', 'DateTime: 2024-01-01T12:00:00') + + assert dash_dcc.get_logs() == [] + + +def test_time_input_with_debounce(dash_dcc): + """Test that time input works correctly with debounce.""" + app = Dash(__name__) + app.layout = html.Div([ + dcc.Input( + id='time-debounce-input', + type='time', + debounce=True, + value='12:00:00' + ), + html.Div(id='time-debounce-output') + ]) + + @app.callback( + Output('time-debounce-output', 'children'), + Input('time-debounce-input', 'value') + ) + def update_debounce_output(time_value): + return f"Time with debounce: {time_value}" + + dash_dcc.start_server(app) + + time_input = dash_dcc.find_element('#time-debounce-input') + assert time_input.get_attribute('type') == 'time' + + # With debounce=True, value should update only after losing focus or Enter key + time_input.clear() + time_input.send_keys('15:30:45') + + # Trigger blur event to commit the debounced value + dash_dcc.find_element('body').click() # Click somewhere else to blur + + dash_dcc.wait_for_text_to_equal('#time-debounce-output', 'Time with debounce: 15:30:45') + + assert dash_dcc.get_logs() == [] + + +def test_datetime_local_input_with_min_max(dash_dcc): + """Test that datetime-local input works with min and max attributes.""" + app = Dash(__name__) + app.layout = html.Div([ + dcc.Input( + id='datetime-minmax-input', + type='datetime-local', + min='2023-01-01T00:00:00', + max='2023-12-31T23:59:59', + value='2023-06-15T12:00:00' + ), + html.Div(id='datetime-minmax-output') + ]) + + @app.callback( + Output('datetime-minmax-output', 'children'), + Input('datetime-minmax-input', 'value') + ) + def update_minmax_output(datetime_value): + return f"DateTime with constraints: {datetime_value}" + + dash_dcc.start_server(app) + + datetime_input = dash_dcc.find_element('#datetime-minmax-input') + assert datetime_input.get_attribute('type') == 'datetime-local' + assert datetime_input.get_attribute('min') == '2023-01-01T00:00:00' + assert datetime_input.get_attribute('max') == '2023-12-31T23:59:59' + assert datetime_input.get_attribute('value') == '2023-06-15T12:00:00' + + assert dash_dcc.get_logs() == []