From 646efb6271c20238a796c2253d0e02dec3ecb68d Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:50:39 -0800 Subject: [PATCH 1/5] refactor private API folders --- src/reactpy_django/router/__init__.py | 6 ++++-- src/reactpy_django/router/{components.py => resolvers.py} | 4 ---- 2 files changed, 4 insertions(+), 6 deletions(-) rename src/reactpy_django/router/{components.py => resolvers.py} (95%) diff --git a/src/reactpy_django/router/__init__.py b/src/reactpy_django/router/__init__.py index ea3e1d3b..3c48e1ab 100644 --- a/src/reactpy_django/router/__init__.py +++ b/src/reactpy_django/router/__init__.py @@ -1,3 +1,5 @@ -from reactpy_django.router.components import django_router +from reactpy_router.core import create_router -__all__ = ["django_router"] +from reactpy_django.router.resolvers import DjangoResolver + +django_router = create_router(DjangoResolver) diff --git a/src/reactpy_django/router/components.py b/src/reactpy_django/router/resolvers.py similarity index 95% rename from src/reactpy_django/router/components.py rename to src/reactpy_django/router/resolvers.py index 60aca8fd..598e5da5 100644 --- a/src/reactpy_django/router/components.py +++ b/src/reactpy_django/router/resolvers.py @@ -3,7 +3,6 @@ import re from typing import Any -from reactpy_router.core import create_router from reactpy_router.simple import ConverterMapping from reactpy_router.types import Route @@ -51,6 +50,3 @@ def parse_path(path: str) -> tuple[re.Pattern[str], ConverterMapping]: last_match_end = match.end() pattern += f"{re.escape(path[last_match_end:])}$" return re.compile(pattern), converters - - -django_router = create_router(DjangoResolver) From 1c39865f3b42abe58a5c84694a1ac3154ca381ec Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:17:35 -0800 Subject: [PATCH 2/5] support star pattern --- src/reactpy_django/router/components.py | 5 +++++ tests/test_app/router/components.py | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/reactpy_django/router/components.py b/src/reactpy_django/router/components.py index 60aca8fd..bb37803d 100644 --- a/src/reactpy_django/router/components.py +++ b/src/reactpy_django/router/components.py @@ -33,6 +33,7 @@ def resolve(self, path: str) -> tuple[Any, dict[str, Any]] | None: # TODO: Make reactpy_router's parse_path generic enough to where we don't have to define our own def parse_path(path: str) -> tuple[re.Pattern[str], ConverterMapping]: + # Convert path to regex pattern, and make sure to interpret the registered converters (ex. ) pattern = "^" last_match_end = 0 converters: ConverterMapping = {} @@ -50,6 +51,10 @@ def parse_path(path: str) -> tuple[re.Pattern[str], ConverterMapping]: converters[param_name] = param_conv["func"] last_match_end = match.end() pattern += f"{re.escape(path[last_match_end:])}$" + + # Replace literal `*` with "match anything" regex pattern + pattern = pattern.replace("\*", ".*") + return re.compile(pattern), converters diff --git a/tests/test_app/router/components.py b/tests/test_app/router/components.py index 8f020990..e1162fb2 100644 --- a/tests/test_app/router/components.py +++ b/tests/test_app/router/components.py @@ -39,4 +39,17 @@ def main(): "/router/two///", display_params("Path 9", route_info), ), + route( + "/router/multi-tier-route/", + display_params("Path 10", route_info), + route("one/", display_params("Path 11", route_info)), + route("two/", display_params("Path 12", route_info)), + route("*", display_params("Path 13", route_info)), + ), + route( + "/router/star-in-middle/*/", + display_params("Path 14", route_info), + route("one/", display_params("Path 15", route_info)), + route("two/", display_params("Path 16", route_info)), + ), ) From 97afb07025d06924520f2cea5b02cff461b94cd5 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Mon, 29 Jan 2024 20:52:10 -0800 Subject: [PATCH 3/5] add tests --- src/reactpy_django/router/resolvers.py | 5 ++- tests/test_app/router/components.py | 52 ++++++++++--------------- tests/test_app/tests/test_components.py | 15 +++++++ 3 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/reactpy_django/router/resolvers.py b/src/reactpy_django/router/resolvers.py index e0ea8eab..9732ba37 100644 --- a/src/reactpy_django/router/resolvers.py +++ b/src/reactpy_django/router/resolvers.py @@ -51,7 +51,8 @@ def parse_path(path: str) -> tuple[re.Pattern[str], ConverterMapping]: last_match_end = match.end() pattern += f"{re.escape(path[last_match_end:])}$" - # Replace literal `*` with "match anything" regex pattern - pattern = pattern.replace("\*", ".*") + # Replace literal `*` with "match anything" regex pattern, if it's at the end of the path + if pattern.endswith("\*$"): + pattern = f"{pattern[:-3]}.*$" return re.compile(pattern), converters diff --git a/tests/test_app/router/components.py b/tests/test_app/router/components.py index e1162fb2..19eee0ba 100644 --- a/tests/test_app/router/components.py +++ b/tests/test_app/router/components.py @@ -4,20 +4,14 @@ @component -def display_params(*args): - params = use_params() - return html._( - html.div(f"Params: {params}"), - *args, - ) - - -@component -def main(): +def display_params(string: str): location = use_location() query = use_query() + params = use_params() - route_info = html._( + return html._( + html.div({"id": "router-string"}, string), + html.div(f"Params: {params}"), html.div( {"id": "router-path", "data-path": location.pathname}, f"Path Name: {location.pathname}", @@ -26,30 +20,26 @@ def main(): html.div(f"Query: {query}"), ) + +@component +def main(): return django_router( - route("/router/", html.div("Path 1", route_info)), - route("/router/any//", display_params("Path 2", route_info)), - route("/router/integer//", display_params("Path 3", route_info)), - route("/router/path//", display_params("Path 4", route_info)), - route("/router/slug//", display_params("Path 5", route_info)), - route("/router/string//", display_params("Path 6", route_info)), - route("/router/uuid//", display_params("Path 7", route_info)), - route("/router/", None, route("abc/", display_params("Path 8", route_info))), + route("/router/", display_params("Path 1")), + route("/router/any//", display_params("Path 2")), + route("/router/integer//", display_params("Path 3")), + route("/router/path//", display_params("Path 4")), + route("/router/slug//", display_params("Path 5")), + route("/router/string//", display_params("Path 6")), + route("/router/uuid//", display_params("Path 7")), + route("/router/", None, route("abc/", display_params("Path 8"))), route( "/router/two///", - display_params("Path 9", route_info), - ), - route( - "/router/multi-tier-route/", - display_params("Path 10", route_info), - route("one/", display_params("Path 11", route_info)), - route("two/", display_params("Path 12", route_info)), - route("*", display_params("Path 13", route_info)), + display_params("Path 9"), ), route( - "/router/star-in-middle/*/", - display_params("Path 14", route_info), - route("one/", display_params("Path 15", route_info)), - route("two/", display_params("Path 16", route_info)), + "/router/star/", + None, + route("one/", display_params("Path 11")), + route("*", display_params("Path 12")), ), ) diff --git a/tests/test_app/tests/test_components.py b/tests/test_app/tests/test_components.py index 61a0ca86..15d5b553 100644 --- a/tests/test_app/tests/test_components.py +++ b/tests/test_app/tests/test_components.py @@ -606,6 +606,21 @@ def test_url_router(self): path = new_page.wait_for_selector("#router-path") self.assertIn("/router/two/123/abc/", path.get_attribute("data-path")) + new_page.goto(f"{self.live_server_url}/router/star/one/") + path = new_page.wait_for_selector("#router-path") + self.assertIn("/router/star/one/", path.get_attribute("data-path")) + + new_page.goto( + f"{self.live_server_url}/router/star/adslkjgklasdjhfah/6789543256/" + ) + path = new_page.wait_for_selector("#router-path") + self.assertIn( + "/router/star/adslkjgklasdjhfah/6789543256/", + path.get_attribute("data-path"), + ) + string = new_page.query_selector("#router-string") + self.assertEquals("Path 12", string.text_content()) + finally: new_page.close() From 73ffa241cc85e27af876d257e155819974c54241 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:10:00 -0800 Subject: [PATCH 4/5] Add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 977c85eb..de6c4d35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ Using the following categories, list your changes in this order: ### Added - An "offline component" can now be displayed when the client disconnects from the server. +- URL router now supports a `*` wildcard to create default routes. ## [3.6.0] - 2024-01-10 From ab782aac615bd269e58a7e49ab6deb6c97edd99a Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:11:36 -0800 Subject: [PATCH 5/5] Add docs --- docs/examples/python/django-router.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/examples/python/django-router.py b/docs/examples/python/django-router.py index 714ca585..5c845967 100644 --- a/docs/examples/python/django-router.py +++ b/docs/examples/python/django-router.py @@ -13,5 +13,6 @@ def my_component(): route("/router/slug//", html.div("Example 5")), route("/router/string//", html.div("Example 6")), route("/router/uuid//", html.div("Example 7")), - route("/router/two_values///", html.div("Example 9")), + route("/router/two_values///", html.div("Example 8")), + route("/router/*", html.div("Fallback")), )