Do not stop prop is there is no prop to stop ()

Check that desired event actions are defined on the object passed as the DOM
event before calling them to avoid frontend errors.
This commit is contained in:
Masen Furer 2023-11-02 10:21:41 -07:00 committed by GitHub
parent 6e71393ed5
commit bf20a530df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 6 deletions
integration
reflex/.templates/web/utils

View File

@ -1,5 +1,6 @@
"""Ensure stopPropagation and preventDefault work as expected.""" """Ensure stopPropagation and preventDefault work as expected."""
import asyncio
from typing import Callable, Coroutine, Generator from typing import Callable, Coroutine, Generator
import pytest import pytest
@ -21,13 +22,31 @@ def TestEventAction():
def on_click2(self): def on_click2(self):
self.order.append("on_click2") self.order.append("on_click2")
@rx.var class EventFiringComponent(rx.Component):
def token(self) -> str: """A component that fires onClick event without passing DOM event."""
return self.get_token()
tag = "EventFiringComponent"
def _get_custom_code(self) -> str | None:
return """
function EventFiringComponent(props) {
return (
<div id={props.id} onClick={(e) => props.onClick("foo")}>
Event Firing Component
</div>
)
}"""
def get_event_triggers(self):
return {"on_click": lambda: []}
def index(): def index():
return rx.vstack( return rx.vstack(
rx.input(value=EventActionState.token, is_read_only=True, id="token"), rx.input(
value=EventActionState.router.session.client_token,
is_read_only=True,
id="token",
),
rx.button("No events", id="btn-no-events"), rx.button("No events", id="btn-no-events"),
rx.button( rx.button(
"Stop Prop Only", "Stop Prop Only",
@ -90,6 +109,18 @@ def TestEventAction():
).stop_propagation.prevent_default, ).stop_propagation.prevent_default,
id="link-stop-propagation-prevent-default", id="link-stop-propagation-prevent-default",
), ),
EventFiringComponent.create(
id="custom-stop-propagation",
on_click=EventActionState.on_click( # type: ignore
"custom-stop-propagation"
).stop_propagation,
),
EventFiringComponent.create(
id="custom-prevent-default",
on_click=EventActionState.on_click( # type: ignore
"custom-prevent-default"
).prevent_default,
),
rx.list( rx.list(
rx.foreach( rx.foreach(
EventActionState.order, # type: ignore EventActionState.order, # type: ignore
@ -202,6 +233,14 @@ def poll_for_order(
("link-prevent-default", ["on_click:link_prevent_default", "on_click:outer"]), ("link-prevent-default", ["on_click:link_prevent_default", "on_click:outer"]),
("link-prevent-default-only", ["on_click:outer"]), ("link-prevent-default-only", ["on_click:outer"]),
("link-stop-propagation-prevent-default", ["on_click:link_both"]), ("link-stop-propagation-prevent-default", ["on_click:link_both"]),
(
"custom-stop-propagation",
["on_click:custom-stop-propagation", "on_click:outer"],
),
(
"custom-prevent-default",
["on_click:custom-prevent-default", "on_click:outer"],
),
], ],
) )
@pytest.mark.usefixtures("token") @pytest.mark.usefixtures("token")
@ -226,6 +265,9 @@ async def test_event_actions(
prev_url = driver.current_url prev_url = driver.current_url
el.click() el.click()
if "on_click:outer" not in exp_order:
# really make sure the outer event is not fired
await asyncio.sleep(0.5)
await poll_for_order(exp_order) await poll_for_order(exp_order)
if element_id.startswith("link") and "prevent-default" not in element_id: if element_id.startswith("link") and "prevent-default" not in element_id:

View File

@ -488,10 +488,10 @@ export const useEventLoop = (
// Function to add new events to the event queue. // Function to add new events to the event queue.
const addEvents = (events, _e, event_actions) => { const addEvents = (events, _e, event_actions) => {
if (event_actions?.preventDefault && _e) { if (event_actions?.preventDefault && _e?.preventDefault) {
_e.preventDefault(); _e.preventDefault();
} }
if (event_actions?.stopPropagation && _e) { if (event_actions?.stopPropagation && _e?.stopPropagation) {
_e.stopPropagation(); _e.stopPropagation();
} }
queueEvents(events, socket) queueEvents(events, socket)