Browse Source

chore: get event test working

Jonathan Kelley 2 years ago
parent
commit
0027cdd938

+ 50 - 3
packages/core/src/arena.rs

@@ -6,17 +6,23 @@ pub struct ElementId(pub usize);
 
 pub struct ElementRef {
     // the pathway of the real element inside the template
-    pub path: &'static [u8],
+    pub path: ElementPath,
 
     // The actual template
     pub template: *mut VNode<'static>,
 }
 
+#[derive(Clone, Copy)]
+pub enum ElementPath {
+    Deep(&'static [u8]),
+    Root(usize),
+}
+
 impl ElementRef {
     pub fn null() -> Self {
         Self {
             template: std::ptr::null_mut(),
-            path: &[],
+            path: ElementPath::Root(0),
         }
     }
 }
@@ -28,7 +34,21 @@ impl<'b> VirtualDom {
 
         entry.insert(ElementRef {
             template: template as *const _ as *mut _,
-            path,
+            path: ElementPath::Deep(path),
+        });
+
+        println!("Claiming {}", id);
+
+        ElementId(id)
+    }
+
+    pub fn next_root(&mut self, template: &VNode, path: usize) -> ElementId {
+        let entry = self.elements.vacant_entry();
+        let id = entry.key();
+
+        entry.insert(ElementRef {
+            template: template as *const _ as *mut _,
+            path: ElementPath::Root(path),
         });
 
         println!("Claiming {}", id);
@@ -76,3 +96,30 @@ impl<'b> VirtualDom {
         // }
     }
 }
+impl ElementPath {
+    pub(crate) fn is_ascendant(&self, big: &&[u8]) -> bool {
+        match *self {
+            ElementPath::Deep(small) => small.len() <= big.len() && small == &big[..small.len()],
+            ElementPath::Root(r) => big.len() == 1 && big[0] == r as u8,
+        }
+    }
+}
+
+#[test]
+fn path_ascendant() {
+    // assert!(&ElementPath::Deep(&[]).is_ascendant(&&[0_u8]));
+    // assert!(&ElementPath::Deep(&[1, 2]), &[1, 2, 3]);
+    // assert!(!is_path_ascendant(
+    //     &ElementPath::Deep(&[1, 2, 3, 4]),
+    //     &[1, 2, 3]
+    // ));
+}
+
+impl PartialEq<&[u8]> for ElementPath {
+    fn eq(&self, other: &&[u8]) -> bool {
+        match *self {
+            ElementPath::Deep(deep) => deep.eq(*other),
+            ElementPath::Root(r) => other.len() == 1 && other[0] == r as u8,
+        }
+    }
+}

+ 2 - 1
packages/core/src/create.rs

@@ -63,7 +63,8 @@ impl<'b: 'static> VirtualDom {
                 }
 
                 TemplateNode::Element { .. } | TemplateNode::Text(_) => {
-                    let this_id = self.next_element(template, &[]);
+                    let this_id = self.next_root(template, root_idx);
+
                     template.root_ids[root_idx].set(this_id);
                     self.mutations.push(LoadTemplate {
                         name: template.template.id,

+ 3 - 7
packages/core/src/virtual_dom.rs

@@ -6,7 +6,7 @@ use crate::{
     any_props::VProps,
     arena::{ElementId, ElementRef},
     factory::RenderReturn,
-    innerlude::{DirtyScope, Mutations, Scheduler, SchedulerMsg},
+    innerlude::{DirtyScope, ElementPath, Mutations, Scheduler, SchedulerMsg},
     mutations::Mutation,
     nodes::{Template, TemplateId},
     scheduler::{SuspenseBoundary, SuspenseId},
@@ -368,15 +368,11 @@ impl VirtualDom {
             let target_path = el_ref.path;
 
             for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
-                fn is_path_ascendant(small: &[u8], big: &[u8]) -> bool {
-                    small.len() >= big.len() && small == &big[..small.len()]
-                }
-
                 let this_path = template.template.attr_paths[idx];
 
                 // listeners are required to be prefixed with "on", but they come back to the virtualdom with that missing
                 // we should fix this so that we look for "onclick" instead of "click"
-                if &attr.name[2..] == name && is_path_ascendant(&target_path, &this_path) {
+                if &attr.name[2..] == name && target_path.is_ascendant(&this_path) {
                     listeners.push(&attr.value);
 
                     // Break if the event doesn't bubble anyways
@@ -387,7 +383,7 @@ impl VirtualDom {
                     // Break if this is the exact target element.
                     // This means we won't call two listeners with the same name on the same element. This should be
                     // documented, or be rejected from the rsx! macro outright
-                    if this_path == target_path {
+                    if target_path == this_path {
                         break;
                     }
                 }

+ 225 - 220
packages/core/tests/lifecycle.rs

@@ -4,6 +4,7 @@
 //! Tests for the lifecycle of components.
 use dioxus::core::{ElementId, Mutation::*};
 use dioxus::prelude::*;
+use std::rc::Rc;
 use std::sync::{Arc, Mutex};
 
 type Shared<T> = Arc<Mutex<T>>;
@@ -14,21 +15,26 @@ fn manual_diffing() {
         value: Shared<&'static str>,
     }
 
-    static App: Component<AppProps> = |cx| {
+    fn app(cx: Scope<AppProps>) -> Element {
         let val = cx.props.value.lock().unwrap();
-        cx.render(rsx_without_templates! { div { "{val}" } })
+        cx.render(rsx! { div { "{val}" } })
     };
 
     let value = Arc::new(Mutex::new("Hello"));
-    let mut dom = VirtualDom::new_with_props(App, AppProps { value: value.clone() });
+    let mut dom = VirtualDom::new_with_props(app, AppProps { value: value.clone() });
 
     let _ = dom.rebuild();
 
     *value.lock().unwrap() = "goodbye";
 
-    let edits = dom.rebuild();
-
-    println!("edits: {:?}", edits);
+    assert_eq!(
+        dom.rebuild().santize().edits,
+        [
+            LoadTemplate { name: "template", index: 0, id: ElementId(3) },
+            HydrateText { path: &[0], value: "goodbye", id: ElementId(4) },
+            AppendChildren { m: 1 }
+        ]
+    );
 }
 
 #[test]
@@ -36,229 +42,228 @@ fn events_generate() {
     fn app(cx: Scope) -> Element {
         let count = cx.use_hook(|| 0);
 
-        let inner = match *count {
-            0 => {
-                rsx_without_templates! {
-                    div {
-                        onclick: move |_| *count += 1,
-                        div {
-                            "nested"
-                        }
-                        "Click me!"
-                    }
+        match *count {
+            0 => cx.render(rsx! {
+                div {
+                    onclick: move |_| {
+                        *count += 1
+                    },
+                    div { "nested" }
+                    "Click me!"
                 }
-            }
-            _ => todo!(),
-        };
-
-        cx.render(inner)
+            }),
+            _ => cx.render(rsx!(())),
+        }
     };
 
     let mut dom = VirtualDom::new(app);
-    let mut channel = dom.get_scheduler_channel();
-    assert!(dom.has_work());
-
-    let edits = dom.rebuild();
-    assert_eq!(
-        edits.edits,
-        [
-            CreateElement { root: Some(1), tag: "div", children: 0 },
-            NewEventListener { event_name: "click", scope: ScopeId(0), root: Some(1) },
-            CreateElement { root: Some(2), tag: "div", children: 0 },
-            CreateTextNode { root: Some(3), text: "nested" },
-            AppendChildren { root: Some(2), children: vec![3] },
-            CreateTextNode { root: Some(4), text: "Click me!" },
-            AppendChildren { root: Some(1), children: vec![2, 4] },
-            AppendChildren { root: Some(0), children: vec![1] }
-        ]
-    )
-}
-
-#[test]
-fn components_generate() {
-    fn app(cx: Scope) -> Element {
-        let render_phase = cx.use_hook(|| 0);
-        *render_phase += 1;
-
-        cx.render(match *render_phase {
-            1 => rsx_without_templates!("Text0"),
-            2 => rsx_without_templates!(div {}),
-            3 => rsx_without_templates!("Text2"),
-            4 => rsx_without_templates!(Child {}),
-            5 => rsx_without_templates!({ None as Option<()> }),
-            6 => rsx_without_templates!("text 3"),
-            7 => rsx_without_templates!({ (0..2).map(|f| rsx_without_templates!("text {f}")) }),
-            8 => rsx_without_templates!(Child {}),
-            _ => todo!(),
-        })
-    };
-
-    fn Child(cx: Scope) -> Element {
-        println!("Running child");
-        cx.render(rsx_without_templates! {
-            h1 {}
-        })
-    }
-
-    let mut dom = VirtualDom::new(app);
-    let edits = dom.rebuild();
-    assert_eq!(
-        edits.edits,
-        [
-            CreateTextNode { root: Some(1), text: "Text0" },
-            AppendChildren { root: Some(0), children: vec![1] }
-        ]
+    _ = dom.rebuild();
+
+    dom.handle_event(
+        "click",
+        Rc::new(MouseData::default()),
+        ElementId(1),
+        true,
+        EventPriority::Immediate,
     );
 
-    assert_eq!(
-        dom.hard_diff(ScopeId(0)).edits,
-        [
-            CreateElement { root: Some(2), tag: "div", children: 0 },
-            ReplaceWith { root: Some(1), nodes: vec![2] }
-        ]
-    );
+    dom.mark_dirty_scope(ScopeId(0));
+    let edits = dom.render_immediate();
 
     assert_eq!(
-        dom.hard_diff(ScopeId(0)).edits,
-        [
-            CreateTextNode { root: Some(1), text: "Text2" },
-            ReplaceWith { root: Some(2), nodes: vec![1] }
-        ]
-    );
-
-    // child {}
-    assert_eq!(
-        dom.hard_diff(ScopeId(0)).edits,
-        [
-            CreateElement { root: Some(2), tag: "h1", children: 0 },
-            ReplaceWith { root: Some(1), nodes: vec![2] }
-        ]
-    );
-
-    // placeholder
-    assert_eq!(
-        dom.hard_diff(ScopeId(0)).edits,
-        [
-            CreatePlaceholder { root: Some(1) },
-            ReplaceWith { root: Some(2), nodes: vec![1] }
-        ]
-    );
-
-    assert_eq!(
-        dom.hard_diff(ScopeId(0)).edits,
-        [
-            CreateTextNode { root: Some(2), text: "text 3" },
-            ReplaceWith { root: Some(1), nodes: vec![2] }
-        ]
-    );
-
-    assert_eq!(
-        dom.hard_diff(ScopeId(0)).edits,
-        [
-            CreateTextNode { text: "text 0", root: Some(1) },
-            CreateTextNode { text: "text 1", root: Some(3) },
-            ReplaceWith { root: Some(2), nodes: vec![1, 3] },
-        ]
-    );
-
-    assert_eq!(
-        dom.hard_diff(ScopeId(0)).edits,
+        edits.edits,
         [
-            CreateElement { tag: "h1", root: Some(2), children: 0 },
-            ReplaceWith { root: Some(1), nodes: vec![2] },
-            Remove { root: Some(3) },
+            CreatePlaceholder { id: ElementId(2) },
+            ReplaceWith { id: ElementId(1), m: 1 }
         ]
-    );
+    )
 }
 
-#[test]
-fn component_swap() {
-    fn app(cx: Scope) -> Element {
-        let render_phase = cx.use_hook(|| 0);
-        *render_phase += 1;
-
-        cx.render(match *render_phase {
-            0 => rsx_without_templates!(
-                div {
-                    NavBar {}
-                    Dashboard {}
-                }
-            ),
-            1 => rsx_without_templates!(
-                div {
-                    NavBar {}
-                    Results {}
-                }
-            ),
-            2 => rsx_without_templates!(
-                div {
-                    NavBar {}
-                    Dashboard {}
-                }
-            ),
-            3 => rsx_without_templates!(
-                div {
-                    NavBar {}
-                    Results {}
-                }
-            ),
-            4 => rsx_without_templates!(
-                div {
-                    NavBar {}
-                    Dashboard {}
-                }
-            ),
-            _ => rsx_without_templates!("blah"),
-        })
-    };
-
-    static NavBar: Component = |cx| {
-        println!("running navbar");
-        cx.render(rsx_without_templates! {
-            h1 {
-                "NavBar"
-                {(0..3).map(|f| rsx_without_templates!(NavLink {}))}
-            }
-        })
-    };
-
-    static NavLink: Component = |cx| {
-        println!("running navlink");
-        cx.render(rsx_without_templates! {
-            h1 {
-                "NavLink"
-            }
-        })
-    };
-
-    static Dashboard: Component = |cx| {
-        println!("running dashboard");
-        cx.render(rsx_without_templates! {
-            div {
-                "dashboard"
-            }
-        })
-    };
-
-    static Results: Component = |cx| {
-        println!("running results");
-        cx.render(rsx_without_templates! {
-            div {
-                "results"
-            }
-        })
-    };
-
-    let mut dom = VirtualDom::new(app);
-    let edits = dom.rebuild();
-    dbg!(&edits);
-
-    let edits = dom.work_with_deadline(|| false);
-    dbg!(&edits);
-    let edits = dom.work_with_deadline(|| false);
-    dbg!(&edits);
-    let edits = dom.work_with_deadline(|| false);
-    dbg!(&edits);
-    let edits = dom.work_with_deadline(|| false);
-    dbg!(&edits);
-}
+// #[test]
+// fn components_generate() {
+//     fn app(cx: Scope) -> Element {
+//         let render_phase = cx.use_hook(|| 0);
+//         *render_phase += 1;
+
+//         cx.render(match *render_phase {
+//             1 => rsx_without_templates!("Text0"),
+//             2 => rsx_without_templates!(div {}),
+//             3 => rsx_without_templates!("Text2"),
+//             4 => rsx_without_templates!(Child {}),
+//             5 => rsx_without_templates!({ None as Option<()> }),
+//             6 => rsx_without_templates!("text 3"),
+//             7 => rsx_without_templates!({ (0..2).map(|f| rsx_without_templates!("text {f}")) }),
+//             8 => rsx_without_templates!(Child {}),
+//             _ => todo!(),
+//         })
+//     };
+
+//     fn Child(cx: Scope) -> Element {
+//         println!("Running child");
+//         cx.render(rsx_without_templates! {
+//             h1 {}
+//         })
+//     }
+
+//     let mut dom = VirtualDom::new(app);
+//     let edits = dom.rebuild();
+//     assert_eq!(
+//         edits.edits,
+//         [
+//             CreateTextNode { root: Some(1), text: "Text0" },
+//             AppendChildren { root: Some(0), children: vec![1] }
+//         ]
+//     );
+
+//     assert_eq!(
+//         dom.hard_diff(ScopeId(0)).edits,
+//         [
+//             CreateElement { root: Some(2), tag: "div", children: 0 },
+//             ReplaceWith { root: Some(1), nodes: vec![2] }
+//         ]
+//     );
+
+//     assert_eq!(
+//         dom.hard_diff(ScopeId(0)).edits,
+//         [
+//             CreateTextNode { root: Some(1), text: "Text2" },
+//             ReplaceWith { root: Some(2), nodes: vec![1] }
+//         ]
+//     );
+
+//     // child {}
+//     assert_eq!(
+//         dom.hard_diff(ScopeId(0)).edits,
+//         [
+//             CreateElement { root: Some(2), tag: "h1", children: 0 },
+//             ReplaceWith { root: Some(1), nodes: vec![2] }
+//         ]
+//     );
+
+//     // placeholder
+//     assert_eq!(
+//         dom.hard_diff(ScopeId(0)).edits,
+//         [
+//             CreatePlaceholder { root: Some(1) },
+//             ReplaceWith { root: Some(2), nodes: vec![1] }
+//         ]
+//     );
+
+//     assert_eq!(
+//         dom.hard_diff(ScopeId(0)).edits,
+//         [
+//             CreateTextNode { root: Some(2), text: "text 3" },
+//             ReplaceWith { root: Some(1), nodes: vec![2] }
+//         ]
+//     );
+
+//     assert_eq!(
+//         dom.hard_diff(ScopeId(0)).edits,
+//         [
+//             CreateTextNode { text: "text 0", root: Some(1) },
+//             CreateTextNode { text: "text 1", root: Some(3) },
+//             ReplaceWith { root: Some(2), nodes: vec![1, 3] },
+//         ]
+//     );
+
+//     assert_eq!(
+//         dom.hard_diff(ScopeId(0)).edits,
+//         [
+//             CreateElement { tag: "h1", root: Some(2), children: 0 },
+//             ReplaceWith { root: Some(1), nodes: vec![2] },
+//             Remove { root: Some(3) },
+//         ]
+//     );
+// }
+
+// #[test]
+// fn component_swap() {
+//     fn app(cx: Scope) -> Element {
+//         let render_phase = cx.use_hook(|| 0);
+//         *render_phase += 1;
+
+//         cx.render(match *render_phase {
+//             0 => rsx_without_templates!(
+//                 div {
+//                     NavBar {}
+//                     Dashboard {}
+//                 }
+//             ),
+//             1 => rsx_without_templates!(
+//                 div {
+//                     NavBar {}
+//                     Results {}
+//                 }
+//             ),
+//             2 => rsx_without_templates!(
+//                 div {
+//                     NavBar {}
+//                     Dashboard {}
+//                 }
+//             ),
+//             3 => rsx_without_templates!(
+//                 div {
+//                     NavBar {}
+//                     Results {}
+//                 }
+//             ),
+//             4 => rsx_without_templates!(
+//                 div {
+//                     NavBar {}
+//                     Dashboard {}
+//                 }
+//             ),
+//             _ => rsx_without_templates!("blah"),
+//         })
+//     };
+
+//     static NavBar: Component = |cx| {
+//         println!("running navbar");
+//         cx.render(rsx_without_templates! {
+//             h1 {
+//                 "NavBar"
+//                 {(0..3).map(|f| rsx_without_templates!(NavLink {}))}
+//             }
+//         })
+//     };
+
+//     static NavLink: Component = |cx| {
+//         println!("running navlink");
+//         cx.render(rsx_without_templates! {
+//             h1 {
+//                 "NavLink"
+//             }
+//         })
+//     };
+
+//     static Dashboard: Component = |cx| {
+//         println!("running dashboard");
+//         cx.render(rsx_without_templates! {
+//             div {
+//                 "dashboard"
+//             }
+//         })
+//     };
+
+//     static Results: Component = |cx| {
+//         println!("running results");
+//         cx.render(rsx_without_templates! {
+//             div {
+//                 "results"
+//             }
+//         })
+//     };
+
+//     let mut dom = VirtualDom::new(app);
+//     let edits = dom.rebuild();
+//     dbg!(&edits);
+
+//     let edits = dom.work_with_deadline(|| false);
+//     dbg!(&edits);
+//     let edits = dom.work_with_deadline(|| false);
+//     dbg!(&edits);
+//     let edits = dom.work_with_deadline(|| false);
+//     dbg!(&edits);
+//     let edits = dom.work_with_deadline(|| false);
+//     dbg!(&edits);
+// }

+ 1 - 1
packages/html/src/events/mouse.rs

@@ -10,7 +10,7 @@ pub type MouseEvent = UiEvent<MouseData>;
 
 /// A synthetic event that wraps a web-style [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
 #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Clone)]
+#[derive(Clone, Default)]
 /// Data associated with a mouse event
 ///
 /// Do not use the deprecated fields; they may change or become private in the future.