From 9fd53436d79b45b5035acfc2c8bde3f220b5f79b Mon Sep 17 00:00:00 2001 From: VojtechPetru <54205005+VojtechPetru@users.noreply.github.com> Date: Thu, 18 May 2023 15:46:46 +0200 Subject: [PATCH] Fix - fill inside loop (#273) * simple iteration fill test case * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * a couple more tests * distinguish between filled & default value --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: adriaan --- .../template_with_slot_in_a_loop.html | 7 + tests/test_templatetags.py | 257 +++++++++++++++++- 2 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 tests/templates/template_with_slot_in_a_loop.html diff --git a/tests/templates/template_with_slot_in_a_loop.html b/tests/templates/template_with_slot_in_a_loop.html new file mode 100644 index 00000000..59ea8888 --- /dev/null +++ b/tests/templates/template_with_slot_in_a_loop.html @@ -0,0 +1,7 @@ +{% load component_tags %} + +{% for object in objects %} + {% slot 'slot_inner' %} + {{ object }} default + {% endslot %} +{% endfor %} diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py index 4b03648d..ce4031e1 100644 --- a/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -1,6 +1,6 @@ import re from textwrap import dedent -from typing import Callable, Optional +from typing import Callable, Iterable, Optional from django.template import Context, Template, TemplateSyntaxError @@ -1262,3 +1262,258 @@ class RegressionTests(SimpleTestCase): """ self.assertHTMLEqual(rendered, expected) + + +class IterationFillTest(SimpleTestCase): + """Tests a behaviour of {% fill .. %} tag which is inside a template {% for .. %} loop.""" + + class ComponentSimpleSlotInALoop(django_components.component.Component): + template_name = "template_with_slot_in_a_loop.html" + + def get_context_data(self, objects: Iterable) -> dict: + return { + "objects": objects, + } + + def setUp(self): + django_components.component.registry.clear() + + def test_inner_slot_iteration_basic(self): + component.registry.register( + "slot_in_a_loop", self.ComponentSimpleSlotInALoop + ) + + template = Template( + """ + {% load component_tags %} + {% component_block "slot_in_a_loop" objects=objects %} + {% fill "slot_inner" %} + {{ object }} + {% endfill %} + {% endcomponent_block %} + """ + ) + objects = ["OBJECT1", "OBJECT2"] + rendered = template.render(Context({"objects": objects})) + + self.assertHTMLEqual( + rendered, + """ + OBJECT1 + OBJECT2 + """, + ) + + def test_inner_slot_iteration_with_variable_from_outer_scope(self): + component.registry.register( + "slot_in_a_loop", self.ComponentSimpleSlotInALoop + ) + + template = Template( + """ + {% load component_tags %} + {% component_block "slot_in_a_loop" objects=objects %} + {% fill "slot_inner" %} + {{ outer_scope_variable }} + {{ object }} + {% endfill %} + {% endcomponent_block %} + """ + ) + objects = ["OBJECT1", "OBJECT2"] + rendered = template.render( + Context( + { + "objects": objects, + "outer_scope_variable": "OUTER_SCOPE_VARIABLE", + } + ) + ) + + self.assertHTMLEqual( + rendered, + """ + OUTER_SCOPE_VARIABLE + OBJECT1 + OUTER_SCOPE_VARIABLE + OBJECT2 + """, + ) + + def test_inner_slot_iteration_nested(self): + component.registry.register( + "slot_in_a_loop", self.ComponentSimpleSlotInALoop + ) + + objects = [ + {"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]}, + {"inner": ["OBJECT1_ITER2", "OBJECT2_ITER2"]}, + ] + + template = Template( + """ + {% load component_tags %} + {% component_block "slot_in_a_loop" objects=objects %} + {% fill "slot_inner" %} + {% component_block "slot_in_a_loop" objects=object.inner %} + {% fill "slot_inner" %} + {{ object }} + {% endfill %} + {% endcomponent_block %} + {% endfill %} + {% endcomponent_block %} + """ + ) + rendered = template.render(Context({"objects": objects})) + + self.assertHTMLEqual( + rendered, + """ + OBJECT1_ITER1 + OBJECT2_ITER1 + OBJECT1_ITER2 + OBJECT2_ITER2 + """, + ) + + def test_inner_slot_iteration_nested_with_outer_scope_variable(self): + component.registry.register( + "slot_in_a_loop", self.ComponentSimpleSlotInALoop + ) + + objects = [ + {"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]}, + {"inner": ["OBJECT1_ITER2", "OBJECT2_ITER2"]}, + ] + + template = Template( + """ + {% load component_tags %} + {% component_block "slot_in_a_loop" objects=objects %} + {% fill "slot_inner" %} + {{ outer_scope_variable_1 }} + {% component_block "slot_in_a_loop" objects=object.inner %} + {% fill "slot_inner" %} + {{ outer_scope_variable_2 }} + {{ object }} + {% endfill %} + {% endcomponent_block %} + {% endfill %} + {% endcomponent_block %} + """ + ) + rendered = template.render( + Context( + { + "objects": objects, + "outer_scope_variable_1": "OUTER_SCOPE_VARIABLE1", + "outer_scope_variable_2": "OUTER_SCOPE_VARIABLE2", + } + ) + ) + + self.assertHTMLEqual( + rendered, + """ + OUTER_SCOPE_VARIABLE1 + OUTER_SCOPE_VARIABLE2 + OBJECT1_ITER1 + OUTER_SCOPE_VARIABLE2 + OBJECT2_ITER1 + OUTER_SCOPE_VARIABLE1 + OUTER_SCOPE_VARIABLE2 + OBJECT1_ITER2 + OUTER_SCOPE_VARIABLE2 + OBJECT2_ITER2 + """, + ) + + def test_inner_slot_iteration_nested_with_slot_default(self): + component.registry.register( + "slot_in_a_loop", self.ComponentSimpleSlotInALoop + ) + + objects = [ + {"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]}, + {"inner": ["OBJECT1_ITER2", "OBJECT2_ITER2"]}, + ] + + template = Template( + """ + {% load component_tags %} + {% component_block "slot_in_a_loop" objects=objects %} + {% fill "slot_inner" %} + {% component_block "slot_in_a_loop" objects=object.inner %} + {% fill "slot_inner" as "super_slot_inner" %} + {{ super_slot_inner.default }} + {% endfill %} + {% endcomponent_block %} + {% endfill %} + {% endcomponent_block %} + """ + ) + rendered = template.render(Context({"objects": objects})) + + self.assertHTMLEqual( + rendered, + """ + OBJECT1_ITER1 default + OBJECT2_ITER1 default + OBJECT1_ITER2 default + OBJECT2_ITER2 default + """, + ) + + def test_inner_slot_iteration_nested_with_slot_default_and_outer_scope_variable( + self, + ): + component.registry.register( + "slot_in_a_loop", self.ComponentSimpleSlotInALoop + ) + + objects = [ + {"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]}, + {"inner": ["OBJECT1_ITER2", "OBJECT2_ITER2"]}, + ] + + template = Template( + """ + {% load component_tags %} + {% component_block "slot_in_a_loop" objects=objects %} + {% fill "slot_inner" %} + {{ outer_scope_variable_1 }} + {% component_block "slot_in_a_loop" objects=object.inner %} + {% fill "slot_inner" as "super_slot_inner" %} + {{ outer_scope_variable_2 }} + {{ super_slot_inner.default }} + {% endfill %} + {% endcomponent_block %} + {% endfill %} + {% endcomponent_block %} + """ + ) + rendered = template.render( + Context( + { + "objects": objects, + "outer_scope_variable_1": "OUTER_SCOPE_VARIABLE1", + "outer_scope_variable_2": "OUTER_SCOPE_VARIABLE2", + } + ) + ) + + self.assertHTMLEqual( + rendered, + """ + OUTER_SCOPE_VARIABLE1 + OUTER_SCOPE_VARIABLE2 + OBJECT1_ITER1 default + OUTER_SCOPE_VARIABLE2 + OBJECT2_ITER1 default + OUTER_SCOPE_VARIABLE1 + OUTER_SCOPE_VARIABLE2 + OBJECT1_ITER2 default + OUTER_SCOPE_VARIABLE2 + OBJECT2_ITER2 default + """, + )