diff --git a/src/django_components/slots.py b/src/django_components/slots.py
index 7cf02b1d..6d25263f 100644
--- a/src/django_components/slots.py
+++ b/src/django_components/slots.py
@@ -417,13 +417,14 @@ def _collect_slot_fills_from_component_template(
continue
slot_name = node.name
+
+ # If true then the template contains multiple slot of the same name.
+ # No action needed, since even tho there's mutliple slots, we will
+ # still apply only a single fill to all of them. And each slot handles
+ # their own fallback content.
if slot_name in slot_name2fill_content:
- raise TemplateSyntaxError(
- f"Slot name '{slot_name}' re-used within the same template. "
- f"Slot names must be unique."
- f"To fix, check template '{template.name}' "
- f"of component '{registered_name}'."
- )
+ continue
+
if node.is_required:
required_slot_names.add(node.name)
diff --git a/tests/templates/template_with_nonunique_slots_nested.html b/tests/templates/template_with_nonunique_slots_nested.html
new file mode 100644
index 00000000..ce53e83e
--- /dev/null
+++ b/tests/templates/template_with_nonunique_slots_nested.html
@@ -0,0 +1,16 @@
+{% load component_tags %}
+{% slot "header" %}START{% endslot %}
+
+ {% component "calendar" date="2020-06-06" %}
+ {% fill "header" %} {# fills and slots with same name relate to diff. things. #}
+ {% slot "header" %}NESTED{% endslot %}
+ {% endfill %}
+ {% fill "body" %}Here are your to-do items for today:{% endfill %}
+ {% endcomponent %}
+
+ {% for item in items %}
+ - {{ item }}
+ {% slot "header" %}LOOP {{ item }} {% endslot %}
+ {% endfor %}
+
+
diff --git a/tests/test_component.py b/tests/test_component.py
index c83c401b..03e5c99d 100644
--- a/tests/test_component.py
+++ b/tests/test_component.py
@@ -1,6 +1,6 @@
import sys
from pathlib import Path
-from typing import Any, Dict, Optional
+from typing import Any, Dict, List, Optional
from django.core.exceptions import ImproperlyConfigured
from django.template import Context, Template
@@ -38,11 +38,33 @@ def get_context_data(self, shadowing_variable=None, new_variable=None):
return context
+class DuplicateSlotComponent(component.Component):
+ template_name = "template_with_nonunique_slots.html"
+
+ def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]:
+ return {
+ "name": name,
+ }
+
+
+class DuplicateSlotNestedComponent(component.Component):
+ template_name = "template_with_nonunique_slots_nested.html"
+
+ def get_context_data(self, items: List) -> Dict[str, Any]:
+ return {
+ "items": items,
+ }
+
+class CalendarComponent(component.Component):
+ """Nested in ComponentWithNestedComponent"""
+
+ template_name = "slotted_component_nesting_template_pt1_calendar.html"
+
+
#########################
# TESTS
#########################
-
class ComponentTest(BaseTestCase):
@classmethod
def setUpClass(cls):
@@ -386,6 +408,137 @@ def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]:
)
+class DuplicateSlotTest(BaseTestCase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ component.registry.register(name="duplicate_slot", component=DuplicateSlotComponent)
+ component.registry.register(name="duplicate_slot_nested", component=DuplicateSlotNestedComponent)
+ component.registry.register(name="calendar", component=CalendarComponent)
+
+ def test_duplicate_slots(self):
+ self.template = Template(
+ """
+ {% load component_tags %}
+ {% component "duplicate_slot" %}
+ {% fill "header" %}
+ Name: {{ name }}
+ {% endfill %}
+ {% fill "footer" %}
+ Hello
+ {% endfill %}
+ {% endcomponent %}
+ """
+ )
+
+ rendered = self.template.render(Context({"name": "Jannete"}))
+ self.assertHTMLEqual(
+ rendered,
+ """
+
+ Name: Jannete
+
+ """,
+ )
+
+ def test_duplicate_slots_fallback(self):
+ self.template = Template(
+ """
+ {% load component_tags %}
+ {% component "duplicate_slot" %}
+ {% endcomponent %}
+ """
+ )
+ rendered = self.template.render(Context({}))
+
+ # NOTE: Slots should have different fallbacks even though they use the same name
+ self.assertHTMLEqual(
+ rendered,
+ """
+
+ Default main header
+
+ """,
+ )
+
+ def test_duplicate_slots_nested(self):
+ self.template = Template(
+ """
+ {% load component_tags %}
+ {% component "duplicate_slot_nested" items=items %}
+ {% fill "header" %}
+ OVERRIDDEN!
+ {% endfill %}
+ {% endcomponent %}
+ """
+ )
+ rendered = self.template.render(Context({"items": [1, 2, 3]}))
+
+ # NOTE: Slots should have different fallbacks even though they use the same name
+ self.assertHTMLEqual(
+ rendered,
+ """
+ OVERRIDDEN!
+
+
+
+ OVERRIDDEN!
+
+
+ Here are your to-do items for today:
+
+
+
+
+ - 1
+ OVERRIDDEN!
+ - 2
+ OVERRIDDEN!
+ - 3
+ OVERRIDDEN!
+
+
+ """,
+ )
+
+ def test_duplicate_slots_nested_fallback(self):
+ self.template = Template(
+ """
+ {% load component_tags %}
+ {% component "duplicate_slot_nested" items=items %}
+ {% endcomponent %}
+ """
+ )
+ rendered = self.template.render(Context({"items": [1, 2, 3]}))
+
+ # NOTE: Slots should have different fallbacks even though they use the same name
+ self.assertHTMLEqual(
+ rendered,
+ """
+ START
+
+
+
+ NESTED
+
+
+ Here are your to-do items for today:
+
+
+
+
+ - 1
+ LOOP 1
+ - 2
+ LOOP 2
+ - 3
+ LOOP 3
+
+
+ """,
+ )
+
+
class InlineComponentTest(BaseTestCase):
def test_inline_html_component(self):
class InlineHTMLComponent(component.Component):
diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py
index ab8ea7d8..10f63de8 100644
--- a/tests/test_templatetags.py
+++ b/tests/test_templatetags.py
@@ -941,7 +941,6 @@ def setUpClass(cls):
super().setUpClass()
component.registry.register("test", SlottedComponent)
component.registry.register("broken_component", BrokenComponent)
- component.registry.register("nonunique_slot_component", NonUniqueSlotsComponent)
@classmethod
def tearDownClass(cls) -> None:
@@ -1031,16 +1030,6 @@ def test_non_unique_fill_names_is_error(self):
"""
).render(Context({}))
- def test_non_unique_slot_names_is_error(self):
- with self.assertRaises(TemplateSyntaxError):
- Template(
- """
- {% load component_tags %}
- {% component "nonunique_slot_component" %}
- {% endcomponent %}
- """
- ).render(Context({}))
-
class ComponentNestingTests(BaseTestCase):
@classmethod