-
Notifications
You must be signed in to change notification settings - Fork 0
feature: Add client-side Router to uni-dom #402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Summary of ChangesHello @xerial, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances uni-dom by integrating a robust client-side routing system. The new routing functionality allows developers to build dynamic Single Page Applications (SPAs) with ease, managing navigation and URL state reactively. It provides a structured way to define routes, extract parameters, and programmatically control the browser's history, moving uni-dom closer to a full-fledged web framework. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a client-side router, a significant and well-implemented feature for uni-dom. The core implementation is solid, with a clean API for defining routes, navigation, and reactive route matching. The use of the History API and handling of navigation edge cases like modifier key clicks are well done.
My review includes a few suggestions for improvement:
- The design document has a small discrepancy with the final implementation regarding the
outletproperty, which should be updated for clarity. - Some of the new tests are quite weak, only checking types rather than behavior. I've provided a suggestion to make one of the reactive tests more robust by asserting on actual values after state changes.
- There's a minor bug in the route pattern compilation logic that prevents matching literal
*characters in paths.
Overall, this is a great addition. Addressing these points will improve documentation accuracy, test coverage, and robustness.
plans/2026-02-04-dom-enhancements.md
Outdated
| ```scala | ||
| class RouterInstance[A](routes: Seq[Route[A]]): | ||
| // Current matched route | ||
| def outlet: Rx[Option[A]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API design for outlet has diverged from the implementation. The implementation provides outlet: Rx[A] (which throws on no match) and a new outletOption: Rx[Option[A]]. This design document should be updated to reflect the actual API. The usage example on line 91 should also be updated to use outletOption (e.g., router.outletOption.map(_.getOrElse(div("Loading...")))).
| def outlet: Rx[Option[A]] | |
| def outlet: Rx[A] | |
| def outletOption: Rx[Option[A]] |
| test("Router.isActive emits values reactively"): | ||
| val router = Router(Route("/", _ => "home")) | ||
| var result = false | ||
| val cancel = router | ||
| .isActive("/") | ||
| .run { v => | ||
| result = v | ||
| } | ||
| // In jsdom test environment, current location may vary | ||
| result shouldMatch { case _: Boolean => | ||
| } | ||
| cancel.cancel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test only verifies that isActive returns a boolean, but it doesn't check the actual value or test the reactive nature of the operator. To make this test more robust, you should programmatically change the URL using Router.push and assert that the isActive stream emits the expected boolean values in response.
test("Router.isActive emits values reactively"):
val router = Router(Route("/", _ => "home"))
var result = false
val cancel = router
.isActive("/")
.run { v =>
result = v
}
// Set a known state and verify
Router.push("/other")
result shouldBe false
Router.push("/")
result shouldBe true
cancel.cancel| val regexPattern = paramPattern.replaceAllIn( | ||
| scala.util.matching.Regex.quote(route.pattern).replace("\\*", ".*"), | ||
| "([^/]+)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation for compiling route patterns has a subtle bug. By calling replace("\\*", ".*") after Regex.quote, it becomes impossible to match a literal * character in a route path, as it will always be treated as a wildcard. This could lead to unexpected routing behavior.
A more robust approach would be to parse the pattern segment by segment, or use a regex that can distinguish between a wildcard * and a path parameter, without using a broad string replacement after quoting.
9ce8ac7 to
d126642
Compare
Add client-side routing support using the History API for building SPAs: - Router object for global navigation (push, replace, back, forward) - RouterInstance for route matching with reactive outlet - Route patterns with named parameters (:id) and wildcards (*) - SPA-style links that prevent page reloads - Query string and hash parsing utilities - Reactive location state (pathname, search, hash) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
d126642 to
068664d
Compare
Summary
Routerobject for global navigation (push,replace,back,forward)RouterInstancefor route matching with reactiveoutletandparams:id) and wildcards (*)link()that prevents page reloads and adds active stateLocationandRouteParamspathname,search,hash)Usage
Test plan
🤖 Generated with Claude Code