package bonsai
Source file vdom_based_tests.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154open! Core open Bonsai.Let_syntax open Virtual_dom open Proc (* This test basically just exists because it's really hard to dispatch events deep into a tree without an intermediate representation. Turns out that Vdom.Node.t is a pretty good intermediate representation. *) let%expect_test "recursive component" = let module M = struct type t = { label : string ; children : t Int.Map.t } [@@deriving fields] end in let rec tree input path = let%sub { M.label; children } = return input in let%sub children = Bonsai.assoc (module Int) children ~f:(fun index child -> let path = Bonsai.Value.map2 index path ~f:List.cons in let%sub child = Bonsai.lazy_ (lazy (tree child path)) in let%arr child = child and index = index in Vdom.Node.div [ Vdom.Node.textf "%d" index; child ]) in let%sub state = Bonsai.state (module Int) ~default_model:0 in let%arr children = children and label = label and path = path and state, set_state = state in let path_text = String.concat ~sep:"_" ("path" :: List.rev_map path ~f:Int.to_string) in Vdom.Node.div ~attrs:[ Vdom.Attr.id path_text ] (Vdom.Node.textf "%s %d" label state :: Vdom.Node.button ~attrs:[ Vdom.Attr.on_click (fun _ -> set_state (state + 1)) ] [ Vdom.Node.text "+1" ] :: Map.data (children : _ Int.Map.t)) in let var = Bonsai.Var.create { M.label = "hi" ; children = Int.Map.of_alist_exn [ ( 0 , { M.label = "hello" ; children = Int.Map.of_alist_exn [ 99, { M.label = "another"; children = Int.Map.empty } ] } ) ; 1, { M.label = "test"; children = Int.Map.empty } ; 2, { M.label = "foobar"; children = Int.Map.empty } ] } in let value = Bonsai.Var.value var in let handle = Handle.create (Result_spec.vdom Fn.id) (tree value (Bonsai.Value.return [ 0 ])) in Handle.show handle; let before = [%expect.output] in Handle.click_on handle ~get_vdom:Fn.id ~selector:"#path_0_0_99 button"; Handle.show handle; let after = [%expect.output] in Expect_test_patdiff.print_patdiff before after; [%expect {| -1,28 +1,28 <div id="path_0"> hi 0 <button onclick> +1 </button> <div> 0 <div id="path_0_0"> hello 0 <button onclick> +1 </button> <div> 99 <div id="path_0_0_99"> -| another 0 +| another 1 <button onclick> +1 </button> </div> </div> </div> </div> <div> 1 <div id="path_0_1"> test 0 <button onclick> +1 </button> </div> </div> <div> 2 <div id="path_0_2"> foobar 0 |}]; (* test sending an action to an inactive component *) (* capture the result so that we can schedule actions from it. *) let old_input = Bonsai.Var.get var in let old_result = Handle.result handle in Bonsai.Var.set var { M.label = "hi"; children = Int.Map.empty }; Handle.show handle; [%expect {| <div id="path_0"> hi 0 <button onclick> +1 </button> </div> |}]; Handle.click_on handle ~get_vdom:(fun _ -> old_result) ~selector:"#path_0_0_99 button"; Bonsai.Var.set var old_input; Handle.show handle; [%expect {| <div id="path_0"> hi 0 <button onclick> +1 </button> <div> 0 <div id="path_0_0"> hello 0 <button onclick> +1 </button> <div> 99 <div id="path_0_0_99"> another 2 <button onclick> +1 </button> </div> </div> </div> </div> <div> 1 <div id="path_0_1"> test 0 <button onclick> +1 </button> </div> </div> <div> 2 <div id="path_0_2"> foobar 0 <button onclick> +1 </button> </div> </div> </div> |}] ;;