Skip to content

Commit

Permalink
feat: allow allow a slot with a same name to be
Browse files Browse the repository at this point in the history
  • Loading branch information
JuroOravec committed Apr 25, 2024
1 parent 29c931f commit 3b2d63e
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 19 deletions.
13 changes: 7 additions & 6 deletions src/django_components/slots.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
16 changes: 16 additions & 0 deletions tests/templates/template_with_nonunique_slots_nested.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{% load component_tags %}
{% slot "header" %}START{% endslot %}
<div class="dashboard-component">
{% 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 %}
<ol>
{% for item in items %}
<li>{{ item }}</li>
{% slot "header" %}LOOP {{ item }} {% endslot %}
{% endfor %}
</ol>
</div>
157 changes: 155 additions & 2 deletions tests/test_component.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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,
"""
<header>Name: Jannete</header>
<main>Name: Jannete</main>
<footer>Hello</footer>
""",
)

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,
"""
<header>Default header</header>
<main>Default main header</main>
<footer>Default footer</footer>
""",
)

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!
<div class="dashboard-component">
<div class="calendar-component">
<h1>
OVERRIDDEN!
</h1>
<main>
Here are your to-do items for today:
</main>
</div>
<ol>
<li>1</li>
OVERRIDDEN!
<li>2</li>
OVERRIDDEN!
<li>3</li>
OVERRIDDEN!
</ol>
</div>
""",
)

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
<div class="dashboard-component">
<div class="calendar-component">
<h1>
NESTED
</h1>
<main>
Here are your to-do items for today:
</main>
</div>
<ol>
<li>1</li>
LOOP 1
<li>2</li>
LOOP 2
<li>3</li>
LOOP 3
</ol>
</div>
""",
)


class InlineComponentTest(BaseTestCase):
def test_inline_html_component(self):
class InlineHTMLComponent(component.Component):
Expand Down
11 changes: 0 additions & 11 deletions tests/test_templatetags.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 3b2d63e

Please sign in to comment.