From 6fb254ae3da82d3cba94dd5595c61a552032c9e6 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Wed, 8 May 2024 14:39:22 -0700 Subject: [PATCH] [REF-2789] Graceful deprecation of rx.input.root and rx.input.input (#3249) * [REF-2789] Graceful deprecation of rx.input.root and rx.input.input Handle previously valid code where rx.input.root wrapped rx.input/rx.input.input and rx.input.slot. Raise deprecation warnings with hints about how to refactor code. Copy props from rx.input.root to children inputs and apply any rx.input.slot components to children inputs in an attempt to keep existing code working as best as possible. Fix DebounceInput: * pass children through (for rx.input.slots) * pass _rename_props through (for color_scheme) * Fix for case where `rx.input.root` had event triggers Fix for case where `rx.input.root` had no input children --- reflex/components/core/debounce.py | 2 + .../radix/themes/components/text_field.py | 86 ++++++++++++++++++- .../radix/themes/components/text_field.pyi | 10 ++- 3 files changed, 94 insertions(+), 4 deletions(-) diff --git a/reflex/components/core/debounce.py b/reflex/components/core/debounce.py index e24a6563d..5fabd4486 100644 --- a/reflex/components/core/debounce.py +++ b/reflex/components/core/debounce.py @@ -121,6 +121,8 @@ class DebounceInput(Component): component = super().create(**props) component._get_style = child._get_style component.event_triggers.update(child.event_triggers) + component.children = child.children + component._rename_props = child._rename_props return component def get_event_triggers(self) -> dict[str, Any]: diff --git a/reflex/components/radix/themes/components/text_field.py b/reflex/components/radix/themes/components/text_field.py index 148bddcff..970b950e3 100644 --- a/reflex/components/radix/themes/components/text_field.py +++ b/reflex/components/radix/themes/components/text_field.py @@ -1,11 +1,15 @@ """Interactive components provided by @radix-ui/themes.""" +from __future__ import annotations from typing import Any, Dict, Literal, Union from reflex.components import el +from reflex.components.base.fragment import Fragment from reflex.components.component import Component, ComponentNamespace from reflex.components.core.debounce import DebounceInput from reflex.constants import EventTriggers +from reflex.style import Style, format_as_emotion +from reflex.utils import console from reflex.vars import Var from ..base import ( @@ -79,10 +83,85 @@ class TextFieldRoot(el.Div, RadixThemesComponent): Returns: The component. """ + component = super().create(*children, **props) if props.get("value") is not None and props.get("on_change"): # create a debounced input if the user requests full control to avoid typing jank - return DebounceInput.create(super().create(*children, **props)) - return super().create(*children, **props) + return DebounceInput.create(component) + return component + + @classmethod + def create_root_deprecated(cls, *children, **props) -> Component: + """Create a Fragment component (wrapper for deprecated name). + + Copy the attributes that were previously defined on TextFieldRoot in 0.4.9 to + any child input elements (via custom_attrs). + + Args: + *children: The children of the component. + **props: The properties of the component. + + Returns: + The component. + """ + console.deprecate( + feature_name="rx.input.root", + reason="use rx.input without the .root suffix", + deprecation_version="0.5.0", + removal_version="0.6.0", + ) + inputs = [ + child + for child in children + if isinstance(child, (TextFieldRoot, DebounceInput)) + ] + if not inputs: + # Old-style where no explicit child input was provided + return cls.create(*children, **props) + slots = [child for child in children if isinstance(child, TextFieldSlot)] + carry_props = { + prop: props.pop(prop) + for prop in ["size", "variant", "color_scheme", "radius"] + if prop in props + } + template = cls.create(**props) + for child in inputs: + child.children.extend(slots) + custom_attrs = child.custom_attrs + custom_attrs.update( + { + prop: value + for prop, value in carry_props.items() + if prop not in custom_attrs and getattr(child, prop) is None + } + ) + style = Style(template.style) + style.update(child.style) + child._get_style = lambda style=style: { + "css": Var.create(format_as_emotion(style)) + } + for trigger in template.event_triggers: + if trigger not in child.event_triggers: + child.event_triggers[trigger] = template.event_triggers[trigger] + return Fragment.create(*inputs) + + @classmethod + def create_input_deprecated(cls, *children, **props) -> Component: + """Create a TextFieldRoot component (wrapper for deprecated name). + + Args: + *children: The children of the component. + **props: The properties of the component. + + Returns: + The component. + """ + console.deprecate( + feature_name="rx.input.input", + reason="use rx.input without the .input suffix", + deprecation_version="0.5.0", + removal_version="0.6.0", + ) + return cls.create(*children, **props) def get_event_triggers(self) -> Dict[str, Any]: """Get the event triggers that pass the component's value to the handler. @@ -112,7 +191,8 @@ class TextFieldSlot(RadixThemesComponent): class TextField(ComponentNamespace): """TextField components namespace.""" - root = staticmethod(TextFieldRoot.create) + root = staticmethod(TextFieldRoot.create_root_deprecated) + input = staticmethod(TextFieldRoot.create_input_deprecated) slot = staticmethod(TextFieldSlot.create) __call__ = staticmethod(TextFieldRoot.create) diff --git a/reflex/components/radix/themes/components/text_field.pyi b/reflex/components/radix/themes/components/text_field.pyi index 7811d2bba..c14fb031a 100644 --- a/reflex/components/radix/themes/components/text_field.pyi +++ b/reflex/components/radix/themes/components/text_field.pyi @@ -9,9 +9,12 @@ from reflex.event import EventChain, EventHandler, EventSpec from reflex.style import Style from typing import Any, Dict, Literal, Union from reflex.components import el +from reflex.components.base.fragment import Fragment from reflex.components.component import Component, ComponentNamespace from reflex.components.core.debounce import DebounceInput from reflex.constants import EventTriggers +from reflex.style import Style, format_as_emotion +from reflex.utils import console from reflex.vars import Var from ..base import LiteralAccentColor, LiteralRadius, RadixThemesComponent @@ -263,6 +266,10 @@ class TextFieldRoot(el.Div, RadixThemesComponent): The component. """ ... + @classmethod + def create_root_deprecated(cls, *children, **props) -> Component: ... + @classmethod + def create_input_deprecated(cls, *children, **props) -> Component: ... def get_event_triggers(self) -> Dict[str, Any]: ... class TextFieldSlot(RadixThemesComponent): @@ -408,7 +415,8 @@ class TextFieldSlot(RadixThemesComponent): ... class TextField(ComponentNamespace): - root = staticmethod(TextFieldRoot.create) + root = staticmethod(TextFieldRoot.create_root_deprecated) + input = staticmethod(TextFieldRoot.create_input_deprecated) slot = staticmethod(TextFieldSlot.create) @staticmethod