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"] }
|
gloo = { version = "0.6", features = ["futures"] }
|
||||||
route-recognizer = "0.3"
|
route-recognizer = "0.3"
|
||||||
serde = "1"
|
serde = "1"
|
||||||
|
serde_urlencoded = "0.7.1"
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
|
|
@ -7,6 +7,7 @@ use yew::virtual_dom::AttrValue;
|
||||||
|
|
||||||
use crate::navigator::NavigatorKind;
|
use crate::navigator::NavigatorKind;
|
||||||
use crate::scope_ext::RouterScopeExt;
|
use crate::scope_ext::RouterScopeExt;
|
||||||
|
use crate::utils;
|
||||||
use crate::Routable;
|
use crate::Routable;
|
||||||
|
|
||||||
/// Props for [`Link`]
|
/// Props for [`Link`]
|
||||||
|
@ -86,6 +87,7 @@ where
|
||||||
let LinkProps {
|
let LinkProps {
|
||||||
classes,
|
classes,
|
||||||
to,
|
to,
|
||||||
|
query,
|
||||||
children,
|
children,
|
||||||
disabled,
|
disabled,
|
||||||
..
|
..
|
||||||
|
@ -100,11 +102,15 @@ where
|
||||||
.navigator()
|
.navigator()
|
||||||
.expect_throw("failed to get navigator");
|
.expect_throw("failed to get navigator");
|
||||||
let href: AttrValue = {
|
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() {
|
match navigator.kind() {
|
||||||
NavigatorKind::Hash => format!("#{}", href).into(),
|
NavigatorKind::Hash => format!("#{}", path),
|
||||||
_ => href,
|
_ => path,
|
||||||
}
|
}
|
||||||
.into()
|
.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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use gloo::utils::document;
|
use gloo::utils::document;
|
||||||
|
@ -78,4 +90,17 @@ mod tests {
|
||||||
.set_inner_html(r#"<base href="/base">"#);
|
.set_inner_html(r#"<base href="/base">"#);
|
||||||
assert_eq!(fetch_base_url(), Some("/base".to_string()));
|
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;
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn obtain_result_by_id(id: &str) -> String {
|
pub fn obtain_result_by_id(id: &str) -> String {
|
||||||
gloo::utils::document()
|
gloo::utils::document()
|
||||||
.get_element_by_id(id)
|
.get_element_by_id(id)
|
||||||
|
@ -7,6 +8,7 @@ pub fn obtain_result_by_id(id: &str) -> String {
|
||||||
.inner_html()
|
.inner_html()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn click(selector: &str) {
|
pub fn click(selector: &str) {
|
||||||
gloo::utils::document()
|
gloo::utils::document()
|
||||||
.query_selector(selector)
|
.query_selector(selector)
|
||||||
|
@ -17,6 +19,7 @@ pub fn click(selector: &str) {
|
||||||
.click();
|
.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn history_length() -> u32 {
|
pub fn history_length() -> u32 {
|
||||||
gloo::utils::window()
|
gloo::utils::window()
|
||||||
.history()
|
.history()
|
||||||
|
@ -24,3 +27,13 @@ pub fn history_length() -> u32 {
|
||||||
.length()
|
.length()
|
||||||
.expect("No history length found")
|
.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