Fix flakey windows tests (#2332)

This commit is contained in:
Evan Almloff 2024-04-17 17:08:38 -05:00 committed by GitHub
parent c9ab09b348
commit 5ce91e1bfc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 217 additions and 136 deletions

3
Cargo.lock generated
View file

@ -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",

View file

@ -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",

View file

@ -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<HashMap<u32, TodoItem>>) -> Element {
value: "{draft}",
autofocus: "true",
oninput: move |evt| draft.set(evt.value()),
onkeydown,
onkeydown
}
}
}
@ -165,7 +170,7 @@ fn TodoEntry(mut todos: Signal<HashMap<u32, TodoItem>>, 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<HashMap<u32, TodoItem>>, 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"
}
}
}
}
}
}

View file

@ -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))
/// })
/// }
/// ```

View file

@ -310,7 +310,7 @@ impl Scope {
///
/// pub fn use_custom_state() -> CustomState {
/// use_hook(|| CustomState {
/// inner: Signal::new(InnerCustomState)
/// inner: Signal::new(InnerCustomState(0))
/// })
/// }
/// ```

View file

@ -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<P: Clone + 'static, M: 'static>(
root: impl ComponentFunction<P, M>,
@ -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) {

View file

@ -1,4 +1,3 @@
use crate::dioxus_elements::SerializedMouseData;
use dioxus::prelude::*;
use dioxus_core::ElementId;
use dioxus_elements::SerializedHtmlEventConverter;

View file

@ -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

View file

@ -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" }
}
}
}

View file

@ -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: "<p>hello world</p>",
}
};
let dyn_element = rsx! { div { dangerous_inner_html: "<p>hello world</p>" } };
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}
}
}

View file

@ -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

View file

@ -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"]

View file

@ -1,5 +1,5 @@
<div>
<h1>🌗🚀 Dioxus</h1>
<h1>🌗🚀 Dioxus (lib)</h1>
<p>
<strong>A concurrent, functional, virtual DOM for Rust</strong>
</p>
@ -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() {

View file

@ -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() {

View file

@ -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"]

View file

@ -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:?}" }

View file

@ -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
/// }

View file

@ -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!()
/// }

View file

@ -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!()
/// }

View file

@ -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<O, D: Dependency>(
///
/// # Example
///
/// ```rust
/// ```rust, no_run
/// use dioxus::prelude::*;
///
/// let data = 5;

View file

@ -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!()
/// }

View file

@ -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;

View file

@ -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");
///

View file

@ -20,6 +20,9 @@ quote = { workspace = true }
proc-macro2 = { workspace = true }
slab = { workspace = true }
[dev-dependencies]
dioxus = { workspace = true, features = ["router"] }
[features]
default = []
web = []

View file

@ -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,

View file

@ -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#"<button disabled="true" dioxus-prevent-default="onclick">go forward</button>"#
@ -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}
}
}
}

View file

@ -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 href="/" dioxus-prevent-default="" class="link_class active" id="link_id" rel="link_rel" target="_blank">A fully configured link</a>"#
/// # r#"<a href="/" dioxus-prevent-default="" class="link_class active" rel="link_rel" target="_blank" id="link_id">A fully configured link</a>"#
/// # );
/// ```
#[allow(non_snake_case)]

View file

@ -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), "<h1>App</h1><p>Child</p>");
/// ```
pub fn Outlet<R: Routable + Clone>() -> Element {

View file

@ -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 {

View file

@ -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), "<h1>App</h1><h2>Current Path</h2><p>/</p>")
/// ```
#[must_use]

View file

@ -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, "<div>hello world!</div>")
@ -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;