mirror of
https://github.com/django-components/django-components.git
synced 2025-08-18 13:10:13 +00:00
fix: TemplateDoesNotExist when using {% extends %} on main template and two components with same parent template (#862)
This commit is contained in:
parent
6bb73bd8af
commit
85fc6e3497
9 changed files with 876 additions and 394 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -74,6 +74,8 @@ poetry.lock
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.python-version
|
.python-version
|
||||||
site
|
site
|
||||||
|
.direnv/
|
||||||
|
.envrc
|
||||||
|
|
||||||
# JS, NPM Dependency directories
|
# JS, NPM Dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
|
@ -692,6 +692,10 @@ class Component(
|
||||||
if not isinstance(context, Context):
|
if not isinstance(context, Context):
|
||||||
context = RequestContext(request, context) if request else Context(context)
|
context = RequestContext(request, context) if request else Context(context)
|
||||||
|
|
||||||
|
# Required for compatibility with Django's {% extends %} tag
|
||||||
|
# See https://github.com/EmilStenstrom/django-components/pull/859
|
||||||
|
context.render_context.push({BLOCK_CONTEXT_KEY: context.render_context.get(BLOCK_CONTEXT_KEY, {})})
|
||||||
|
|
||||||
# By adding the current input to the stack, we temporarily allow users
|
# By adding the current input to the stack, we temporarily allow users
|
||||||
# to access the provided context, slots, etc. Also required so users can
|
# to access the provided context, slots, etc. Also required so users can
|
||||||
# call `self.inject()` from within `get_context_data()`.
|
# call `self.inject()` from within `get_context_data()`.
|
||||||
|
@ -768,6 +772,7 @@ class Component(
|
||||||
# After rendering is done, remove the current state from the stack, which means
|
# After rendering is done, remove the current state from the stack, which means
|
||||||
# properties like `self.context` will no longer return the current state.
|
# properties like `self.context` will no longer return the current state.
|
||||||
self._render_stack.pop()
|
self._render_stack.pop()
|
||||||
|
context.render_context.pop()
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,10 @@ def make_isolated_context_copy(context: Context) -> Context:
|
||||||
context_copy = context.new()
|
context_copy = context.new()
|
||||||
copy_forloop_context(context, context_copy)
|
copy_forloop_context(context, context_copy)
|
||||||
|
|
||||||
|
# Required for compatibility with Django's {% extends %} tag
|
||||||
|
# See https://github.com/EmilStenstrom/django-components/pull/859
|
||||||
|
context_copy.render_context = context.render_context
|
||||||
|
|
||||||
# Pass through our internal keys
|
# Pass through our internal keys
|
||||||
context_copy[_REGISTRY_CONTEXT_KEY] = context.get(_REGISTRY_CONTEXT_KEY, None)
|
context_copy[_REGISTRY_CONTEXT_KEY] = context.get(_REGISTRY_CONTEXT_KEY, None)
|
||||||
if _ROOT_CTX_CONTEXT_KEY in context:
|
if _ROOT_CTX_CONTEXT_KEY in context:
|
||||||
|
|
|
@ -338,10 +338,16 @@ class SlotNode(BaseNode):
|
||||||
# came from (or current context if configured so)
|
# came from (or current context if configured so)
|
||||||
used_ctx = self._resolve_slot_context(context, slot_fill)
|
used_ctx = self._resolve_slot_context(context, slot_fill)
|
||||||
with used_ctx.update(extra_context):
|
with used_ctx.update(extra_context):
|
||||||
# Render slot as a function
|
# Required for compatibility with Django's {% extends %} tag
|
||||||
# NOTE: While `{% fill %}` tag has to opt in for the `default` and `data` variables,
|
# This makes sure that the render context used outside of a component
|
||||||
# the render function ALWAYS receives them.
|
# is the same as the one used inside the slot.
|
||||||
output = slot_fill.slot(used_ctx, kwargs, slot_ref)
|
# See https://github.com/EmilStenstrom/django-components/pull/859
|
||||||
|
render_ctx_layer = used_ctx.render_context.dicts[-2] if len(used_ctx.render_context.dicts) > 1 else {}
|
||||||
|
with used_ctx.render_context.push(render_ctx_layer):
|
||||||
|
# Render slot as a function
|
||||||
|
# NOTE: While `{% fill %}` tag has to opt in for the `default` and `data` variables,
|
||||||
|
# the render function ALWAYS receives them.
|
||||||
|
output = slot_fill.slot(used_ctx, kwargs, slot_ref)
|
||||||
|
|
||||||
trace_msg("RENDR", "SLOT", self.trace_id, self.node_id, msg="...Done!")
|
trace_msg("RENDR", "SLOT", self.trace_id, self.node_id, msg="...Done!")
|
||||||
return output
|
return output
|
||||||
|
|
7
tests/templates/blocked_and_slotted_template.html
Normal file
7
tests/templates/blocked_and_slotted_template.html
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{% load component_tags %}
|
||||||
|
{% block before_custom %}{% endblock %}
|
||||||
|
<custom-template>
|
||||||
|
<header>{% slot "header" %}Default header{% endslot %}</header>
|
||||||
|
<main>{% slot "main" %}Default main{% endslot %}</main>
|
||||||
|
<footer>{% slot "footer" %}Default footer{% endslot %}</footer>
|
||||||
|
</custom-template>
|
7
tests/templates/blocked_and_slotted_template_2.html
Normal file
7
tests/templates/blocked_and_slotted_template_2.html
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{% load component_tags %}
|
||||||
|
{% block before_custom %}{% endblock %}
|
||||||
|
<custom-template>
|
||||||
|
<header>{% slot "header" %}Default header{% endslot %}</header>
|
||||||
|
<main>{% slot "main" %}Default main{% endslot %}</main>
|
||||||
|
<footer>{% slot "footer" %}Default footer{% endslot %}</footer>
|
||||||
|
</custom-template>
|
4
tests/templates/included.html
Normal file
4
tests/templates/included.html
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{% extends "simple_template.html" %}
|
||||||
|
{% block before_custom %}
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
{% endblock %}
|
|
@ -94,396 +94,6 @@ class TemplateInstrumentationTest(BaseTestCase):
|
||||||
self.assertIn("simple_template.html", templates_used)
|
self.assertIn("simple_template.html", templates_used)
|
||||||
|
|
||||||
|
|
||||||
class BlockCompatTests(BaseTestCase):
|
|
||||||
@parametrize_context_behavior(["django", "isolated"])
|
|
||||||
def test_slots_inside_extends(self):
|
|
||||||
registry.register("slotted_component", SlottedComponent)
|
|
||||||
|
|
||||||
@register("slot_inside_extends")
|
|
||||||
class SlotInsideExtendsComponent(Component):
|
|
||||||
template: types.django_html = """
|
|
||||||
{% extends "block_in_slot_in_component.html" %}
|
|
||||||
"""
|
|
||||||
|
|
||||||
template: types.django_html = """
|
|
||||||
{% load component_tags %}
|
|
||||||
{% component "slot_inside_extends" %}
|
|
||||||
{% fill "body" %}
|
|
||||||
BODY_FROM_FILL
|
|
||||||
{% endfill %}
|
|
||||||
{% endcomponent %}
|
|
||||||
"""
|
|
||||||
rendered = Template(template).render(Context())
|
|
||||||
expected = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<custom-template>
|
|
||||||
<header></header>
|
|
||||||
<main>BODY_FROM_FILL</main>
|
|
||||||
<footer>Default footer</footer>
|
|
||||||
</custom-template>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
self.assertHTMLEqual(rendered, expected)
|
|
||||||
|
|
||||||
@parametrize_context_behavior(["django", "isolated"])
|
|
||||||
def test_slots_inside_include(self):
|
|
||||||
registry.register("slotted_component", SlottedComponent)
|
|
||||||
|
|
||||||
@register("slot_inside_include")
|
|
||||||
class SlotInsideIncludeComponent(Component):
|
|
||||||
template: types.django_html = """
|
|
||||||
{% include "block_in_slot_in_component.html" %}
|
|
||||||
"""
|
|
||||||
|
|
||||||
template: types.django_html = """
|
|
||||||
{% load component_tags %}
|
|
||||||
{% component "slot_inside_include" %}
|
|
||||||
{% fill "body" %}
|
|
||||||
BODY_FROM_FILL
|
|
||||||
{% endfill %}
|
|
||||||
{% endcomponent %}
|
|
||||||
"""
|
|
||||||
rendered = Template(template).render(Context())
|
|
||||||
expected = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<custom-template>
|
|
||||||
<header></header>
|
|
||||||
<main>BODY_FROM_FILL</main>
|
|
||||||
<footer>Default footer</footer>
|
|
||||||
</custom-template>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
self.assertHTMLEqual(rendered, expected)
|
|
||||||
|
|
||||||
@parametrize_context_behavior(["django", "isolated"])
|
|
||||||
def test_component_inside_block(self):
|
|
||||||
registry.register("slotted_component", SlottedComponent)
|
|
||||||
template: types.django_html = """
|
|
||||||
{% extends "block.html" %}
|
|
||||||
{% load component_tags %}
|
|
||||||
{% block body %}
|
|
||||||
{% component "slotted_component" %}
|
|
||||||
{% fill "header" %}{% endfill %}
|
|
||||||
{% fill "main" %}
|
|
||||||
TEST
|
|
||||||
{% endfill %}
|
|
||||||
{% fill "footer" %}{% endfill %}
|
|
||||||
{% endcomponent %}
|
|
||||||
{% endblock %}
|
|
||||||
"""
|
|
||||||
rendered = Template(template).render(Context())
|
|
||||||
expected = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<main role="main">
|
|
||||||
<div class='container main-container'>
|
|
||||||
<custom-template>
|
|
||||||
<header></header>
|
|
||||||
<main>TEST</main>
|
|
||||||
<footer></footer>
|
|
||||||
</custom-template>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
self.assertHTMLEqual(rendered, expected)
|
|
||||||
|
|
||||||
@parametrize_context_behavior(["django", "isolated"])
|
|
||||||
def test_block_inside_component(self):
|
|
||||||
registry.register("slotted_component", SlottedComponent)
|
|
||||||
|
|
||||||
template: types.django_html = """
|
|
||||||
{% extends "block_in_component.html" %}
|
|
||||||
{% load component_tags %}
|
|
||||||
{% block body %}
|
|
||||||
<div>
|
|
||||||
58 giraffes and 2 pantaloons
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
"""
|
|
||||||
rendered = Template(template).render(Context())
|
|
||||||
expected = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<custom-template>
|
|
||||||
<header></header>
|
|
||||||
<main>
|
|
||||||
<div> 58 giraffes and 2 pantaloons </div>
|
|
||||||
</main>
|
|
||||||
<footer>Default footer</footer>
|
|
||||||
</custom-template>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
self.assertHTMLEqual(rendered, expected)
|
|
||||||
|
|
||||||
@parametrize_context_behavior(["django", "isolated"])
|
|
||||||
def test_block_inside_component_parent(self):
|
|
||||||
registry.register("slotted_component", SlottedComponent)
|
|
||||||
|
|
||||||
@register("block_in_component_parent")
|
|
||||||
class BlockInCompParent(Component):
|
|
||||||
template_name = "block_in_component_parent.html"
|
|
||||||
|
|
||||||
template: types.django_html = """
|
|
||||||
{% load component_tags %}
|
|
||||||
{% component "block_in_component_parent" %}{% endcomponent %}
|
|
||||||
"""
|
|
||||||
rendered = Template(template).render(Context())
|
|
||||||
expected = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<custom-template>
|
|
||||||
<header></header>
|
|
||||||
<main>
|
|
||||||
<div> 58 giraffes and 2 pantaloons </div>
|
|
||||||
</main>
|
|
||||||
<footer>Default footer</footer>
|
|
||||||
</custom-template>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
self.assertHTMLEqual(rendered, expected)
|
|
||||||
|
|
||||||
@parametrize_context_behavior(["django", "isolated"])
|
|
||||||
def test_block_does_not_affect_inside_component(self):
|
|
||||||
"""
|
|
||||||
Assert that when we call a component with `{% component %}`, that
|
|
||||||
the `{% block %}` will NOT affect the inner component.
|
|
||||||
"""
|
|
||||||
registry.register("slotted_component", SlottedComponent)
|
|
||||||
|
|
||||||
@register("block_inside_slot_v1")
|
|
||||||
class BlockInSlotInComponent(Component):
|
|
||||||
template_name = "block_in_slot_in_component.html"
|
|
||||||
|
|
||||||
template: types.django_html = """
|
|
||||||
{% load component_tags %}
|
|
||||||
{% component "block_inside_slot_v1" %}
|
|
||||||
{% fill "body" %}
|
|
||||||
BODY_FROM_FILL
|
|
||||||
{% endfill %}
|
|
||||||
{% endcomponent %}
|
|
||||||
{% block inner %}
|
|
||||||
wow
|
|
||||||
{% endblock %}
|
|
||||||
"""
|
|
||||||
rendered = Template(template).render(Context())
|
|
||||||
expected = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<custom-template>
|
|
||||||
<header></header>
|
|
||||||
<main>BODY_FROM_FILL</main>
|
|
||||||
<footer>Default footer</footer>
|
|
||||||
</custom-template>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
wow
|
|
||||||
"""
|
|
||||||
self.assertHTMLEqual(rendered, expected)
|
|
||||||
|
|
||||||
@parametrize_context_behavior(["django", "isolated"])
|
|
||||||
def test_slot_inside_block__slot_default_block_default(self):
|
|
||||||
registry.register("slotted_component", SlottedComponent)
|
|
||||||
|
|
||||||
@register("slot_inside_block")
|
|
||||||
class _SlotInsideBlockComponent(Component):
|
|
||||||
template: types.django_html = """
|
|
||||||
{% extends "slot_inside_block.html" %}
|
|
||||||
"""
|
|
||||||
|
|
||||||
template: types.django_html = """
|
|
||||||
{% load component_tags %}
|
|
||||||
{% component "slot_inside_block" %}{% endcomponent %}
|
|
||||||
"""
|
|
||||||
rendered = Template(template).render(Context())
|
|
||||||
expected = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<custom-template>
|
|
||||||
<header></header>
|
|
||||||
<main>
|
|
||||||
Helloodiddoo
|
|
||||||
Default inner
|
|
||||||
</main>
|
|
||||||
<footer>Default footer</footer>
|
|
||||||
</custom-template>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
self.assertHTMLEqual(rendered, expected)
|
|
||||||
|
|
||||||
@parametrize_context_behavior(["django", "isolated"])
|
|
||||||
def test_slot_inside_block__slot_default_block_override(self):
|
|
||||||
registry.clear()
|
|
||||||
registry.register("slotted_component", SlottedComponent)
|
|
||||||
|
|
||||||
@register("slot_inside_block")
|
|
||||||
class _SlotInsideBlockComponent(Component):
|
|
||||||
template: types.django_html = """
|
|
||||||
{% extends "slot_inside_block.html" %}
|
|
||||||
{% block inner %}
|
|
||||||
INNER BLOCK OVERRIDEN
|
|
||||||
{% endblock %}
|
|
||||||
"""
|
|
||||||
|
|
||||||
template: types.django_html = """
|
|
||||||
{% load component_tags %}
|
|
||||||
{% component "slot_inside_block" %}{% endcomponent %}
|
|
||||||
"""
|
|
||||||
rendered = Template(template).render(Context())
|
|
||||||
expected = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<custom-template>
|
|
||||||
<header></header>
|
|
||||||
<main>
|
|
||||||
Helloodiddoo
|
|
||||||
INNER BLOCK OVERRIDEN
|
|
||||||
</main>
|
|
||||||
<footer>Default footer</footer>
|
|
||||||
</custom-template>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
self.assertHTMLEqual(rendered, expected)
|
|
||||||
|
|
||||||
@parametrize_context_behavior(["isolated", "django"])
|
|
||||||
def test_slot_inside_block__slot_overriden_block_default(self):
|
|
||||||
registry.register("slotted_component", SlottedComponent)
|
|
||||||
|
|
||||||
@register("slot_inside_block")
|
|
||||||
class _SlotInsideBlockComponent(Component):
|
|
||||||
template: types.django_html = """
|
|
||||||
{% extends "slot_inside_block.html" %}
|
|
||||||
"""
|
|
||||||
|
|
||||||
template: types.django_html = """
|
|
||||||
{% load component_tags %}
|
|
||||||
{% component "slot_inside_block" %}
|
|
||||||
{% fill "body" %}
|
|
||||||
SLOT OVERRIDEN
|
|
||||||
{% endfill %}
|
|
||||||
{% endcomponent %}
|
|
||||||
"""
|
|
||||||
rendered = Template(template).render(Context())
|
|
||||||
expected = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<custom-template>
|
|
||||||
<header></header>
|
|
||||||
<main>
|
|
||||||
Helloodiddoo
|
|
||||||
SLOT OVERRIDEN
|
|
||||||
</main>
|
|
||||||
<footer>Default footer</footer>
|
|
||||||
</custom-template>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
self.assertHTMLEqual(rendered, expected)
|
|
||||||
|
|
||||||
@parametrize_context_behavior(["django", "isolated"])
|
|
||||||
def test_slot_inside_block__slot_overriden_block_overriden(self):
|
|
||||||
registry.register("slotted_component", SlottedComponent)
|
|
||||||
|
|
||||||
@register("slot_inside_block")
|
|
||||||
class _SlotInsideBlockComponent(Component):
|
|
||||||
template: types.django_html = """
|
|
||||||
{% extends "slot_inside_block.html" %}
|
|
||||||
{% block inner %}
|
|
||||||
{% load component_tags %}
|
|
||||||
{% slot "new_slot" %}{% endslot %}
|
|
||||||
{% endblock %}
|
|
||||||
whut
|
|
||||||
"""
|
|
||||||
|
|
||||||
# NOTE: The "body" fill will NOT show up, because we override the `inner` block
|
|
||||||
# with a different slot. But the "new_slot" WILL show up.
|
|
||||||
template: types.django_html = """
|
|
||||||
{% load component_tags %}
|
|
||||||
{% component "slot_inside_block" %}
|
|
||||||
{% fill "body" %}
|
|
||||||
SLOT_BODY__OVERRIDEN
|
|
||||||
{% endfill %}
|
|
||||||
{% fill "new_slot" %}
|
|
||||||
SLOT_NEW__OVERRIDEN
|
|
||||||
{% endfill %}
|
|
||||||
{% endcomponent %}
|
|
||||||
"""
|
|
||||||
rendered = Template(template).render(Context())
|
|
||||||
expected = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<custom-template>
|
|
||||||
<header></header>
|
|
||||||
<main>
|
|
||||||
Helloodiddoo
|
|
||||||
SLOT_NEW__OVERRIDEN
|
|
||||||
</main>
|
|
||||||
<footer>Default footer</footer>
|
|
||||||
</custom-template>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
self.assertHTMLEqual(rendered, expected)
|
|
||||||
|
|
||||||
@parametrize_context_behavior(["django", "isolated"])
|
|
||||||
def test_inject_inside_block(self):
|
|
||||||
registry.register("slotted_component", SlottedComponent)
|
|
||||||
|
|
||||||
@register("injectee")
|
|
||||||
class InjectComponent(Component):
|
|
||||||
template: types.django_html = """
|
|
||||||
<div> injected: {{ var|safe }} </div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_context_data(self):
|
|
||||||
var = self.inject("block_provide")
|
|
||||||
return {"var": var}
|
|
||||||
|
|
||||||
template: types.django_html = """
|
|
||||||
{% extends "block_in_component_provide.html" %}
|
|
||||||
{% load component_tags %}
|
|
||||||
{% block body %}
|
|
||||||
{% component "injectee" %}
|
|
||||||
{% endcomponent %}
|
|
||||||
{% endblock %}
|
|
||||||
"""
|
|
||||||
rendered = Template(template).render(Context())
|
|
||||||
expected = """
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<custom-template>
|
|
||||||
<header></header>
|
|
||||||
<main>
|
|
||||||
<div> injected: DepInject(hello='from_block') </div>
|
|
||||||
</main>
|
|
||||||
<footer>Default footer</footer>
|
|
||||||
</custom-template>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
self.assertHTMLEqual(rendered, expected)
|
|
||||||
|
|
||||||
|
|
||||||
class MultilineTagsTests(BaseTestCase):
|
class MultilineTagsTests(BaseTestCase):
|
||||||
@parametrize_context_behavior(["django", "isolated"])
|
@parametrize_context_behavior(["django", "isolated"])
|
||||||
def test_multiline_tags(self):
|
def test_multiline_tags(self):
|
||||||
|
|
837
tests/test_templatetags_extends.py
Normal file
837
tests/test_templatetags_extends.py
Normal file
|
@ -0,0 +1,837 @@
|
||||||
|
"""Catch-all for tests that use template tags and don't fit other files"""
|
||||||
|
|
||||||
|
from django.template import Context, Template
|
||||||
|
|
||||||
|
from django_components import Component, register, registry, types
|
||||||
|
|
||||||
|
from .django_test_setup import setup_test_config
|
||||||
|
from .testutils import BaseTestCase, parametrize_context_behavior
|
||||||
|
|
||||||
|
setup_test_config({"autodiscover": False})
|
||||||
|
|
||||||
|
|
||||||
|
class SlottedComponent(Component):
|
||||||
|
template_name = "slotted_template.html"
|
||||||
|
|
||||||
|
|
||||||
|
class BlockedAndSlottedComponent(Component):
|
||||||
|
template_name = "blocked_and_slotted_template.html"
|
||||||
|
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# TESTS
|
||||||
|
#######################
|
||||||
|
|
||||||
|
|
||||||
|
class ExtendsCompatTests(BaseTestCase):
|
||||||
|
@parametrize_context_behavior(["isolated", "django"])
|
||||||
|
def test_double_extends_on_main_template_and_component_one_component(self):
|
||||||
|
registry.register("blocked_and_slotted_component", BlockedAndSlottedComponent)
|
||||||
|
|
||||||
|
@register("extended_component")
|
||||||
|
class _ExtendedComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "blocked_and_slotted_template.html" %}
|
||||||
|
{% block before_custom %}
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends 'block.html' %}
|
||||||
|
{% load component_tags %}
|
||||||
|
{% block body %}
|
||||||
|
{% component "extended_component" %}
|
||||||
|
{% fill "header" %}
|
||||||
|
SLOT OVERRIDEN
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<main role="main">
|
||||||
|
<div class='container main-container'>
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
<custom-template>
|
||||||
|
<header>SLOT OVERRIDEN</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["isolated", "django"])
|
||||||
|
def test_double_extends_on_main_template_and_component_two_identical_components(self):
|
||||||
|
registry.register("blocked_and_slotted_component", BlockedAndSlottedComponent)
|
||||||
|
|
||||||
|
@register("extended_component")
|
||||||
|
class _ExtendedComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "blocked_and_slotted_template.html" %}
|
||||||
|
{% block before_custom %}
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends 'block.html' %}
|
||||||
|
{% load component_tags %}
|
||||||
|
{% block body %}
|
||||||
|
{% component "extended_component" %}
|
||||||
|
{% fill "header" %}
|
||||||
|
SLOT OVERRIDEN
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% component "extended_component" %}
|
||||||
|
{% fill "header" %}
|
||||||
|
SLOT OVERRIDEN 2
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<main role="main">
|
||||||
|
<div class='container main-container'>
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
<custom-template>
|
||||||
|
<header>SLOT OVERRIDEN</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
<custom-template>
|
||||||
|
<header>SLOT OVERRIDEN 2</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["isolated", "django"])
|
||||||
|
def test_double_extends_on_main_template_and_component_two_different_components_same_parent(self):
|
||||||
|
registry.register("blocked_and_slotted_component", BlockedAndSlottedComponent)
|
||||||
|
|
||||||
|
@register("extended_component")
|
||||||
|
class _ExtendedComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "blocked_and_slotted_template.html" %}
|
||||||
|
{% block before_custom %}
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
@register("second_extended_component")
|
||||||
|
class _SecondExtendedComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "blocked_and_slotted_template.html" %}
|
||||||
|
{% block before_custom %}
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template_str: types.django_html = """
|
||||||
|
{% extends 'block.html' %}
|
||||||
|
{% load component_tags %}
|
||||||
|
{% block body %}
|
||||||
|
{% component "extended_component" %}
|
||||||
|
{% fill "header" %}
|
||||||
|
SLOT OVERRIDEN
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% component "second_extended_component" %}
|
||||||
|
{% fill "header" %}
|
||||||
|
SLOT OVERRIDEN 2
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
template = Template(template_str)
|
||||||
|
rendered = template.render(Context())
|
||||||
|
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<main role="main">
|
||||||
|
<div class='container main-container'>
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
<custom-template>
|
||||||
|
<header>SLOT OVERRIDEN</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
<custom-template>
|
||||||
|
<header>SLOT OVERRIDEN 2</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["isolated", "django"])
|
||||||
|
def test_double_extends_on_main_template_and_component_two_different_components_different_parent(self):
|
||||||
|
registry.register("blocked_and_slotted_component", BlockedAndSlottedComponent)
|
||||||
|
|
||||||
|
@register("extended_component")
|
||||||
|
class _ExtendedComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "blocked_and_slotted_template.html" %}
|
||||||
|
{% block before_custom %}
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
@register("second_extended_component")
|
||||||
|
class _SecondExtendedComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "blocked_and_slotted_template_2.html" %}
|
||||||
|
{% block before_custom %}
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends 'block.html' %}
|
||||||
|
{% load component_tags %}
|
||||||
|
{% block body %}
|
||||||
|
{% component "extended_component" %}
|
||||||
|
{% fill "header" %}
|
||||||
|
SLOT OVERRIDEN
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% component "second_extended_component" %}
|
||||||
|
{% fill "header" %}
|
||||||
|
SLOT OVERRIDEN 2
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<main role="main">
|
||||||
|
<div class='container main-container'>
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
<custom-template>
|
||||||
|
<header>SLOT OVERRIDEN</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
<custom-template>
|
||||||
|
<header>SLOT OVERRIDEN 2</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["isolated", "django"])
|
||||||
|
def test_extends_on_component_one_component(self):
|
||||||
|
registry.register("blocked_and_slotted_component", BlockedAndSlottedComponent)
|
||||||
|
|
||||||
|
@register("extended_component")
|
||||||
|
class _ExtendedComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "blocked_and_slotted_template.html" %}
|
||||||
|
{% block before_custom %}
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% load component_tags %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
{% component "extended_component" %}
|
||||||
|
{% fill "header" %}
|
||||||
|
SLOT OVERRIDEN
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
<custom-template>
|
||||||
|
<header>SLOT OVERRIDEN</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["isolated", "django"])
|
||||||
|
def test_extends_on_component_two_component(self):
|
||||||
|
registry.register("blocked_and_slotted_component", BlockedAndSlottedComponent)
|
||||||
|
|
||||||
|
@register("extended_component")
|
||||||
|
class _ExtendedComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "blocked_and_slotted_template.html" %}
|
||||||
|
{% block before_custom %}
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% load component_tags %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
{% component "extended_component" %}
|
||||||
|
{% fill "header" %}
|
||||||
|
SLOT OVERRIDEN
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% component "extended_component" %}
|
||||||
|
{% fill "header" %}
|
||||||
|
SLOT OVERRIDEN 2
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
<custom-template>
|
||||||
|
<header>SLOT OVERRIDEN</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
<custom-template>
|
||||||
|
<header>SLOT OVERRIDEN 2</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["isolated", "django"])
|
||||||
|
def test_double_extends_on_main_template_and_nested_component(self):
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
registry.register("blocked_and_slotted_component", BlockedAndSlottedComponent)
|
||||||
|
|
||||||
|
@register("extended_component")
|
||||||
|
class _ExtendedComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "blocked_and_slotted_template.html" %}
|
||||||
|
{% block before_custom %}
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends 'block.html' %}
|
||||||
|
{% load component_tags %}
|
||||||
|
{% block body %}
|
||||||
|
{% component "slotted_component" %}
|
||||||
|
{% fill "main" %}
|
||||||
|
{% component "extended_component" %}
|
||||||
|
{% fill "header" %}
|
||||||
|
SLOT OVERRIDEN
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<main role="main">
|
||||||
|
<div class='container main-container'>
|
||||||
|
<custom-template>
|
||||||
|
<header>Default header</header>
|
||||||
|
<main>
|
||||||
|
<div>BLOCK OVERRIDEN</div>
|
||||||
|
<custom-template>
|
||||||
|
<header>SLOT OVERRIDEN</header>
|
||||||
|
<main>Default main</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["isolated", "django"])
|
||||||
|
def test_double_extends_on_main_template_and_nested_component_and_include(self):
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
registry.register("blocked_and_slotted_component", BlockedAndSlottedComponent)
|
||||||
|
|
||||||
|
@register("extended_component")
|
||||||
|
class _ExtendedComponent(Component):
|
||||||
|
template_name = "included.html"
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends 'block.html' %}
|
||||||
|
{% load component_tags %}
|
||||||
|
{% block body %}
|
||||||
|
{% include 'included.html' %}
|
||||||
|
{% component "extended_component" / %}
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<main role="main">
|
||||||
|
<div class='container main-container'>
|
||||||
|
Variable: <strong></strong>
|
||||||
|
Variable: <strong></strong>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
# second rendering after cache built
|
||||||
|
rendered_2 = Template(template).render(Context())
|
||||||
|
expected_2 = expected.replace("data-djc-id-a1bc3f", "data-djc-id-a1bc41")
|
||||||
|
self.assertHTMLEqual(rendered_2, expected_2)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["django", "isolated"])
|
||||||
|
def test_slots_inside_extends(self):
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
|
||||||
|
@register("slot_inside_extends")
|
||||||
|
class SlotInsideExtendsComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "block_in_slot_in_component.html" %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component "slot_inside_extends" %}
|
||||||
|
{% fill "body" %}
|
||||||
|
BODY_FROM_FILL
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<custom-template>
|
||||||
|
<header></header>
|
||||||
|
<main>BODY_FROM_FILL</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["django", "isolated"])
|
||||||
|
def test_slots_inside_include(self):
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
|
||||||
|
@register("slot_inside_include")
|
||||||
|
class SlotInsideIncludeComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% include "block_in_slot_in_component.html" %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component "slot_inside_include" %}
|
||||||
|
{% fill "body" %}
|
||||||
|
BODY_FROM_FILL
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<custom-template>
|
||||||
|
<header></header>
|
||||||
|
<main>BODY_FROM_FILL</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["django", "isolated"])
|
||||||
|
def test_component_inside_block(self):
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "block.html" %}
|
||||||
|
{% load component_tags %}
|
||||||
|
{% block body %}
|
||||||
|
{% component "slotted_component" %}
|
||||||
|
{% fill "header" %}{% endfill %}
|
||||||
|
{% fill "main" %}
|
||||||
|
TEST
|
||||||
|
{% endfill %}
|
||||||
|
{% fill "footer" %}{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<main role="main">
|
||||||
|
<div class='container main-container'>
|
||||||
|
<custom-template>
|
||||||
|
<header></header>
|
||||||
|
<main>TEST</main>
|
||||||
|
<footer></footer>
|
||||||
|
</custom-template>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["django", "isolated"])
|
||||||
|
def test_block_inside_component(self):
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "block_in_component.html" %}
|
||||||
|
{% load component_tags %}
|
||||||
|
{% block body %}
|
||||||
|
<div>
|
||||||
|
58 giraffes and 2 pantaloons
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<custom-template>
|
||||||
|
<header></header>
|
||||||
|
<main>
|
||||||
|
<div> 58 giraffes and 2 pantaloons </div>
|
||||||
|
</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["django", "isolated"])
|
||||||
|
def test_block_inside_component_parent(self):
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
|
||||||
|
@register("block_in_component_parent")
|
||||||
|
class BlockInCompParent(Component):
|
||||||
|
template_name = "block_in_component_parent.html"
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component "block_in_component_parent" %}{% endcomponent %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<custom-template>
|
||||||
|
<header></header>
|
||||||
|
<main>
|
||||||
|
<div> 58 giraffes and 2 pantaloons </div>
|
||||||
|
</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["django", "isolated"])
|
||||||
|
def test_block_does_not_affect_inside_component(self):
|
||||||
|
"""
|
||||||
|
Assert that when we call a component with `{% component %}`, that
|
||||||
|
the `{% block %}` will NOT affect the inner component.
|
||||||
|
"""
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
|
||||||
|
@register("block_inside_slot_v1")
|
||||||
|
class BlockInSlotInComponent(Component):
|
||||||
|
template_name = "block_in_slot_in_component.html"
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component "block_inside_slot_v1" %}
|
||||||
|
{% fill "body" %}
|
||||||
|
BODY_FROM_FILL
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% block inner %}
|
||||||
|
wow
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<custom-template>
|
||||||
|
<header></header>
|
||||||
|
<main>BODY_FROM_FILL</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
wow
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["django", "isolated"])
|
||||||
|
def test_slot_inside_block__slot_default_block_default(self):
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
|
||||||
|
@register("slot_inside_block")
|
||||||
|
class _SlotInsideBlockComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "slot_inside_block.html" %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component "slot_inside_block" %}{% endcomponent %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<custom-template>
|
||||||
|
<header></header>
|
||||||
|
<main>
|
||||||
|
Helloodiddoo
|
||||||
|
Default inner
|
||||||
|
</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["django", "isolated"])
|
||||||
|
def test_slot_inside_block__slot_default_block_override(self):
|
||||||
|
registry.clear()
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
|
||||||
|
@register("slot_inside_block")
|
||||||
|
class _SlotInsideBlockComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "slot_inside_block.html" %}
|
||||||
|
{% block inner %}
|
||||||
|
INNER BLOCK OVERRIDEN
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component "slot_inside_block" %}{% endcomponent %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<custom-template>
|
||||||
|
<header></header>
|
||||||
|
<main>
|
||||||
|
Helloodiddoo
|
||||||
|
INNER BLOCK OVERRIDEN
|
||||||
|
</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["isolated", "django"])
|
||||||
|
def test_slot_inside_block__slot_overriden_block_default(self):
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
|
||||||
|
@register("slot_inside_block")
|
||||||
|
class _SlotInsideBlockComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "slot_inside_block.html" %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component "slot_inside_block" %}
|
||||||
|
{% fill "body" %}
|
||||||
|
SLOT OVERRIDEN
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<custom-template>
|
||||||
|
<header></header>
|
||||||
|
<main>
|
||||||
|
Helloodiddoo
|
||||||
|
SLOT OVERRIDEN
|
||||||
|
</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["django", "isolated"])
|
||||||
|
def test_slot_inside_block__slot_overriden_block_overriden(self):
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
|
||||||
|
@register("slot_inside_block")
|
||||||
|
class _SlotInsideBlockComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "slot_inside_block.html" %}
|
||||||
|
{% block inner %}
|
||||||
|
{% load component_tags %}
|
||||||
|
{% slot "new_slot" %}{% endslot %}
|
||||||
|
{% endblock %}
|
||||||
|
whut
|
||||||
|
"""
|
||||||
|
|
||||||
|
# NOTE: The "body" fill will NOT show up, because we override the `inner` block
|
||||||
|
# with a different slot. But the "new_slot" WILL show up.
|
||||||
|
template: types.django_html = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component "slot_inside_block" %}
|
||||||
|
{% fill "body" %}
|
||||||
|
SLOT_BODY__OVERRIDEN
|
||||||
|
{% endfill %}
|
||||||
|
{% fill "new_slot" %}
|
||||||
|
SLOT_NEW__OVERRIDEN
|
||||||
|
{% endfill %}
|
||||||
|
{% endcomponent %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<custom-template>
|
||||||
|
<header></header>
|
||||||
|
<main>
|
||||||
|
Helloodiddoo
|
||||||
|
SLOT_NEW__OVERRIDEN
|
||||||
|
</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
|
@parametrize_context_behavior(["django", "isolated"])
|
||||||
|
def test_inject_inside_block(self):
|
||||||
|
registry.register("slotted_component", SlottedComponent)
|
||||||
|
|
||||||
|
@register("injectee")
|
||||||
|
class InjectComponent(Component):
|
||||||
|
template: types.django_html = """
|
||||||
|
<div> injected: {{ var|safe }} </div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_context_data(self):
|
||||||
|
var = self.inject("block_provide")
|
||||||
|
return {"var": var}
|
||||||
|
|
||||||
|
template: types.django_html = """
|
||||||
|
{% extends "block_in_component_provide.html" %}
|
||||||
|
{% load component_tags %}
|
||||||
|
{% block body %}
|
||||||
|
{% component "injectee" %}
|
||||||
|
{% endcomponent %}
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
rendered = Template(template).render(Context())
|
||||||
|
expected = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<custom-template>
|
||||||
|
<header></header>
|
||||||
|
<main>
|
||||||
|
<div> injected: DepInject(hello='from_block') </div>
|
||||||
|
</main>
|
||||||
|
<footer>Default footer</footer>
|
||||||
|
</custom-template>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
self.assertHTMLEqual(rendered, expected)
|
Loading…
Add table
Add a link
Reference in a new issue