yew_stdweb/virtual_dom/
vlist.rs

1//! This module contains fragments implementation.
2use super::{Key, VDiff, VNode, VText};
3use crate::html::{AnyScope, NodeRef};
4use cfg_if::cfg_if;
5use std::collections::{HashMap, HashSet};
6use std::ops::{Deref, DerefMut};
7cfg_if! {
8    if #[cfg(feature = "std_web")] {
9        use stdweb::web::Element;
10    } else if #[cfg(feature = "web_sys")] {
11        use web_sys::Element;
12    }
13}
14
15/// This struct represents a fragment of the Virtual DOM tree.
16#[derive(Clone, Debug, PartialEq, Default)]
17pub struct VList {
18    /// The list of children nodes.
19    pub children: Vec<VNode>,
20    pub key: Option<Key>,
21}
22
23impl Deref for VList {
24    type Target = Vec<VNode>;
25
26    fn deref(&self) -> &Self::Target {
27        &self.children
28    }
29}
30
31impl DerefMut for VList {
32    fn deref_mut(&mut self) -> &mut Self::Target {
33        &mut self.children
34    }
35}
36
37impl VList {
38    /// Creates a new empty `VList` instance.
39    pub fn new() -> Self {
40        Self::default()
41    }
42
43    /// Creates a new `VList` instance with children.
44    pub fn new_with_children(children: Vec<VNode>, key: Option<Key>) -> Self {
45        VList { children, key }
46    }
47
48    /// Add `VNode` child.
49    pub fn add_child(&mut self, child: VNode) {
50        self.children.push(child);
51    }
52
53    /// Add multiple `VNode` children.
54    pub fn add_children(&mut self, children: impl IntoIterator<Item = VNode>) {
55        self.children.extend(children);
56    }
57
58    fn children_keys(&self, warn: bool) -> HashSet<Key> {
59        let mut hash_set = HashSet::with_capacity(self.children.len());
60        for l in self.children.iter() {
61            if let Some(k) = l.key() {
62                if !hash_set.insert(k.clone()) && warn {
63                    log::warn!("Key '{}' is not unique in list but must be.", k);
64                }
65            }
66        }
67        hash_set
68    }
69}
70
71impl VDiff for VList {
72    fn detach(&mut self, parent: &Element) {
73        for mut child in self.children.drain(..) {
74            child.detach(parent);
75        }
76    }
77
78    fn apply(
79        &mut self,
80        parent_scope: &AnyScope,
81        parent: &Element,
82        next_sibling: NodeRef,
83        ancestor: Option<VNode>,
84    ) -> NodeRef {
85        // Here, we will try to diff the previous list elements with the new
86        // ones we want to insert. For that, we will use two lists:
87        //  - lefts: new elements to render in the DOM
88        //  - rights: previously rendered elements.
89        //
90        // The left items are known since we want to insert them
91        // (self.children). For the right ones, we will look at the ancestor,
92        // i.e. the current DOM list element that we want to replace with self.
93
94        if self.children.is_empty() {
95            // Without a placeholder the next element becomes first
96            // and corrupts the order of rendering
97            // We use empty text element to stake out a place
98            let placeholder = VText::new("");
99            self.children.push(placeholder.into());
100        }
101
102        let left_keys = self.children_keys(true);
103        let lefts_keyed = left_keys.len() == self.children.len();
104
105        let right_keys = if let Some(VNode::VList(vlist)) = &ancestor {
106            vlist.children_keys(false)
107        } else {
108            HashSet::new()
109        };
110
111        let mut right_children = match ancestor {
112            // If the ancestor is also a VList, then the "right" list is the
113            // previously rendered items.
114            Some(VNode::VList(vlist)) => vlist.children,
115            // If the ancestor was not a VList, then the "right" list is a single node
116            Some(vnode) => vec![vnode],
117            None => vec![],
118        };
119        let rights_keyed = right_keys.len() == right_children.len();
120
121        // If the existing list and the new list are both properly keyed,
122        // then move existing list nodes into the new list's order before diffing
123        if lefts_keyed && rights_keyed {
124            // Find the intersection of keys to determine which right nodes can be reused
125            let matched_keys: HashSet<_> = left_keys.intersection(&right_keys).collect();
126
127            // Detach any right nodes that were not matched with a left node
128            right_children = right_children
129                .into_iter()
130                .filter_map(|mut right| {
131                    if matched_keys.contains(right.key().as_ref().unwrap()) {
132                        Some(right)
133                    } else {
134                        right.detach(parent);
135                        None
136                    }
137                })
138                .collect();
139
140            // Determine which rights are already in correct order and which
141            // rights need to be moved in the DOM before being reused
142            let mut rights_to_move = HashMap::with_capacity(right_children.len());
143            let mut matched_lefts = self
144                .children
145                .iter()
146                .filter(|left| matched_keys.contains(left.key().as_ref().unwrap()))
147                .peekable();
148            let mut left = matched_lefts.next();
149
150            // Note: `filter_map` is used to move rights into `rights_to_move`
151            #[allow(clippy::unnecessary_filter_map)]
152            let rights_in_place: Vec<_> = right_children
153                .into_iter()
154                .filter_map(|right| {
155                    if right.key() == left.and_then(|l| l.key()) {
156                        left = matched_lefts.next();
157                        return Some(right);
158                    } else if right.key() == matched_lefts.peek().and_then(|l| l.key()) {
159                        matched_lefts.next();
160                        left = matched_lefts.next();
161                        return Some(right);
162                    }
163
164                    rights_to_move.insert(right.key().unwrap(), right);
165                    None
166                })
167                .collect();
168
169            // Move rights into correct order and build `right_children`
170            right_children = Vec::with_capacity(matched_keys.len());
171            let mut matched_lefts = self
172                .children
173                .iter()
174                .filter(|left| matched_keys.contains(left.key().as_ref().unwrap()));
175
176            for right in rights_in_place.into_iter() {
177                let mut left = matched_lefts.next().unwrap();
178                while right.key() != left.key() {
179                    let right_to_move = rights_to_move.remove(&left.key().unwrap()).unwrap();
180                    right_to_move.move_before(parent, Some(right.first_node()));
181                    right_children.push(right_to_move);
182                    left = matched_lefts.next().unwrap();
183                }
184                right_children.push(right);
185            }
186
187            for left in matched_lefts {
188                let right_to_move = rights_to_move.remove(&left.key().unwrap()).unwrap();
189                right_to_move.move_before(parent, next_sibling.get());
190                right_children.push(right_to_move);
191            }
192
193            assert!(rights_to_move.is_empty())
194        }
195
196        let mut rights = right_children.into_iter().peekable();
197        let mut last_next_sibling = NodeRef::default();
198        let mut nodes: Vec<NodeRef> = self
199            .children
200            .iter_mut()
201            .map(|left| {
202                let ancestor = rights.next();
203
204                // Create a new `next_sibling` reference which points to the next `right` or
205                // the outer list's `next_sibling` if there are no more `rights`.
206                let new_next_sibling = NodeRef::default();
207                if let Some(next_right) = rights.peek() {
208                    new_next_sibling.set(Some(next_right.first_node()));
209                } else {
210                    new_next_sibling.link(next_sibling.clone());
211                }
212
213                // Update the next list item and then link the previous left's `next_sibling` to the
214                // returned `node` reference so that the previous left has an up-to-date `next_sibling`.
215                // This is important for rendering a `VComp` because each `VComp` keeps track of its
216                // `next_sibling` to properly render its children.
217                let node = left.apply(parent_scope, parent, new_next_sibling.clone(), ancestor);
218                last_next_sibling.link(node.clone());
219                last_next_sibling = new_next_sibling;
220                node
221            })
222            .collect();
223
224        // If there are more `rights` than `lefts`, we need to make sure to link the last left's `next_sibling`
225        // to the outer list's `next_sibling` so that it doesn't point at a `right` that is detached.
226        last_next_sibling.link(next_sibling);
227
228        // Detach all extra rights
229        for mut right in rights {
230            right.detach(parent);
231        }
232
233        assert!(!nodes.is_empty(), "VList should have at least one child");
234        nodes.swap_remove(0)
235    }
236}
237
238#[cfg(all(test, feature = "web_sys"))]
239mod layout_tests {
240    extern crate self as yew;
241
242    use crate::html;
243    use crate::virtual_dom::layout_tests::{diff_layouts, TestLayout};
244
245    #[cfg(feature = "wasm_test")]
246    use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
247
248    #[cfg(feature = "wasm_test")]
249    wasm_bindgen_test_configure!(run_in_browser);
250
251    #[test]
252    fn diff() {
253        let layout1 = TestLayout {
254            name: "1",
255            node: html! {
256                <>
257                    {"a"}
258                    {"b"}
259                    <>
260                        {"c"}
261                        {"d"}
262                    </>
263                    {"e"}
264                </>
265            },
266            expected: "abcde",
267        };
268
269        let layout2 = TestLayout {
270            name: "2",
271            node: html! {
272                <>
273                    {"a"}
274                    {"b"}
275                    <></>
276                    {"e"}
277                    {"f"}
278                </>
279            },
280            expected: "abef",
281        };
282
283        let layout3 = TestLayout {
284            name: "3",
285            node: html! {
286                <>
287                    {"a"}
288                    <></>
289                    {"b"}
290                    {"e"}
291                </>
292            },
293            expected: "abe",
294        };
295
296        let layout4 = TestLayout {
297            name: "4",
298            node: html! {
299                <>
300                    {"a"}
301                    <>
302                        {"c"}
303                        {"d"}
304                    </>
305                    {"b"}
306                    {"e"}
307                </>
308            },
309            expected: "acdbe",
310        };
311
312        diff_layouts(vec![layout1, layout2, layout3, layout4]);
313    }
314}
315
316#[cfg(all(test, feature = "web_sys"))]
317mod layout_tests_keys {
318    extern crate self as yew;
319
320    use crate::html;
321    use crate::virtual_dom::layout_tests::{diff_layouts, TestLayout};
322    use crate::virtual_dom::VNode;
323    use crate::{Children, Component, ComponentLink, Html, Properties, ShouldRender};
324    use web_sys::Node;
325
326    #[cfg(feature = "wasm_test")]
327    use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
328
329    #[cfg(feature = "wasm_test")]
330    wasm_bindgen_test_configure!(run_in_browser);
331
332    struct Comp {
333        id: usize,
334        panic_if_changes: bool,
335    }
336
337    #[derive(Properties, Clone)]
338    struct CountingCompProps {
339        id: usize,
340        #[prop_or(false)]
341        can_change: bool,
342    }
343
344    impl Component for Comp {
345        type Message = ();
346        type Properties = CountingCompProps;
347
348        fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
349            Comp {
350                id: props.id,
351                panic_if_changes: props.can_change,
352            }
353        }
354
355        fn change(&mut self, props: Self::Properties) -> ShouldRender {
356            #[cfg(feature = "wasm_test")]
357            wasm_bindgen_test::console_log!("Comp changed: {} -> {}", self.id, props.id);
358            let changed = self.id != props.id;
359            if self.panic_if_changes && changed {
360                panic!(
361                    "VComp changed but should not have: {} -> {}.",
362                    self.id, props.id
363                );
364            }
365            self.id = props.id;
366            changed
367        }
368
369        fn update(&mut self, _: Self::Message) -> ShouldRender {
370            unimplemented!();
371        }
372
373        fn view(&self) -> Html {
374            html! { <p>{ self.id }</p> }
375        }
376    }
377
378    #[derive(Clone, Properties)]
379    pub struct ListProps {
380        pub children: Children,
381    }
382
383    pub struct List(ListProps);
384
385    impl Component for List {
386        type Message = ();
387        type Properties = ListProps;
388
389        fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
390            Self(props)
391        }
392
393        fn update(&mut self, _: Self::Message) -> ShouldRender {
394            unimplemented!();
395        }
396
397        fn change(&mut self, mut props: Self::Properties) -> ShouldRender {
398            std::mem::swap(&mut self.0, &mut props);
399            self.0.children != props.children
400        }
401
402        fn view(&self) -> Html {
403            html! { <>{ for self.0.children.iter() }</> }
404        }
405    }
406
407    #[test]
408    fn diff() {
409        let mut layouts = vec![];
410
411        let vref_node: Node = crate::utils::document().create_element("i").unwrap().into();
412        layouts.push(TestLayout {
413            name: "All VNode types as children",
414            node: html! {
415                <>
416                    {"a"}
417                    <span key="vtag"></span>
418                    {"c"}
419                    {"d"}
420                    <Comp id=0 key="vchild" />
421                    <key="vlist">
422                        {"foo"}
423                        {"bar"}
424                    </>
425                    {VNode::VRef(vref_node)}
426                </>
427            },
428            expected: "a<span></span>cd<p>0</p>foobar<i></i>",
429        });
430
431        layouts.extend(vec![
432            TestLayout {
433                name: "Inserting into VList first child - before",
434                node: html! {
435                    <>
436                        <key="VList">
437                            <i key="i"></i>
438                        </>
439                        <p key="p"></p>
440                    </>
441                },
442                expected: "<i></i><p></p>",
443            },
444            TestLayout {
445                name: "Inserting into VList first child - after",
446                node: html! {
447                    <>
448                        <key="VList">
449                            <i key="i"></i>
450                            <e key="e"></e>
451                        </>
452                        <p key="p"></p>
453                    </>
454                },
455                expected: "<i></i><e></e><p></p>",
456            },
457        ]);
458
459        layouts.extend(vec![
460            TestLayout {
461                name: "No matches - before",
462                node: html! {
463                    <>
464                        <i key="i"></i>
465                        <e key="e"></e>
466                    </>
467                },
468                expected: "<i></i><e></e>",
469            },
470            TestLayout {
471                name: "No matches - after",
472                node: html! {
473                    <>
474                        <a key="a"></a>
475                        <p key="p"></p>
476                    </>
477                },
478                expected: "<a></a><p></p>",
479            },
480        ]);
481
482        layouts.extend(vec![
483            TestLayout {
484                name: "Append - before",
485                node: html! {
486                    <>
487                        <i key="i"></i>
488                        <e key="e"></e>
489                    </>
490                },
491                expected: "<i></i><e></e>",
492            },
493            TestLayout {
494                name: "Append - after",
495                node: html! {
496                    <>
497                        <i key="i"></i>
498                        <e key="e"></e>
499                        <p key="p"></p>
500                    </>
501                },
502                expected: "<i></i><e></e><p></p>",
503            },
504        ]);
505
506        layouts.extend(vec![
507            TestLayout {
508                name: "Prepend - before",
509                node: html! {
510                    <>
511                        <i key="i"></i>
512                        <e key="e"></e>
513                    </>
514                },
515                expected: "<i></i><e></e>",
516            },
517            TestLayout {
518                name: "Prepend - after",
519                node: html! {
520                    <>
521                        <p key="p"></p>
522                        <i key="i"></i>
523                        <e key="e"></e>
524                    </>
525                },
526                expected: "<p></p><i></i><e></e>",
527            },
528        ]);
529
530        layouts.extend(vec![
531            TestLayout {
532                name: "Delete first - before",
533                node: html! {
534                    <>
535                        <i key="i"></i>
536                        <e key="e"></e>
537                        <p key="p"></p>
538                    </>
539                },
540                expected: "<i></i><e></e><p></p>",
541            },
542            TestLayout {
543                name: "Delete first - after",
544                node: html! {
545                    <>
546                        <e key="e"></e>
547                        <p key="p"></p>
548                    </>
549                },
550                expected: "<e></e><p></p>",
551            },
552        ]);
553
554        layouts.extend(vec![
555            TestLayout {
556                name: "Delete last - before",
557                node: html! {
558                    <>
559                        <i key="i"></i>
560                        <e key="e"></e>
561                        <p key="p"></p>
562                    </>
563                },
564                expected: "<i></i><e></e><p></p>",
565            },
566            TestLayout {
567                name: "Delete last - after",
568                node: html! {
569                    <>
570                        <i key="i"></i>
571                        <e key="e"></e>
572                    </>
573                },
574                expected: "<i></i><e></e>",
575            },
576        ]);
577
578        layouts.extend(vec![
579            TestLayout {
580                name: "Delete last and change node type - before",
581                node: html! {
582                    <>
583                        <i key="i"></i>
584                        <e key="e"></e>
585                        <p key="p"></p>
586                    </>
587                },
588                expected: "<i></i><e></e><p></p>",
589            },
590            TestLayout {
591                name: "Delete last - after",
592                node: html! {
593                    <>
594                        <List key="i"><i/></List>
595                        <List key="e"><e/></List>
596                        <List key="a"><a/></List>
597                    </>
598                },
599                expected: "<i></i><e></e><a></a>",
600            },
601        ]);
602
603        layouts.extend(vec![
604            TestLayout {
605                name: "Delete middle - before",
606                node: html! {
607                    <>
608                        <i key="i"></i>
609                        <e key="e"></e>
610                        <p key="p"></p>
611                        <a key="a"></a>
612                    </>
613                },
614                expected: "<i></i><e></e><p></p><a></a>",
615            },
616            TestLayout {
617                name: "Delete middle - after",
618                node: html! {
619                    <>
620                        <i key="i"></i>
621                        <e key="e2"></e>
622                        <p key="p2"></p>
623                        <a key="a"></a>
624                    </>
625                },
626                expected: "<i></i><e></e><p></p><a></a>",
627            },
628        ]);
629
630        layouts.extend(vec![
631            TestLayout {
632                name: "Delete middle and change node type - before",
633                node: html! {
634                    <>
635                        <i key="i"></i>
636                        <e key="e"></e>
637                        <p key="p"></p>
638                        <a key="a"></a>
639                    </>
640                },
641                expected: "<i></i><e></e><p></p><a></a>",
642            },
643            TestLayout {
644                name: "Delete middle and change node type- after",
645                node: html! {
646                    <>
647                        <List key="i2"><i/></List>
648                        <e key="e"></e>
649                        <List key="p"><p/></List>
650                        <List key="a2"><a/></List>
651                    </>
652                },
653                expected: "<i></i><e></e><p></p><a></a>",
654            },
655        ]);
656
657        layouts.extend(vec![
658            TestLayout {
659                name: "Reverse - before",
660                node: html! {
661                    <>
662                        <i key="i"></i>
663                        <e key="e"></e>
664                        <p key="p"></p>
665                        <u key="u"></u>
666                    </>
667                },
668                expected: "<i></i><e></e><p></p><u></u>",
669            },
670            TestLayout {
671                name: "Reverse - after",
672                node: html! {
673                    <>
674                        <u key="u"></u>
675                        <p key="p"></p>
676                        <e key="e"></e>
677                        <i key="i"></i>
678                    </>
679                },
680                expected: "<u></u><p></p><e></e><i></i>",
681            },
682        ]);
683
684        layouts.extend(vec![
685            TestLayout {
686                name: "Reverse and change node type - before",
687                node: html! {
688                    <>
689                        <i key="i"></i>
690                        <key="i1"></>
691                        <key="i2"></>
692                        <key="i3"></>
693                        <e key="e"></e>
694                        <key="yo">
695                            <p key="p"></p>
696                        </>
697                        <u key="u"></u>
698                    </>
699                },
700                expected: "<i></i><e></e><p></p><u></u>",
701            },
702            TestLayout {
703                name: "Reverse and change node type - after",
704                node: html! {
705                    <>
706                        <List key="u"><u/></List>
707                        <List key="p"><p/></List>
708                        <List key="e"><e/></List>
709                        <List key="i"><i/></List>
710                    </>
711                },
712                expected: "<u></u><p></p><e></e><i></i>",
713            },
714        ]);
715
716        layouts.extend(vec![
717            TestLayout {
718                name: "Swap 1&2 - before",
719                node: html! {
720                    <>
721                        <i key="1"></i>
722                        <e key="2"></e>
723                        <p key="3"></p>
724                        <a key="4"></a>
725                        <u key="5"></u>
726                    </>
727                },
728                expected: "<i></i><e></e><p></p><a></a><u></u>",
729            },
730            TestLayout {
731                name: "Swap 1&2 - after",
732                node: html! {
733                    <>
734                        <e key="2"></e>
735                        <i key="1"></i>
736                        <p key="3"></p>
737                        <a key="4"></a>
738                        <u key="5"></u>
739                    </>
740                },
741                expected: "<e></e><i></i><p></p><a></a><u></u>",
742            },
743        ]);
744
745        layouts.extend(vec![
746            TestLayout {
747                name: "Swap 1&2 and change node type - before",
748                node: html! {
749                    <>
750                        <i key="1"></i>
751                        <e key="2"></e>
752                        <p key="3"></p>
753                        <a key="4"></a>
754                        <u key="5"></u>
755                    </>
756                },
757                expected: "<i></i><e></e><p></p><a></a><u></u>",
758            },
759            TestLayout {
760                name: "Swap 1&2 and change node type - after",
761                node: html! {
762                    <>
763                        <List key="2"><e/></List>
764                        <List key="1"><i/></List>
765                        <List key="3"><p/></List>
766                        <List key="4"><a/></List>
767                        <List key="5"><u/></List>
768                    </>
769                },
770                expected: "<e></e><i></i><p></p><a></a><u></u>",
771            },
772        ]);
773
774        layouts.extend(vec![
775            TestLayout {
776                name: "test - before",
777                node: html! {
778                    <>
779                        <key="1">
780                            <e key="e"></e>
781                            <p key="p"></p>
782                            <a key="a"></a>
783                            <u key="u"></u>
784                        </>
785                        <key="2">
786                            <e key="e"></e>
787                            <p key="p"></p>
788                            <a key="a"></a>
789                            <u key="u"></u>
790                        </>
791                    </>
792                },
793                expected: "<e></e><p></p><a></a><u></u><e></e><p></p><a></a><u></u>",
794            },
795            TestLayout {
796                name: "Swap 4&5 - after",
797                node: html! {
798                    <>
799                        <e key="1"></e>
800                        <key="2">
801                            <p key="p"></p>
802                            <i key="i"></i>
803                        </>
804                    </>
805                },
806                expected: "<e></e><p></p><i></i>",
807            },
808        ]);
809
810        layouts.extend(vec![
811            TestLayout {
812                name: "Swap 4&5 - before",
813                node: html! {
814                    <>
815                        <i key="1"></i>
816                        <e key="2"></e>
817                        <p key="3"></p>
818                        <a key="4"></a>
819                        <u key="5"></u>
820                    </>
821                },
822                expected: "<i></i><e></e><p></p><a></a><u></u>",
823            },
824            TestLayout {
825                name: "Swap 4&5 - after",
826                node: html! {
827                    <>
828                        <i key="1"></i>
829                        <e key="2"></e>
830                        <p key="3"></p>
831                        <u key="5"></u>
832                        <a key="4"></a>
833                    </>
834                },
835                expected: "<i></i><e></e><p></p><u></u><a></a>",
836            },
837        ]);
838
839        layouts.extend(vec![
840            TestLayout {
841                name: "Swap 1&5 - before",
842                node: html! {
843                    <>
844                        <i key="1"></i>
845                        <e key="2"></e>
846                        <p key="3"></p>
847                        <a key="4"></a>
848                        <u key="5"></u>
849                    </>
850                },
851                expected: "<i></i><e></e><p></p><a></a><u></u>",
852            },
853            TestLayout {
854                name: "Swap 1&5 - after",
855                node: html! {
856                    <>
857                        <u key="5"></u>
858                        <e key="2"></e>
859                        <p key="3"></p>
860                        <a key="4"></a>
861                        <i key="1"></i>
862                    </>
863                },
864                expected: "<u></u><e></e><p></p><a></a><i></i>",
865            },
866        ]);
867
868        layouts.extend(vec![
869            TestLayout {
870                name: "Move 2 after 4 - before",
871                node: html! {
872                    <>
873                        <i key="1"></i>
874                        <e key="2"></e>
875                        <p key="3"></p>
876                        <a key="4"></a>
877                        <u key="5"></u>
878                    </>
879                },
880                expected: "<i></i><e></e><p></p><a></a><u></u>",
881            },
882            TestLayout {
883                name: "Move 2 after 4 - after",
884                node: html! {
885                    <>
886                        <i key="1"></i>
887                        <p key="3"></p>
888                        <a key="4"></a>
889                        <e key="2"></e>
890                        <u key="5"></u>
891                    </>
892                },
893                expected: "<i></i><p></p><a></a><e></e><u></u>",
894            },
895        ]);
896
897        layouts.extend(vec![
898            TestLayout {
899                name: "Swap lists - before",
900                node: html! {
901                    <>
902                        <key="1">
903                            <i></i>
904                            <e></e>
905                        </>
906                        <key="2">
907                            <a></a>
908                            <u></u>
909                        </>
910                    </>
911                },
912                expected: "<i></i><e></e><a></a><u></u>",
913            },
914            TestLayout {
915                name: "Swap lists - after",
916                node: html! {
917                    <>
918                        <key="2">
919                            <a></a>
920                            <u></u>
921                        </>
922                        <key="1">
923                            <i></i>
924                            <e></e>
925                        </>
926                    </>
927                },
928                expected: "<a></a><u></u><i></i><e></e>",
929            },
930        ]);
931
932        layouts.extend(vec![
933            TestLayout {
934                name: "Swap lists with in-between - before",
935                node: html! {
936                    <>
937                        <key="1">
938                            <i></i>
939                            <e></e>
940                        </>
941                        <p key="between"></p>
942                        <key="2">
943                            <a></a>
944                            <u></u>
945                        </>
946                    </>
947                },
948                expected: "<i></i><e></e><p></p><a></a><u></u>",
949            },
950            TestLayout {
951                name: "Swap lists with in-between - after",
952                node: html! {
953                    <>
954                        <key="2">
955                            <a></a>
956                            <u></u>
957                        </>
958                        <p key="between"></p>
959                        <key="1">
960                            <i></i>
961                            <e></e>
962                        </>
963                    </>
964                },
965                expected: "<a></a><u></u><p></p><i></i><e></e>",
966            },
967        ]);
968
969        layouts.extend(vec![
970            TestLayout {
971                name: "Insert VComp front - before",
972                node: html! {
973                    <>
974                        <u key=1></u>
975                        <a key=2></a>
976                    </>
977                },
978                expected: "<u></u><a></a>",
979            },
980            TestLayout {
981                name: "Insert VComp front - after",
982                node: html! {
983                    <>
984                        <Comp id=0 key="comp"/>
985                        <u key=1></u>
986                        <a key=2></a>
987                    </>
988                },
989                expected: "<p>0</p><u></u><a></a>",
990            },
991        ]);
992
993        layouts.extend(vec![
994            TestLayout {
995                name: "Insert VComp middle - before",
996                node: html! {
997                    <>
998                        <u key=1></u>
999                        <a key=2></a>
1000                    </>
1001                },
1002                expected: "<u></u><a></a>",
1003            },
1004            TestLayout {
1005                name: "Insert VComp middle - after",
1006                node: html! {
1007                    <>
1008                        <u key=1></u>
1009                        <Comp id=0 key="comp"/>
1010                        <a key=2></a>
1011                    </>
1012                },
1013                expected: "<u></u><p>0</p><a></a>",
1014            },
1015        ]);
1016
1017        layouts.extend(vec![
1018            TestLayout {
1019                name: "Insert VComp back - before",
1020                node: html! {
1021                    <>
1022                        <u key=1></u>
1023                        <a key=2></a>
1024                    </>
1025                },
1026                expected: "<u></u><a></a>",
1027            },
1028            TestLayout {
1029                name: "Insert VComp back - after",
1030                node: html! {
1031                    <>
1032                        <u key=1></u>
1033                        <a key=2></a>
1034                        <Comp id=0 key="comp"/>
1035                    </>
1036                },
1037                expected: "<u></u><a></a><p>0</p>",
1038            },
1039        ]);
1040
1041        layouts.extend(vec![
1042            TestLayout {
1043                name: "Reverse VComp children - before",
1044                node: html! {
1045                    <>
1046                        <Comp id=1 key="comp-1"/>
1047                        <Comp id=2 key="comp-2"/>
1048                        <Comp id=3 key="comp-3"/>
1049                    </>
1050                },
1051                expected: "<p>1</p><p>2</p><p>3</p>",
1052            },
1053            TestLayout {
1054                name: "Reverse VComp children - after",
1055                node: html! {
1056                    <>
1057                        <Comp id=3 key="comp-3"/>
1058                        <Comp id=2 key="comp-2"/>
1059                        <Comp id=1 key="comp-1"/>
1060                    </>
1061                },
1062                expected: "<p>3</p><p>2</p><p>1</p>",
1063            },
1064        ]);
1065
1066        layouts.extend(vec![
1067            TestLayout {
1068                name: "Reverse VComp children with children - before",
1069                node: html! {
1070                    <>
1071                        <List key="comp-1"><p>{"11"}</p><p>{"12"}</p></List>
1072                        <List key="comp-2"><p>{"21"}</p><p>{"22"}</p></List>
1073                        <List key="comp-3"><p>{"31"}</p><p>{"32"}</p></List>
1074                    </>
1075                },
1076                expected: "<p>11</p><p>12</p><p>21</p><p>22</p><p>31</p><p>32</p>",
1077            },
1078            TestLayout {
1079                name: "Reverse VComp children with children - after",
1080                node: html! {
1081                    <>
1082                        <List key="comp-3"><p>{"31"}</p><p>{"32"}</p></List>
1083                        <List key="comp-2"><p>{"21"}</p><p>{"22"}</p></List>
1084                        <List key="comp-1"><p>{"11"}</p><p>{"12"}</p></List>
1085                    </>
1086                },
1087                expected: "<p>31</p><p>32</p><p>21</p><p>22</p><p>11</p><p>12</p>",
1088            },
1089        ]);
1090
1091        layouts.extend(vec![
1092            TestLayout {
1093                name: "Complex component update - before",
1094                node: html! {
1095                    <List>
1096                        <Comp id=1 key="comp-1"/>
1097                        <Comp id=2 key="comp-2"/>
1098                    </List>
1099                },
1100                expected: "<p>1</p><p>2</p>",
1101            },
1102            TestLayout {
1103                name: "Complex component update - after",
1104                node: html! {
1105                    <List>
1106                        <List key="comp-1">
1107                            <Comp id=1 />
1108                        </List>
1109                        <List key="comp-2">
1110                            <p>{"2"}</p>
1111                        </List>
1112                    </List>
1113                },
1114                expected: "<p>1</p><p>2</p>",
1115            },
1116        ]);
1117
1118        layouts.extend(vec![
1119            TestLayout {
1120                name: "Reorder VComp children with children - before",
1121                node: html! {
1122                    <>
1123                        <List key="comp-1"><p>{"1"}</p></List>
1124                        <List key="comp-3"><p>{"3"}</p></List>
1125                        <List key="comp-5"><p>{"5"}</p></List>
1126                        <List key="comp-2"><p>{"2"}</p></List>
1127                        <List key="comp-4"><p>{"4"}</p></List>
1128                        <List key="comp-6"><p>{"6"}</p></List>
1129                    </>
1130                },
1131                expected: "<p>1</p><p>3</p><p>5</p><p>2</p><p>4</p><p>6</p>",
1132            },
1133            TestLayout {
1134                name: "Reorder VComp children with children - after",
1135                node: html! {
1136                    <>
1137                        <Comp id=6 key="comp-6"/>
1138                        <Comp id=5 key="comp-5"/>
1139                        <Comp id=4 key="comp-4"/>
1140                        <Comp id=3 key="comp-3"/>
1141                        <Comp id=2 key="comp-2"/>
1142                        <Comp id=1 key="comp-1"/>
1143                    </>
1144                },
1145                expected: "<p>6</p><p>5</p><p>4</p><p>3</p><p>2</p><p>1</p>",
1146            },
1147        ]);
1148
1149        layouts.extend(vec![
1150            TestLayout {
1151                name: "Replace and reorder components - before",
1152                node: html! {
1153                    <List>
1154                        <List key="comp-1"><p>{"1"}</p></List>
1155                        <List key="comp-2"><p>{"2"}</p></List>
1156                        <List key="comp-3"><p>{"3"}</p></List>
1157                    </List>
1158                },
1159                expected: "<p>1</p><p>2</p><p>3</p>",
1160            },
1161            TestLayout {
1162                name: "Replace and reorder components - after",
1163                node: html! {
1164                    <List>
1165                        <Comp id=3 key="comp-3" />
1166                        <Comp id=2 key="comp-2" />
1167                        <Comp id=1 key="comp-1" />
1168                    </List>
1169                },
1170                expected: "<p>3</p><p>2</p><p>1</p>",
1171            },
1172        ]);
1173
1174        diff_layouts(layouts);
1175    }
1176}