mirror of
https://github.com/django-components/django-components.git
synced 2025-07-07 17:34:59 +00:00
refactor: Rename {% fill default=... %}
to {% fill fallback=... %}
(#1190)
This commit is contained in:
parent
0d05ef4cb2
commit
b6b574d875
7 changed files with 167 additions and 91 deletions
22
CHANGELOG.md
22
CHANGELOG.md
|
@ -322,10 +322,30 @@
|
|||
ctx.template_data["my_template_var"] = "my_value"
|
||||
```
|
||||
|
||||
**Miscellaneous**
|
||||
**Slots**
|
||||
|
||||
- `SlotContent` was renamed to `SlotInput`. The old name is deprecated and will be removed in v1.
|
||||
|
||||
- `SlotRef` was renamed to `SlotFallback`. The old name is deprecated and will be removed in v1.
|
||||
|
||||
- The `default` kwarg in `{% fill %}` tag was renamed to `fallback`. The old name is deprecated and will be removed in v1.
|
||||
|
||||
Before:
|
||||
|
||||
```django
|
||||
{% fill "footer" default="footer" %}
|
||||
{{ footer }}
|
||||
{% endfill %}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```django
|
||||
{% fill "footer" fallback="footer" %}
|
||||
{{ footer }}
|
||||
{% endfill %}
|
||||
```
|
||||
|
||||
#### Feat
|
||||
|
||||
- New method to render template variables - `get_template_data()`
|
||||
|
|
|
@ -50,7 +50,7 @@ from django_components.extensions.defaults import ComponentDefaults, Default
|
|||
from django_components.extensions.view import ComponentView, get_component_url
|
||||
from django_components.library import TagProtectedError
|
||||
from django_components.node import BaseNode, template_tag
|
||||
from django_components.slots import Slot, SlotContent, SlotFunc, SlotInput, SlotRef, SlotResult
|
||||
from django_components.slots import Slot, SlotContent, SlotFallback, SlotFunc, SlotInput, SlotRef, SlotResult
|
||||
from django_components.tag_formatter import (
|
||||
ComponentFormatter,
|
||||
ShorthandComponentFormatter,
|
||||
|
@ -125,6 +125,7 @@ __all__ = [
|
|||
"ShorthandComponentFormatter",
|
||||
"Slot",
|
||||
"SlotContent",
|
||||
"SlotFallback",
|
||||
"SlotFunc",
|
||||
"SlotInput",
|
||||
"SlotRef",
|
||||
|
|
|
@ -67,11 +67,11 @@ from django_components.perfutil.provide import register_provide_reference, unreg
|
|||
from django_components.provide import get_injected_context_var
|
||||
from django_components.slots import (
|
||||
Slot,
|
||||
SlotFallback,
|
||||
SlotFunc,
|
||||
SlotInput,
|
||||
SlotIsFilled,
|
||||
SlotName,
|
||||
SlotRef,
|
||||
SlotResult,
|
||||
_is_extracting_fill,
|
||||
resolve_fills,
|
||||
|
@ -2723,8 +2723,8 @@ class Component(metaclass=ComponentMeta):
|
|||
# so we can assign metadata to our internal copies.
|
||||
if not isinstance(content, Slot) or not content.escaped:
|
||||
# We wrap the original function so we post-process it by escaping the result.
|
||||
def content_fn(ctx: Context, slot_data: Dict, slot_ref: SlotRef) -> SlotResult:
|
||||
rendered = content(ctx, slot_data, slot_ref)
|
||||
def content_fn(ctx: Context, slot_data: Dict, fallback: SlotFallback) -> SlotResult:
|
||||
rendered = content(ctx, slot_data, fallback)
|
||||
return conditional_escape(rendered) if escape_content else rendered
|
||||
|
||||
content_func = cast(SlotFunc, content_fn)
|
||||
|
|
|
@ -45,7 +45,7 @@ SLOT_NAME_KWARG = "name"
|
|||
SLOT_REQUIRED_FLAG = "required"
|
||||
SLOT_DEFAULT_FLAG = "default"
|
||||
FILL_DATA_KWARG = "data"
|
||||
FILL_DEFAULT_KWARG = "default"
|
||||
FILL_FALLBACK_KWARG = "fallback"
|
||||
|
||||
|
||||
# Public types
|
||||
|
@ -54,7 +54,7 @@ SlotResult = Union[str, SafeString]
|
|||
|
||||
@runtime_checkable
|
||||
class SlotFunc(Protocol, Generic[TSlotData]):
|
||||
def __call__(self, ctx: Context, slot_data: TSlotData, slot_ref: "SlotRef") -> SlotResult: ... # noqa E704
|
||||
def __call__(self, ctx: Context, slot_data: TSlotData, slot_ref: "SlotFallback") -> SlotResult: ... # noqa E704
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -102,7 +102,7 @@ class Slot(Generic[TSlotData]):
|
|||
raise ValueError(f"Slot content must be a callable, got: {self.content_func}")
|
||||
|
||||
# Allow to treat the instances as functions
|
||||
def __call__(self, ctx: Context, slot_data: TSlotData, slot_ref: "SlotRef") -> SlotResult:
|
||||
def __call__(self, ctx: Context, slot_data: TSlotData, slot_ref: "SlotFallback") -> SlotResult:
|
||||
return self.content_func(ctx, slot_data, slot_ref)
|
||||
|
||||
# Make Django pass the instances of this class within the templates without calling
|
||||
|
@ -125,7 +125,7 @@ class Slot(Generic[TSlotData]):
|
|||
nodelist=NodeList([TextNode(contents)]),
|
||||
contents=contents,
|
||||
data_var=None,
|
||||
default_var=None,
|
||||
fallback_var=None,
|
||||
)
|
||||
return slot.contents, slot.nodelist, slot.content_func
|
||||
|
||||
|
@ -191,25 +191,36 @@ class SlotFill(Generic[TSlotData]):
|
|||
slot: Slot[TSlotData]
|
||||
|
||||
|
||||
class SlotRef:
|
||||
class SlotFallback:
|
||||
"""
|
||||
SlotRef allows to treat a slot as a variable. The slot is rendered only once
|
||||
SlotFallback allows to treat a slot fallback as a variable. The slot is rendered only once
|
||||
the instance is coerced to string.
|
||||
|
||||
This is used to access slots as variables inside the templates. When a SlotRef
|
||||
This is used to access slots as variables inside the templates. When a `SlotFallback`
|
||||
is rendered in the template with `{{ my_lazy_slot }}`, it will output the contents
|
||||
of the slot.
|
||||
|
||||
Usage in slot functions:
|
||||
|
||||
```py
|
||||
def slot_function(self, ctx: Context, slot_data: TSlotData, fallback: SlotFallback):
|
||||
return f"Hello, {fallback}!"
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(self, slot: "SlotNode", context: Context):
|
||||
self._slot = slot
|
||||
self._context = context
|
||||
|
||||
# Render the slot when the template coerces SlotRef to string
|
||||
# Render the slot when the template coerces SlotFallback to string
|
||||
def __str__(self) -> str:
|
||||
return mark_safe(self._slot.nodelist.render(self._context))
|
||||
|
||||
|
||||
# TODO_v1 - REMOVE - superseded by SlotFallback
|
||||
SlotRef = SlotFallback
|
||||
|
||||
|
||||
class SlotIsFilled(dict):
|
||||
"""
|
||||
Dictionary that returns `True` if the slot is filled (key is found), `False` otherwise.
|
||||
|
@ -312,13 +323,13 @@ class SlotNode(BaseNode):
|
|||
\"\"\"
|
||||
```
|
||||
|
||||
### Accessing default slot content
|
||||
### Accessing fallback slot content
|
||||
|
||||
The content between the `{% slot %}..{% endslot %}` tags is the default content that
|
||||
The content between the `{% slot %}..{% endslot %}` tags is the fallback content that
|
||||
will be rendered if no fill is given for the slot.
|
||||
|
||||
This default content can then be accessed from within the [`{% fill %}`](#fill) tag using
|
||||
the fill's `default` kwarg.
|
||||
This fallback content can then be accessed from within the [`{% fill %}`](#fill) tag using
|
||||
the fill's `fallback` kwarg.
|
||||
This is useful if you need to wrap / prepend / append the original slot's content.
|
||||
|
||||
```python
|
||||
|
@ -327,7 +338,7 @@ class SlotNode(BaseNode):
|
|||
template = \"\"\"
|
||||
<div>
|
||||
{% slot "content" %}
|
||||
This is default content!
|
||||
This is fallback content!
|
||||
{% endslot %}
|
||||
</div>
|
||||
\"\"\"
|
||||
|
@ -337,10 +348,10 @@ class SlotNode(BaseNode):
|
|||
@register("parent")
|
||||
class Parent(Component):
|
||||
template = \"\"\"
|
||||
{# Parent can access the slot's default content #}
|
||||
{# Parent can access the slot's fallback content #}
|
||||
{% component "child" %}
|
||||
{% fill "content" default="default" %}
|
||||
{{ default }}
|
||||
{% fill "content" fallback="fallback" %}
|
||||
{{ fallback }}
|
||||
{% endfill %}
|
||||
{% endcomponent %}
|
||||
\"\"\"
|
||||
|
@ -547,7 +558,7 @@ class SlotNode(BaseNode):
|
|||
slot=slot_fill_fn,
|
||||
)
|
||||
else:
|
||||
# No fill was supplied, render the slot's default content
|
||||
# No fill was supplied, render the slot's fallback content
|
||||
slot_fill = SlotFill(
|
||||
name=slot_name,
|
||||
is_filled=False,
|
||||
|
@ -557,7 +568,7 @@ class SlotNode(BaseNode):
|
|||
nodelist=self.nodelist,
|
||||
contents=self.contents,
|
||||
data_var=None,
|
||||
default_var=None,
|
||||
fallback_var=None,
|
||||
# Escaped because this was defined in the template
|
||||
escaped=True,
|
||||
),
|
||||
|
@ -625,7 +636,7 @@ class SlotNode(BaseNode):
|
|||
if key.startswith(_INJECT_CONTEXT_KEY_PREFIX):
|
||||
extra_context[key] = value
|
||||
|
||||
slot_ref = SlotRef(self, context)
|
||||
slot_ref = SlotFallback(self, context)
|
||||
|
||||
# For the user-provided slot fill, we want to use the context of where the slot
|
||||
# came from (or current context if configured so)
|
||||
|
@ -644,7 +655,7 @@ class SlotNode(BaseNode):
|
|||
with used_ctx.render_context.push(render_ctx_layer):
|
||||
with add_slot_to_error_message(component_name, slot_name):
|
||||
# Render slot as a function
|
||||
# NOTE: While `{% fill %}` tag has to opt in for the `default` and `data` variables,
|
||||
# NOTE: While `{% fill %}` tag has to opt in for the `fallback` and `data` variables,
|
||||
# the render function ALWAYS receives them.
|
||||
output = slot_fill.slot(used_ctx, kwargs, slot_ref)
|
||||
|
||||
|
@ -669,7 +680,7 @@ class SlotNode(BaseNode):
|
|||
component_ctx: "ComponentContext",
|
||||
) -> Context:
|
||||
"""Prepare the context used in a slot fill based on the settings."""
|
||||
# If slot is NOT filled, we use the slot's default AKA content between
|
||||
# If slot is NOT filled, we use the slot's fallback AKA content between
|
||||
# the `{% slot %}` tags. These should be evaluated as if the `{% slot %}`
|
||||
# tags weren't even there, which means that we use the current context.
|
||||
if not slot_fill.is_filled:
|
||||
|
@ -696,11 +707,10 @@ class FillNode(BaseNode):
|
|||
|
||||
- `name` (str, required): Name of the slot to insert this content into. Use `"default"` for
|
||||
the default slot.
|
||||
- `default` (str, optional): This argument allows you to access the original content of the slot
|
||||
under the specified variable name. See
|
||||
[Accessing original content of slots](../../concepts/fundamentals/slots#accessing-original-content-of-slots)
|
||||
- `fallback` (str, optional): This argument allows you to access the original content of the slot
|
||||
under the specified variable name. See [Slot fallback](../../concepts/fundamentals/slots#slot-fallback).
|
||||
- `data` (str, optional): This argument allows you to access the data passed to the slot
|
||||
under the specified variable name. See [Scoped slots](../../concepts/fundamentals/slots#scoped-slots)
|
||||
under the specified variable name. See [Slot data](../../concepts/fundamentals/slots#slot-data).
|
||||
|
||||
**Examples:**
|
||||
|
||||
|
@ -713,7 +723,7 @@ class FillNode(BaseNode):
|
|||
{% endcomponent %}
|
||||
```
|
||||
|
||||
### Accessing slot's default content with the `default` kwarg
|
||||
### Accessing slot's fallback content with the `fallback` kwarg
|
||||
|
||||
```django
|
||||
{# my_table.html #}
|
||||
|
@ -727,9 +737,9 @@ class FillNode(BaseNode):
|
|||
|
||||
```django
|
||||
{% component "my_table" %}
|
||||
{% fill "pagination" default="default_pag" %}
|
||||
{% fill "pagination" fallback="fallback" %}
|
||||
<div class="my-class">
|
||||
{{ default_pag }}
|
||||
{{ fallback }}
|
||||
</div>
|
||||
{% endfill %}
|
||||
{% endcomponent %}
|
||||
|
@ -759,16 +769,16 @@ class FillNode(BaseNode):
|
|||
{% endcomponent %}
|
||||
```
|
||||
|
||||
### Accessing slot data and default content on the default slot
|
||||
### Accessing slot data and fallback content on the default slot
|
||||
|
||||
To access slot data and the default slot content on the default slot,
|
||||
To access slot data and the fallback slot content on the default slot,
|
||||
use `{% fill %}` with `name` set to `"default"`:
|
||||
|
||||
```django
|
||||
{% component "button" %}
|
||||
{% fill name="default" data="slot_data" default="default_slot" %}
|
||||
{% fill name="default" data="slot_data" fallback="slot_fallback" %}
|
||||
You clicked me {{ slot_data.count }} times!
|
||||
{{ default_slot }}
|
||||
{{ slot_fallback }}
|
||||
{% endfill %}
|
||||
{% endcomponent %}
|
||||
```
|
||||
|
@ -778,7 +788,25 @@ class FillNode(BaseNode):
|
|||
end_tag = "endfill"
|
||||
allowed_flags = []
|
||||
|
||||
def render(self, context: Context, name: str, *, data: Optional[str] = None, default: Optional[str] = None) -> str:
|
||||
def render(
|
||||
self,
|
||||
context: Context,
|
||||
name: str,
|
||||
*,
|
||||
data: Optional[str] = None,
|
||||
fallback: Optional[str] = None,
|
||||
# TODO_V1: Use `fallback` kwarg instead of `default`
|
||||
default: Optional[str] = None,
|
||||
) -> str:
|
||||
# TODO_V1: Use `fallback` kwarg instead of `default`
|
||||
if fallback is not None and default is not None:
|
||||
raise TemplateSyntaxError(
|
||||
f"Fill tag received both 'default' and '{FILL_FALLBACK_KWARG}' kwargs. "
|
||||
f"Use '{FILL_FALLBACK_KWARG}' instead."
|
||||
)
|
||||
elif fallback is None and default is not None:
|
||||
fallback = default
|
||||
|
||||
if not _is_extracting_fill(context):
|
||||
raise TemplateSyntaxError(
|
||||
"FillNode.render() (AKA {% fill ... %} block) cannot be rendered outside of a Component context. "
|
||||
|
@ -797,28 +825,28 @@ class FillNode(BaseNode):
|
|||
f"Fill tag kwarg '{FILL_DATA_KWARG}' does not resolve to a valid Python identifier, got '{data}'"
|
||||
)
|
||||
|
||||
if default is not None:
|
||||
if not isinstance(default, str):
|
||||
if fallback is not None:
|
||||
if not isinstance(fallback, str):
|
||||
raise TemplateSyntaxError(
|
||||
f"Fill tag '{FILL_DEFAULT_KWARG}' kwarg must resolve to a string, got {default}"
|
||||
f"Fill tag '{FILL_FALLBACK_KWARG}' kwarg must resolve to a string, got {fallback}"
|
||||
)
|
||||
if not is_identifier(default):
|
||||
if not is_identifier(fallback):
|
||||
raise RuntimeError(
|
||||
f"Fill tag kwarg '{FILL_DEFAULT_KWARG}' does not resolve to a valid Python identifier,"
|
||||
f" got '{default}'"
|
||||
f"Fill tag kwarg '{FILL_FALLBACK_KWARG}' does not resolve to a valid Python identifier,"
|
||||
f" got '{fallback}'"
|
||||
)
|
||||
|
||||
# data and default cannot be bound to the same variable
|
||||
if data and default and data == default:
|
||||
# data and fallback cannot be bound to the same variable
|
||||
if data and fallback and data == fallback:
|
||||
raise RuntimeError(
|
||||
f"Fill '{name}' received the same string for slot default ({FILL_DEFAULT_KWARG}=...)"
|
||||
f"Fill '{name}' received the same string for slot fallback ({FILL_FALLBACK_KWARG}=...)"
|
||||
f" and slot data ({FILL_DATA_KWARG}=...)"
|
||||
)
|
||||
|
||||
fill_data = FillWithData(
|
||||
fill=self,
|
||||
name=name,
|
||||
default_var=default,
|
||||
fallback_var=fallback,
|
||||
data_var=data,
|
||||
extra_context={},
|
||||
)
|
||||
|
@ -905,7 +933,7 @@ class FillNode(BaseNode):
|
|||
class FillWithData(NamedTuple):
|
||||
fill: FillNode
|
||||
name: str
|
||||
default_var: Optional[str]
|
||||
fallback_var: Optional[str]
|
||||
data_var: Optional[str]
|
||||
extra_context: Dict[str, Any]
|
||||
|
||||
|
@ -989,7 +1017,7 @@ def resolve_fills(
|
|||
nodelist=nodelist,
|
||||
contents=contents,
|
||||
data_var=None,
|
||||
default_var=None,
|
||||
fallback_var=None,
|
||||
# Escaped because this was defined in the template
|
||||
escaped=True,
|
||||
)
|
||||
|
@ -1005,7 +1033,7 @@ def resolve_fills(
|
|||
nodelist=fill.fill.nodelist,
|
||||
contents=fill.fill.contents,
|
||||
data_var=fill.data_var,
|
||||
default_var=fill.default_var,
|
||||
fallback_var=fill.fallback_var,
|
||||
extra_context=fill.extra_context,
|
||||
# Escaped because this was defined in the template
|
||||
escaped=True,
|
||||
|
@ -1080,7 +1108,7 @@ def _nodelist_to_slot(
|
|||
nodelist: NodeList,
|
||||
contents: Optional[str] = None,
|
||||
data_var: Optional[str] = None,
|
||||
default_var: Optional[str] = None,
|
||||
fallback_var: Optional[str] = None,
|
||||
escaped: bool = False,
|
||||
extra_context: Optional[Dict[str, Any]] = None,
|
||||
) -> Slot:
|
||||
|
@ -1090,10 +1118,10 @@ def _nodelist_to_slot(
|
|||
f"Slot data alias in fill '{slot_name}' must be a valid identifier. Got '{data_var}'"
|
||||
)
|
||||
|
||||
if default_var:
|
||||
if not default_var.isidentifier():
|
||||
if fallback_var:
|
||||
if not fallback_var.isidentifier():
|
||||
raise TemplateSyntaxError(
|
||||
f"Slot default alias in fill '{slot_name}' must be a valid identifier. Got '{default_var}'"
|
||||
f"Slot fallback alias in fill '{slot_name}' must be a valid identifier. Got '{fallback_var}'"
|
||||
)
|
||||
|
||||
# We use Template.render() to render the nodelist, so that Django correctly sets up
|
||||
|
@ -1103,17 +1131,17 @@ def _nodelist_to_slot(
|
|||
# This allows the template to access current RenderContext layer.
|
||||
template._djc_is_component_nested = True
|
||||
|
||||
def render_func(ctx: Context, slot_data: Dict[str, Any], slot_ref: SlotRef) -> SlotResult:
|
||||
def render_func(ctx: Context, slot_data: Dict[str, Any], slot_ref: SlotFallback) -> SlotResult:
|
||||
# Expose the kwargs that were passed to the `{% slot %}` tag. These kwargs
|
||||
# are made available through a variable name that was set on the `{% fill %}`
|
||||
# tag.
|
||||
if data_var:
|
||||
ctx[data_var] = slot_data
|
||||
|
||||
# If slot fill is using `{% fill "myslot" default="abc" %}`, then set the "abc" to
|
||||
# the context, so users can refer to the default slot from within the fill content.
|
||||
if default_var:
|
||||
ctx[default_var] = slot_ref
|
||||
# If slot fill is using `{% fill "myslot" fallback="abc" %}`, then set the "abc" to
|
||||
# the context, so users can refer to the fallback slot from within the fill content.
|
||||
if fallback_var:
|
||||
ctx[fallback_var] = slot_ref
|
||||
|
||||
# NOTE: If a `{% fill %}` tag inside a `{% component %}` tag is inside a forloop,
|
||||
# the `extra_context` contains the forloop variables. We want to make these available
|
||||
|
|
|
@ -12,7 +12,7 @@ from django.template.base import NodeList, TextNode
|
|||
from pytest_django.asserts import assertHTMLEqual
|
||||
|
||||
from django_components import Component, register, types
|
||||
from django_components.slots import Slot, SlotRef
|
||||
from django_components.slots import Slot, SlotFallback
|
||||
|
||||
from django_components.testing import djc_test
|
||||
from .testutils import PARAMETRIZE_CONTEXT_BEHAVIOR, setup_test_config
|
||||
|
@ -49,7 +49,7 @@ class TestSlot:
|
|||
"kwargs": kwargs,
|
||||
}
|
||||
|
||||
def first_slot(ctx: Context, slot_data: Dict, slot_ref: SlotRef):
|
||||
def first_slot(ctx: Context, slot_data: Dict, slot_ref: SlotFallback):
|
||||
assert isinstance(ctx, Context)
|
||||
# NOTE: Since the slot has access to the Context object, it should behave
|
||||
# the same way as it does in templates - when in "isolated" mode, then the
|
||||
|
@ -72,7 +72,7 @@ class TestSlot:
|
|||
}
|
||||
assert slot_data_expected == slot_data
|
||||
|
||||
assert isinstance(slot_ref, SlotRef)
|
||||
assert isinstance(slot_ref, SlotFallback)
|
||||
assert "SLOT_DEFAULT" == str(slot_ref).strip()
|
||||
|
||||
return f"FROM_INSIDE_FIRST_SLOT | {slot_ref}"
|
||||
|
|
|
@ -895,8 +895,8 @@ class TestPassthroughSlots:
|
|||
{% component "test" %}
|
||||
{% if slot_names %}
|
||||
{% for slot in slot_names %}
|
||||
{% fill name=slot default="default" %}
|
||||
OVERRIDEN_SLOT "{{ slot }}" - INDEX {{ forloop.counter0 }} - ORIGINAL "{{ default }}"
|
||||
{% fill name=slot fallback="fallback" %}
|
||||
OVERRIDEN_SLOT "{{ slot }}" - INDEX {{ forloop.counter0 }} - ORIGINAL "{{ fallback }}"
|
||||
{% endfill %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
@ -950,8 +950,8 @@ class TestPassthroughSlots:
|
|||
{% load component_tags %}
|
||||
{% component "test" %}
|
||||
{% with slot="header" %}
|
||||
{% fill name=slot default="default" %}
|
||||
OVERRIDEN_SLOT "{{ slot }}" - ORIGINAL "{{ default }}"
|
||||
{% fill name=slot fallback="fallback" %}
|
||||
OVERRIDEN_SLOT "{{ slot }}" - ORIGINAL "{{ fallback }}"
|
||||
{% endfill %}
|
||||
{% endwith %}
|
||||
{% endcomponent %}
|
||||
|
@ -996,7 +996,7 @@ class TestPassthroughSlots:
|
|||
{% if slot_names %}
|
||||
{% for slot in slot_names %}
|
||||
{{ forloop.counter0 }}
|
||||
{% fill name=slot default="default" %}
|
||||
{% fill name=slot fallback="fallback" %}
|
||||
OVERRIDEN_SLOT
|
||||
{% endfill %}
|
||||
{% endfor %}
|
||||
|
@ -1365,9 +1365,11 @@ class TestSlottedTemplateRegression:
|
|||
|
||||
|
||||
@djc_test
|
||||
class TestSlotDefault:
|
||||
class TestSlotFallback:
|
||||
|
||||
# TODO_v1 - REMOVE
|
||||
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
|
||||
def test_basic(self, components_settings):
|
||||
def test_basic_legacy(self, components_settings):
|
||||
registry.register("test", _gen_slotted_component())
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -1391,13 +1393,38 @@ class TestSlotDefault:
|
|||
""",
|
||||
)
|
||||
|
||||
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
|
||||
def test_basic(self, components_settings):
|
||||
registry.register("test", _gen_slotted_component())
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component "test" %}
|
||||
{% fill "header" fallback="header" %}Before: {{ header }}{% endfill %}
|
||||
{% fill "main" fallback="main" %}{{ main }}{% endfill %}
|
||||
{% fill "footer" fallback="footer" %}{{ footer }}, after{% endfill %}
|
||||
{% endcomponent %}
|
||||
"""
|
||||
template = Template(template_str)
|
||||
rendered = template.render(Context({}))
|
||||
|
||||
assertHTMLEqual(
|
||||
rendered,
|
||||
"""
|
||||
<custom-template data-djc-id-ca1bc42>
|
||||
<header>Before: Default header</header>
|
||||
<main>Default main</main>
|
||||
<footer>Default footer, after</footer>
|
||||
</custom-template>
|
||||
""",
|
||||
)
|
||||
|
||||
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
|
||||
def test_multiple_calls(self, components_settings):
|
||||
registry.register("test", _gen_slotted_component())
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component "test" %}
|
||||
{% fill "header" default="header" %}
|
||||
{% fill "header" fallback="header" %}
|
||||
First: {{ header }};
|
||||
Second: {{ header }}
|
||||
{% endfill %}
|
||||
|
@ -1423,7 +1450,7 @@ class TestSlotDefault:
|
|||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component "test" %}
|
||||
{% fill "header" default="header" %}
|
||||
{% fill "header" fallback="header" %}
|
||||
{% for i in range %}
|
||||
{% if forloop.first %}
|
||||
First {{ header }}
|
||||
|
@ -1454,14 +1481,14 @@ class TestSlotDefault:
|
|||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component "test" %}
|
||||
{% fill "header" default="header1" %}
|
||||
{% fill "header" fallback="header1" %}
|
||||
header1_in_header1: {{ header1 }}
|
||||
{% component "test" %}
|
||||
{% fill "header" default="header2" %}
|
||||
{% fill "header" fallback="header2" %}
|
||||
header1_in_header2: {{ header1 }}
|
||||
header2_in_header2: {{ header2 }}
|
||||
{% endfill %}
|
||||
{% fill "footer" default="footer2" %}
|
||||
{% fill "footer" fallback="footer2" %}
|
||||
header1_in_footer2: {{ header1 }}
|
||||
footer2_in_footer2: {{ footer2 }}
|
||||
{% endfill %}
|
||||
|
@ -1570,7 +1597,7 @@ class TestScopedSlot:
|
|||
assertHTMLEqual(rendered, expected)
|
||||
|
||||
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
|
||||
def test_slot_data_with_slot_default(self, components_settings):
|
||||
def test_slot_data_with_slot_fallback(self, components_settings):
|
||||
@register("test")
|
||||
class TestComponent(Component):
|
||||
template: types.django_html = """
|
||||
|
@ -1589,8 +1616,8 @@ class TestScopedSlot:
|
|||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component "test" %}
|
||||
{% fill "my_slot" data="slot_data_in_fill" default="slot_var" %}
|
||||
{{ slot_var }}
|
||||
{% fill "my_slot" data="slot_data_in_fill" fallback="fallback" %}
|
||||
{{ fallback }}
|
||||
{{ slot_data_in_fill.abc }}
|
||||
{{ slot_data_in_fill.var123 }}
|
||||
{% endfill %}
|
||||
|
@ -1681,7 +1708,7 @@ class TestScopedSlot:
|
|||
assertHTMLEqual(rendered, expected)
|
||||
|
||||
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
|
||||
def test_slot_data_and_default_on_default_slot(self, components_settings):
|
||||
def test_slot_data_and_fallback_on_default_slot(self, components_settings):
|
||||
@register("test")
|
||||
class TestComponent(Component):
|
||||
template: types.django_html = """
|
||||
|
@ -1701,7 +1728,7 @@ class TestScopedSlot:
|
|||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component "test" %}
|
||||
{% fill name="default" data="slot_data_in_fill" default="slot_var" %}
|
||||
{% fill name="default" data="slot_data_in_fill" fallback="slot_var" %}
|
||||
{{ slot_data_in_fill.abc }}
|
||||
{{ slot_var }}
|
||||
{{ slot_data_in_fill.var123 }}
|
||||
|
@ -1718,7 +1745,7 @@ class TestScopedSlot:
|
|||
assertHTMLEqual(rendered, expected)
|
||||
|
||||
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
|
||||
def test_slot_data_raises_on_slot_data_and_slot_default_same_var(self, components_settings):
|
||||
def test_slot_data_raises_on_slot_data_and_slot_fallback_same_var(self, components_settings):
|
||||
@register("test")
|
||||
class TestComponent(Component):
|
||||
template: types.django_html = """
|
||||
|
@ -1737,14 +1764,14 @@ class TestScopedSlot:
|
|||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component "test" %}
|
||||
{% fill "my_slot" data="slot_var" default="slot_var" %}
|
||||
{% fill "my_slot" data="slot_var" fallback="slot_var" %}
|
||||
{{ slot_var }}
|
||||
{% endfill %}
|
||||
{% endcomponent %}
|
||||
"""
|
||||
with pytest.raises(
|
||||
RuntimeError,
|
||||
match=re.escape("Fill 'my_slot' received the same string for slot default (default=...) and slot data (data=...)"), # noqa: E501
|
||||
match=re.escape("Fill 'my_slot' received the same string for slot fallback (fallback=...) and slot data (data=...)"), # noqa: E501
|
||||
):
|
||||
Template(template).render(Context())
|
||||
|
||||
|
|
|
@ -445,7 +445,7 @@ class TestSlotIteration:
|
|||
["django", "isolated"],
|
||||
)
|
||||
)
|
||||
def test_inner_slot_iteration_nested_with_slot_default(self, components_settings, expected):
|
||||
def test_inner_slot_iteration_nested_with_slot_fallback(self, components_settings, expected):
|
||||
registry.register("slot_in_a_loop", self._get_component_simple_slot_in_a_loop())
|
||||
|
||||
objects = [
|
||||
|
@ -458,7 +458,7 @@ class TestSlotIteration:
|
|||
{% component "slot_in_a_loop" objects=objects %}
|
||||
{% fill "slot_inner" %}
|
||||
{% component "slot_in_a_loop" objects=object.inner %}
|
||||
{% fill "slot_inner" default="super_slot_inner" %}
|
||||
{% fill "slot_inner" fallback="super_slot_inner" %}
|
||||
{{ super_slot_inner }}
|
||||
{% endfill %}
|
||||
{% endcomponent %}
|
||||
|
@ -498,7 +498,7 @@ class TestSlotIteration:
|
|||
["django", "isolated"],
|
||||
)
|
||||
)
|
||||
def test_inner_slot_iteration_nested_with_slot_default_and_outer_scope_variable(
|
||||
def test_inner_slot_iteration_nested_with_slot_fallback_and_outer_scope_variable(
|
||||
self,
|
||||
components_settings,
|
||||
expected,
|
||||
|
@ -516,7 +516,7 @@ class TestSlotIteration:
|
|||
{% fill "slot_inner" %}
|
||||
{{ outer_scope_variable_1 }}
|
||||
{% component "slot_in_a_loop" objects=object.inner %}
|
||||
{% fill "slot_inner" default="super_slot_inner" %}
|
||||
{% fill "slot_inner" fallback="super_slot_inner" %}
|
||||
{{ outer_scope_variable_2 }}
|
||||
{{ super_slot_inner }}
|
||||
{% endfill %}
|
||||
|
@ -537,7 +537,7 @@ class TestSlotIteration:
|
|||
assertHTMLEqual(rendered, expected)
|
||||
|
||||
@djc_test(components_settings={"context_behavior": "isolated"})
|
||||
def test_inner_slot_iteration_nested_with_slot_default_and_outer_scope_variable__isolated_2(
|
||||
def test_inner_slot_iteration_nested_with_slot_fallback_and_outer_scope_variable__isolated_2(
|
||||
self,
|
||||
):
|
||||
registry.register("slot_in_a_loop", self._get_component_simple_slot_in_a_loop())
|
||||
|
@ -556,7 +556,7 @@ class TestSlotIteration:
|
|||
{% fill "slot_inner" %}
|
||||
{{ outer_scope_variable_1|safe }}
|
||||
{% component "slot_in_a_loop" objects=objects %}
|
||||
{% fill "slot_inner" default="super_slot_inner" %}
|
||||
{% fill "slot_inner" fallback="super_slot_inner" %}
|
||||
{{ outer_scope_variable_2|safe }}
|
||||
{{ super_slot_inner }}
|
||||
{% endfill %}
|
||||
|
@ -959,14 +959,14 @@ class TestComponentNesting:
|
|||
["django", "isolated"],
|
||||
)
|
||||
)
|
||||
def test_component_nesting_component_with_slot_default(self, components_settings, expected):
|
||||
def test_component_nesting_component_with_slot_fallback(self, components_settings, expected):
|
||||
registry.register("dashboard", self._get_dashboard_component())
|
||||
registry.register("calendar", self._get_calendar_component())
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component "dashboard" %}
|
||||
{% fill "header" default="h" %} Hello! {{ h }} {% endfill %}
|
||||
{% fill "header" fallback="h" %} Hello! {{ h }} {% endfill %}
|
||||
{% endcomponent %}
|
||||
"""
|
||||
template = Template(template_str)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue