diff --git a/reflex/components/el/elements/forms.py b/reflex/components/el/elements/forms.py index 1963f8b37..cb6273cda 100644 --- a/reflex/components/el/elements/forms.py +++ b/reflex/components/el/elements/forms.py @@ -10,7 +10,7 @@ from jinja2 import Environment from reflex.components.el.element import Element from reflex.components.tags.tag import Tag from reflex.constants import Dirs, EventTriggers -from reflex.event import EventChain, EventHandler, prevent_default +from reflex.event import EventChain, EventHandler, input_event, prevent_default from reflex.utils.imports import ImportDict from reflex.vars import VarData from reflex.vars.base import LiteralVar, Var @@ -345,13 +345,13 @@ class Input(BaseHTML): value: Var[Union[str, int, float]] # Fired when the input value changes - on_change: EventHandler[lambda e0: [e0.target.value]] + on_change: EventHandler[input_event] # Fired when the input gains focus - on_focus: EventHandler[lambda e0: [e0.target.value]] + on_focus: EventHandler[input_event] # Fired when the input loses focus - on_blur: EventHandler[lambda e0: [e0.target.value]] + on_blur: EventHandler[input_event] # Fired when a key is pressed down on_key_down: EventHandler[lambda e0: [e0.key]] @@ -496,7 +496,7 @@ class Select(BaseHTML): size: Var[Union[str, int, bool]] # Fired when the select value changes - on_change: EventHandler[lambda e0: [e0.target.value]] + on_change: EventHandler[input_event] AUTO_HEIGHT_JS = """ @@ -586,13 +586,13 @@ class Textarea(BaseHTML): wrap: Var[Union[str, int, bool]] # Fired when the input value changes - on_change: EventHandler[lambda e0: [e0.target.value]] + on_change: EventHandler[input_event] # Fired when the input gains focus - on_focus: EventHandler[lambda e0: [e0.target.value]] + on_focus: EventHandler[input_event] # Fired when the input loses focus - on_blur: EventHandler[lambda e0: [e0.target.value]] + on_blur: EventHandler[input_event] # Fired when a key is pressed down on_key_down: EventHandler[lambda e0: [e0.key]] diff --git a/reflex/components/radix/primitives/drawer.py b/reflex/components/radix/primitives/drawer.py index b814e878f..f0efa7e18 100644 --- a/reflex/components/radix/primitives/drawer.py +++ b/reflex/components/radix/primitives/drawer.py @@ -11,6 +11,7 @@ from reflex.components.radix.primitives.base import RadixPrimitiveComponent from reflex.components.radix.themes.base import Theme from reflex.components.radix.themes.layout.flex import Flex from reflex.event import EventHandler +from reflex.utils import console from reflex.vars.base import Var @@ -127,20 +128,20 @@ class DrawerContent(DrawerComponent): base_style.update(style) return {"css": base_style} - # Fired when the drawer content is opened. - on_open_auto_focus: EventHandler[lambda e0: [e0.target.value]] + # Fired when the drawer content is opened. Deprecated. + on_open_auto_focus: EventHandler[lambda e0: []] - # Fired when the drawer content is closed. - on_close_auto_focus: EventHandler[lambda e0: [e0.target.value]] + # Fired when the drawer content is closed. Deprecated. + on_close_auto_focus: EventHandler[lambda e0: []] - # Fired when the escape key is pressed. - on_escape_key_down: EventHandler[lambda e0: [e0.target.value]] + # Fired when the escape key is pressed. Deprecated. + on_escape_key_down: EventHandler[lambda e0: []] - # Fired when the pointer is down outside the drawer content. - on_pointer_down_outside: EventHandler[lambda e0: [e0.target.value]] + # Fired when the pointer is down outside the drawer content. Deprecated. + on_pointer_down_outside: EventHandler[lambda e0: []] - # Fired when interacting outside the drawer content. - on_interact_outside: EventHandler[lambda e0: [e0.target.value]] + # Fired when interacting outside the drawer content. Deprecated. + on_interact_outside: EventHandler[lambda e0: []] @classmethod def create(cls, *children, **props): @@ -157,6 +158,23 @@ class DrawerContent(DrawerComponent): Returns: The drawer content. """ + deprecated_properties = [ + "on_open_auto_focus", + "on_close_auto_focus", + "on_escape_key_down", + "on_pointer_down_outside", + "on_interact_outside", + ] + + for prop in deprecated_properties: + if prop in props: + console.deprecate( + feature_name="drawer content events", + reason=f"The `{prop}` event is deprecated and will be removed in 0.7.0.", + deprecation_version="0.6.3", + removal_version="0.7.0", + ) + comp = super().create(*children, **props) return Theme.create(comp) diff --git a/reflex/components/radix/themes/components/dropdown_menu.py b/reflex/components/radix/themes/components/dropdown_menu.py index 5ed1f9f64..c853619dd 100644 --- a/reflex/components/radix/themes/components/dropdown_menu.py +++ b/reflex/components/radix/themes/components/dropdown_menu.py @@ -164,7 +164,7 @@ class DropdownMenuSub(RadixThemesComponent): default_open: Var[bool] # Fired when the open state changes. - on_open_change: EventHandler[lambda e0: [e0.target.value]] + on_open_change: EventHandler[lambda e0: [e0]] class DropdownMenuSubContent(RadixThemesComponent): @@ -240,7 +240,7 @@ class DropdownMenuItem(RadixThemesComponent): _valid_parents: List[str] = ["DropdownMenuContent", "DropdownMenuSubContent"] # Fired when the item is selected. - on_select: EventHandler[lambda e0: [e0.target.value]] + on_select: EventHandler[lambda e0: []] class DropdownMenuSeparator(RadixThemesComponent): diff --git a/reflex/components/radix/themes/components/text_area.py b/reflex/components/radix/themes/components/text_area.py index 8b3b531cb..c0a905533 100644 --- a/reflex/components/radix/themes/components/text_area.py +++ b/reflex/components/radix/themes/components/text_area.py @@ -6,7 +6,7 @@ from reflex.components.component import Component from reflex.components.core.breakpoints import Responsive from reflex.components.core.debounce import DebounceInput from reflex.components.el import elements -from reflex.event import EventHandler +from reflex.event import EventHandler, input_event from reflex.vars.base import Var from ..base import ( @@ -83,13 +83,13 @@ class TextArea(RadixThemesComponent, elements.Textarea): wrap: Var[str] # Fired when the value of the textarea changes. - on_change: EventHandler[lambda e0: [e0.target.value]] + on_change: EventHandler[input_event] # Fired when the textarea is focused. - on_focus: EventHandler[lambda e0: [e0.target.value]] + on_focus: EventHandler[input_event] # Fired when the textarea is blurred. - on_blur: EventHandler[lambda e0: [e0.target.value]] + on_blur: EventHandler[input_event] # Fired when a key is pressed down. on_key_down: EventHandler[lambda e0: [e0.key]] diff --git a/reflex/components/radix/themes/components/text_field.py b/reflex/components/radix/themes/components/text_field.py index 6c3372985..bea54ab1e 100644 --- a/reflex/components/radix/themes/components/text_field.py +++ b/reflex/components/radix/themes/components/text_field.py @@ -8,7 +8,7 @@ from reflex.components.component import Component, ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.core.debounce import DebounceInput from reflex.components.el import elements -from reflex.event import EventHandler +from reflex.event import EventHandler, input_event from reflex.vars.base import Var from ..base import ( @@ -72,13 +72,13 @@ class TextFieldRoot(elements.Div, RadixThemesComponent): value: Var[Union[str, int, float]] # Fired when the value of the textarea changes. - on_change: EventHandler[lambda e0: [e0.target.value]] + on_change: EventHandler[input_event] # Fired when the textarea is focused. - on_focus: EventHandler[lambda e0: [e0.target.value]] + on_focus: EventHandler[input_event] # Fired when the textarea is blurred. - on_blur: EventHandler[lambda e0: [e0.target.value]] + on_blur: EventHandler[input_event] # Fired when a key is pressed down. on_key_down: EventHandler[lambda e0: [e0.key]] diff --git a/reflex/components/radix/themes/components/tooltip.py b/reflex/components/radix/themes/components/tooltip.py index f39de68a8..7fd181465 100644 --- a/reflex/components/radix/themes/components/tooltip.py +++ b/reflex/components/radix/themes/components/tooltip.py @@ -85,13 +85,13 @@ class Tooltip(RadixThemesComponent): aria_label: Var[str] # Fired when the open state changes. - on_open_change: EventHandler[lambda e0: [e0.target.value]] + on_open_change: EventHandler[lambda e0: [e0]] # Fired when the escape key is pressed. - on_escape_key_down: EventHandler[lambda e0: [e0.target.value]] + on_escape_key_down: EventHandler[lambda e0: []] # Fired when the pointer is down outside the tooltip. - on_pointer_down_outside: EventHandler[lambda e0: [e0.target.value]] + on_pointer_down_outside: EventHandler[lambda e0: []] @classmethod def create(cls, *children, **props) -> Component: diff --git a/reflex/event.py b/reflex/event.py index 7384cf5bf..e778b03d1 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -24,7 +24,7 @@ from typing import ( from typing_extensions import get_args, get_origin from reflex import constants -from reflex.utils import format +from reflex.utils import console, format from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch from reflex.utils.types import ArgsSpec, GenericType from reflex.vars import VarData @@ -399,23 +399,32 @@ prevent_default = EventChain(events=[], args_spec=lambda: []).prevent_default init=True, frozen=True, ) -class Target: - """A Javascript event target.""" +class JavascriptHTMLInputElement: + """Interface for a Javascript HTMLInputElement https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement.""" - checked: bool = False - value: Any = None + value: str = "" @dataclasses.dataclass( init=True, frozen=True, ) -class FrontendEvent: - """A Javascript event.""" +class JavascriptInputEvent: + """Interface for a Javascript InputEvent https://developer.mozilla.org/en-US/docs/Web/API/InputEvent.""" - target: Target = Target() - key: str = "" - value: Any = None + target: JavascriptHTMLInputElement = JavascriptHTMLInputElement() + + +def input_event(e: Var[JavascriptInputEvent]) -> Tuple[str]: + """Get the value from an input event. + + Args: + e: The input event. + + Returns: + The value from the input event. + """ + return (e.target.value,) @dataclasses.dataclass( @@ -946,6 +955,28 @@ def unwrap_var_annotation(annotation: GenericType): return annotation +def resolve_annotation(annotations: dict[str, Any], arg_name: str): + """Resolve the annotation for the given argument name. + + Args: + annotations: The annotations. + arg_name: The argument name. + + Returns: + The resolved annotation. + """ + annotation = annotations.get(arg_name) + if annotation is None: + console.deprecate( + feature_name="Unannotated event handler arguments", + reason="Provide type annotations for event handler arguments.", + deprecation_version="0.6.3", + removal_version="0.7.0", + ) + return JavascriptInputEvent + return annotation + + def parse_args_spec(arg_spec: ArgsSpec): """Parse the args provided in the ArgsSpec of an event trigger. @@ -961,7 +992,7 @@ def parse_args_spec(arg_spec: ArgsSpec): return arg_spec( *[ Var(f"_{l_arg}").to( - unwrap_var_annotation(annotations.get(l_arg, FrontendEvent)) + unwrap_var_annotation(resolve_annotation(annotations, l_arg)) ) for l_arg in spec.args ] diff --git a/tests/units/components/test_component.py b/tests/units/components/test_component.py index 5e94db052..dccfd13fe 100644 --- a/tests/units/components/test_component.py +++ b/tests/units/components/test_component.py @@ -16,7 +16,7 @@ from reflex.components.component import ( ) from reflex.components.radix.themes.layout.box import Box from reflex.constants import EventTriggers -from reflex.event import EventChain, EventHandler, parse_args_spec +from reflex.event import EventChain, EventHandler, input_event, parse_args_spec from reflex.state import BaseState from reflex.style import Style from reflex.utils import imports @@ -1778,7 +1778,7 @@ def test_custom_component_declare_event_handlers_in_fields(): return { **super().get_event_triggers(), "on_a": lambda e0: [e0], - "on_b": lambda e0: [e0.target.value], + "on_b": input_event, "on_c": lambda e0: [], "on_d": lambda: [], "on_e": lambda: [], @@ -1787,7 +1787,7 @@ def test_custom_component_declare_event_handlers_in_fields(): class TestComponent(Component): on_a: EventHandler[lambda e0: [e0]] - on_b: EventHandler[lambda e0: [e0.target.value]] + on_b: EventHandler[input_event] on_c: EventHandler[lambda e0: []] on_d: EventHandler[lambda: []] on_e: EventHandler diff --git a/tests/units/components/test_component_future_annotations.py b/tests/units/components/test_component_future_annotations.py index 37aeb813a..e0b02fdbd 100644 --- a/tests/units/components/test_component_future_annotations.py +++ b/tests/units/components/test_component_future_annotations.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Any from reflex.components.component import Component -from reflex.event import EventHandler +from reflex.event import EventHandler, input_event # This is a repeat of its namesake in test_component.py. @@ -25,7 +25,7 @@ def test_custom_component_declare_event_handlers_in_fields(): class TestComponent(Component): on_a: EventHandler[lambda e0: [e0]] - on_b: EventHandler[lambda e0: [e0.target.value]] + on_b: EventHandler[input_event] on_c: EventHandler[lambda e0: []] on_d: EventHandler[lambda: []] diff --git a/tests/units/utils/test_format.py b/tests/units/utils/test_format.py index d7b0c791e..17485d52e 100644 --- a/tests/units/utils/test_format.py +++ b/tests/units/utils/test_format.py @@ -8,7 +8,7 @@ import plotly.graph_objects as go import pytest from reflex.components.tags.tag import Tag -from reflex.event import EventChain, EventHandler, EventSpec, FrontendEvent +from reflex.event import EventChain, EventHandler, EventSpec, JavascriptInputEvent from reflex.style import Style from reflex.utils import format from reflex.utils.serializers import serialize_figure @@ -387,7 +387,7 @@ def test_format_match( Var( _js_expr="_e", ) - .to(ObjectVar, FrontendEvent) + .to(ObjectVar, JavascriptInputEvent) .target.value, ), ),