123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156open!CoreopenBonsai.Let_syntaxopenVirtual_domopenProc(* 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"=letmoduleM=structtypet={label:string;children:tInt.Map.t}[@@derivingfields]endinletrectreeinputpath=let%pattern_bind{M.label;children}=inputinlet%subchildren=Bonsai.assoc(moduleInt)children~f:(funindexchild->letpath=Bonsai.Value.map2indexpath~f:List.consinlet%subchild=Bonsai.lazy_(lazy(treechildpath))inreturn@@let%mapchild=childandindex=indexinVdom.Node.div[Vdom.Node.textf"%d"index;child])inlet%substate=Bonsai.state[%here](moduleInt)~default_model:0inreturn@@let%mapchildren=childrenandlabel=labelandpath=pathandstate,set_state=stateinletpath_text=String.concat~sep:"_"("path"::List.rev_mappath~f:Int.to_string)inVdom.Node.div~attr:(Vdom.Attr.idpath_text)(Vdom.Node.textf"%s %d"labelstate::Vdom.Node.button~attr:(Vdom.Attr.on_click(fun_->set_state(state+1)))[Vdom.Node.text"+1"]::Int.Map.datachildren)inletvar=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}]}inletvalue=Bonsai.Var.valuevarinlethandle=Handle.create(Result_spec.vdomFn.id)(treevalue(Bonsai.Value.return[0]))inHandle.showhandle;letbefore=[%expect.output]inHandle.click_onhandle~get_vdom:Fn.id~selector:"#path_0_0_99 button";Handle.showhandle;letafter=[%expect.output]inExpect_test_patdiff.print_patdiffbeforeafter;[%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. *)letold_input=Bonsai.Var.getvarinletold_result=Handle.resulthandleinBonsai.Var.setvar{M.label="hi";children=Int.Map.empty};Handle.showhandle;[%expect{|
<div id="path_0">
hi 0
<button onclick> +1 </button>
</div> |}];Handle.click_onhandle~get_vdom:(fun_->old_result)~selector:"#path_0_0_99 button";Bonsai.Var.setvarold_input;Handle.showhandle;[%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> |}];;