diff --git a/reflex/components/component.py b/reflex/components/component.py index 8b6999adb..c0b721ba9 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -1113,6 +1113,42 @@ class Component(BaseComponent, ABC): return vars + def _event_triggers_contains_excluded_values( + self, exclude_event_trigger_values: list[str] + ) -> bool: + """Check if the component's event triggers contain excluded value names. + When an event trigger value is a Var, we check the var name else, if the value is + an EventChain, we go through all the events comparing the function names (or qualname for non-state + event handlers). + + Args: + exclude_event_trigger_values: excluded values to check for. + + Returns: + If the component's event triggers contain any excluded value + + """ + for trigger in self.event_triggers.values(): + if isinstance(trigger, EventChain): + for event in trigger.events: + if ( + ( + event.handler.state_full_name + and event.handler.fn.__name__ + in exclude_event_trigger_values + ) + or not event.handler.state_full_name + and event.handler.fn.__qualname__ + in exclude_event_trigger_values + ): + return True + elif ( + isinstance(trigger, Var) + and trigger._var_name in exclude_event_trigger_values + ): + return True + return False + def _has_event_triggers( self, exclude_event_trigger_values: list[str] | None = None ) -> bool: @@ -1126,11 +1162,9 @@ class Component(BaseComponent, ABC): """ if exclude_event_trigger_values is None: exclude_event_trigger_values = [] - if self.event_triggers and not any( - [ - Var.create_safe(trigger_value)._var_name in exclude_event_trigger_values - for trigger_value in self.event_triggers.values() - ] + + if self.event_triggers and not self._event_triggers_contains_excluded_values( + exclude_event_trigger_values ): return True else: diff --git a/tests/components/test_component.py b/tests/components/test_component.py index 2e395ce37..d98085373 100644 --- a/tests/components/test_component.py +++ b/tests/components/test_component.py @@ -2073,3 +2073,85 @@ def test_add_style_foreach(): # Expect only one instance of this CSS dict in the rendered page assert str(page).count('css={{"color": "red"}}') == 1 + + +class TriggerState(rx.State): + """Test state with event handlers.""" + + def do_something(self): + """Sample event handler.""" + pass + + +@pytest.mark.parametrize( + "component, exclude_event_trigger_values, output", + [ + (rx.box(rx.text("random text")), None, False), + (rx.box(rx.text("random text", on_click=rx.console_log("log"))), None, True), + ( + rx.box(rx.text("random text", on_click=rx.console_log("log"))), + ["_console"], + False, + ), + ( + rx.box( + rx.text("random text", on_click=rx.console_log("log")), + rx.text( + "random text", + on_click=BaseVar(_var_name="toggleColorMode", _var_type=EventChain), + ), + ), + ["_console", "toggleColorMode"], + False, + ), + ( + rx.box( + rx.text("random text", on_click=rx.console_log("log")), + rx.text( + "random text", + on_click=BaseVar(_var_name="toggleColorMode", _var_type=EventChain), + ), + ), + ["_console"], + True, + ), + ( + rx.box( + rx.text("random text", on_click=rx.console_log("log")), + rx.text( + "random text", + on_click=BaseVar(_var_name="toggleColorMode", _var_type=EventChain), + ), + ), + ["toggleColorMode"], + True, + ), + ( + rx.box(rx.text("random text", on_click=TriggerState.do_something)), + ["do_something"], + False, + ), + ( + rx.box(rx.text("random text", on_click=TriggerState.do_something)), + ["non_existent"], + True, + ), + ( + rx.box( + rx.text( + "random text", + on_click=[rx.console_log("log"), rx.window_alert("alert")], + ), + ), + ["_console", "_alert"], + False, + ), + ], +) +def test_has_event_triggers(component, exclude_event_trigger_values, output): + assert ( + component._has_event_triggers( + exclude_event_trigger_values=exclude_event_trigger_values + ) + == output + )