mirror of https://github.com/yewstack/yew
Includes query parameters in rendered Link component (#2464)
* Add tests for Link component * Include query parameters in href of Link component * Add test cases for HashRouter and basename option * Fix test name, format, clippy * Ignore clippy warnings for test helpers
This commit is contained in:
parent
f39afdc5e1
commit
e2b91caabd
|
@ -23,6 +23,7 @@ js-sys = "0.3"
|
|||
gloo = { version = "0.6", features = ["futures"] }
|
||||
route-recognizer = "0.3"
|
||||
serde = "1"
|
||||
serde_urlencoded = "0.7.1"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
|
|
|
@ -7,6 +7,7 @@ use yew::virtual_dom::AttrValue;
|
|||
|
||||
use crate::navigator::NavigatorKind;
|
||||
use crate::scope_ext::RouterScopeExt;
|
||||
use crate::utils;
|
||||
use crate::Routable;
|
||||
|
||||
/// Props for [`Link`]
|
||||
|
@ -86,6 +87,7 @@ where
|
|||
let LinkProps {
|
||||
classes,
|
||||
to,
|
||||
query,
|
||||
children,
|
||||
disabled,
|
||||
..
|
||||
|
@ -100,11 +102,15 @@ where
|
|||
.navigator()
|
||||
.expect_throw("failed to get navigator");
|
||||
let href: AttrValue = {
|
||||
let href = navigator.route_to_url(to);
|
||||
let pathname = navigator.route_to_url(to);
|
||||
let path = query
|
||||
.and_then(|query| serde_urlencoded::to_string(query).ok())
|
||||
.and_then(|query| utils::compose_path(&pathname, &query))
|
||||
.unwrap_or_else(|| pathname.to_string());
|
||||
|
||||
match navigator.kind() {
|
||||
NavigatorKind::Hash => format!("#{}", href).into(),
|
||||
_ => href,
|
||||
NavigatorKind::Hash => format!("#{}", path),
|
||||
_ => path,
|
||||
}
|
||||
.into()
|
||||
};
|
||||
|
|
|
@ -41,6 +41,18 @@ pub fn fetch_base_url() -> Option<String> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn compose_path(pathname: &str, query: &str) -> Option<String> {
|
||||
gloo::utils::window()
|
||||
.location()
|
||||
.href()
|
||||
.ok()
|
||||
.and_then(|base| web_sys::Url::new_with_base(pathname, &base).ok())
|
||||
.map(|url| {
|
||||
url.set_search(query);
|
||||
format!("{}{}", url.pathname(), url.search())
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gloo::utils::document;
|
||||
|
@ -78,4 +90,17 @@ mod tests {
|
|||
.set_inner_html(r#"<base href="/base">"#);
|
||||
assert_eq!(fetch_base_url(), Some("/base".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compose_path() {
|
||||
assert_eq!(compose_path("/home", ""), Some("/home".to_string()));
|
||||
assert_eq!(
|
||||
compose_path("/path/to", "foo=bar"),
|
||||
Some("/path/to?foo=bar".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
compose_path("/events", "from=2019&to=2021"),
|
||||
Some("/events?from=2019&to=2021".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
use gloo::timers::future::sleep;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
|
||||
use yew::functional::function_component;
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
mod utils;
|
||||
use utils::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, PartialEq)]
|
||||
struct PageParam {
|
||||
page: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, PartialEq)]
|
||||
struct SearchParams {
|
||||
q: String,
|
||||
lang: Option<String>,
|
||||
}
|
||||
|
||||
impl SearchParams {
|
||||
fn new(q: &str) -> Self {
|
||||
Self {
|
||||
q: q.to_string(),
|
||||
lang: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_with_lang(q: &str, lang: &str) -> Self {
|
||||
Self {
|
||||
q: q.to_string(),
|
||||
lang: Some(lang.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Routable)]
|
||||
enum Routes {
|
||||
#[at("/posts")]
|
||||
Posts,
|
||||
#[at("/search")]
|
||||
Search,
|
||||
}
|
||||
|
||||
#[function_component(NavigationMenu)]
|
||||
fn navigation_menu() -> Html {
|
||||
html! {
|
||||
<ul>
|
||||
<li class="posts">
|
||||
<Link<Routes> to={Routes::Posts}>
|
||||
{ "Posts without parameters" }
|
||||
</Link<Routes>>
|
||||
</li>
|
||||
<li class="posts-page-2">
|
||||
<Link<Routes, PageParam> to={Routes::Posts} query={Some(PageParam { page: 2 })}>
|
||||
{ "Posts of 2nd page" }
|
||||
</Link<Routes, PageParam>>
|
||||
</li>
|
||||
<li class="search">
|
||||
<Link<Routes> to={Routes::Search}>
|
||||
{ "Search withfout parameters" }
|
||||
</Link<Routes>>
|
||||
</li>
|
||||
<li class="search-q">
|
||||
<Link<Routes, SearchParams> to={Routes::Search} query={Some(SearchParams::new("Rust"))}>
|
||||
{ "Search with keyword parameter" }
|
||||
</Link<Routes, SearchParams>>
|
||||
</li>
|
||||
<li class="search-q-lang">
|
||||
<Link<Routes, SearchParams> to={Routes::Search} query={Some(SearchParams::new_with_lang("Rust", "en_US"))}>
|
||||
{ "Search with keyword and language parameters" }
|
||||
</Link<Routes, SearchParams>>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(RootForBrowserRouter)]
|
||||
fn root_for_browser_router() -> Html {
|
||||
html! {
|
||||
<BrowserRouter>
|
||||
<NavigationMenu />
|
||||
</BrowserRouter>
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn link_in_browser_router() {
|
||||
let div = gloo::utils::document().create_element("div").unwrap();
|
||||
let _ = div.set_attribute("id", "browser-router");
|
||||
let _ = gloo::utils::body().append_child(&div);
|
||||
yew::start_app_in_element::<RootForBrowserRouter>(div);
|
||||
|
||||
sleep(Duration::ZERO).await;
|
||||
|
||||
assert_eq!("/posts", link_href("#browser-router ul > li.posts > a"));
|
||||
assert_eq!(
|
||||
"/posts?page=2",
|
||||
link_href("#browser-router ul > li.posts-page-2 > a")
|
||||
);
|
||||
|
||||
assert_eq!("/search", link_href("#browser-router ul > li.search > a"));
|
||||
assert_eq!(
|
||||
"/search?q=Rust",
|
||||
link_href("#browser-router ul > li.search-q > a")
|
||||
);
|
||||
assert_eq!(
|
||||
"/search?q=Rust&lang=en_US",
|
||||
link_href("#browser-router ul > li.search-q-lang > a")
|
||||
);
|
||||
}
|
||||
|
||||
#[function_component(RootForBasename)]
|
||||
fn root_for_basename() -> Html {
|
||||
html! {
|
||||
<BrowserRouter basename="/base/">
|
||||
<NavigationMenu />
|
||||
</BrowserRouter>
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn link_with_basename() {
|
||||
let div = gloo::utils::document().create_element("div").unwrap();
|
||||
let _ = div.set_attribute("id", "with-basename");
|
||||
let _ = gloo::utils::body().append_child(&div);
|
||||
yew::start_app_in_element::<RootForBasename>(div);
|
||||
|
||||
sleep(Duration::ZERO).await;
|
||||
|
||||
assert_eq!("/base/posts", link_href("#with-basename ul > li.posts > a"));
|
||||
assert_eq!(
|
||||
"/base/posts?page=2",
|
||||
link_href("#with-basename ul > li.posts-page-2 > a")
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"/base/search",
|
||||
link_href("#with-basename ul > li.search > a")
|
||||
);
|
||||
assert_eq!(
|
||||
"/base/search?q=Rust",
|
||||
link_href("#with-basename ul > li.search-q > a")
|
||||
);
|
||||
assert_eq!(
|
||||
"/base/search?q=Rust&lang=en_US",
|
||||
link_href("#with-basename ul > li.search-q-lang > a")
|
||||
);
|
||||
}
|
||||
|
||||
#[function_component(RootForHashRouter)]
|
||||
fn root_for_hash_router() -> Html {
|
||||
html! {
|
||||
<HashRouter>
|
||||
<NavigationMenu />
|
||||
</HashRouter>
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn link_in_hash_router() {
|
||||
let div = gloo::utils::document().create_element("div").unwrap();
|
||||
let _ = div.set_attribute("id", "hash-router");
|
||||
let _ = gloo::utils::body().append_child(&div);
|
||||
yew::start_app_in_element::<RootForHashRouter>(div);
|
||||
|
||||
sleep(Duration::ZERO).await;
|
||||
|
||||
assert_eq!("#/posts", link_href("#hash-router ul > li.posts > a"));
|
||||
assert_eq!(
|
||||
"#/posts?page=2",
|
||||
link_href("#hash-router ul > li.posts-page-2 > a")
|
||||
);
|
||||
|
||||
assert_eq!("#/search", link_href("#hash-router ul > li.search > a"));
|
||||
assert_eq!(
|
||||
"#/search?q=Rust",
|
||||
link_href("#hash-router ul > li.search-q > a")
|
||||
);
|
||||
assert_eq!(
|
||||
"#/search?q=Rust&lang=en_US",
|
||||
link_href("#hash-router ul > li.search-q-lang > a")
|
||||
);
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use wasm_bindgen::JsCast;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn obtain_result_by_id(id: &str) -> String {
|
||||
gloo::utils::document()
|
||||
.get_element_by_id(id)
|
||||
|
@ -7,6 +8,7 @@ pub fn obtain_result_by_id(id: &str) -> String {
|
|||
.inner_html()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn click(selector: &str) {
|
||||
gloo::utils::document()
|
||||
.query_selector(selector)
|
||||
|
@ -17,6 +19,7 @@ pub fn click(selector: &str) {
|
|||
.click();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn history_length() -> u32 {
|
||||
gloo::utils::window()
|
||||
.history()
|
||||
|
@ -24,3 +27,13 @@ pub fn history_length() -> u32 {
|
|||
.length()
|
||||
.expect("No history length found")
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn link_href(selector: &str) -> String {
|
||||
gloo::utils::document()
|
||||
.query_selector(selector)
|
||||
.expect("Failed to run query selector")
|
||||
.unwrap_or_else(|| panic!("No such link: {}", selector))
|
||||
.get_attribute("href")
|
||||
.expect("No href attribute")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue