From 5ce91e1bfc4a0a463f7b7dfdd9d711f7a725c254 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 17 Apr 2024 17:08:38 -0500 Subject: [PATCH] Fix flakey windows tests (#2332) --- Cargo.lock | 3 + Makefile.toml | 15 +++- examples/todomvc.rs | 79 ++++++++++--------- packages/core/src/global_context.rs | 2 +- packages/core/src/scope_context.rs | 2 +- packages/core/src/virtual_dom.rs | 10 +-- packages/core/tests/miri_full_app.rs | 1 - packages/core/tests/task.rs | 6 +- packages/desktop/headless_tests/events.rs | 42 +++++++--- packages/desktop/headless_tests/rendering.rs | 18 +---- packages/desktop/headless_tests/utils.rs | 2 +- packages/dioxus-lib/Cargo.toml | 3 + packages/dioxus-lib/README.md | 6 +- packages/dioxus/README.md | 4 +- packages/fullstack/Cargo.toml | 3 + packages/fullstack/README.md | 4 +- packages/fullstack/src/hooks/server_cached.rs | 4 +- packages/hooks/src/use_effect.rs | 2 +- packages/hooks/src/use_memo.rs | 2 +- packages/hooks/src/use_reactive.rs | 4 +- packages/hooks/src/use_resource.rs | 6 +- packages/html/README.md | 8 +- packages/liveview/src/lib.rs | 4 +- packages/router-macro/Cargo.toml | 3 + packages/router-macro/src/lib.rs | 70 ++++++++++++---- .../router/src/components/history_buttons.rs | 16 +++- packages/router/src/components/link.rs | 22 +++--- packages/router/src/components/outlet.rs | 2 +- packages/router/src/hooks/use_navigator.rs | 2 +- packages/router/src/hooks/use_route.rs | 2 +- packages/ssr/README.md | 6 +- 31 files changed, 217 insertions(+), 136 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60cb2f65f..f2a1e410e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2371,6 +2371,7 @@ dependencies = [ "base64 0.21.7", "bytes", "ciborium", + "dioxus", "dioxus-cli-config", "dioxus-desktop", "dioxus-hot-reload", @@ -2488,6 +2489,7 @@ dependencies = [ name = "dioxus-lib" version = "0.5.2" dependencies = [ + "dioxus", "dioxus-config-macro", "dioxus-core 0.5.2", "dioxus-core-macro", @@ -2603,6 +2605,7 @@ dependencies = [ name = "dioxus-router-macro" version = "0.5.2" dependencies = [ + "dioxus", "proc-macro2", "quote", "slab", diff --git a/Makefile.toml b/Makefile.toml index 98af226aa..8f175d760 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -88,7 +88,7 @@ command = "cargo" args = ["build"] [tasks.test-flow] -dependencies = ["test"] +dependencies = ["test", "docs"] private = true [tasks.test] @@ -101,8 +101,7 @@ args = [ "--tests", "--examples", "--workspace", - "--exclude", - "dioxus-router", + # These tests run on Ubuntu without a screen. Desktop tests that require a screen fail, so we skip them "--exclude", "dioxus-desktop", "--exclude", @@ -110,6 +109,16 @@ args = [ ] private = true +[tasks.docs] +dependencies = ["build"] +command = "cargo" +args = [ + "test", + "--doc", + "--workspace", +] +private = true + [tasks.test-with-browser] env = { CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS = [ "**/packages/router", diff --git a/examples/todomvc.rs b/examples/todomvc.rs index 7b2405f40..c96ab2501 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -1,7 +1,6 @@ //! The typical TodoMVC app, implemented in Dioxus. use dioxus::prelude::*; -use dioxus_elements::input_data::keyboard_types::Key; use std::collections::HashMap; fn main() { @@ -74,7 +73,7 @@ fn app() -> Element { class: "toggle-all", r#type: "checkbox", onchange: toggle_all, - checked: active_todo_count() == 0, + checked: active_todo_count() == 0 } label { r#for: "toggle-all" } } @@ -98,8 +97,14 @@ fn app() -> Element { // A simple info footer footer { class: "info", p { "Double-click to edit a todo" } - p { "Created by " a { href: "http://github.com/jkelleyrtp/", "jkelleyrtp" } } - p { "Part of " a { href: "http://todomvc.com", "TodoMVC" } } + p { + "Created by " + a { href: "http://github.com/jkelleyrtp/", "jkelleyrtp" } + } + p { + "Part of " + a { href: "http://todomvc.com", "TodoMVC" } + } } } } @@ -132,7 +137,7 @@ fn TodoHeader(mut todos: Signal>) -> Element { value: "{draft}", autofocus: "true", oninput: move |evt| draft.set(evt.value()), - onkeydown, + onkeydown } } } @@ -165,7 +170,7 @@ fn TodoEntry(mut todos: Signal>, id: u32) -> Element { r#type: "checkbox", id: "cbg-{id}", checked: "{checked}", - oninput: move |evt| todos.write().get_mut(&id).unwrap().checked = evt.checked(), + oninput: move |evt| todos.write().get_mut(&id).unwrap().checked = evt.checked() } label { r#for: "cbg-{id}", @@ -175,7 +180,9 @@ fn TodoEntry(mut todos: Signal>, id: u32) -> Element { } button { class: "destroy", - onclick: move |_| { todos.write().remove(&id); }, + onclick: move |_| { + todos.write().remove(&id); + }, prevent_default: "onclick" } } @@ -211,41 +218,41 @@ fn ListFooter( let show_clear_completed = use_memo(move || todos.read().values().any(|todo| todo.checked)); rsx! { - footer { class: "footer", - span { class: "todo-count", - strong { "{active_todo_count} " } - span { - match active_todo_count() { - 1 => "item", - _ => "items", + footer { class: "footer", + span { class: "todo-count", + strong { "{active_todo_count} " } + span { + match active_todo_count() { + 1 => "item", + _ => "items", + }, + " left" } - " left" } - } - ul { class: "filters", - for (state , state_text , url) in [ - (FilterState::All, "All", "#/"), - (FilterState::Active, "Active", "#/active"), - (FilterState::Completed, "Completed", "#/completed"), - ] { - li { - a { - href: url, - class: if filter() == state { "selected" }, - onclick: move |_| filter.set(state), - prevent_default: "onclick", - {state_text} + ul { class: "filters", + for (state , state_text , url) in [ + (FilterState::All, "All", "#/"), + (FilterState::Active, "Active", "#/active"), + (FilterState::Completed, "Completed", "#/completed"), + ] { + li { + a { + href: url, + class: if filter() == state { "selected" }, + onclick: move |_| filter.set(state), + prevent_default: "onclick", + {state_text} + } } } } - } - if show_clear_completed() { - button { - class: "clear-completed", - onclick: move |_| todos.write().retain(|_, todo| !todo.checked), - "Clear completed" + if show_clear_completed() { + button { + class: "clear-completed", + onclick: move |_| todos.write().retain(|_, todo| !todo.checked), + "Clear completed" + } } } } - } } diff --git a/packages/core/src/global_context.rs b/packages/core/src/global_context.rs index 3ff52092d..7783deccd 100644 --- a/packages/core/src/global_context.rs +++ b/packages/core/src/global_context.rs @@ -145,7 +145,7 @@ pub fn remove_future(id: Task) { /// /// pub fn use_custom_state() -> CustomState { /// use_hook(|| CustomState { -/// inner: Signal::new(InnerCustomState) +/// inner: Signal::new(InnerCustomState(0)) /// }) /// } /// ``` diff --git a/packages/core/src/scope_context.rs b/packages/core/src/scope_context.rs index 1c605626d..c90cdc6c0 100644 --- a/packages/core/src/scope_context.rs +++ b/packages/core/src/scope_context.rs @@ -310,7 +310,7 @@ impl Scope { /// /// pub fn use_custom_state() -> CustomState { /// use_hook(|| CustomState { - /// inner: Signal::new(InnerCustomState) + /// inner: Signal::new(InnerCustomState(0)) /// }) /// } /// ``` diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index f223118ec..d88df76c6 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -138,7 +138,7 @@ use tracing::instrument; /// /// let dom = VirtualDom::new(app); /// -/// real_dom.apply(dom.rebuild()); +/// dom.rebuild(real_dom.apply()); /// /// loop { /// select! { @@ -258,7 +258,7 @@ impl VirtualDom { /// /// ```rust, ignore /// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" }); - /// let mutations = dom.rebuild(); + /// dom.rebuild_in_place(); /// ``` pub fn new_with_props( root: impl ComponentFunction, @@ -302,7 +302,7 @@ impl VirtualDom { /// /// ```rust, ignore /// let mut dom = VirtualDom::new_from_root(VComponent::new(Example, SomeProps { name: "jane" }, "Example")); - /// let mutations = dom.rebuild(); + /// dom.rebuild(to_writer); /// ``` #[instrument(skip(root), level = "trace", name = "VirtualDom::new")] pub(crate) fn new_with_component(root: impl AnyProps + 'static) -> Self { @@ -611,9 +611,7 @@ impl VirtualDom { /// static app: Component = |cx| rsx!{ "hello world" }; /// /// let mut dom = VirtualDom::new(); - /// let edits = dom.rebuild(); - /// - /// apply_edits(edits); + /// dom.rebuild(to_writer); /// ``` #[instrument(skip(self, to), level = "trace", name = "VirtualDom::rebuild")] pub fn rebuild(&mut self, to: &mut impl WriteMutations) { diff --git a/packages/core/tests/miri_full_app.rs b/packages/core/tests/miri_full_app.rs index a62d782b7..e21138055 100644 --- a/packages/core/tests/miri_full_app.rs +++ b/packages/core/tests/miri_full_app.rs @@ -1,4 +1,3 @@ -use crate::dioxus_elements::SerializedMouseData; use dioxus::prelude::*; use dioxus_core::ElementId; use dioxus_elements::SerializedHtmlEventConverter; diff --git a/packages/core/tests/task.rs b/packages/core/tests/task.rs index b72079b37..726a7c0fd 100644 --- a/packages/core/tests/task.rs +++ b/packages/core/tests/task.rs @@ -66,7 +66,7 @@ async fn spawn_forever_persists() { #[component] fn Child() -> Element { spawn_forever(async move { - loop { + for _ in 0..10 { POLL_COUNT.fetch_add(1, Ordering::Relaxed); tokio::time::sleep(Duration::from_millis(50)).await; } @@ -82,7 +82,9 @@ async fn spawn_forever_persists() { tokio::select! { _ = dom.wait_for_work() => {} - _ = tokio::time::sleep(Duration::from_millis(500)) => {} + // We intentionally wait a bit longer than 50ms*10 to make sure the test has time to finish + // Without the extra time, the test can fail on windows + _ = tokio::time::sleep(Duration::from_millis(1000)) => {} }; // By the time the tasks are finished, we should've accumulated ticks from two tasks diff --git a/packages/desktop/headless_tests/events.rs b/packages/desktop/headless_tests/events.rs index c2bdef52e..51c774702 100644 --- a/packages/desktop/headless_tests/events.rs +++ b/packages/desktop/headless_tests/events.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use dioxus::html::geometry::euclid::Vector3D; use dioxus::prelude::*; -use dioxus_core::prelude::consume_context; use dioxus_desktop::DesktopContext; #[path = "./utils.rs"] @@ -179,7 +178,10 @@ fn test_mouse_dblclick_div() -> Element { println!("{:?}", event.data); assert!(event.data.modifiers().is_empty()); assert!( - event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Primary), + event + .data + .held_buttons() + .contains(dioxus_html::input_data::MouseButton::Primary), ); assert!( event @@ -282,7 +284,8 @@ fn test_mouse_scroll_div() -> Element { onwheel: move |event| { println!("{:?}", event.data); let dioxus_html::geometry::WheelDelta::Pixels(delta) = event.data.delta() else { - panic!("Expected delta to be in pixels") }; + panic!("Expected delta to be in pixels") + }; assert_eq!(delta, Vector3D::new(1.0, 2.0, 3.0)); RECEIVED_EVENTS.with_mut(|x| *x += 1); } @@ -476,11 +479,16 @@ fn test_form_input() -> Element { r#type: "text", name: "username", id: "form-username", - oninput: set_username, + oninput: set_username } input { r#type: "text", name: "full-name", value: "lorem" } input { r#type: "password", name: "password", value: "ipsum" } - input { r#type: "radio", name: "color", value: "red", checked: true } + input { + r#type: "radio", + name: "color", + value: "red", + checked: true + } input { r#type: "radio", name: "color", value: "blue" } button { r#type: "submit", value: "Submit", "Submit the form" } } @@ -511,13 +519,21 @@ fn test_form_submit() -> Element { rsx! { div { h1 { "Form" } - form { - id: "form-submitter", - onsubmit: set_values, - input { r#type: "text", name: "username", id: "username", value: "goodbye" } + form { id: "form-submitter", onsubmit: set_values, + input { + r#type: "text", + name: "username", + id: "username", + value: "goodbye" + } input { r#type: "text", name: "full-name", value: "lorem" } input { r#type: "password", name: "password", value: "ipsum" } - input { r#type: "radio", name: "color", value: "red", checked: true } + input { + r#type: "radio", + name: "color", + value: "red", + checked: true + } input { r#type: "radio", name: "color", value: "blue" } button { r#type: "submit", value: "Submit", "Submit the form" } } @@ -547,9 +563,9 @@ fn test_select_multiple_options() -> Element { assert_eq!(values, vec!["usa", "canada"]); RECEIVED_EVENTS.with_mut(|x| *x += 1); }, - option { id: "usa", value: "usa", "USA" } - option { id: "canada", value: "canada", "Canada" } - option { id: "mexico", value: "mexico", selected: true, "Mexico" } + option { id: "usa", value: "usa", "USA" } + option { id: "canada", value: "canada", "Canada" } + option { id: "mexico", value: "mexico", selected: true, "Mexico" } } } } diff --git a/packages/desktop/headless_tests/rendering.rs b/packages/desktop/headless_tests/rendering.rs index f5e63acd8..3cf4125c8 100644 --- a/packages/desktop/headless_tests/rendering.rs +++ b/packages/desktop/headless_tests/rendering.rs @@ -1,5 +1,4 @@ use dioxus::prelude::*; -use dioxus_core::Element; use dioxus_desktop::DesktopContext; #[path = "./utils.rs"] @@ -53,26 +52,17 @@ fn check_html_renders() -> Element { } let dyn_value = 0; - let dyn_element = rsx! { - div { - dangerous_inner_html: "

hello world

", - } - }; + let dyn_element = rsx! { div { dangerous_inner_html: "

hello world

" } }; rsx! { - div { - id: "main_div", + div { id: "main_div", div { width: "100px", height: "100px", color: "rgb({dyn_value}, {dyn_value}, {dyn_value})", id: 5, - input { - "type": "checkbox", - }, - h1 { - "text" - } + input { "type": "checkbox" } + h1 { "text" } {dyn_element} } } diff --git a/packages/desktop/headless_tests/utils.rs b/packages/desktop/headless_tests/utils.rs index a497d3586..1f15cdd53 100644 --- a/packages/desktop/headless_tests/utils.rs +++ b/packages/desktop/headless_tests/utils.rs @@ -18,7 +18,7 @@ pub fn check_app_exits(app: fn() -> Element) { }); LaunchBuilder::desktop() - .with_cfg(Config::new().with_window(WindowBuilder::new().with_visible(true))) + .with_cfg(Config::new().with_window(WindowBuilder::new().with_visible(false))) .launch(app); // Stop deadman's switch diff --git a/packages/dioxus-lib/Cargo.toml b/packages/dioxus-lib/Cargo.toml index bf1b84757..d0ffab03c 100644 --- a/packages/dioxus-lib/Cargo.toml +++ b/packages/dioxus-lib/Cargo.toml @@ -19,6 +19,9 @@ dioxus-hooks = { workspace = true, optional = true } dioxus-rsx = { workspace = true, optional = true } dioxus-signals = { workspace = true, optional = true } +[dev-dependencies] +dioxus = { workspace = true } + [features] default = ["macro", "html", "signals", "hooks"] signals = ["dioxus-signals"] diff --git a/packages/dioxus-lib/README.md b/packages/dioxus-lib/README.md index e8f51e17d..be8a9e18e 100644 --- a/packages/dioxus-lib/README.md +++ b/packages/dioxus-lib/README.md @@ -1,5 +1,5 @@
-

🌗🚀 Dioxus

+

🌗🚀 Dioxus (lib)

A concurrent, functional, virtual DOM for Rust

@@ -38,9 +38,9 @@ Remember: Dioxus is a library for declaring interactive user interfaces—it is All Dioxus apps are built by composing functions that return an `Element`. -To launch an app, we use the `launch` method and use features in ``Cargo.toml`` to specify which renderer we want to use. In the launch function, we pass the app's root `Component`. +To launch an app, we use the `launch` method and use features in `Cargo.toml` to specify which renderer we want to use. In the launch function, we pass the app's root `Component`. -```rust +```rust, no_run use dioxus::prelude::*; fn main() { diff --git a/packages/dioxus/README.md b/packages/dioxus/README.md index 3a733388d..ea78c0e78 100644 --- a/packages/dioxus/README.md +++ b/packages/dioxus/README.md @@ -36,9 +36,9 @@ Remember: Dioxus is a library for declaring interactive user interfaces—it is All Dioxus apps are built by composing functions that return an `Element`. -To launch an app, we use the `launch` method and use features in ``Cargo.toml`` to specify which renderer we want to use. In the launch function, we pass the app's root `Component`. +To launch an app, we use the `launch` method and use features in `Cargo.toml` to specify which renderer we want to use. In the launch function, we pass the app's root `Component`. -```rust +```rust, no_run use dioxus::prelude::*; fn main() { diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 8f117b7e5..c8599bd66 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -66,6 +66,9 @@ tokio = { workspace = true, features = ["rt", "sync"], optional = true } dioxus-hot-reload = { workspace = true } tokio = { workspace = true, features = ["rt", "sync", "rt-multi-thread"], optional = true } +[dev-dependencies] +dioxus = { workspace = true, features = ["fullstack"] } + [features] default = ["hot-reload"] hot-reload = ["serde_json"] diff --git a/packages/fullstack/README.md b/packages/fullstack/README.md index 3a547839e..2166889c4 100644 --- a/packages/fullstack/README.md +++ b/packages/fullstack/README.md @@ -32,7 +32,7 @@ Fullstack utilities for the [`Dioxus`](https://dioxuslabs.com) framework. Full stack Dioxus in under 30 lines of code -```rust +```rust, no_run #![allow(non_snake_case)] use dioxus::prelude::*; @@ -42,7 +42,7 @@ fn main() { #[component] fn App() -> Element { - let meaning = use_signal(|| None); + let mut meaning = use_signal(|| None); rsx! { h1 { "Meaning of life: {meaning:?}" } diff --git a/packages/fullstack/src/hooks/server_cached.rs b/packages/fullstack/src/hooks/server_cached.rs index 113ad8c0e..2c6338098 100644 --- a/packages/fullstack/src/hooks/server_cached.rs +++ b/packages/fullstack/src/hooks/server_cached.rs @@ -12,9 +12,9 @@ use serde::{de::DeserializeOwned, Serialize}; /// use dioxus_fullstack::prelude::*; /// /// fn app() -> Element { -/// let state1 = server_cached(|| from_server(|| { +/// let state1 = server_cached(|| { /// 1234 -/// })); +/// }); /// /// None /// } diff --git a/packages/hooks/src/use_effect.rs b/packages/hooks/src/use_effect.rs index 5a7792756..c16a79ee6 100644 --- a/packages/hooks/src/use_effect.rs +++ b/packages/hooks/src/use_effect.rs @@ -34,7 +34,7 @@ use crate::use_callback; /// #[component] /// fn Comp(count: u32) -> Element { /// // Because the memo subscribes to `count` by adding it as a dependency, the memo will rerun every time `count` changes. -/// use_effect(use_reactive((&count, |(count,)| println!("Manually manipulate the dom") ))); +/// use_effect(use_reactive((&count,), |(count,)| println!("Manually manipulate the dom") )); /// /// todo!() /// } diff --git a/packages/hooks/src/use_memo.rs b/packages/hooks/src/use_memo.rs index bd5b98171..809811ac9 100644 --- a/packages/hooks/src/use_memo.rs +++ b/packages/hooks/src/use_memo.rs @@ -32,7 +32,7 @@ use dioxus_signals::{Memo, Signal}; /// #[component] /// fn Comp(count: u32) -> Element { /// // Because the memo subscribes to `count` by adding it as a dependency, the memo will rerun every time `count` changes. -/// let new_count = use_memo(use_reactive((&count, |(count,)| count + 1))); +/// let new_count = use_memo(use_reactive((&count,), |(count,)| count + 1)); /// /// todo!() /// } diff --git a/packages/hooks/src/use_reactive.rs b/packages/hooks/src/use_reactive.rs index 96a7289e4..99531852a 100644 --- a/packages/hooks/src/use_reactive.rs +++ b/packages/hooks/src/use_reactive.rs @@ -74,7 +74,7 @@ impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2, D = d1 d2, E = e1 e2, F = f1 f2, G = /// /// # Example /// -/// ```rust +/// ```rust, no_run /// use dioxus::prelude::*; /// /// let data = 5; @@ -104,7 +104,7 @@ pub fn use_reactive( /// /// # Example /// -/// ```rust +/// ```rust, no_run /// use dioxus::prelude::*; /// /// let data = 5; diff --git a/packages/hooks/src/use_resource.rs b/packages/hooks/src/use_resource.rs index aa19e7aad..4006dfb53 100644 --- a/packages/hooks/src/use_resource.rs +++ b/packages/hooks/src/use_resource.rs @@ -2,10 +2,6 @@ use crate::{use_callback, use_signal, UseCallback}; use dioxus_core::prelude::*; -use dioxus_core::{ - prelude::{spawn, use_hook}, - Task, -}; use dioxus_signals::*; use futures_util::{future, pin_mut, FutureExt, StreamExt}; use std::ops::Deref; @@ -66,7 +62,7 @@ use std::{cell::Cell, future::Future, rc::Rc}; /// #[component] /// fn Comp(count: u32) -> Element { /// // Because the memo subscribes to `count` by adding it as a dependency, the memo will rerun every time `count` changes. -/// let new_count = use_resource(use_reactive((&count, |(count,)| async move {count + 1} ))); +/// let new_count = use_resource(use_reactive((&count,), |(count,)| async move {count + 1} )); /// /// todo!() /// } diff --git a/packages/html/README.md b/packages/html/README.md index e244a1f88..6f47bbda9 100644 --- a/packages/html/README.md +++ b/packages/html/README.md @@ -25,7 +25,7 @@ The Dioxus `rsx!` and `html!` macros can accept any compile-time correct namespa However, this abstraction enables you to add any namespace of elements, provided they're in scope when rsx! is called. For an example, a UI that is designed for Augmented Reality might use different primitives than HTML: -```rust +```rust, ignore use ar_namespace::*; rsx! { @@ -46,7 +46,7 @@ This is currently a not-very-explored part of Dioxus. However, the namespacing s Elements for dioxus must implement the (simple) DioxusElement trait to be used in the rsx! macro. -```rust +```rust, ignore struct div; impl DioxusElement for div { const TAG_NAME: &'static str = "div"; @@ -60,7 +60,7 @@ Attributes would then be implemented as constants on these unit structs. The HTML namespace is defined mostly with macros. However, the expanded form would look something like this: -```rust +```rust, ignore struct base; impl DioxusElement for base { const TAG_NAME: &'static str = "base"; @@ -78,7 +78,7 @@ Because attributes are defined as methods on the unit struct, they guard the att Whenever the rsx! macro is called, it relies on a module `dioxus_elements` to be in scope. When you enable the `html` feature in dioxus, this module gets imported in the prelude. However, you can extend this with your own set of custom elements by making your own `dioxus_elements` module and re-exporting the html namespace. -```rust +```rust, ignore mod dioxus_elements { use dioxus::prelude::dioxus_elements::*; struct my_element; diff --git a/packages/liveview/src/lib.rs b/packages/liveview/src/lib.rs index e520ebc17..458f29dcd 100644 --- a/packages/liveview/src/lib.rs +++ b/packages/liveview/src/lib.rs @@ -106,7 +106,9 @@ fn handle_edits_code() -> String { /// If you enter a relative path, the web client automatically prefixes the host address in /// `window.location` when creating a web socket to LiveView. /// -/// ``` +/// ```rust +/// use dioxus_liveview::interpreter_glue; +/// /// // Creates websocket connection to same host as current page /// interpreter_glue("/api/liveview"); /// diff --git a/packages/router-macro/Cargo.toml b/packages/router-macro/Cargo.toml index 923169906..96cd63646 100644 --- a/packages/router-macro/Cargo.toml +++ b/packages/router-macro/Cargo.toml @@ -20,6 +20,9 @@ quote = { workspace = true } proc-macro2 = { workspace = true } slab = { workspace = true } +[dev-dependencies] +dioxus = { workspace = true, features = ["router"] } + [features] default = [] web = [] diff --git a/packages/router-macro/src/lib.rs b/packages/router-macro/src/lib.rs index f6ac186b3..5d9325c1f 100644 --- a/packages/router-macro/src/lib.rs +++ b/packages/router-macro/src/lib.rs @@ -42,7 +42,9 @@ mod segment; /// 2. By the order they are defined in the enum /// /// All features: -/// ```rust, skip +/// ```rust +/// use dioxus::prelude::*; +/// /// #[rustfmt::skip] /// #[derive(Clone, Debug, PartialEq, Routable)] /// enum Route { @@ -75,9 +77,17 @@ mod segment; /// #[redirect("/:id/user", |id: usize| Route::Route3 { dynamic: id.to_string()})] /// #[route("/:dynamic")] /// Route3 { dynamic: String }, -/// #[child] -/// NestedRoute(NestedRoute), /// } +/// # #[component] +/// # fn Route1(user_id: usize, dynamic: usize, query: String) -> Element { None } +/// # #[component] +/// # fn Route2(user_id: usize) -> Element { None } +/// # #[component] +/// # fn Route3(dynamic: String) -> Element { None } +/// # #[component] +/// # fn UserFrame(user_id: usize) -> Element { None } +/// # #[component] +/// # fn IndexComponent() -> Element { None } /// ``` /// /// # `#[route("path", component)]` @@ -89,7 +99,9 @@ mod segment; /// Routes are the most basic attribute. They allow you to define a route and the component to render when the route is matched. The component must take all dynamic parameters of the route and all parent nests. /// The next variant will be tied to the component. If you link to that variant, the component will be rendered. /// -/// ```rust, skip +/// ```rust +/// use dioxus::prelude::*; +/// /// #[derive(Clone, Debug, PartialEq, Routable)] /// enum Route { /// // Define routes that renders the IndexComponent @@ -97,6 +109,8 @@ mod segment; /// #[route("/", Index)] /// Index {}, /// } +/// # #[component] +/// # fn Index() -> Element { None } /// ``` /// /// # `#[redirect("path", function)]` @@ -105,14 +119,18 @@ mod segment; /// - `path`: The path to the enum variant (relative to the parent nest) /// - `function`: A function that takes the parameters from the path and returns a new route /// -/// ```rust, skip +/// ```rust +/// use dioxus::prelude::*; +/// /// #[derive(Clone, Debug, PartialEq, Routable)] /// enum Route { /// // Redirects the /:id route to the Index route -/// #[redirect("/:id", |_: usize| Route::Index {})] +/// #[redirect("/:id", |id: usize| Route::Index {})] /// #[route("/", Index)] /// Index {}, /// } +/// # #[component] +/// # fn Index() -> Element { None } /// ``` /// /// Redirects allow you to redirect a route to another route. The function must take all dynamic parameters of the route and all parent nests. @@ -124,29 +142,35 @@ mod segment; /// /// Nests effect all nests, routes and redirects defined until the next `#[end_nest]` attribute. All children of nests are relative to the nest route and must include all dynamic parameters of the nest. /// -/// ```rust, skip +/// ```rust +/// use dioxus::prelude::*; +/// /// #[derive(Clone, Debug, PartialEq, Routable)] /// enum Route { /// // Nests all child routes in the /blog route /// #[nest("/blog")] /// // This is at /blog/:id -/// #[redirect("/:id", |_: usize| Route::Index {})] +/// #[redirect("/:id", |id: usize| Route::Index {})] /// // This is at /blog /// #[route("/", Index)] /// Index {}, /// } +/// # #[component] +/// # fn Index() -> Element { None } /// ``` /// /// # `#[end_nest]` /// /// The `#[end_nest]` attribute is used to end a nest. It takes no parameters. /// -/// ```rust, skip +/// ```rust +/// use dioxus::prelude::*; +/// /// #[derive(Clone, Debug, PartialEq, Routable)] /// enum Route { /// #[nest("/blog")] /// // This is at /blog/:id -/// #[redirect("/:id", |_: usize| Route::Index {})] +/// #[redirect("/:id", |id: usize| Route::Index {})] /// // This is at /blog /// #[route("/", Index)] /// Index {}, @@ -156,6 +180,10 @@ mod segment; /// #[route("/")] /// Home {}, /// } +/// # #[component] +/// # fn Index() -> Element { None } +/// # #[component] +/// # fn Home() -> Element { None } /// ``` /// /// # `#[layout(component)]` @@ -165,26 +193,34 @@ mod segment; /// /// The layout component allows you to wrap all children of the layout in a component. The child routes are rendered in the Outlet of the layout component. The layout component must take all dynamic parameters of the nests it is nested in. /// -/// ```rust, skip +/// ```rust +/// use dioxus::prelude::*; +/// /// #[derive(Clone, Debug, PartialEq, Routable)] /// enum Route { /// #[layout(BlogFrame)] -/// #[redirect("/:id", |_: usize| Route::Index {})] +/// #[redirect("/:id", |id: usize| Route::Index {})] /// // Index will be rendered in the Outlet of the BlogFrame component /// #[route("/", Index)] /// Index {}, /// } +/// # #[component] +/// # fn Index() -> Element { None } +/// # #[component] +/// # fn BlogFrame() -> Element { None } /// ``` /// /// # `#[end_layout]` /// /// The `#[end_layout]` attribute is used to end a layout. It takes no parameters. /// -/// ```rust, skip +/// ```rust +/// use dioxus::prelude::*; +/// /// #[derive(Clone, Debug, PartialEq, Routable)] /// enum Route { /// #[layout(BlogFrame)] -/// #[redirect("/:id", |_: usize| Route::Index {})] +/// #[redirect("/:id", |id: usize| Route::Index {})] /// // Index will be rendered in the Outlet of the BlogFrame component /// #[route("/", Index)] /// Index {}, @@ -194,6 +230,12 @@ mod segment; /// #[route("/")] /// Home {}, /// } +/// # #[component] +/// # fn Index() -> Element { None } +/// # #[component] +/// # fn BlogFrame() -> Element { None } +/// # #[component] +/// # fn Home() -> Element { None } /// ``` #[proc_macro_derive( Routable, diff --git a/packages/router/src/components/history_buttons.rs b/packages/router/src/components/history_buttons.rs index 5c3592d26..b4605c926 100644 --- a/packages/router/src/components/history_buttons.rs +++ b/packages/router/src/components/history_buttons.rs @@ -73,7 +73,12 @@ pub fn GoBackButton(props: HistoryButtonProps) -> Element { let disabled = !router.can_go_back(); rsx! { - button { disabled: "{disabled}", prevent_default: "onclick", onclick: move |_| router.go_back(), {children} } + button { + disabled: "{disabled}", + prevent_default: "onclick", + onclick: move |_| router.go_back(), + {children} + } } } @@ -114,7 +119,7 @@ pub fn GoBackButton(props: HistoryButtonProps) -> Element { /// } /// # /// # let mut vdom = VirtualDom::new(App); -/// # let _ = vdom.rebuild(); +/// # vdom.rebuild_in_place(); /// # assert_eq!( /// # dioxus_ssr::render(&vdom), /// # r#""# @@ -139,6 +144,11 @@ pub fn GoForwardButton(props: HistoryButtonProps) -> Element { let disabled = !router.can_go_forward(); rsx! { - button { disabled: "{disabled}", prevent_default: "onclick", onclick: move |_| router.go_forward(), {children} } + button { + disabled: "{disabled}", + prevent_default: "onclick", + onclick: move |_| router.go_forward(), + {children} + } } } diff --git a/packages/router/src/components/link.rs b/packages/router/src/components/link.rs index e8f69f7ff..067895f11 100644 --- a/packages/router/src/components/link.rs +++ b/packages/router/src/components/link.rs @@ -178,26 +178,24 @@ impl Debug for LinkProps { /// #[component] /// fn Index() -> Element { /// rsx! { -/// rsx! { -/// Link { -/// active_class: "active", -/// class: "link_class", -/// id: "link_id", -/// new_tab: true, -/// rel: "link_rel", -/// to: Route::Index {}, +/// Link { +/// active_class: "active", +/// class: "link_class", +/// id: "link_id", +/// new_tab: true, +/// rel: "link_rel", +/// to: Route::Index {}, /// -/// "A fully configured link" -/// } +/// "A fully configured link" /// } /// } /// } /// # /// # let mut vdom = VirtualDom::new(App); -/// # let _ = vdom.rebuild(); +/// # vdom.rebuild_in_place(); /// # assert_eq!( /// # dioxus_ssr::render(&vdom), -/// # r#"A fully configured link"# +/// # r#"A fully configured link"# /// # ); /// ``` #[allow(non_snake_case)] diff --git a/packages/router/src/components/outlet.rs b/packages/router/src/components/outlet.rs index cf2fe6a0e..e7ffd9c9a 100644 --- a/packages/router/src/components/outlet.rs +++ b/packages/router/src/components/outlet.rs @@ -65,7 +65,7 @@ use dioxus_lib::prelude::*; /// # } /// # /// # let mut vdom = VirtualDom::new(App); -/// # let _ = vdom.rebuild(); +/// # vdom.rebuild_in_place(); /// # assert_eq!(dioxus_ssr::render(&vdom), "

App

Child

"); /// ``` pub fn Outlet() -> Element { diff --git a/packages/router/src/hooks/use_navigator.rs b/packages/router/src/hooks/use_navigator.rs index 1207b1fb2..9ca164464 100644 --- a/packages/router/src/hooks/use_navigator.rs +++ b/packages/router/src/hooks/use_navigator.rs @@ -46,7 +46,7 @@ use crate::prelude::{Navigator, RouterContext}; /// } /// /// # let mut vdom = VirtualDom::new(App); -/// # let _ = vdom.rebuild(); +/// # vdom.rebuild_in_place(); /// ``` #[must_use] pub fn use_navigator() -> Navigator { diff --git a/packages/router/src/hooks/use_route.rs b/packages/router/src/hooks/use_route.rs index d0c66ebfb..4eac76a8e 100644 --- a/packages/router/src/hooks/use_route.rs +++ b/packages/router/src/hooks/use_route.rs @@ -37,7 +37,7 @@ use crate::utils::use_router_internal::use_router_internal; /// } /// # /// # let mut vdom = VirtualDom::new(App); -/// # let _ = vdom.rebuild(); +/// # vdom.rebuild_in_place(); /// # assert_eq!(dioxus_ssr::render(&vdom), "

App

Current Path

/

") /// ``` #[must_use] diff --git a/packages/ssr/README.md b/packages/ssr/README.md index 366f16340..d6603e6ea 100644 --- a/packages/ssr/README.md +++ b/packages/ssr/README.md @@ -21,7 +21,7 @@ Dioxus SSR provides utilities to render Dioxus components to valid HTML. Once re let app: Component = |cx| rsx!(div {"hello world!"}); let mut vdom = VirtualDom::new(app); -let _ = vdom.rebuild(); +vdom.rebuild_in_place(); let text = dioxus_ssr::render(&vdom); assert_eq!(text, "
hello world!
") @@ -45,7 +45,7 @@ let content = dioxus_ssr::render_element(rsx!{ ```rust, ignore let mut vdom = VirtualDom::new(app); -let _ = vdom.rebuild(); +vdom.rebuild_in_place(); let content = dioxus_ssr::render(&vdom); ``` @@ -63,7 +63,7 @@ To enable pre-rendering, simply set the pre-rendering flag to true. ```rust, ignore let mut vdom = VirtualDom::new(App); -let _ = vdom.rebuild(); +vdom.rebuild_in_place(); let mut renderer = dioxus_ssr::Renderer::new(); renderer.pre_render = true;