update tests

This commit is contained in:
Evan Almloff committed 2023-05-31 13:57:33 -05:00
1 parent 58b74c1155
commit 35befa1784
12 files changed
+334 -236

No files matched your search

+14 -7
View File
@@ -1,6 +1,6 @@
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use syn::Ident; use syn::Path;
use crate::nest::{Nest, NestId}; use crate::nest::{Nest, NestId};
@@ -9,8 +9,8 @@ pub struct LayoutId(pub usize);
#[derive(Debug)] #[derive(Debug)]
pub struct Layout { pub struct Layout {
pub comp: Ident, pub comp: Path,
pub props_name: Ident, pub props_name: Path,
pub active_nests: Vec<NestId>, pub active_nests: Vec<NestId>,
} }
@@ -38,13 +38,20 @@ impl Layout {
pub fn parse(input: syn::parse::ParseStream, active_nests: Vec<NestId>) -> syn::Result<Self> { pub fn parse(input: syn::parse::ParseStream, active_nests: Vec<NestId>) -> syn::Result<Self> {
// Then parse the component name // Then parse the component name
let _ = input.parse::<syn::Token![,]>(); let _ = input.parse::<syn::Token![,]>();
let comp: Ident = input.parse()?; let comp: Path = input.parse()?;
// Then parse the props name // Then parse the props name
let _ = input.parse::<syn::Token![,]>(); let _ = input.parse::<syn::Token![,]>();
let props_name: Ident = input let props_name = input.parse::<Path>().unwrap_or_else(|_| {
.parse() let last = format_ident!("{}Props", comp.segments.last().unwrap().ident.to_string());
.unwrap_or_else(|_| format_ident!("{}Props", comp.to_string())); let mut segments = comp.segments.clone();
segments.pop();
segments.push(last.into());
Path {
leading_colon: None,
segments,
}
});
Ok(Self { Ok(Self {
comp, comp,
+5 -18
View File
@@ -57,8 +57,6 @@ pub fn routable(input: TokenStream) -> TokenStream {
dioxus_router::prelude::use_generic_router::<R>(cx) dioxus_router::prelude::use_generic_router::<R>(cx)
} }
#route_enum
#error_type #error_type
#parse_impl #parse_impl
@@ -296,9 +294,13 @@ impl RouteEnum {
type Err = dioxus_router::routable::RouteParseError<#error_name>; type Err = dioxus_router::routable::RouteParseError<#error_name>;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let route = s.strip_prefix('/').unwrap_or(s); let route = s;
let (route, query) = route.split_once('?').unwrap_or((route, "")); let (route, query) = route.split_once('?').unwrap_or((route, ""));
let mut segments = route.split('/'); let mut segments = route.split('/');
// skip the first empty segment
if s.starts_with('/') {
segments.next();
}
let mut errors = Vec::new(); let mut errors = Vec::new();
#(#tokens)* #(#tokens)*
@@ -427,21 +429,6 @@ impl RouteEnum {
} }
} }
impl ToTokens for RouteEnum {
fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) {
let routes = &self.routes;
tokens.extend(quote!(
#[path = "pages"]
mod pages {
#(#routes)*
}
pub use pages::*;
));
}
}
struct SiteMapSegment { struct SiteMapSegment {
pub segment_type: SegmentType, pub segment_type: SegmentType,
pub children: Vec<SiteMapSegment>, pub children: Vec<SiteMapSegment>,
+21 -46
View File
@@ -1,6 +1,8 @@
use quote::{format_ident, quote, ToTokens}; use quote::{format_ident, quote};
use syn::parse::Parse; use syn::parse::Parse;
use syn::parse::ParseStream; use syn::parse::ParseStream;
use syn::parse_quote;
use syn::Path;
use syn::{Ident, LitStr}; use syn::{Ident, LitStr};
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
@@ -16,8 +18,8 @@ use crate::segment::RouteSegment;
struct RouteArgs { struct RouteArgs {
route: LitStr, route: LitStr,
comp_name: Option<Ident>, comp_name: Option<Path>,
props_name: Option<Ident>, props_name: Option<Path>,
} }
impl Parse for RouteArgs { impl Parse for RouteArgs {
@@ -40,10 +42,9 @@ impl Parse for RouteArgs {
#[derive(Debug)] #[derive(Debug)]
pub struct Route { pub struct Route {
pub file_based: bool,
pub route_name: Ident, pub route_name: Ident,
pub comp_name: Ident, pub comp_name: Path,
pub props_name: Ident, pub props_name: Path,
pub route: String, pub route: String,
pub segments: Vec<RouteSegment>, pub segments: Vec<RouteSegment>,
pub query: Option<QuerySegment>, pub query: Option<QuerySegment>,
@@ -73,13 +74,20 @@ impl Route {
let route_name = variant.ident.clone(); let route_name = variant.ident.clone();
let args = route_attr.parse_args::<RouteArgs>()?; let args = route_attr.parse_args::<RouteArgs>()?;
let route = args.route.value(); let route = args.route.value();
let file_based = args.comp_name.is_none(); let comp_name = args.comp_name.unwrap_or_else(|| parse_quote!(#route_name));
let comp_name = args let props_name = args.props_name.unwrap_or_else(|| {
.comp_name let last = format_ident!(
.unwrap_or_else(|| format_ident!("{}", route_name)); "{}Props",
let props_name = args comp_name.segments.last().unwrap().ident.to_string()
.props_name );
.unwrap_or_else(|| format_ident!("{}Props", comp_name)); let mut segments = comp_name.segments.clone();
segments.pop();
segments.push(last.into());
Path {
leading_colon: None,
segments,
}
});
let named_fields = match &variant.fields { let named_fields = match &variant.fields {
syn::Fields::Named(fields) => fields, syn::Fields::Named(fields) => fields,
@@ -106,7 +114,6 @@ impl Route {
route_name, route_name,
segments: route_segments, segments: route_segments,
route, route,
file_based,
query, query,
nests, nests,
layouts, layouts,
@@ -234,35 +241,3 @@ impl Route {
} }
} }
} }
impl ToTokens for Route {
fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) {
if !self.file_based {
return;
}
let without_leading_slash = &self.route[1..];
let route_path = std::path::Path::new(without_leading_slash);
let with_extension = route_path.with_extension("rs");
let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let dir = std::path::Path::new(&dir);
let route = dir.join("src").join("pages").join(with_extension.clone());
// check if the route exists or if not use the index route
let route = if route.exists() && !without_leading_slash.is_empty() {
with_extension.to_str().unwrap().to_string()
} else {
route_path.join("index.rs").to_str().unwrap().to_string()
};
let route_name: Ident = self.route_name.clone();
let prop_name = &self.props_name;
tokens.extend(quote!(
#[path = #route]
#[allow(non_snake_case)]
mod #route_name;
pub use #route_name::{#prop_name, #route_name};
));
}
}
+10 -2
View File
@@ -291,13 +291,21 @@ impl<'a> RouteTreeSegmentData<'a> {
enum_varient, enum_varient,
}, },
} => { } => {
let error_ident = static_segment_idx(*index);
let children = children.iter().map(|child| { let children = children.iter().map(|child| {
let child = tree.get(*child).unwrap(); let child = tree.get(*child).unwrap();
child.to_tokens(nests, tree, enum_name.clone(), error_enum_name.clone()) child.to_tokens(nests, tree, enum_name.clone(), error_enum_name.clone())
}); });
if segment.is_empty() {
return quote! {
{
#(#children)*
}
};
}
let error_ident = static_segment_idx(*index);
quote! { quote! {
{ {
let mut segments = segments.clone(); let mut segments = segments.clone();
+3 -3
View File
@@ -132,7 +132,7 @@ enum Route {
// UserFrame is a layout component that will receive the `user_id: String` parameter // UserFrame is a layout component that will receive the `user_id: String` parameter
#[layout(UserFrame)] #[layout(UserFrame)]
// Route1 is a non-layout component that will receive the `user_id: String` and `dynamic: String` parameters // Route1 is a non-layout component that will receive the `user_id: String` and `dynamic: String` parameters
#[route("/:dynamic?:query", Route1)] #[route("/:dynamic?:query")]
Route1 { Route1 {
// The type is taken from the first instance of the dynamic parameter // The type is taken from the first instance of the dynamic parameter
user_id: usize, user_id: usize,
@@ -141,13 +141,13 @@ enum Route {
extra: String, extra: String,
}, },
// Route2 is a non-layout component that will receive the `user_id: String` parameter // Route2 is a non-layout component that will receive the `user_id: String` parameter
#[route("/hello_world", Route2)] #[route("/hello_world")]
// You can opt out of the layout by using the `!` prefix // You can opt out of the layout by using the `!` prefix
#[layout(!UserFrame)] #[layout(!UserFrame)]
Route2 { user_id: usize }, Route2 { user_id: usize },
#[end_layout] #[end_layout]
#[end_nest] #[end_nest]
#[redirect("/:id/user", |id: usize| Route::Route3 { dynamic: id.to_string()})] #[redirect("/:id/user", |id: usize| Route::Route3 { dynamic: id.to_string()})]
#[route("/:dynamic", Route3)] #[route("/:dynamic")]
Route3 { dynamic: String }, Route3 { dynamic: String },
} }
+3 -11
View File
@@ -152,18 +152,10 @@ pub fn GenericLink<'a, R: Routable + Clone>(cx: Scope<'a, GenericLinkProps<'a, R
}; };
let current_route = router.current(); let current_route = router.current();
let href = current_route.to_string(); let current_url = current_route.to_string();
let href = target.to_string();
let ac = active_class let ac = active_class
.and_then(|active_class| match target { .and_then(|active_class| (href == current_url).then(|| format!(" {active_class}")))
NavigationTarget::Internal(target) => {
if href == target.to_string() {
Some(format!(" {active_class}"))
} else {
None
}
}
_ => None,
})
.unwrap_or_default(); .unwrap_or_default();
let id = id.unwrap_or_default(); let id = id.unwrap_or_default();
-9
View File
@@ -38,8 +38,6 @@ pub struct GenericRouterProps<R: Routable>
where where
<R as FromStr>::Err: std::fmt::Display, <R as FromStr>::Err: std::fmt::Display,
{ {
#[props(into)]
initial_url: Option<String>,
#[props(default, into)] #[props(default, into)]
config: RouterCfg<R>, config: RouterCfg<R>,
} }
@@ -71,13 +69,6 @@ where
cx.props.config.config.take().unwrap_or_default(), cx.props.config.config.take().unwrap_or_default(),
cx.schedule_update_any(), cx.schedule_update_any(),
); );
if let Some(initial) = cx.props.initial_url.as_ref() {
router.replace(
initial
.parse()
.unwrap_or_else(|_| panic!("failed to parse initial url")),
);
}
router router
}); });
+11
View File
@@ -51,6 +51,17 @@ pub mod prelude {
pub use crate::routable::*; pub use crate::routable::*;
pub use crate::router_cfg::RouterConfiguration; pub use crate::router_cfg::RouterConfiguration;
pub use dioxus_router_macro::Routable; pub use dioxus_router_macro::Routable;
#[doc(hidden)]
/// A component with props used in the macro
pub trait HasProps {
/// The props type of the component.
type Props;
}
impl<P> HasProps for dioxus::prelude::Component<P> {
type Props = P;
}
} }
mod utils { mod utils {
+16
View File
@@ -29,6 +29,22 @@ pub enum NavigationTarget<R: Routable> {
External(String), External(String),
} }
impl<R: Routable> From<&str> for NavigationTarget<R>
where
<R as FromStr>::Err: Display,
{
fn from(value: &str) -> Self {
Self::from_str(value).unwrap_or_else(|err| match err {
NavigationTargetParseError::InvalidUrl(e) => {
panic!("Failed to parse `{}` as a URL: {}", value, e)
}
NavigationTargetParseError::InvalidInternalURL(e) => {
panic!("Failed to parse `{}` as a `Routable`: {}", value, e)
}
})
}
}
impl<R: Routable> From<R> for NavigationTarget<R> { impl<R: Routable> From<R> for NavigationTarget<R> {
fn from(value: R) -> Self { fn from(value: R) -> Self {
Self::Internal(value) Self::Internal(value)
+162 -66
View File
@@ -1,47 +1,69 @@
#![allow(non_snake_case)]
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_router::prelude::*; use dioxus_router::prelude::*;
use std::str::FromStr;
fn prepare(link: Component) -> String { fn prepare<R: Routable>() -> String
#![allow(non_snake_case)] where
<R as FromStr>::Err: std::fmt::Display,
let mut vdom = VirtualDom::new_with_props(App, AppProps { link }); {
let mut vdom = VirtualDom::new_with_props(
App,
AppProps::<R> {
phantom: std::marker::PhantomData,
},
);
let _ = vdom.rebuild(); let _ = vdom.rebuild();
return dioxus_ssr::render(&vdom); return dioxus_ssr::render(&vdom);
#[derive(Props)] #[derive(Props)]
struct AppProps { struct AppProps<R: Routable> {
link: Component, phantom: std::marker::PhantomData<R>,
} }
impl PartialEq for AppProps { impl<R: Routable> PartialEq for AppProps<R> {
fn eq(&self, _other: &Self) -> bool { fn eq(&self, _other: &Self) -> bool {
false false
} }
} }
fn App(cx: Scope<AppProps>) -> Element { fn App<R: Routable>(cx: Scope<AppProps<R>>) -> Element
use_router( where
cx, <R as FromStr>::Err: std::fmt::Display,
&|| RouterConfiguration { {
synchronous: true,
..Default::default()
},
&|| Segment::content(comp(cx.props.link)),
);
render! { render! {
h1 { "App" } h1 { "App" }
Outlet { } GenericRouter::<R> {
config: RouterConfiguration {
history: Box::<MemoryHistory::<R>>::default(),
..Default::default()
}
}
} }
} }
} }
#[test] #[test]
fn href_internal() { fn href_internal() {
fn content(cx: Scope) -> Element { #[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[inline_props]
fn Test(cx: Scope) -> Element {
todo!()
}
#[inline_props]
fn Root(cx: Scope) -> Element {
render! { render! {
Link { Link {
target: "/test", target: Route::Test {},
"Link" "Link"
} }
} }
@@ -57,36 +79,26 @@ fn href_internal() {
target = r#"target="""# target = r#"target="""#
); );
assert_eq!(prepare(content), expected); assert_eq!(prepare::<Route>(), expected);
}
#[test]
fn href_named() {
fn content(cx: Scope) -> Element {
render! {
Link {
target: named::<RootIndex>(),
"Link"
}
}
}
let expected = format!(
"<h1>App</h1><a {href} {default} {class} {id} {rel} {target}>Link</a>",
href = r#"href="/""#,
default = r#"dioxus-prevent-default="onclick""#,
class = r#"class="""#,
id = r#"id="""#,
rel = r#"rel="""#,
target = r#"target="""#
);
assert_eq!(prepare(content), expected);
} }
#[test] #[test]
fn href_external() { fn href_external() {
fn content(cx: Scope) -> Element { #[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[inline_props]
fn Test(cx: Scope) -> Element {
todo!()
}
#[inline_props]
fn Root(cx: Scope) -> Element {
render! { render! {
Link { Link {
target: "https://dioxuslabs.com/", target: "https://dioxuslabs.com/",
@@ -105,15 +117,29 @@ fn href_external() {
target = r#"target="""# target = r#"target="""#
); );
assert_eq!(prepare(content), expected); assert_eq!(prepare::<Route>(), expected);
} }
#[test] #[test]
fn with_class() { fn with_class() {
fn content(cx: Scope) -> Element { #[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[inline_props]
fn Test(cx: Scope) -> Element {
todo!()
}
#[inline_props]
fn Root(cx: Scope) -> Element {
render! { render! {
Link { Link {
target: "/test", target: Route::Test {},
class: "test_class", class: "test_class",
"Link" "Link"
} }
@@ -130,12 +156,19 @@ fn with_class() {
target = r#"target="""# target = r#"target="""#
); );
assert_eq!(prepare(content), expected); assert_eq!(prepare::<Route>(), expected);
} }
#[test] #[test]
fn with_active_class_active() { fn with_active_class_active() {
fn content(cx: Scope) -> Element { #[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
}
#[inline_props]
fn Root(cx: Scope) -> Element {
render! { render! {
Link { Link {
target: "/", target: "/",
@@ -156,15 +189,29 @@ fn with_active_class_active() {
target = r#"target="""# target = r#"target="""#
); );
assert_eq!(prepare(content), expected); assert_eq!(prepare::<Route>(), expected);
} }
#[test] #[test]
fn with_active_class_inactive() { fn with_active_class_inactive() {
fn content(cx: Scope) -> Element { #[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[inline_props]
fn Test(cx: Scope) -> Element {
todo!()
}
#[inline_props]
fn Root(cx: Scope) -> Element {
render! { render! {
Link { Link {
target: "/test", target: Route::Test {},
active_class: "active_class", active_class: "active_class",
class: "test_class", class: "test_class",
"Link" "Link"
@@ -182,15 +229,29 @@ fn with_active_class_inactive() {
target = r#"target="""# target = r#"target="""#
); );
assert_eq!(prepare(content), expected); assert_eq!(prepare::<Route>(), expected);
} }
#[test] #[test]
fn with_id() { fn with_id() {
fn content(cx: Scope) -> Element { #[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[inline_props]
fn Test(cx: Scope) -> Element {
todo!()
}
#[inline_props]
fn Root(cx: Scope) -> Element {
render! { render! {
Link { Link {
target: "/test", target: Route::Test {},
id: "test_id", id: "test_id",
"Link" "Link"
} }
@@ -207,15 +268,29 @@ fn with_id() {
target = r#"target="""# target = r#"target="""#
); );
assert_eq!(prepare(content), expected); assert_eq!(prepare::<Route>(), expected);
} }
#[test] #[test]
fn with_new_tab() { fn with_new_tab() {
fn content(cx: Scope) -> Element { #[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[inline_props]
fn Test(cx: Scope) -> Element {
todo!()
}
#[inline_props]
fn Root(cx: Scope) -> Element {
render! { render! {
Link { Link {
target: "/test", target: Route::Test {},
new_tab: true, new_tab: true,
"Link" "Link"
} }
@@ -232,12 +307,19 @@ fn with_new_tab() {
target = r#"target="_blank""# target = r#"target="_blank""#
); );
assert_eq!(prepare(content), expected); assert_eq!(prepare::<Route>(), expected);
} }
#[test] #[test]
fn with_new_tab_external() { fn with_new_tab_external() {
fn content(cx: Scope) -> Element { #[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
}
#[inline_props]
fn Root(cx: Scope) -> Element {
render! { render! {
Link { Link {
target: "https://dioxuslabs.com/", target: "https://dioxuslabs.com/",
@@ -257,15 +339,29 @@ fn with_new_tab_external() {
target = r#"target="_blank""# target = r#"target="_blank""#
); );
assert_eq!(prepare(content), expected); assert_eq!(prepare::<Route>(), expected);
} }
#[test] #[test]
fn with_rel() { fn with_rel() {
fn content(cx: Scope) -> Element { #[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[inline_props]
fn Test(cx: Scope) -> Element {
todo!()
}
#[inline_props]
fn Root(cx: Scope) -> Element {
render! { render! {
Link { Link {
target: "/test", target: Route::Test {},
rel: "test_rel", rel: "test_rel",
"Link" "Link"
} }
@@ -282,5 +378,5 @@ fn with_rel() {
target = r#"target="""# target = r#"target="""#
); );
assert_eq!(prepare(content), expected); assert_eq!(prepare::<Route>(), expected);
} }
+41 -29
View File
@@ -1,52 +1,61 @@
#![allow(non_snake_case, unused)]
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_router::{history::MemoryHistory, prelude::*}; use dioxus_router::{history::MemoryHistory, prelude::*};
fn prepare(path: impl Into<String>) -> VirtualDom { fn prepare(path: impl Into<String>) -> VirtualDom {
#![allow(non_snake_case)]
let mut vdom = VirtualDom::new_with_props(App, AppProps { path: path.into() }); let mut vdom = VirtualDom::new_with_props(App, AppProps { path: path.into() });
let _ = vdom.rebuild(); let _ = vdom.rebuild();
return vdom; return vdom;
#[derive(Routable, Clone)]
#[rustfmt::skip]
enum Route {
#[route("/")]
RootIndex {},
#[nest("/fixed")]
#[layout(Fixed)]
#[route("/")]
FixedIndex {},
#[route("/fixed")]
FixedFixed {},
#[end_layout]
#[end_nest]
#[nest("/:id")]
#[layout(Parameter)]
#[route("/")]
ParameterIndex { id: u8 },
#[route("/fixed")]
ParameterFixed { id: u8 },
}
#[derive(Debug, Props, PartialEq)] #[derive(Debug, Props, PartialEq)]
struct AppProps { struct AppProps {
path: String, path: String,
} }
fn App(cx: Scope<AppProps>) -> Element { fn App(cx: Scope<AppProps>) -> Element {
use_router( let cfg = RouterConfiguration {
cx, history: Box::new(MemoryHistory::with_initial_path(cx.props.path.clone()).unwrap()),
&|| RouterConfiguration { ..Default::default()
synchronous: true, };
history: Box::new(MemoryHistory::with_initial_path(cx.props.path.clone()).unwrap()),
..Default::default()
},
&|| {
Segment::content(comp(RootIndex))
.fixed(
"fixed",
Route::content(comp(Fixed)).nested(
Segment::content(comp(FixedIndex)).fixed("fixed", comp(FixedFixed)),
),
)
.catch_all(ParameterRoute::content::<u8>(comp(Parameter)).nested(
Segment::content(comp(ParameterIndex)).fixed("fixed", comp(ParameterFixed)),
))
},
);
render! { render! {
h1 { "App" } h1 { "App" }
Outlet { } Router {
config: cfg
}
} }
} }
#[inline_props]
fn RootIndex(cx: Scope) -> Element { fn RootIndex(cx: Scope) -> Element {
render! { render! {
h2 { "Root Index" } h2 { "Root Index" }
} }
} }
#[inline_props]
fn Fixed(cx: Scope) -> Element { fn Fixed(cx: Scope) -> Element {
render! { render! {
h2 { "Fixed" } h2 { "Fixed" }
@@ -54,34 +63,37 @@ fn prepare(path: impl Into<String>) -> VirtualDom {
} }
} }
#[inline_props]
fn FixedIndex(cx: Scope) -> Element { fn FixedIndex(cx: Scope) -> Element {
render! { render! {
h3 { "Fixed - Index" } h3 { "Fixed - Index" }
} }
} }
#[inline_props]
fn FixedFixed(cx: Scope) -> Element { fn FixedFixed(cx: Scope) -> Element {
render! { render! {
h3 { "Fixed - Fixed"} h3 { "Fixed - Fixed"}
} }
} }
fn Parameter(cx: Scope) -> Element { #[inline_props]
let val = use_route(cx)?.parameter::<u8>().unwrap(); fn Parameter(cx: Scope, id: u8) -> Element {
render! { render! {
h2 { "Parameter {val}" } h2 { "Parameter {id}" }
Outlet { } Outlet { }
} }
} }
fn ParameterIndex(cx: Scope) -> Element { #[inline_props]
fn ParameterIndex(cx: Scope, id: u8) -> Element {
render! { render! {
h3 { "Parameter - Index" } h3 { "Parameter - Index" }
} }
} }
fn ParameterFixed(cx: Scope) -> Element { #[inline_props]
fn ParameterFixed(cx: Scope, id: u8) -> Element {
render! { render! {
h3 { "Parameter - Fixed" } h3 { "Parameter - Fixed" }
} }
+48 -45
View File
@@ -6,10 +6,58 @@ use std::sync::Arc;
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_router::prelude::*; use dioxus_router::prelude::*;
use gloo::utils::document; use gloo::utils::document;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use wasm_bindgen_test::*; use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser); wasm_bindgen_test_configure!(run_in_browser);
#[rustfmt::skip]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Routable)]
enum Route {
#[route("/")]
Home {},
#[nest("/blog")]
#[route("/")]
BlogList {},
#[route("/:id")]
BlogPost { id: usize },
}
fn App(cx: Scope) -> Element {
render!(Router {
config: RouterConfiguration {
history: Box::<WebHistory<Route>>::default(),
..Default::default()
}
})
}
#[inline_props]
fn Home(cx: Scope) -> Element {
cx.render(rsx! {
div {
h1 { "Home" }
}
})
}
#[inline_props]
fn BlogList(cx: Scope) -> Element {
cx.render(rsx! {
div {
}
})
}
#[inline_props]
fn BlogPost(cx: Scope, id: usize) -> Element {
cx.render(rsx! {
div { }
})
}
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn simple_test() { fn simple_test() {
fn main() { fn main() {
@@ -18,51 +66,6 @@ fn simple_test() {
dioxus_web::launch(App); dioxus_web::launch(App);
} }
fn App(cx: Scope) -> Element {
use_router(
cx,
&|| RouterConfiguration {
on_update: Some(Arc::new(|_| None)),
..Default::default()
},
&|| {
Segment::content(comp(Home)).fixed(
"blog",
Route::empty().nested(
Segment::content(comp(BlogList)).catch_all((comp(BlogPost), PostId {})),
),
)
},
);
render!(Outlet {})
}
fn Home(cx: Scope) -> Element {
cx.render(rsx! {
div {
h1 { "Home" }
}
})
}
fn BlogList(cx: Scope) -> Element {
cx.render(rsx! {
div {
}
})
}
struct PostId;
fn BlogPost(cx: Scope) -> Element {
let _id = use_route(cx)?.parameter::<PostId>().unwrap();
cx.render(rsx! {
div { }
})
}
main(); main();
let _ = document(); let _ = document();