1313from idom .core .component import component
1414from idom .core .dispatcher import render_json_patch
1515from idom .core .hooks import use_effect , use_state
16- from idom .core .layout import LayoutEvent
16+ from idom .core .layout import Layout , LayoutEvent
1717from idom .testing import (
1818 HookCatcher ,
1919 StaticEventHandler ,
2020 assert_idom_logged ,
2121 capture_idom_logs ,
2222)
23+ from idom .utils import Ref
2324from tests .assert_utils import assert_same_items
2425
2526
@@ -96,6 +97,15 @@ def SimpleComponent():
9697 ]
9798
9899
100+ async def test_component_can_return_none ():
101+ @idom .component
102+ def SomeComponent ():
103+ return None
104+
105+ with idom .Layout (SomeComponent ()) as layout :
106+ assert (await layout .render ()).new == {"tagName" : "" }
107+
108+
99109async def test_nested_component_layout ():
100110 parent_set_state = idom .Ref (None )
101111 child_set_state = idom .Ref (None )
@@ -716,14 +726,18 @@ def HasNestedEventHandler():
716726
717727async def test_duplicate_sibling_keys_causes_error (caplog ):
718728 hook = HookCatcher ()
729+ should_error = True
719730
720731 @idom .component
721732 @hook .capture
722733 def ComponentReturnsDuplicateKeys ():
723- return idom .html .div (
724- idom .html .div (key = "duplicate" ),
725- idom .html .div (key = "duplicate" ),
726- )
734+ if should_error :
735+ return idom .html .div (
736+ idom .html .div (key = "duplicate" ),
737+ idom .html .div (key = "duplicate" ),
738+ )
739+ else :
740+ return idom .html .div ()
727741
728742 with idom .Layout (ComponentReturnsDuplicateKeys ()) as layout :
729743 with assert_idom_logged (
@@ -735,6 +749,11 @@ def ComponentReturnsDuplicateKeys():
735749
736750 hook .latest .schedule_render ()
737751
752+ should_error = False
753+ await layout .render ()
754+
755+ should_error = True
756+ hook .latest .schedule_render ()
738757 with assert_idom_logged (
739758 error_type = ValueError ,
740759 match_error = r"Duplicate keys \['duplicate'\] at '/children/0'" ,
@@ -838,9 +857,9 @@ def use_toggle():
838857 def Root ():
839858 toggle , set_toggle .current = use_toggle ()
840859 if toggle :
841- return idom . html . div ( SomeComponent ("x" ) )
860+ return SomeComponent ("x" )
842861 else :
843- return idom .html .div (idom . html . div ( SomeComponent ("y" ) ))
862+ return idom .html .div (SomeComponent ("y" ))
844863
845864 @idom .component
846865 def SomeComponent (name ):
@@ -1063,3 +1082,82 @@ async def record_if_state_is_reset():
10631082 await layout .render ()
10641083 assert effect_calls_without_state == ["some-key" , "key-0" ]
10651084 did_call_effect .clear ()
1085+
1086+
1087+ async def test_changing_key_of_component_resets_state ():
1088+ set_key = Ref ()
1089+ did_init_state = Ref (0 )
1090+ hook = HookCatcher ()
1091+
1092+ @component
1093+ @hook .capture
1094+ def Root ():
1095+ key , set_key .current = use_state ("key-1" )
1096+ return Child (key = key )
1097+
1098+ @component
1099+ def Child ():
1100+ use_state (lambda : did_init_state .set_current (did_init_state .current + 1 ))
1101+
1102+ with Layout (Root ()) as layout :
1103+ await layout .render ()
1104+ assert did_init_state .current == 1
1105+
1106+ set_key .current ("key-2" )
1107+ await layout .render ()
1108+ assert did_init_state .current == 2
1109+
1110+ hook .latest .schedule_render ()
1111+ await layout .render ()
1112+ assert did_init_state .current == 2
1113+
1114+
1115+ async def test_changing_event_handlers_in_the_next_render ():
1116+ set_event_name = Ref ()
1117+ event_handler = StaticEventHandler ()
1118+ did_trigger = Ref (False )
1119+
1120+ @component
1121+ def Root ():
1122+ event_name , set_event_name .current = use_state ("first" )
1123+ return html .button (
1124+ {event_name : event_handler .use (lambda : did_trigger .set_current (True ))}
1125+ )
1126+
1127+ with Layout (Root ()) as layout :
1128+ await layout .render ()
1129+ await layout .deliver (LayoutEvent (event_handler .target , []))
1130+ assert did_trigger .current
1131+ did_trigger .current = False
1132+
1133+ set_event_name .current ("second" )
1134+ await layout .render ()
1135+ await layout .deliver (LayoutEvent (event_handler .target , []))
1136+ assert did_trigger .current
1137+ did_trigger .current = False
1138+
1139+
1140+ async def test_change_element_to_string_causes_unmount ():
1141+ set_toggle = Ref ()
1142+ did_unmount = Ref (False )
1143+
1144+ @component
1145+ def Root ():
1146+ toggle , set_toggle .current = use_toggle (True )
1147+ if toggle :
1148+ return html .div (Child ())
1149+ else :
1150+ return html .div ("some-string" )
1151+
1152+ @component
1153+ def Child ():
1154+ use_effect (lambda : lambda : did_unmount .set_current (True ))
1155+
1156+ with Layout (Root ()) as layout :
1157+ await layout .render ()
1158+
1159+ set_toggle .current ()
1160+
1161+ await layout .render ()
1162+
1163+ assert did_unmount .current
0 commit comments