Use DebounceInput wrapper for fully controlled Editable (#1650)
This commit is contained in:
parent
5c3685f0c4
commit
d884b7ba96
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
|
from reflex.components.component import Component
|
||||||
|
from reflex.components.forms.debounce import DebounceInput
|
||||||
from reflex.components.libs.chakra import ChakraComponent
|
from reflex.components.libs.chakra import ChakraComponent
|
||||||
from reflex.event import EVENT_ARG
|
from reflex.event import EVENT_ARG
|
||||||
from reflex.vars import Var
|
from reflex.vars import Var
|
||||||
@ -36,6 +38,29 @@ class Editable(ChakraComponent):
|
|||||||
# The initial value of the Editable in both edit and preview mode.
|
# The initial value of the Editable in both edit and preview mode.
|
||||||
default_value: Var[str]
|
default_value: Var[str]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, *children, **props) -> Component:
|
||||||
|
"""Create an Editable component.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
children: The children of the component.
|
||||||
|
props: The properties of the component.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The component.
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
isinstance(props.get("value"), Var) and props.get("on_change")
|
||||||
|
) or "debounce_timeout" in props:
|
||||||
|
# Create a debounced input if the user requests full control to avoid typing jank
|
||||||
|
# Currently default to 50ms, which appears to be a good balance
|
||||||
|
debounce_timeout = props.pop("debounce_timeout", 50)
|
||||||
|
return DebounceInput.create(
|
||||||
|
super().create(*children, **props),
|
||||||
|
debounce_timeout=debounce_timeout,
|
||||||
|
)
|
||||||
|
return super().create(*children, **props)
|
||||||
|
|
||||||
def get_controlled_triggers(self) -> Dict[str, Var]:
|
def get_controlled_triggers(self) -> Dict[str, Var]:
|
||||||
"""Get the event triggers that pass the component's value to the handler.
|
"""Get the event triggers that pass the component's value to the handler.
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ def test_render_child_props_recursive():
|
|||||||
)
|
)
|
||||||
assert tag.props["forceNotifyOnBlur"].name == "false"
|
assert tag.props["forceNotifyOnBlur"].name == "false"
|
||||||
assert tag.props["forceNotifyByEnter"].name == "false"
|
assert tag.props["forceNotifyByEnter"].name == "false"
|
||||||
assert tag.props["debounceTimeout"] == 42
|
assert tag.props["debounceTimeout"].name == "42"
|
||||||
assert len(tag.props["onChange"].events) == 1
|
assert len(tag.props["onChange"].events) == 1
|
||||||
assert tag.props["onChange"].events[0].handler == S.on_change
|
assert tag.props["onChange"].events[0].handler == S.on_change
|
||||||
assert tag.contents == ""
|
assert tag.contents == ""
|
||||||
@ -101,7 +101,7 @@ def test_full_control_implicit_debounce():
|
|||||||
value=S.value,
|
value=S.value,
|
||||||
on_change=S.on_change,
|
on_change=S.on_change,
|
||||||
)._render()
|
)._render()
|
||||||
assert tag.props["debounceTimeout"] == 0
|
assert tag.props["debounceTimeout"].name == "50"
|
||||||
assert len(tag.props["onChange"].events) == 1
|
assert len(tag.props["onChange"].events) == 1
|
||||||
assert tag.props["onChange"].events[0].handler == S.on_change
|
assert tag.props["onChange"].events[0].handler == S.on_change
|
||||||
assert tag.contents == ""
|
assert tag.contents == ""
|
||||||
@ -113,7 +113,7 @@ def test_full_control_implicit_debounce_text_area():
|
|||||||
value=S.value,
|
value=S.value,
|
||||||
on_change=S.on_change,
|
on_change=S.on_change,
|
||||||
)._render()
|
)._render()
|
||||||
assert tag.props["debounceTimeout"] == 0
|
assert tag.props["debounceTimeout"].name == "50"
|
||||||
assert len(tag.props["onChange"].events) == 1
|
assert len(tag.props["onChange"].events) == 1
|
||||||
assert tag.props["onChange"].events[0].handler == S.on_change
|
assert tag.props["onChange"].events[0].handler == S.on_change
|
||||||
assert tag.contents == ""
|
assert tag.contents == ""
|
||||||
|
48
tests/components/forms/test_editable.py
Normal file
48
tests/components/forms/test_editable.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import reflex as rx
|
||||||
|
|
||||||
|
|
||||||
|
class S(rx.State):
|
||||||
|
"""Example state for debounce tests."""
|
||||||
|
|
||||||
|
value: str = ""
|
||||||
|
|
||||||
|
def on_change(self, v: str):
|
||||||
|
"""Dummy on_change handler.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
v: The changed value.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_full_control_implicit_debounce_editable():
|
||||||
|
"""DebounceInput is used when value and on_change are used together."""
|
||||||
|
tag = rx.editable(
|
||||||
|
value=S.value,
|
||||||
|
on_change=S.on_change,
|
||||||
|
)._render()
|
||||||
|
assert tag.props["debounceTimeout"].name == "50"
|
||||||
|
assert len(tag.props["onChange"].events) == 1
|
||||||
|
assert tag.props["onChange"].events[0].handler == S.on_change
|
||||||
|
assert tag.contents == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_full_control_explicit_debounce_editable():
|
||||||
|
"""DebounceInput is used when user specifies `debounce_timeout`."""
|
||||||
|
tag = rx.editable(
|
||||||
|
on_change=S.on_change,
|
||||||
|
debounce_timeout=33,
|
||||||
|
)._render()
|
||||||
|
assert tag.props["debounceTimeout"].name == "33"
|
||||||
|
assert len(tag.props["onChange"].events) == 1
|
||||||
|
assert tag.props["onChange"].events[0].handler == S.on_change
|
||||||
|
assert tag.contents == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_editable_no_debounce():
|
||||||
|
"""DebounceInput is not used for regular editable."""
|
||||||
|
tag = rx.editable(
|
||||||
|
placeholder=S.value,
|
||||||
|
)._render()
|
||||||
|
assert "debounceTimeout" not in tag.props
|
||||||
|
assert tag.contents == ""
|
Loading…
Reference in New Issue
Block a user