Skip to content

Commit

Permalink
Support event_actions in Form on_submit (#4912)
Browse files Browse the repository at this point in the history
  • Loading branch information
masenf authored Mar 11, 2025
1 parent d90b283 commit 41d3514
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 3 deletions.
2 changes: 1 addition & 1 deletion reflex/components/el/elements/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
ev.preventDefault()
const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}};
({{ on_submit_event_chain }}());
({{ on_submit_event_chain }}(ev));
if ({{ reset_on_submit }}) {
$form.reset()
Expand Down
2 changes: 1 addition & 1 deletion reflex/components/el/elements/forms.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ from .base import BaseHTML

FORM_DATA = Var(_js_expr="form_data")
HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
"\n const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {\n const $form = ev.target\n ev.preventDefault()\n const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}};\n\n ({{ on_submit_event_chain }}());\n\n if ({{ reset_on_submit }}) {\n $form.reset()\n }\n })\n "
"\n const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {\n const $form = ev.target\n ev.preventDefault()\n const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}};\n\n ({{ on_submit_event_chain }}(ev));\n\n if ({{ reset_on_submit }}) {\n $form.reset()\n }\n })\n "
)
ButtonType = Literal["submit", "reset", "button"]

Expand Down
53 changes: 52 additions & 1 deletion tests/integration/test_event_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@

import pytest
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

from reflex.testing import AppHarness, WebDriver


def TestEventAction():
"""App for testing event_actions."""
from typing import Any

import reflex as rx

class EventActionState(rx.State):
Expand All @@ -32,6 +37,10 @@ def on_click_throttle(self):
def on_click_debounce(self):
self.order.append("on_click_debounce")

@rx.event
def on_submit(self, form_data: dict[str, Any]):
self.order.append("on_submit")

class EventFiringComponent(rx.Component):
"""A component that fires onClick event without passing DOM event."""

Expand Down Expand Up @@ -152,10 +161,26 @@ def index():
),
),
on_click=EventActionState.on_click("outer"), # pyright: ignore [reportCallIssue]
), rx.form(
rx.dialog.root(
rx.dialog.trigger(
rx.button("Open Dialog", type="button", id="btn-dialog"),
on_click=rx.stop_propagation, # pyright: ignore [reportArgumentType]
),
rx.dialog.content(
rx.dialog.close(
rx.form(
rx.button("Submit", id="btn-submit"),
on_submit=EventActionState.on_submit.stop_propagation, # pyright: ignore [reportCallIssue]
),
),
),
),
on_submit=EventActionState.on_submit, # pyright: ignore [reportCallIssue]
)

app = rx.App(_state=rx.State)
app.add_page(index)
app.add_page(index) # pyright: ignore [reportArgumentType]


@pytest.fixture(scope="module")
Expand Down Expand Up @@ -332,3 +357,29 @@ async def test_event_actions_throttle_debounce(
await poll_for_order(
["on_click_throttle"] * (exp_events - 1) + ["on_click_debounce"]
)


@pytest.mark.usefixtures("token")
@pytest.mark.asyncio
async def test_event_actions_dialog_form_in_form(
driver: WebDriver,
poll_for_order: Callable[[list[str]], Coroutine[None, None, None]],
):
"""Click links and buttons and assert on fired events.
Args:
driver: WebDriver instance.
poll_for_order: function that polls for the order list to match the expected order.
"""
open_dialog_id = "btn-dialog"
submit_button_id = "btn-submit"
wait = WebDriverWait(driver, 10)

driver.find_element(By.ID, open_dialog_id).click()
el = wait.until(EC.element_to_be_clickable((By.ID, submit_button_id)))
el.click() # pyright: ignore[reportAttributeAccessIssue]
el.send_keys(Keys.ESCAPE) # pyright: ignore[reportAttributeAccessIssue]

btn_no_events = wait.until(EC.element_to_be_clickable((By.ID, "btn-no-events")))
btn_no_events.click()
await poll_for_order(["on_submit", "on_click:outer"])

0 comments on commit 41d3514

Please sign in to comment.