Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion reflex/.templates/web/utils/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,11 @@ export const applyEvent = async (event, socket, navigate, params) => {
return false;
}
if (event.payload.external) {
window.open(event.payload.path, "_blank", "noopener");
window.open(
event.payload.path,
"_blank",
"noopener" + (event.payload.popup ? ",popup" : ""),
);
return false;
}
const url = urlFrom(event.payload.path);
Expand Down
23 changes: 23 additions & 0 deletions reflex/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
Annotated,
Any,
Generic,
Literal,
NoReturn,
Protocol,
TypeVar,
Expand Down Expand Up @@ -962,16 +963,37 @@ def fn():
)


@overload
def redirect(
path: str | Var[str],
*,
is_external: Literal[False] = False,
replace: bool = False,
) -> EventSpec: ...


@overload
def redirect(
path: str | Var[str],
*,
is_external: Literal[True],
popup: bool = False,
) -> EventSpec: ...


def redirect(
path: str | Var[str],
*,
is_external: bool = False,
popup: bool = False,
replace: bool = False,
) -> EventSpec:
"""Redirect to a new path.

Args:
path: The path to redirect to.
is_external: Whether to open in new tab or not.
popup: Whether to open in a new window or not.
replace: If True, the current page will not create a new history entry.

Returns:
Expand All @@ -982,6 +1004,7 @@ def redirect(
get_fn_signature(redirect),
path=path,
external=is_external,
popup=popup,
replace=replace,
)

Expand Down
35 changes: 11 additions & 24 deletions tests/units/test_event.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from collections.abc import Callable

import pytest
Expand Down Expand Up @@ -169,29 +170,13 @@ def fn_with_args(_, arg1, arg2):
("input", "output"),
[
(
("/path", None, None),
'ReflexEvent("_redirect", {path:"/path",external:false,replace:false})',
),
(
("/path", True, None),
'ReflexEvent("_redirect", {path:"/path",external:true,replace:false})',
),
(
("/path", False, None),
'ReflexEvent("_redirect", {path:"/path",external:false,replace:false})',
),
(
(Var(_js_expr="path"), None, None),
'ReflexEvent("_redirect", {path:path,external:false,replace:false})',
),
(
("/path", None, True),
'ReflexEvent("_redirect", {path:"/path",external:false,replace:true})',
),
(
("/path", True, True),
'ReflexEvent("_redirect", {path:"/path",external:true,replace:true})',
),
(path, is_external, replace, popup),
f'ReflexEvent("_redirect", {{path:{json.dumps(path) if isinstance(path, str) else path._js_expr},external:{"true" if is_external else "false"},popup:{"true" if popup else "false"},replace:{"true" if replace else "false"}}})',
)
for path in ("/path", Var(_js_expr="path"))
for is_external in (None, True, False)
for replace in (None, True, False)
for popup in (None, True, False)
],
)
def test_event_redirect(input, output):
Expand All @@ -201,12 +186,14 @@ def test_event_redirect(input, output):
input: The input for running the test.
output: The expected output to validate the test.
"""
path, is_external, replace = input
path, is_external, replace, popup = input
kwargs = {}
if is_external is not None:
kwargs["is_external"] = is_external
if replace is not None:
kwargs["replace"] = replace
if popup is not None:
kwargs["popup"] = popup
spec = event.redirect(path, **kwargs)
assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_redirect"
Expand Down
Loading