diff --git a/idom/core/layout.py b/idom/core/layout.py index e8e2d956b..fc4174fd6 100644 --- a/idom/core/layout.py +++ b/idom/core/layout.py @@ -6,6 +6,7 @@ Tuple, Mapping, NamedTuple, + Optional, Any, Set, Iterator, @@ -154,29 +155,41 @@ def _render_element(self, element_state: ElementState) -> Dict[str, Any]: return element_state.model def _render_model( - self, element_state: ElementState, model: Mapping[str, Any] + self, + element_state: ElementState, + model: Mapping[str, Any], + path: Optional[str] = None, ) -> Dict[str, Any]: + if path is None: + path = element_state.path + serialized_model: Dict[str, Any] = {} event_handlers = self._render_model_event_targets(element_state, model) if event_handlers: serialized_model["eventHandlers"] = event_handlers if "children" in model: serialized_model["children"] = self._render_model_children( - element_state, model["children"] + element_state, model["children"], path ) return {**model, **serialized_model} def _render_model_children( - self, element_state: ElementState, children: Union[List[Any], Tuple[Any, ...]] + self, + element_state: ElementState, + children: Union[List[Any], Tuple[Any, ...]], + path: str, ) -> List[Any]: resolved_children: List[Any] = [] for index, child in enumerate( children if isinstance(children, (list, tuple)) else [children] ): if isinstance(child, dict): - resolved_children.append(self._render_model(element_state, child)) + child_path = f"{path}/children/{index}" + resolved_children.append( + self._render_model(element_state, child, child_path) + ) elif isinstance(child, AbstractElement): - child_path = f"{element_state.path}/children/{index}" + child_path = f"{path}/children/{index}" child_state = self._create_element_state(child, child_path, save=True) resolved_children.append(self._render_element(child_state)) element_state.child_elements_ids.append(id(child)) diff --git a/tests/test_core/test_layout.py b/tests/test_core/test_layout.py index 8f782ccda..8b2eb591d 100644 --- a/tests/test_core/test_layout.py +++ b/tests/test_core/test_layout.py @@ -248,3 +248,24 @@ def AnElement(): pass # the render should still be rendering since we only update once assert run_count.current == 2 + + +async def test_update_path_to_element_that_is_not_direct_child_is_correct(): + hook = HookCatcher() + + @idom.element + def Parent(): + return idom.html.div(idom.html.div(Child())) + + @idom.element + @hook.capture + def Child(): + return idom.html.div() + + async with idom.Layout(Parent()) as layout: + await layout.render() + + hook.current.schedule_render() + + update = await layout.render() + assert update.path == "/children/0/children/0"