Skip to content

Commit 2f9be01

Browse files
authored
New Navigate component (#34)
- Create `navigate` component - Better key value identity of route components - Remove initial URL handling from the `History` component due to reactpy rendering bugs - This is now handled by a new `FirstLoad` element. - Fix docs publishing workflow - Add arg descriptions to all public functions - Better styling for autodocs. - Support Python 3.12
1 parent 0c04073 commit 2f9be01

22 files changed

+486
-162
lines changed

Diff for: .github/workflows/test-docs.yml

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ jobs:
2525
pip install -r requirements/build-docs.txt
2626
pip install -r requirements/check-types.txt
2727
pip install -r requirements/check-style.txt
28-
pip install -e .
2928
- name: Check docs build
3029
run: |
3130
linkcheckMarkdown docs/ -v -r

Diff for: .github/workflows/test-src.yaml

+35-35
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
11
name: Test
22

33
on:
4-
push:
5-
branches:
6-
- main
7-
pull_request:
8-
branches:
9-
- main
10-
schedule:
11-
- cron: "0 0 * * *"
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
schedule:
11+
- cron: "0 0 * * *"
1212

1313
jobs:
14-
source:
15-
runs-on: ubuntu-latest
16-
strategy:
17-
matrix:
18-
python-version: ["3.9", "3.10", "3.11"]
19-
steps:
20-
- uses: actions/checkout@v4
21-
- name: Use Python ${{ matrix.python-version }}
22-
uses: actions/setup-python@v5
23-
with:
24-
python-version: ${{ matrix.python-version }}
25-
- name: Install Python Dependencies
26-
run: pip install -r requirements/test-run.txt
27-
- name: Run Tests
28-
run: nox -t test
29-
coverage:
30-
runs-on: ubuntu-latest
31-
steps:
32-
- uses: actions/checkout@v4
33-
- name: Use Latest Python
34-
uses: actions/setup-python@v5
35-
with:
36-
python-version: "3.10"
37-
- name: Install Python Dependencies
38-
run: pip install -r requirements/test-run.txt
39-
- name: Run Tests
40-
run: nox -t test -- --coverage
14+
source:
15+
runs-on: ubuntu-latest
16+
strategy:
17+
matrix:
18+
python-version: ["3.9", "3.10", "3.11", "3.12"]
19+
steps:
20+
- uses: actions/checkout@v4
21+
- name: Use Python ${{ matrix.python-version }}
22+
uses: actions/setup-python@v5
23+
with:
24+
python-version: ${{ matrix.python-version }}
25+
- name: Install Python Dependencies
26+
run: pip install -r requirements/test-run.txt
27+
- name: Run Tests
28+
run: nox -t test
29+
coverage:
30+
runs-on: ubuntu-latest
31+
steps:
32+
- uses: actions/checkout@v4
33+
- name: Use Latest Python
34+
uses: actions/setup-python@v5
35+
with:
36+
python-version: "3.x"
37+
- name: Install Python Dependencies
38+
run: pip install -r requirements/test-run.txt
39+
- name: Run Tests
40+
run: nox -t test -- --coverage

Diff for: CHANGELOG.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,15 @@ Using the following categories, list your changes in this order:
4242
- Rename `CONVERSION_TYPES` to `CONVERTERS`.
4343
- Change "Match Any" syntax from a star `*` to `{name:any}`.
4444
- Rewrite `reactpy_router.link` to be a server-side component.
45-
- Simplified top-level exports within `reactpy_router`.
45+
- Simplified top-level exports that are available within `reactpy_router.*`.
4646

4747
### Added
4848

49-
- New error for ReactPy router elements being used outside router context.
50-
- Configurable/inheritable `Resolver` base class.
5149
- Add debug log message for when there are no router matches.
5250
- Add slug as a supported type.
51+
- Add `reactpy_router.navigate` component that will force the client to navigate to a new URL (when rendered).
52+
- New error for ReactPy router elements being used outside router context.
53+
- Configurable/inheritable `Resolver` base class.
5354

5455
### Fixed
5556

@@ -58,6 +59,7 @@ Using the following categories, list your changes in this order:
5859
- Fix bug where `link` elements could not have `@component` type children.
5960
- Fix bug where the ReactPy would not detect the current URL after a reconnection.
6061
- Fix bug where `ctrl` + `click` on a `link` element would not open in a new tab.
62+
- Fix test suite on Windows machines.
6163

6264
## [0.1.1] - 2023-12-13
6365

Diff for: docs/examples/python/basic-routing-more-routes.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from reactpy import component, html, run
2+
23
from reactpy_router import browser_router, route
34

45

Diff for: docs/examples/python/basic-routing.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from reactpy import component, html, run
2+
23
from reactpy_router import browser_router, route
34

45

Diff for: docs/mkdocs.yml

+15-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ nav:
88
- Hooks: learn/hooks.md
99
- Creating a Custom Router 🚧: learn/custom-router.md
1010
- Reference:
11-
- Router Components: reference/router.md
11+
- Routers: reference/routers.md
1212
- Components: reference/components.md
1313
- Hooks: reference/hooks.md
1414
- Types: reference/types.md
@@ -96,8 +96,21 @@ plugins:
9696
- https://reactpy.dev/docs/objects.inv
9797
- https://installer.readthedocs.io/en/stable/objects.inv
9898
options:
99-
show_bases: false
99+
signature_crossrefs: true
100+
scoped_crossrefs: true
101+
relative_crossrefs: true
102+
modernize_annotations: true
103+
unwrap_annotated: true
104+
find_stubs_package: true
100105
show_root_members_full_path: true
106+
show_bases: false
107+
show_source: false
108+
show_root_toc_entry: false
109+
show_labels: false
110+
show_symbol_type_toc: true
111+
show_symbol_type_heading: true
112+
show_object_full_path: true
113+
heading_level: 3
101114
extra:
102115
generator: false
103116
version:

Diff for: docs/src/dictionary.txt

+1
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,4 @@ misconfiguration
3737
misconfigurations
3838
backhaul
3939
sublicense
40+
contravariant

Diff for: docs/src/reference/components.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
::: reactpy_router
22

33
options:
4-
members: ["route", "link"]
4+
members: ["route", "link", "navigate"]
File renamed without changes.

Diff for: docs/src/reference/types.md

+4
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
::: reactpy_router.types
2+
3+
options:
4+
summary: true
5+
docstring_section_style: "list"

Diff for: pyproject.toml

-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,3 @@ line-length = 120
1717

1818
[tool.pytest.ini_options]
1919
testpaths = "tests"
20-
asyncio_mode = "auto"

Diff for: requirements/build-docs.txt

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ mkdocs-minify-plugin
99
mkdocs-section-index
1010
mike
1111
mkdocstrings[python]
12+
black # for mkdocstrings automatic code formatting
13+
.

Diff for: requirements/test-env.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
twine
22
pytest
3-
pytest-asyncio
3+
anyio
44
pytest-cov
55
reactpy[testing,starlette]
66
nodejs-bin==18.4.0a4

Diff for: src/js/src/index.js

+88-18
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,23 @@ export function bind(node) {
1212
};
1313
}
1414

15-
export function History({ onHistoryChange }) {
16-
// Capture browser "history go back" action and tell the server about it
17-
// Note: Browsers do not allow us to detect "history go forward" actions.
15+
/**
16+
* History component that captures browser "history go back" actions and notifies the server.
17+
*
18+
* @param {Object} props - The properties object.
19+
* @param {Function} props.onHistoryChangeCallback - Callback function to notify the server about history changes.
20+
* @returns {null} This component does not render any visible output.
21+
* @description
22+
* This component uses the `popstate` event to detect when the user navigates back in the browser history.
23+
* It then calls the `onHistoryChangeCallback` with the current pathname and search parameters.
24+
* Note: Browsers do not allow detection of "history go forward" actions.
25+
* @see https://github.com/reactive-python/reactpy/pull/1224
26+
*/
27+
export function History({ onHistoryChangeCallback }) {
1828
React.useEffect(() => {
1929
// Register a listener for the "popstate" event and send data back to the server using the `onHistoryChange` callback.
2030
const listener = () => {
21-
onHistoryChange({
31+
onHistoryChangeCallback({
2232
pathname: window.location.pathname,
2333
search: window.location.search,
2434
});
@@ -32,31 +42,42 @@ export function History({ onHistoryChange }) {
3242
});
3343

3444
// Tell the server about the URL during the initial page load
35-
// FIXME: This currently runs every time any component is mounted due to a ReactPy core rendering bug.
45+
// FIXME: This code is commented out since it currently runs every time any component
46+
// is mounted due to a ReactPy core rendering bug. `FirstLoad` component is used instead.
3647
// https://github.com/reactive-python/reactpy/pull/1224
37-
React.useEffect(() => {
38-
onHistoryChange({
39-
pathname: window.location.pathname,
40-
search: window.location.search,
41-
});
42-
return () => {};
43-
}, []);
48+
49+
// React.useEffect(() => {
50+
// onHistoryChange({
51+
// pathname: window.location.pathname,
52+
// search: window.location.search,
53+
// });
54+
// return () => {};
55+
// }, []);
4456
return null;
4557
}
4658

47-
// FIXME: The Link component is unused due to a ReactPy core rendering bug
48-
// which causes duplicate rendering (and thus duplicate event listeners).
49-
// https://github.com/reactive-python/reactpy/pull/1224
50-
export function Link({ onClick, linkClass }) {
59+
/**
60+
* Link component that captures clicks on anchor links and notifies the server.
61+
*
62+
* @param {Object} props - The properties object.
63+
* @param {Function} props.onClickCallback - Callback function to notify the server about link clicks.
64+
* @param {string} props.linkClass - The class name of the anchor link.
65+
* @returns {null} This component does not render any visible output.
66+
*/
67+
export function Link({ onClickCallback, linkClass }) {
68+
// FIXME: This component is currently unused due to a ReactPy core rendering bug
69+
// which causes duplicate rendering (and thus duplicate event listeners).
70+
// https://github.com/reactive-python/reactpy/pull/1224
71+
5172
// This component is not the actual anchor link.
5273
// It is an event listener for the link component created by ReactPy.
5374
React.useEffect(() => {
5475
// Event function that will tell the server about clicks
5576
const handleClick = (event) => {
5677
event.preventDefault();
5778
let to = event.target.getAttribute("href");
58-
window.history.pushState({}, to, new URL(to, window.location));
59-
onClick({
79+
window.history.pushState(null, "", new URL(to, window.location));
80+
onClickCallback({
6081
pathname: window.location.pathname,
6182
search: window.location.search,
6283
});
@@ -78,3 +99,52 @@ export function Link({ onClick, linkClass }) {
7899
});
79100
return null;
80101
}
102+
103+
/**
104+
* Client-side portion of the navigate component, that allows the server to command the client to change URLs.
105+
*
106+
* @param {Object} props - The properties object.
107+
* @param {Function} props.onNavigateCallback - Callback function that transmits data to the server.
108+
* @param {string} props.to - The target URL to navigate to.
109+
* @param {boolean} props.replace - If true, replaces the current history entry instead of adding a new one.
110+
* @returns {null} This component does not render anything.
111+
*/
112+
export function Navigate({ onNavigateCallback, to, replace }) {
113+
React.useEffect(() => {
114+
if (replace) {
115+
window.history.replaceState(null, "", new URL(to, window.location));
116+
} else {
117+
window.history.pushState(null, "", new URL(to, window.location));
118+
}
119+
onNavigateCallback({
120+
pathname: window.location.pathname,
121+
search: window.location.search,
122+
});
123+
return () => {};
124+
}, []);
125+
126+
return null;
127+
}
128+
129+
/**
130+
* FirstLoad component that captures the URL during the initial page load and notifies the server.
131+
*
132+
* @param {Object} props - The properties object.
133+
* @param {Function} props.onFirstLoadCallback - Callback function to notify the server about the first load.
134+
* @returns {null} This component does not render any visible output.
135+
* @description
136+
* This component sends the current URL to the server during the initial page load.
137+
* @see https://github.com/reactive-python/reactpy/pull/1224
138+
*/
139+
export function FirstLoad({ onFirstLoadCallback }) {
140+
// FIXME: This component only exists because of a ReactPy core rendering bug, and should be removed when the bug
141+
// is fixed. Ideally all this logic would be handled by the `History` component.
142+
React.useEffect(() => {
143+
onFirstLoadCallback({
144+
pathname: window.location.pathname,
145+
search: window.location.search,
146+
});
147+
return () => {};
148+
}, []);
149+
return null;
150+
}

Diff for: src/reactpy_router/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
__version__ = "0.1.1"
33

44

5-
from .components import link, route
5+
from .components import link, navigate, route
66
from .hooks import use_params, use_search_params
77
from .routers import browser_router, create_router
88

@@ -13,4 +13,5 @@
1313
"browser_router",
1414
"use_params",
1515
"use_search_params",
16+
"navigate",
1617
)

0 commit comments

Comments
 (0)