mirror of
https://github.com/django-components/django-components.git
synced 2025-08-04 14:28:18 +00:00
refactor: fix slot fills for components nested in themselves (#456)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
091da26da5
commit
29c931f150
4 changed files with 66 additions and 5 deletions
|
@ -263,7 +263,7 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
|||
# NOTE: This if/else is important to avoid nested Contexts,
|
||||
# See https://github.com/EmilStenstrom/django-components/issues/414
|
||||
context = context_data if isinstance(context_data, Context) else Context(context_data)
|
||||
prepare_context(context, self.outer_context or Context(), component_id=self.component_id)
|
||||
prepare_context(context, component_id=self.component_id, outer_context=self.outer_context or Context())
|
||||
template = self.get_template(context)
|
||||
|
||||
# Associate the slots with this component for this context
|
||||
|
@ -304,8 +304,8 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
|||
"""Fill component slots outside of template rendering."""
|
||||
self.fill_content = {
|
||||
slot_name: FillContent(
|
||||
TextNode(escape(content) if escape_content else content),
|
||||
None,
|
||||
nodes=NodeList([TextNode(escape(content) if escape_content else content)]),
|
||||
alias=None,
|
||||
)
|
||||
for (slot_name, content) in slots_data.items()
|
||||
}
|
||||
|
|
|
@ -195,6 +195,13 @@ def set_slot_component_association(
|
|||
Hence, we use the Context to store the associations of SlotNode <-> Component
|
||||
for the current context stack.
|
||||
"""
|
||||
# Store associations on the latest context layer so that we can nest components
|
||||
# onto themselves (component A is rendered in slot fill of component A).
|
||||
# Otherwise, they would overwrite each other as the ComponentNode and SlotNodes
|
||||
# are re-used, so their IDs don't change across these two occurences.
|
||||
latest_dict = context.dicts[-1]
|
||||
if _SLOT_COMPONENT_ASSOC_KEY not in latest_dict:
|
||||
latest_dict[_SLOT_COMPONENT_ASSOC_KEY] = context[_SLOT_COMPONENT_ASSOC_KEY].copy()
|
||||
context[_SLOT_COMPONENT_ASSOC_KEY][slot_id] = component_id
|
||||
|
||||
|
||||
|
|
|
@ -232,7 +232,7 @@ class IfSlotFilledConditionBranchNode(_IfSlotFilledBranchNode, ComponentIdMixin)
|
|||
super().__init__(nodelist)
|
||||
|
||||
def evaluate(self, context: Context) -> bool:
|
||||
slot_fill = get_slot_fill(context, self.component_id, self.slot_name)
|
||||
slot_fill = get_slot_fill(context, component_id=self.component_id, slot_name=self.slot_name)
|
||||
is_filled = slot_fill is not None
|
||||
# Make polarity switchable.
|
||||
# i.e. if slot name is NOT filled and is_positive=False,
|
||||
|
|
|
@ -212,7 +212,7 @@ class ComponentTest(BaseTestCase):
|
|||
Path(__file__).resolve().parent / "components",
|
||||
],
|
||||
)
|
||||
def test_component_media_with_dict_with_relative_paths(self):
|
||||
def test_component_with_relative_paths_as_subcomponent(self):
|
||||
# Ensure that the module is executed again after import in autodiscovery
|
||||
if "tests.components.relative_file.relative_file" in sys.modules:
|
||||
del sys.modules["tests.components.relative_file.relative_file"]
|
||||
|
@ -287,6 +287,60 @@ class ComponentTest(BaseTestCase):
|
|||
""",
|
||||
)
|
||||
|
||||
def test_fill_inside_fill_with_same_name(self):
|
||||
class SlottedComponent(component.Component):
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]:
|
||||
return {
|
||||
"name": name,
|
||||
}
|
||||
|
||||
component.registry.register("test", SlottedComponent)
|
||||
|
||||
self.template = Template(
|
||||
"""
|
||||
{% load component_tags %}
|
||||
{% component "test" name='Igor' %}
|
||||
{% fill "header" %}
|
||||
{% component "test" name='Joe2' %}
|
||||
{% fill "header" %}
|
||||
Name2: {{ name }}
|
||||
{% endfill %}
|
||||
{% fill "main" %}
|
||||
Day2: {{ day }}
|
||||
{% endfill %}
|
||||
{% fill "footer" %}
|
||||
XYZ
|
||||
{% endfill %}
|
||||
{% endcomponent %}
|
||||
{% endfill %}
|
||||
{% fill "footer" %}
|
||||
WWW
|
||||
{% endfill %}
|
||||
{% endcomponent %}
|
||||
"""
|
||||
)
|
||||
|
||||
# {{ name }} should be "Jannete" everywhere
|
||||
rendered = self.template.render(Context({"day": "Monday", "name": "Jannete"}))
|
||||
self.assertHTMLEqual(
|
||||
rendered,
|
||||
"""
|
||||
<custom-template>
|
||||
<header>
|
||||
<custom-template>
|
||||
<header>Name2: Jannete</header>
|
||||
<main>Day2: Monday</main>
|
||||
<footer>XYZ</footer>
|
||||
</custom-template>
|
||||
</header>
|
||||
<main>Default main</main>
|
||||
<footer>WWW</footer>
|
||||
</custom-template>
|
||||
""",
|
||||
)
|
||||
|
||||
@override_settings(
|
||||
COMPONENTS={
|
||||
"context_behavior": "isolated",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue