link.rs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. use dioxus::prelude::*;
  2. use dioxus_history::{History, MemoryHistory};
  3. use dioxus_router::components::HistoryProvider;
  4. use std::{rc::Rc, str::FromStr};
  5. fn prepare<R: Routable>() -> String
  6. where
  7. <R as FromStr>::Err: std::fmt::Display,
  8. {
  9. prepare_at::<R>("/")
  10. }
  11. fn prepare_at<R: Routable>(at: impl ToString) -> String
  12. where
  13. <R as FromStr>::Err: std::fmt::Display,
  14. {
  15. let mut vdom = VirtualDom::new_with_props(
  16. App,
  17. AppProps::<R> {
  18. at: at.to_string(),
  19. phantom: std::marker::PhantomData,
  20. },
  21. );
  22. vdom.rebuild_in_place();
  23. return dioxus_ssr::render(&vdom);
  24. #[derive(Props)]
  25. struct AppProps<R: Routable> {
  26. at: String,
  27. phantom: std::marker::PhantomData<R>,
  28. }
  29. impl<R: Routable> Clone for AppProps<R> {
  30. fn clone(&self) -> Self {
  31. Self {
  32. at: self.at.clone(),
  33. phantom: std::marker::PhantomData,
  34. }
  35. }
  36. }
  37. impl<R: Routable> PartialEq for AppProps<R> {
  38. fn eq(&self, _other: &Self) -> bool {
  39. false
  40. }
  41. }
  42. #[allow(non_snake_case)]
  43. fn App<R: Routable>(props: AppProps<R>) -> Element
  44. where
  45. <R as FromStr>::Err: std::fmt::Display,
  46. {
  47. rsx! {
  48. h1 { "App" }
  49. HistoryProvider {
  50. history: move |_| Rc::new(MemoryHistory::with_initial_path(props.at.clone())) as Rc<dyn History>,
  51. Router::<R> {}
  52. }
  53. }
  54. }
  55. }
  56. #[test]
  57. fn href_internal() {
  58. #[derive(Routable, Clone)]
  59. enum Route {
  60. #[route("/")]
  61. Root {},
  62. #[route("/test")]
  63. Test {},
  64. }
  65. #[component]
  66. fn Test() -> Element {
  67. unimplemented!()
  68. }
  69. #[component]
  70. fn Root() -> Element {
  71. rsx! {
  72. Link {
  73. to: Route::Test {},
  74. "Link"
  75. }
  76. }
  77. }
  78. let expected = format!("<h1>App</h1><a {href}>Link</a>", href = r#"href="/test""#,);
  79. assert_eq!(prepare::<Route>(), expected);
  80. }
  81. #[test]
  82. fn href_external() {
  83. #[derive(Routable, Clone)]
  84. enum Route {
  85. #[route("/")]
  86. Root {},
  87. #[route("/test")]
  88. Test {},
  89. }
  90. #[component]
  91. fn Test() -> Element {
  92. unimplemented!()
  93. }
  94. #[component]
  95. fn Root() -> Element {
  96. rsx! {
  97. Link {
  98. to: "https://dioxuslabs.com/",
  99. "Link"
  100. }
  101. }
  102. }
  103. let expected = format!(
  104. "<h1>App</h1><a {href} {rel}>Link</a>",
  105. href = r#"href="https://dioxuslabs.com/""#,
  106. rel = r#"rel="noopener noreferrer""#,
  107. );
  108. assert_eq!(prepare::<Route>(), expected);
  109. }
  110. #[test]
  111. fn with_class() {
  112. #[derive(Routable, Clone)]
  113. enum Route {
  114. #[route("/")]
  115. Root {},
  116. #[route("/test")]
  117. Test {},
  118. }
  119. #[component]
  120. fn Test() -> Element {
  121. unimplemented!()
  122. }
  123. #[component]
  124. fn Root() -> Element {
  125. rsx! {
  126. Link {
  127. to: Route::Test {},
  128. class: "test_class",
  129. "Link"
  130. }
  131. }
  132. }
  133. let expected = format!(
  134. "<h1>App</h1><a {href} {class}>Link</a>",
  135. href = r#"href="/test""#,
  136. class = r#"class="test_class""#,
  137. );
  138. assert_eq!(prepare::<Route>(), expected);
  139. }
  140. #[test]
  141. fn with_active_class_active() {
  142. #[derive(Routable, Clone)]
  143. enum Route {
  144. #[route("/")]
  145. Root {},
  146. }
  147. #[component]
  148. fn Root() -> Element {
  149. rsx! {
  150. Link {
  151. to: Route::Root {},
  152. active_class: "active_class".to_string(),
  153. class: "test_class",
  154. "Link"
  155. }
  156. }
  157. }
  158. let expected = format!(
  159. "<h1>App</h1><a {href} {class} {aria}>Link</a>",
  160. href = r#"href="/""#,
  161. class = r#"class="test_class active_class""#,
  162. aria = r#"aria-current="page""#,
  163. );
  164. assert_eq!(prepare::<Route>(), expected);
  165. }
  166. #[test]
  167. fn with_active_class_inactive() {
  168. #[derive(Routable, Clone)]
  169. enum Route {
  170. #[route("/")]
  171. Root {},
  172. #[route("/test")]
  173. Test {},
  174. }
  175. #[component]
  176. fn Test() -> Element {
  177. unimplemented!()
  178. }
  179. #[component]
  180. fn Root() -> Element {
  181. rsx! {
  182. Link {
  183. to: Route::Test {},
  184. active_class: "active_class".to_string(),
  185. class: "test_class",
  186. "Link"
  187. }
  188. }
  189. }
  190. let expected = format!(
  191. "<h1>App</h1><a {href} {class}>Link</a>",
  192. href = r#"href="/test""#,
  193. class = r#"class="test_class""#,
  194. );
  195. assert_eq!(prepare::<Route>(), expected);
  196. }
  197. #[test]
  198. fn with_id() {
  199. #[derive(Routable, Clone)]
  200. enum Route {
  201. #[route("/")]
  202. Root {},
  203. #[route("/test")]
  204. Test {},
  205. }
  206. #[component]
  207. fn Test() -> Element {
  208. unimplemented!()
  209. }
  210. #[component]
  211. fn Root() -> Element {
  212. rsx! {
  213. Link {
  214. to: Route::Test {},
  215. id: "test_id",
  216. "Link"
  217. }
  218. }
  219. }
  220. let expected = format!(
  221. "<h1>App</h1><a {href} {id}>Link</a>",
  222. href = r#"href="/test""#,
  223. id = r#"id="test_id""#,
  224. );
  225. assert_eq!(prepare::<Route>(), expected);
  226. }
  227. #[test]
  228. fn with_new_tab() {
  229. #[derive(Routable, Clone)]
  230. enum Route {
  231. #[route("/")]
  232. Root {},
  233. #[route("/test")]
  234. Test {},
  235. }
  236. #[component]
  237. fn Test() -> Element {
  238. unimplemented!()
  239. }
  240. #[component]
  241. fn Root() -> Element {
  242. rsx! {
  243. Link {
  244. to: Route::Test {},
  245. new_tab: true,
  246. "Link"
  247. }
  248. }
  249. }
  250. let expected = format!(
  251. "<h1>App</h1><a {href} {target}>Link</a>",
  252. href = r#"href="/test""#,
  253. target = r#"target="_blank""#
  254. );
  255. assert_eq!(prepare::<Route>(), expected);
  256. }
  257. #[test]
  258. fn with_new_tab_external() {
  259. #[derive(Routable, Clone)]
  260. enum Route {
  261. #[route("/")]
  262. Root {},
  263. }
  264. #[component]
  265. fn Root() -> Element {
  266. rsx! {
  267. Link {
  268. to: "https://dioxuslabs.com/",
  269. new_tab: true,
  270. "Link"
  271. }
  272. }
  273. }
  274. let expected = format!(
  275. "<h1>App</h1><a {href} {rel} {target}>Link</a>",
  276. href = r#"href="https://dioxuslabs.com/""#,
  277. rel = r#"rel="noopener noreferrer""#,
  278. target = r#"target="_blank""#
  279. );
  280. assert_eq!(prepare::<Route>(), expected);
  281. }
  282. #[test]
  283. fn with_rel() {
  284. #[derive(Routable, Clone)]
  285. enum Route {
  286. #[route("/")]
  287. Root {},
  288. #[route("/test")]
  289. Test {},
  290. }
  291. #[component]
  292. fn Test() -> Element {
  293. unimplemented!()
  294. }
  295. #[component]
  296. fn Root() -> Element {
  297. rsx! {
  298. Link {
  299. to: Route::Test {},
  300. rel: "test_rel".to_string(),
  301. "Link"
  302. }
  303. }
  304. }
  305. let expected = format!(
  306. "<h1>App</h1><a {href} {rel}>Link</a>",
  307. href = r#"href="/test""#,
  308. rel = r#"rel="test_rel""#,
  309. );
  310. assert_eq!(prepare::<Route>(), expected);
  311. }
  312. #[test]
  313. fn with_child_route() {
  314. #[derive(Routable, Clone, PartialEq, Debug)]
  315. enum ChildRoute {
  316. #[route("/")]
  317. ChildRoot {},
  318. #[route("/:not_static")]
  319. NotStatic { not_static: String },
  320. }
  321. #[derive(Routable, Clone, PartialEq, Debug)]
  322. enum Route {
  323. #[route("/")]
  324. Root {},
  325. #[route("/test")]
  326. Test {},
  327. #[child("/child")]
  328. Nested { child: ChildRoute },
  329. }
  330. #[component]
  331. fn Test() -> Element {
  332. unimplemented!()
  333. }
  334. #[component]
  335. fn Root() -> Element {
  336. rsx! {
  337. Link {
  338. to: Route::Test {},
  339. "Parent Link"
  340. }
  341. Link {
  342. to: Route::Nested { child: ChildRoute::NotStatic { not_static: "this-is-a-child-route".to_string() } },
  343. "Child Link"
  344. }
  345. }
  346. }
  347. #[component]
  348. fn ChildRoot() -> Element {
  349. rsx! {
  350. Link {
  351. to: Route::Test {},
  352. "Parent Link"
  353. }
  354. Link {
  355. to: ChildRoute::NotStatic { not_static: "this-is-a-child-route".to_string() },
  356. "Child Link 1"
  357. }
  358. Link {
  359. to: Route::Nested { child: ChildRoute::NotStatic { not_static: "this-is-a-child-route".to_string() } },
  360. "Child Link 2"
  361. }
  362. }
  363. }
  364. #[component]
  365. fn NotStatic(not_static: String) -> Element {
  366. unimplemented!()
  367. }
  368. assert_eq!(
  369. prepare_at::<Route>("/"),
  370. "<h1>App</h1><a href=\"/test\">Parent Link</a><a href=\"/child/this-is-a-child-route\">Child Link</a>"
  371. );
  372. assert_eq!(
  373. prepare_at::<Route>("/child"),
  374. "<h1>App</h1><a href=\"/test\">Parent Link</a><a href=\"/child/this-is-a-child-route\">Child Link 1</a><a href=\"/child/this-is-a-child-route\">Child Link 2</a>"
  375. );
  376. }
  377. #[test]
  378. fn with_hash_segment() {
  379. #[derive(Routable, Clone)]
  380. enum Route {
  381. #[route("/#:data")]
  382. Root { data: String },
  383. }
  384. #[component]
  385. fn Root(data: String) -> Element {
  386. rsx! {
  387. Link {
  388. to: Route::Root { data: "test".to_string() },
  389. "Link"
  390. }
  391. Link {
  392. to: Route::Root { data: "".to_string() },
  393. "Empty"
  394. }
  395. }
  396. }
  397. assert_eq!(
  398. prepare_at::<Route>("/#test"),
  399. "<h1>App</h1><a href=\"/#test\" aria-current=\"page\">Link</a><a href=\"/\">Empty</a>"
  400. );
  401. }