Skip to content

Commit

Permalink
more robust docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Archmonger committed Oct 17, 2024
1 parent 1408d09 commit 109d87b
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 46 deletions.
17 changes: 15 additions & 2 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ nav:
- Hooks: learn/hooks.md
- Creating a Custom Router 🚧: learn/custom-router.md
- Reference:
- Router Components: reference/router.md
- Routers: reference/routers.md
- Components: reference/components.md
- Hooks: reference/hooks.md
- Types: reference/types.md
Expand Down Expand Up @@ -96,8 +96,21 @@ plugins:
- https://reactpy.dev/docs/objects.inv
- https://installer.readthedocs.io/en/stable/objects.inv
options:
show_bases: false
signature_crossrefs: true
scoped_crossrefs: true
relative_crossrefs: true
modernize_annotations: true
unwrap_annotated: true
find_stubs_package: true
show_root_members_full_path: true
show_bases: false
show_source: false
show_root_toc_entry: false
show_labels: false
show_symbol_type_toc: true
show_symbol_type_heading: true
show_object_full_path: true
heading_level: 3
extra:
generator: false
version:
Expand Down
1 change: 1 addition & 0 deletions docs/src/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ misconfiguration
misconfigurations
backhaul
sublicense
contravariant
File renamed without changes.
4 changes: 4 additions & 0 deletions docs/src/reference/types.md
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
::: reactpy_router.types

options:
summary: true
docstring_section_style: "list"
1 change: 1 addition & 0 deletions requirements/build-docs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ mkdocs-minify-plugin
mkdocs-section-index
mike
mkdocstrings[python]
black # for mkdocstrings automatic code formatting
.
39 changes: 34 additions & 5 deletions src/reactpy_router/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,21 @@


def link(attributes: dict[str, Any], *children: Any) -> Component:
"""Create a link with the given attributes and children."""
"""
Create a link with the given attributes and children.
Args:
attributes: A dictionary of attributes for the link.
*children: Child elements to be included within the link.
Returns:
A link component with the specified attributes and children.
"""
return _link(attributes, *children)


@component
def _link(attributes: dict[str, Any], *children: Any) -> VdomDict:
"""A component that renders a link to the given path."""
attributes = attributes.copy()
uuid_string = f"link-{uuid4().hex}"
class_name = f"{uuid_string}"
Expand Down Expand Up @@ -110,18 +118,39 @@ def on_click(_event: dict[str, Any]) -> None:


def route(path: str, element: Any | None, *routes: Route) -> Route:
"""Create a route with the given path, element, and child routes."""
"""
Create a route with the given path, element, and child routes.
Args:
path: The path for the route.
element: The element to render for this route. Can be None.
routes: Additional child routes.
Returns:
The created route object.
"""
return Route(path, element, routes)


def navigate(to: str, replace: bool = False) -> Component:
"""A `navigate` element changes the current location when it is rendered."""
"""
Navigate to a specified URL.
This function changes the browser's current URL when it is rendered.
Args:
to: The target URL to navigate to.
replace: If True, the current history entry will be replaced \
with the new URL. Defaults to False.
Returns:
The component responsible for navigation.
"""
return _navigate(to, replace)


@component
def _navigate(to: str, replace: bool = False) -> VdomDict | None:
"""A `navigate` element changes the current location when it is rendered."""
location = use_connection().location
set_location = _use_route_state().set_location
pathname = to.split("?", 1)[0]
Expand Down
33 changes: 17 additions & 16 deletions src/reactpy_router/hooks.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Callable
from typing import Any
from urllib.parse import parse_qs

from reactpy import create_context, use_context, use_location
from reactpy.backend.types import Location
from reactpy.types import Context

from reactpy_router.types import RouteState

@dataclass
class _RouteState:
set_location: Callable[[Location], None]
params: dict[str, Any]
_route_state_context: Context[RouteState | None] = create_context(None)


def _use_route_state() -> _RouteState:
def _use_route_state() -> RouteState:
route_state = use_context(_route_state_context)
if route_state is None: # pragma: no cover
raise RuntimeError(
Expand All @@ -26,16 +22,17 @@ def _use_route_state() -> _RouteState:
return route_state


_route_state_context: Context[_RouteState | None] = create_context(None)


def use_params() -> dict[str, Any]:
"""The `use_params` hook returns an object of key/value pairs of the dynamic parameters \
"""This hook returns an object of key/value pairs of the dynamic parameters \
from the current URL that were matched by the `Route`. Child routes inherit all parameters \
from their parent routes.
For example, if you have a `URL_PARAM` defined in the route `/example/<URL_PARAM>/`,
this hook will return the URL_PARAM value that was matched."""
this hook will return the `URL_PARAM` value that was matched.
Returns:
A dictionary of the current URL's parameters.
"""

# TODO: Check if this returns all parent params
return _use_route_state().params
Expand All @@ -49,10 +46,14 @@ def use_search_params(
separator: str = "&",
) -> dict[str, list[str]]:
"""
The `use_search_params` hook is used to read the query string in the URL \
for the current location.
This hook is used to read the query string in the URL for the current location.
See [`urllib.parse.parse_qs`](https://docs.python.org/3/library/urllib.parse.html#urllib.parse.parse_qs) \
for info on this hook's parameters.
See `urllib.parse.parse_qs` for info on this hook's parameters."""
Returns:
A dictionary of the current URL's query string parameters.
"""
location = use_location()
query_string = location.search[1:] if len(location.search) > 1 else ""

Expand Down
25 changes: 19 additions & 6 deletions src/reactpy_router/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
from reactpy import component, use_memo, use_state
from reactpy.backend.hooks import ConnectionContext, use_connection
from reactpy.backend.types import Connection, Location
from reactpy.core.component import Component
from reactpy.types import ComponentType, VdomDict

from reactpy_router.components import FirstLoad, History
from reactpy_router.hooks import _route_state_context, _RouteState
from reactpy_router.hooks import RouteState, _route_state_context
from reactpy_router.resolvers import StarletteResolver
from reactpy_router.types import CompiledRoute, Resolver, Router, RouteType

Expand All @@ -23,15 +24,27 @@
def create_router(resolver: Resolver[RouteType]) -> Router[RouteType]:
"""A decorator that turns a resolver into a router"""

def wrapper(*routes: RouteType) -> ComponentType:
def wrapper(*routes: RouteType) -> Component:
return router(*routes, resolver=resolver)

return wrapper


browser_router = create_router(StarletteResolver)
"""This is the recommended router for all ReactPy Router web projects.
It uses the JavaScript DOM History API to manage the history stack."""
_starlette_router = create_router(StarletteResolver)


def browser_router(*routes: RouteType) -> Component:
"""This is the recommended router for all ReactPy-Router web projects.
It uses the JavaScript [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API)
to manage the history stack.
Args:
*routes (RouteType): A list of routes to be rendered by the router.
Returns:
A router component that renders the given routes.
"""
return _starlette_router(*routes)


@component
Expand Down Expand Up @@ -59,7 +72,7 @@ def router(
route_elements = [
_route_state_context(
element,
value=_RouteState(set_location, params),
value=RouteState(set_location, params),
)
for element, params in match
]
Expand Down
95 changes: 78 additions & 17 deletions src/reactpy_router/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,36 @@
from dataclasses import dataclass, field
from typing import Any, Callable, Sequence, TypedDict, TypeVar

from reactpy.backend.types import Location
from reactpy.core.component import Component
from reactpy.core.vdom import is_vdom
from reactpy.types import ComponentType, Key
from reactpy.types import Key
from typing_extensions import Protocol, Self, TypeAlias

ConversionFunc: TypeAlias = Callable[[str], Any]
"""A function that converts a string to a specific type."""

ConverterMapping: TypeAlias = dict[str, ConversionFunc]
"""A mapping of conversion types to their respective functions."""


@dataclass(frozen=True)
class Route:
"""A route that can be matched against a path."""
"""
A class representing a route that can be matched against a path.
path: str
"""The path to match against."""
Attributes:
path (str): The path to match against.
element (Any): The element to render if the path matches.
routes (Sequence[Self]): Child routes.
element: Any = field(hash=False)
"""The element to render if the path matches."""
Methods:
__hash__() -> int: Returns a hash value for the route based on its path, element, and child routes.
"""

path: str
element: Any = field(hash=False)
routes: Sequence[Self]
"""Child routes."""

def __hash__(self) -> int:
el = self.element
Expand All @@ -33,36 +43,87 @@ def __hash__(self) -> int:


RouteType = TypeVar("RouteType", bound=Route)
"""A type variable for `Route`."""

RouteType_contra = TypeVar("RouteType_contra", bound=Route, contravariant=True)
"""A contravariant type variable for `Route`."""


class Router(Protocol[RouteType_contra]):
"""Return a component that renders the first matching route."""
"""Return a component that renders the matching route(s)."""

def __call__(self, *routes: RouteType_contra) -> Component:
"""
Process the given routes and return a component that renders the matching route(s).
def __call__(self, *routes: RouteType_contra) -> ComponentType: ...
Args:
*routes: A variable number of route arguments.
Returns:
The resulting component after processing the routes.
"""


class Resolver(Protocol[RouteType_contra]):
"""Compile a route into a resolver that can be matched against a given path."""

def __call__(self, route: RouteType_contra) -> CompiledRoute: ...
def __call__(self, route: RouteType_contra) -> CompiledRoute:
"""
Compile a route into a resolver that can be matched against a given path.
Args:
route: The route to compile.
Returns:
The compiled route.
"""


class CompiledRoute(Protocol):
"""A compiled route that can be matched against a path."""
"""
A protocol for a compiled route that can be matched against a path.
Attributes:
key (Key): A property that uniquely identifies this resolver.
"""

@property
def key(self) -> Key:
"""Uniquely identified this resolver."""
def key(self) -> Key: ...

def resolve(self, path: str) -> tuple[Any, dict[str, Any]] | None:
"""Return the path's associated element and path parameters or None."""
"""
Return the path's associated element and path parameters or None.
Args:
path (str): The path to resolve.
Returns:
A tuple containing the associated element and a dictionary of path parameters, or None if the path cannot be resolved.
"""


class ConversionInfo(TypedDict):
"""Information about a conversion type."""
"""
A TypedDict that holds information about a conversion type.
Attributes:
regex (str): The regex to match the conversion type.
func (ConversionFunc): The function to convert the matched string to the expected type.
"""

regex: str
"""The regex to match the conversion type."""
func: ConversionFunc
"""The function to convert the matched string to the expected type."""


@dataclass
class RouteState:
"""
Represents the state of a route in the application.
Attributes:
set_location: A callable to set the location.
params: A dictionary containing route parameters.
"""

set_location: Callable[[Location], None]
params: dict[str, Any]

0 comments on commit 109d87b

Please sign in to comment.