update tests
This commit is contained in:
1 parent
58b74c1155
commit
35befa1784
12 files changed
+334
-236
No files matched your search
@@ -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,
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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};
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 },
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
@@ -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" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in new issue
Block a user