mirror of
https://github.com/django-components/django-components.git
synced 2025-08-04 06:18:17 +00:00
refactor: Use top-level exports as public API (#562)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
d819f3ff49
commit
e771a0aaaf
30 changed files with 615 additions and 598 deletions
259
README.md
259
README.md
|
@ -1,4 +1,5 @@
|
|||
# <img src="logo/logo-black-on-white.svg" alt="django-components" style="max-width: 100%; background: white; color: black;">
|
||||
|
||||
<a href="https://github.com/EmilStenstrom/django-components/actions?query=workflow%3A%22Run+tests%22"><img align="right" src="https://github.com/EmilStenstrom/django-components/workflows/Run%20tests/badge.svg" alt="Show test status"></a>
|
||||
<a href="https://pepy.tech/project/django-components"><img align="right" src="https://pepy.tech/badge/django-components" alt="Show download stats"></a>
|
||||
|
||||
|
@ -47,11 +48,12 @@ And this is what gets rendered (plus the CSS and Javascript you've specified):
|
|||
## Release notes
|
||||
|
||||
🚨📢 **Version 0.85** Autodiscovery module resolution changed. Following undocumented behavior was removed:
|
||||
|
||||
- Previously, autodiscovery also imported any `[app]/components.py` files, and used `SETTINGS_MODULE` to search for component dirs.
|
||||
- To migrate from:
|
||||
- `[app]/components.py` - Define each module in `COMPONENTS.libraries` setting,
|
||||
or import each module inside the `AppConfig.ready()` hook in respective `apps.py` files.
|
||||
- `SETTINGS_MODULE` - Define component dirs using `STATICFILES_DIRS`
|
||||
- To migrate from:
|
||||
- `[app]/components.py` - Define each module in `COMPONENTS.libraries` setting,
|
||||
or import each module inside the `AppConfig.ready()` hook in respective `apps.py` files.
|
||||
- `SETTINGS_MODULE` - Define component dirs using `STATICFILES_DIRS`
|
||||
- Previously, autodiscovery handled relative files in `STATICFILES_DIRS`. To align with Django, `STATICFILES_DIRS` now must be full paths ([Django docs](https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-STATICFILES_DIRS)).
|
||||
|
||||
🚨📢 **Version 0.81** Aligned the `render_to_response` method with the (now public) `render` method of `Component` class. Moreover, slots passed to these can now be rendered also as functions.
|
||||
|
@ -64,12 +66,12 @@ And this is what gets rendered (plus the CSS and Javascript you've specified):
|
|||
|
||||
- BREAKING CHANGE: Default value for the `COMPONENTS.context_behavior` setting was changes from `"isolated"` to `"django"`. If you did not set this value explicitly before, this may be a breaking change. See the rationale for change [here](https://github.com/EmilStenstrom/django-components/issues/498).
|
||||
|
||||
|
||||
🚨📢 **Version 0.77** CHANGED the syntax for accessing default slot content.
|
||||
|
||||
- Previously, the syntax was
|
||||
`{% fill "my_slot" as "alias" %}` and `{{ alias.default }}`.
|
||||
`{% fill "my_slot" as "alias" %}` and `{{ alias.default }}`.
|
||||
- Now, the syntax is
|
||||
`{% fill "my_slot" default="alias" %}` and `{{ alias }}`.
|
||||
`{% fill "my_slot" default="alias" %}` and `{{ alias }}`.
|
||||
|
||||
**Version 0.74** introduces `html_attrs` tag and `prefix:key=val` construct for passing dicts to components.
|
||||
|
||||
|
@ -88,7 +90,7 @@ This change is done to simplify the API in anticipation of a 1.0 release of djan
|
|||
|
||||
**Version 0.28** introduces 'implicit' slot filling and the `default` option for `slot` tags.
|
||||
|
||||
**Version 0.27** adds a second installable app: *django_components.safer_staticfiles*. It provides the same behavior as *django.contrib.staticfiles* but with extra security guarantees (more info below in Security Notes).
|
||||
**Version 0.27** adds a second installable app: _django_components.safer_staticfiles_. It provides the same behavior as _django.contrib.staticfiles_ but with extra security guarantees (more info below in Security Notes).
|
||||
|
||||
**Version 0.26** changes the syntax for `{% slot %}` tags. From now on, we separate defining a slot (`{% slot %}`) from filling a slot with content (`{% fill %}`). This means you will likely need to change a lot of slot tags to fill. We understand this is annoying, but it's the only way we can get support for nested slots that fill in other slots, which is a very nice featuPpre to have access to. Hoping that this will feel worth it!
|
||||
|
||||
|
@ -98,7 +100,7 @@ This change is done to simplify the API in anticipation of a 1.0 release of djan
|
|||
|
||||
## Security notes 🚨
|
||||
|
||||
*You are advised to read this section before using django-components in production.*
|
||||
_You are advised to read this section before using django-components in production._
|
||||
|
||||
### Static files
|
||||
|
||||
|
@ -108,12 +110,12 @@ This means that files containing backend logic, such as Python modules and HTML
|
|||
|
||||
If your are using _django.contrib.staticfiles_ to collect static files, no distinction is made between the different kinds of files.
|
||||
As a result, your Python code and templates may inadvertently become available on your static file server.
|
||||
You probably don't want this, as parts of your backend logic will be exposed, posing a __potential security vulnerability__.
|
||||
You probably don't want this, as parts of your backend logic will be exposed, posing a **potential security vulnerability**.
|
||||
|
||||
As of *v0.27*, django-components ships with an additional installable app *django_components.__safer_staticfiles__*.
|
||||
It is a drop-in replacement for *django.contrib.staticfiles*.
|
||||
As of _v0.27_, django-components ships with an additional installable app _django_components.**safer_staticfiles**_.
|
||||
It is a drop-in replacement for _django.contrib.staticfiles_.
|
||||
Its behavior is 100% identical except it ignores .py and .html files, meaning these will not end up on your static files server.
|
||||
To use it, add it to INSTALLED_APPS and remove _django.contrib.staticfiles_.
|
||||
To use it, add it to INSTALLED*APPS and remove \_django.contrib.staticfiles*.
|
||||
|
||||
```python
|
||||
INSTALLED_APPS = [
|
||||
|
@ -163,7 +165,8 @@ For a step-by-step guide on deploying production server with static files,
|
|||
```
|
||||
|
||||
4. Modify `TEMPLATES` section of settings.py as follows:
|
||||
- *Remove `'APP_DIRS': True,`*
|
||||
|
||||
- _Remove `'APP_DIRS': True,`_
|
||||
- Add `loaders` to `OPTIONS` list and set it to following value:
|
||||
|
||||
```python
|
||||
|
@ -226,13 +229,13 @@ Read on to find out how to build your first component!
|
|||
|
||||
Django-components supports all supported combinations versions of [Django](https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django) and [Python](https://devguide.python.org/versions/#versions).
|
||||
|
||||
| Python version | Django version |
|
||||
|----------------|--------------------------|
|
||||
| 3.8 | 4.2 |
|
||||
| 3.9 | 4.2 |
|
||||
| 3.10 | 4.2, 5.0 |
|
||||
| 3.11 | 4.2, 5.0 |
|
||||
| 3.12 | 4.2, 5.0 |
|
||||
| Python version | Django version |
|
||||
| -------------- | -------------- |
|
||||
| 3.8 | 4.2 |
|
||||
| 3.9 | 4.2 |
|
||||
| 3.10 | 4.2, 5.0 |
|
||||
| 3.11 | 4.2, 5.0 |
|
||||
| 3.12 | 4.2, 5.0 |
|
||||
|
||||
## Create your first component
|
||||
|
||||
|
@ -293,10 +296,10 @@ Inside this file we create a Component by inheriting from the Component class an
|
|||
|
||||
```python
|
||||
# In a file called [project root]/components/calendar/calendar.py
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
# Templates inside `[your apps]/components` dir and `[project root]/components` dir
|
||||
# will be automatically found. To customize which template to use based on context
|
||||
# you can override method `get_template_name` instead of specifying `template_name`.
|
||||
|
@ -324,26 +327,25 @@ Components can also be defined in a single file, which is useful for small compo
|
|||
|
||||
```python
|
||||
# In a file called [project root]/components/calendar.py
|
||||
from django_components import component
|
||||
from django_components import types as t
|
||||
from django_components import Component, register, types
|
||||
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
def get_context_data(self, date):
|
||||
return {
|
||||
"date": date,
|
||||
}
|
||||
|
||||
template: t.django_html = """
|
||||
template: types.django_html = """
|
||||
<div class="calendar-component">Today's date is <span>{{ date }}</span></div>
|
||||
"""
|
||||
|
||||
css: t.css = """
|
||||
css: types.css = """
|
||||
.calendar-component { width: 200px; background: pink; }
|
||||
.calendar-component span { font-weight: bold; }
|
||||
"""
|
||||
|
||||
js: t.js = """
|
||||
js: types.js = """
|
||||
(function(){
|
||||
if (document.querySelector(".calendar-component")) {
|
||||
document.querySelector(".calendar-component").onclick = function(){ alert("Clicked calendar!"); };
|
||||
|
@ -365,10 +367,10 @@ Note, in the above example, that the `t.django_html`, `t.css`, and `t.js` types
|
|||
If you're a Pycharm user (or any other editor from Jetbrains), you can have coding assistance as well:
|
||||
|
||||
```python
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
def get_context_data(self, date):
|
||||
return {
|
||||
"date": date,
|
||||
|
@ -395,8 +397,8 @@ class Calendar(component.Component):
|
|||
"""
|
||||
```
|
||||
|
||||
You don't need to use `t.django_html`, `t.css`, `t.js` and `from django_components import types as t` since Pycharm uses [language injections](https://www.jetbrains.com/help/pycharm/using-language-injections.html).
|
||||
You only need to write the comments `# language=<lang>` above the variables.
|
||||
You don't need to use `types.django_html`, `types.css`, `types.js` since Pycharm uses [language injections](https://www.jetbrains.com/help/pycharm/using-language-injections.html).
|
||||
You only need to write the comments `# language=<lang>` above the variables.
|
||||
|
||||
## Use components in templates
|
||||
|
||||
|
@ -452,7 +454,7 @@ Components can be rendered outside of Django templates, calling them as regular
|
|||
The component class defines `render` and `render_to_response` class methods. These methods accept positional args, kwargs, and slots, offering the same flexibility as the `{% component %}` tag:
|
||||
|
||||
```py
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template = """
|
||||
{% load component_tags %}
|
||||
hello: {{ hello }}
|
||||
|
@ -500,21 +502,21 @@ Component.render(
|
|||
```
|
||||
|
||||
- _`args`_ - Positional args for the component. This is the same as calling the component
|
||||
as `{% component "my_comp" arg1 arg2 ... %}`
|
||||
as `{% component "my_comp" arg1 arg2 ... %}`
|
||||
|
||||
- _`kwargs`_ - Keyword args for the component. This is the same as calling the component
|
||||
as `{% component "my_comp" key1=val1 key2=val2 ... %}`
|
||||
as `{% component "my_comp" key1=val1 key2=val2 ... %}`
|
||||
|
||||
- _`slots`_ - Component slot fills. This is the same as pasing `{% fill %}` tags to the component.
|
||||
Accepts a dictionary of `{ slot_name: slot_content }` where `slot_content` can be a string
|
||||
or [`SlotRenderFunc`](#slotrenderfunc).
|
||||
Accepts a dictionary of `{ slot_name: slot_content }` where `slot_content` can be a string
|
||||
or [`SlotRenderFunc`](#slotrenderfunc).
|
||||
|
||||
- _`escape_slots_content`_ - Whether the content from `slots` should be escaped. `True` by default to prevent XSS attacks. If you disable escaping, you should make sure that any content you pass to the slots is safe, especially if it comes from user input.
|
||||
|
||||
- _`context`_ - A context (dictionary or Django's Context) within which the component
|
||||
is rendered. The keys on the context can be accessed from within the template.
|
||||
- NOTE: In "isolated" mode, context is NOT accessible, and data MUST be passed via
|
||||
component's args and kwargs.
|
||||
is rendered. The keys on the context can be accessed from within the template.
|
||||
- NOTE: In "isolated" mode, context is NOT accessible, and data MUST be passed via
|
||||
component's args and kwargs.
|
||||
|
||||
#### `SlotRenderFunc`
|
||||
|
||||
|
@ -534,9 +536,10 @@ def render_func(
|
|||
- _`context`_ - Django's Context available to the Slot Node.
|
||||
- _`data`_ - Data passed to the `{% slot %}` tag. See [Scoped Slots](#scoped-slots).
|
||||
- _`slot_ref`_ - The default slot content. See [Accessing original content of slots](#accessing-original-content-of-slots).
|
||||
- NOTE: The slot is lazily evaluated. To render the slot, convert it to string with `str(slot_ref)`.
|
||||
- NOTE: The slot is lazily evaluated. To render the slot, convert it to string with `str(slot_ref)`.
|
||||
|
||||
Example:
|
||||
|
||||
```py
|
||||
def footer_slot(ctx, data, slot_ref):
|
||||
return f"""
|
||||
|
@ -565,7 +568,7 @@ class MyResponse(HttpResponse):
|
|||
self.headers = ...
|
||||
self.status = ...
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
response_class = MyResponse
|
||||
template: types.django_html = "HELLO"
|
||||
|
||||
|
@ -585,10 +588,10 @@ Here's an example of a calendar component defined as a view:
|
|||
|
||||
```python
|
||||
# In a file called [project root]/components/calendar.py
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
|
||||
template = """
|
||||
<div class="calendar-component">
|
||||
|
@ -642,13 +645,13 @@ If you're planning on passing an HTML string, check Django's use of [`format_htm
|
|||
|
||||
## Autodiscovery
|
||||
|
||||
Every component that you want to use in the template with the `{% component %}` tag needs to be registered with the ComponentRegistry. Normally, we use the `@component.register` decorator for that:
|
||||
Every component that you want to use in the template with the `{% component %}` tag needs to be registered with the ComponentRegistry. Normally, we use the `@register` decorator for that:
|
||||
|
||||
```py
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
...
|
||||
```
|
||||
|
||||
|
@ -679,7 +682,7 @@ Autodiscovery occurs when Django is loaded, during the `ready` hook of the `apps
|
|||
If you are using autodiscovery, keep a few points in mind:
|
||||
|
||||
- Avoid defining any logic on the module-level inside the `components` dir, that you would not want to run anyway.
|
||||
- Components inside the auto-imported files still need to be registered with `@component.register()`
|
||||
- Components inside the auto-imported files still need to be registered with `@register()`
|
||||
- Auto-imported component files must be valid Python modules, they must use suffix `.py`, and module name should follow [PEP-8](https://peps.python.org/pep-0008/#package-and-module-names).
|
||||
|
||||
Autodiscovery can be disabled in the [settings](#disable-autodiscovery).
|
||||
|
@ -784,12 +787,8 @@ The rendered result (exactly the same as before):
|
|||
|
||||
```html
|
||||
<div class="calendar-component">
|
||||
<div class="header">
|
||||
Calendar header
|
||||
</div>
|
||||
<div class="body">
|
||||
Can you believe it's already <span>2020-06-06</span>??
|
||||
</div>
|
||||
<div class="header">Calendar header</div>
|
||||
<div class="body">Can you believe it's already <span>2020-06-06</span>??</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
@ -897,17 +896,20 @@ Which you can then use are regular default slot:
|
|||
_Added in version 0.26_
|
||||
|
||||
> NOTE: In version 0.77, the syntax was changed from
|
||||
>
|
||||
> ```django
|
||||
> {% fill "my_slot" as "alias" %} {{ alias.default }}
|
||||
> ```
|
||||
>
|
||||
> to
|
||||
>
|
||||
> ```django
|
||||
> {% fill "my_slot" default="slot_default" %} {{ slot_default }}
|
||||
> ```
|
||||
|
||||
Sometimes you may want to keep the original slot, but only wrap or prepend/append content to it. To do so, you can access the default slot via the `default` kwarg.
|
||||
|
||||
Similarly to the `data` attribute, you specify the variable name through which the default slot will be made available.
|
||||
Similarly to the `data` attribute, you specify the variable name through which the default slot will be made available.
|
||||
|
||||
For instance, let's say you're filling a slot called 'body'. To render the original slot, assign it to a variable using the `'default'` keyword. You then render this variable to insert the default content:
|
||||
|
||||
|
@ -959,11 +961,8 @@ explicit fills, the div containing the slot is still rendered, as shown below:
|
|||
|
||||
```html
|
||||
<div class="frontmatter-component">
|
||||
<div class="title">
|
||||
Title
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
</div>
|
||||
<div class="title">Title</div>
|
||||
<div class="subtitle"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
@ -1033,8 +1032,8 @@ _Added in version 0.76_:
|
|||
Consider a component with slot(s). This component may do some processing on the inputs, and then use the processed variable in the slot's default template:
|
||||
|
||||
```py
|
||||
@component.register("my_comp")
|
||||
class MyComp(component.Component):
|
||||
@register("my_comp")
|
||||
class MyComp(Component):
|
||||
template = """
|
||||
<div>
|
||||
{% slot "content" default %}
|
||||
|
@ -1062,8 +1061,8 @@ Using scoped slots consists of two steps:
|
|||
To pass the data to the `slot` tag, simply pass them as keyword attributes (`key=value`):
|
||||
|
||||
```py
|
||||
@component.register("my_comp")
|
||||
class MyComp(component.Component):
|
||||
@register("my_comp")
|
||||
class MyComp(Component):
|
||||
template = """
|
||||
<div>
|
||||
{% slot "content" default input=input %}
|
||||
|
@ -1153,8 +1152,8 @@ so are still valid:
|
|||
These can then be accessed inside `get_context_data` so:
|
||||
|
||||
```py
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
# Since # . @ - are not valid identifiers, we have to
|
||||
# use `**kwargs` so the method can accept these args.
|
||||
def get_context_data(self, **kwargs):
|
||||
|
@ -1178,8 +1177,8 @@ In such cases, we may want to define some HTML attributes statically, and other
|
|||
But for that, we need to define this dictionary on Python side:
|
||||
|
||||
```py
|
||||
@component.register("my_comp")
|
||||
class MyComp(component.Component):
|
||||
@register("my_comp")
|
||||
class MyComp(Component):
|
||||
template = """
|
||||
{% component "other" attrs=attrs %}
|
||||
{% endcomponent %}
|
||||
|
@ -1206,8 +1205,8 @@ we prefix the key with the name of the dict and `:`. So key `class` of input `at
|
|||
`attrs:class`. And our example becomes:
|
||||
|
||||
```py
|
||||
@component.register("my_comp")
|
||||
class MyComp(component.Component):
|
||||
@register("my_comp")
|
||||
class MyComp(Component):
|
||||
template = """
|
||||
{% component "other"
|
||||
attrs:class="pa-4 flex"
|
||||
|
@ -1296,8 +1295,7 @@ And template:
|
|||
Then this renders:
|
||||
|
||||
```html
|
||||
<div class="text-green">
|
||||
</div>
|
||||
<div class="text-green"></div>
|
||||
```
|
||||
|
||||
### Boolean attributes
|
||||
|
@ -1305,8 +1303,7 @@ Then this renders:
|
|||
In HTML, boolean attributes are usually rendered with no value. Consider the example below where the first button is disabled and the second is not:
|
||||
|
||||
```html
|
||||
<button disabled> Click me! </button>
|
||||
<button> Click me! </button>
|
||||
<button disabled>Click me!</button> <button>Click me!</button>
|
||||
```
|
||||
|
||||
HTML rendering with `html_attrs` tag or `attributes_to_string` works the same way, where `key=True` is rendered simply as `key`, and `key=False` is not render at all.
|
||||
|
@ -1330,8 +1327,7 @@ And template:
|
|||
Then this renders:
|
||||
|
||||
```html
|
||||
<div disabled>
|
||||
</div>
|
||||
<div disabled></div>
|
||||
```
|
||||
|
||||
### Default attributes
|
||||
|
@ -1380,8 +1376,7 @@ And on `html_attrs` tag, we set the key `class`:
|
|||
Then these will be merged and rendered as:
|
||||
|
||||
```html
|
||||
<div data-value="my-class pa-4 some-class">
|
||||
</div>
|
||||
<div data-value="my-class pa-4 some-class"></div>
|
||||
```
|
||||
|
||||
To simplify merging of variables, you can supply the same key multiple times, and these will be all joined together:
|
||||
|
@ -1499,8 +1494,8 @@ Then:
|
|||
### Full example for `html_attrs`
|
||||
|
||||
```py
|
||||
@component.register("my_comp")
|
||||
class MyComp(component.Component):
|
||||
@register("my_comp")
|
||||
class MyComp(Component):
|
||||
template: t.django_html = """
|
||||
<div
|
||||
{% html_attrs attrs
|
||||
|
@ -1521,8 +1516,8 @@ class MyComp(component.Component):
|
|||
"class_from_var": "extra-class"
|
||||
}
|
||||
|
||||
@component.register("parent")
|
||||
class Parent(component.Component):
|
||||
@register("parent")
|
||||
class Parent(Component):
|
||||
template: t.django_html = """
|
||||
{% component "my_comp"
|
||||
date=date
|
||||
|
@ -1555,8 +1550,7 @@ will be appended to it.
|
|||
So by default, `MyComp` renders:
|
||||
|
||||
```html
|
||||
<div class="pa-4 text-red my-comp-date extra-class" data-id="123">
|
||||
...
|
||||
<div class="pa-4 text-red my-comp-date extra-class" data-id="123">...</div>
|
||||
```
|
||||
|
||||
Next, let's consider what will be rendered when we call `MyComp` from `Parent`
|
||||
|
@ -1626,6 +1620,7 @@ attributes_to_string(attrs)
|
|||
_New in version 0.80_:
|
||||
|
||||
Django components supports dependency injection with the combination of:
|
||||
|
||||
1. `{% provide %}` tag
|
||||
1. `inject()` method of the `Component` class
|
||||
|
||||
|
@ -1646,6 +1641,7 @@ This feature is inspired by Vue's [Provide / Inject](https://vuejs.org/guide/com
|
|||
### How to use provide / inject
|
||||
|
||||
As the name suggest, using provide / inject consists of 2 steps
|
||||
|
||||
1. Providing data
|
||||
2. Injecting provided data
|
||||
|
||||
|
@ -1668,6 +1664,7 @@ First we use the `{% provide %}` tag to define the data we want to "provide" (ma
|
|||
Notice that the `provide` tag REQUIRES a name as a first argument. This is the _key_ by which we can then access the data passed to this tag.
|
||||
|
||||
`provide` tag _key_, similarly to the _name_ argument in `component` or `slot` tags, has these requirements:
|
||||
|
||||
- The _key_ must be a string literal
|
||||
- It must be a valid identifier (AKA a valid Python variable name)
|
||||
|
||||
|
@ -1675,9 +1672,10 @@ Once you've set the name, you define the data you want to "provide" by passing i
|
|||
|
||||
> NOTE: Kwargs passed to `{% provide %}` are NOT added to the context.
|
||||
> In the example below, the `{{ key }}` won't render anything:
|
||||
>
|
||||
> ```django
|
||||
> {% provide "my_data" key="hi" another=123 %}
|
||||
> {{ key }}
|
||||
> {{ key }}
|
||||
> {% endprovide %}
|
||||
> ```
|
||||
|
||||
|
@ -1690,7 +1688,7 @@ For a component to be able to "inject" some data, the component (`{% component %
|
|||
In the example from previous section, we've defined two kwargs: `key="hi" another=123`. That means that if we now inject `"my_data"`, we get an object with 2 attributes - `key` and `another`.
|
||||
|
||||
```py
|
||||
class ChildComponent(component.Component):
|
||||
class ChildComponent(Component):
|
||||
def get_context_data(self):
|
||||
my_data = self.inject("my_data")
|
||||
print(my_data.key) # hi
|
||||
|
@ -1705,7 +1703,7 @@ with given key is found, `inject` raises a `KeyError`.
|
|||
To avoid the error, you can pass a second argument to `inject` to which will act as a default value, similar to `dict.get(key, default)`:
|
||||
|
||||
```py
|
||||
class ChildComponent(component.Component):
|
||||
class ChildComponent(Component):
|
||||
def get_context_data(self):
|
||||
my_data = self.inject("invalid_key", DEFAULT_DATA)
|
||||
assert my_data == DEFAUKT_DATA
|
||||
|
@ -1717,12 +1715,11 @@ have all the keys that were passed to the `provide` tag.
|
|||
|
||||
> NOTE: `inject()` works strictly only in `get_context_data`. If you try to call it from elsewhere, it will raise an error.
|
||||
|
||||
|
||||
### Full example
|
||||
|
||||
```py
|
||||
@component.register("child")
|
||||
class ChildComponent(component.Component):
|
||||
@register("child")
|
||||
class ChildComponent(Component):
|
||||
template = """
|
||||
<div> {{ my_data.key }} </div>
|
||||
<div> {{ my_data.another }} </div>
|
||||
|
@ -1744,15 +1741,15 @@ template_str = """
|
|||
renders:
|
||||
|
||||
```html
|
||||
<div> hi </div>
|
||||
<div> 123 </div>
|
||||
<div>hi</div>
|
||||
<div>123</div>
|
||||
```
|
||||
|
||||
## Component context and scope
|
||||
|
||||
By default, context variables are passed down the template as in regular Django - deeper scopes can access the variables from the outer scopes. So if you have several nested forloops, then inside the deep-most loop you can access variables defined by all previous loops.
|
||||
|
||||
With this in mind, the `{% component %}` tag behaves similarly to `{% include %}` tag - inside the component tag, you can access all variables that were defined outside of it.
|
||||
With this in mind, the `{% component %}` tag behaves similarly to `{% include %}` tag - inside the component tag, you can access all variables that were defined outside of it.
|
||||
|
||||
And just like with `{% include %}`, if you don't want a specific component template to have access to the parent context, add `only` to the end of the `{% component %}` tag:
|
||||
|
||||
|
@ -1771,6 +1768,7 @@ Components can also access the outer context in their context methods like `get_
|
|||
django_component's management of files builds on top of [Django's `Media` class](https://docs.djangoproject.com/en/5.0/topics/forms/media/).
|
||||
|
||||
To be familiar with how Django handles static files, we recommend reading also:
|
||||
|
||||
- [How to manage static files (e.g. images, JavaScript, CSS)](https://docs.djangoproject.com/en/5.0/howto/static-files/)
|
||||
|
||||
### Defining file paths relative to component or static dirs
|
||||
|
@ -1780,10 +1778,10 @@ files with a component, you set them as `template_name`, `Media.js` and `Media.c
|
|||
|
||||
```py
|
||||
# In a file [project root]/components/calendar/calendar.py
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
template_name = "template.html"
|
||||
|
||||
class Media:
|
||||
|
@ -1799,10 +1797,10 @@ Assuming that `STATICFILES_DIRS` contains path `[project root]/components`, we c
|
|||
|
||||
```py
|
||||
# In a file [project root]/components/calendar/calendar.py
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
template_name = "calendar/template.html"
|
||||
|
||||
class Media:
|
||||
|
@ -1817,7 +1815,7 @@ NOTE: In case of conflict, the preference goes to resolving the files relative t
|
|||
Each component can have only a single template. However, you can define as many JS or CSS files as you want using a list.
|
||||
|
||||
```py
|
||||
class MyComponent(component.Component):
|
||||
class MyComponent(Component):
|
||||
class Media:
|
||||
js = ["path/to/script1.js", "path/to/script2.js"]
|
||||
css = ["path/to/style1.css", "path/to/style2.css"]
|
||||
|
@ -1833,7 +1831,7 @@ See the corresponding [Django Documentation](https://docs.djangoproject.com/en/5
|
|||
Again, you can set either a single file or a list of files per media type:
|
||||
|
||||
```py
|
||||
class MyComponent(component.Component):
|
||||
class MyComponent(Component):
|
||||
class Media:
|
||||
css = {
|
||||
"all": "path/to/style1.css",
|
||||
|
@ -1842,7 +1840,7 @@ class MyComponent(component.Component):
|
|||
```
|
||||
|
||||
```py
|
||||
class MyComponent(component.Component):
|
||||
class MyComponent(Component):
|
||||
class Media:
|
||||
css = {
|
||||
"all": ["path/to/style1.css", "path/to/style2.css"],
|
||||
|
@ -1852,10 +1850,10 @@ class MyComponent(component.Component):
|
|||
|
||||
NOTE: When you define CSS as a string or a list, the `all` media type is implied.
|
||||
|
||||
|
||||
### Supported types for file paths
|
||||
|
||||
File paths can be any of:
|
||||
|
||||
- `str`
|
||||
- `bytes`
|
||||
- `PathLike` (`__fspath__` method)
|
||||
|
@ -1867,7 +1865,7 @@ from pathlib import Path
|
|||
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
class Media:
|
||||
css = [
|
||||
mark_safe('<link href="/static/calendar/style.css" rel="stylesheet" />'),
|
||||
|
@ -1906,8 +1904,8 @@ class LazyJsPath:
|
|||
f'<script type="module" src="{full_path}"></script>'
|
||||
)
|
||||
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
template_name = "calendar/template.html"
|
||||
|
||||
def get_context_data(self, date):
|
||||
|
@ -1919,7 +1917,7 @@ class Calendar(component.Component):
|
|||
css = "calendar/style.css"
|
||||
js = [
|
||||
# <script> tag constructed by Media class
|
||||
"calendar/script1.js",
|
||||
"calendar/script1.js",
|
||||
# Custom <script> tag
|
||||
LazyJsPath("calendar/script2.js"),
|
||||
]
|
||||
|
@ -1935,7 +1933,7 @@ To change how the tags are constructed, you can override the [`Media.render_js`
|
|||
|
||||
```py
|
||||
from django.forms.widgets import Media
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
class MyMedia(Media):
|
||||
# Same as original Media.render_js, except
|
||||
|
@ -1952,14 +1950,14 @@ class MyMedia(Media):
|
|||
)
|
||||
return tags
|
||||
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
template_name = "calendar/template.html"
|
||||
|
||||
class Media:
|
||||
css = "calendar/style.css"
|
||||
js = "calendar/script.js"
|
||||
|
||||
|
||||
# Override the behavior of Media class
|
||||
media_class = MyMedia
|
||||
```
|
||||
|
@ -1970,6 +1968,7 @@ NOTE: The instance of the `Media` class (or it's subclass) is available under `C
|
|||
|
||||
The JS and CSS files included in components are not automatically rendered.
|
||||
Instead, use the following tags to specify where to render the dependencies:
|
||||
|
||||
- `component_dependencies` - Renders both JS and CSS
|
||||
- `component_js_dependencies` - Renders only JS
|
||||
- `component_css_dependencies` - Reneders only CSS
|
||||
|
@ -2021,16 +2020,16 @@ COMPONENTS = {
|
|||
Where `mysite/components/forms.py` may look like this:
|
||||
|
||||
```py
|
||||
@component.register("form_simple")
|
||||
class FormSimple(component.Component):
|
||||
@register("form_simple")
|
||||
class FormSimple(Component):
|
||||
template = """
|
||||
<form>
|
||||
...
|
||||
</form>
|
||||
"""
|
||||
|
||||
@component.register("form_other")
|
||||
class FormOther(component.Component):
|
||||
@register("form_other")
|
||||
class FormOther(Component):
|
||||
template = """
|
||||
<form>
|
||||
...
|
||||
|
@ -2080,15 +2079,17 @@ This has two modes:
|
|||
|
||||
- `"django"` - Default - The default Django template behavior.
|
||||
|
||||
Inside the `{% fill %}` tag, the context variables you can access are a union of:
|
||||
- All the variables that were OUTSIDE the fill tag, including any loops or with tag
|
||||
- Data returned from `get_context_data()` of the component that wraps the fill tag.
|
||||
Inside the `{% fill %}` tag, the context variables you can access are a union of:
|
||||
|
||||
- All the variables that were OUTSIDE the fill tag, including any loops or with tag
|
||||
- Data returned from `get_context_data()` of the component that wraps the fill tag.
|
||||
|
||||
- `"isolated"` - Similar behavior to [Vue](https://vuejs.org/guide/components/slots.html#render-scope) or React, this is useful if you want to make sure that components don't accidentally access variables defined outside of the component.
|
||||
|
||||
Inside the `{% fill %}` tag, you can ONLY access variables from 2 places:
|
||||
- `get_context_data()` of the component which defined the template (AKA the "root" component)
|
||||
- Any loops (`{% for ... %}`) that the `{% fill %}` tag is part of.
|
||||
Inside the `{% fill %}` tag, you can ONLY access variables from 2 places:
|
||||
|
||||
- `get_context_data()` of the component which defined the template (AKA the "root" component)
|
||||
- Any loops (`{% for ... %}`) that the `{% fill %}` tag is part of.
|
||||
|
||||
```python
|
||||
COMPONENTS = {
|
||||
|
@ -2101,7 +2102,7 @@ COMPONENTS = {
|
|||
Given this template:
|
||||
|
||||
```py
|
||||
class RootComp(component.Component):
|
||||
class RootComp(Component):
|
||||
template = """
|
||||
{% with cheese="feta" %}
|
||||
{% component 'my_comp' %}
|
||||
|
@ -2138,7 +2139,7 @@ all the data defined in the outer layers, like the `{% with %}` tag.
|
|||
Given this template:
|
||||
|
||||
```py
|
||||
class RootComp(component.Component):
|
||||
class RootComp(Component):
|
||||
template = """
|
||||
{% with cheese="feta" %}
|
||||
{% component 'my_comp' %}
|
||||
|
|
|
@ -3,13 +3,13 @@ from time import perf_counter
|
|||
from django.template import Context, Template
|
||||
from django.test import override_settings
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, registry, types
|
||||
from django_components.middleware import CSS_DEPENDENCY_PLACEHOLDER, JS_DEPENDENCY_PLACEHOLDER
|
||||
from tests.django_test_setup import * # NOQA
|
||||
from tests.testutils import BaseTestCase, create_and_process_template_response
|
||||
|
||||
|
||||
class SlottedComponent(component.Component):
|
||||
class SlottedComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<custom-template>
|
||||
|
@ -20,7 +20,7 @@ class SlottedComponent(component.Component):
|
|||
"""
|
||||
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -36,7 +36,7 @@ class SimpleComponent(component.Component):
|
|||
js = ["script.js"]
|
||||
|
||||
|
||||
class BreadcrumbComponent(component.Component):
|
||||
class BreadcrumbComponent(Component):
|
||||
template_name = "mdn_component_template.html"
|
||||
|
||||
LINKS = [
|
||||
|
@ -77,10 +77,10 @@ EXPECTED_JS = """<script src="test.js"></script>"""
|
|||
@override_settings(COMPONENTS={"RENDER_DEPENDENCIES": True})
|
||||
class RenderBenchmarks(BaseTestCase):
|
||||
def setUp(self):
|
||||
component.registry.clear()
|
||||
component.registry.register("test_component", SlottedComponent)
|
||||
component.registry.register("inner_component", SimpleComponent)
|
||||
component.registry.register("breadcrumb_component", BreadcrumbComponent)
|
||||
registry.clear()
|
||||
registry.register("test_component", SlottedComponent)
|
||||
registry.register("inner_component", SimpleComponent)
|
||||
registry.register("breadcrumb_component", BreadcrumbComponent)
|
||||
|
||||
@staticmethod
|
||||
def timed_loop(func, iterations=1000):
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
|
||||
And components that make use of `abc.html` via `include` or `extends`:
|
||||
```py
|
||||
@component.register("my_comp_extends")
|
||||
class MyCompWithExtends(component.Component):
|
||||
from django_components import Component, register
|
||||
|
||||
@register("my_comp_extends")
|
||||
class MyCompWithExtends(Component):
|
||||
template = """{% extends "abc.html" %}"""
|
||||
|
||||
@component.register("my_comp_include")
|
||||
class MyCompWithInclude(component.Component):
|
||||
@register("my_comp_include")
|
||||
class MyCompWithInclude(Component):
|
||||
template = """{% include "abc.html" %}"""
|
||||
```
|
||||
|
||||
|
@ -61,8 +63,8 @@
|
|||
and component `my_comp`:
|
||||
|
||||
```py
|
||||
@component.register("my_comp")
|
||||
class MyComp(component.Component):
|
||||
@register("my_comp")
|
||||
class MyComp(Component):
|
||||
template_name = "abc.html"
|
||||
```
|
||||
|
||||
|
@ -108,8 +110,8 @@
|
|||
`abc.html` will render `OVERRIDEN`:
|
||||
|
||||
````py
|
||||
@component.register("my_comp")
|
||||
class MyComp(component.Component):
|
||||
@register("my_comp")
|
||||
class MyComp(Component):
|
||||
template_name = """
|
||||
{% extends "abc.html" %}
|
||||
|
||||
|
@ -125,8 +127,8 @@
|
|||
new `slots` inside these "overriding" blocks:
|
||||
|
||||
```py
|
||||
@component.register("my_comp")
|
||||
class MyComp(component.Component):
|
||||
@register("my_comp")
|
||||
class MyComp(Component):
|
||||
template_name = """
|
||||
{% extends "abc.html" %}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
# Note that Django will look for templates inside `[your apps]/components` dir and
|
||||
# `[project root]/components` dir. To customize which template to use based on context
|
||||
# you can override def get_template_name() instead of specifying the below variable.
|
||||
|
@ -25,8 +25,8 @@ class Calendar(component.Component):
|
|||
js = "calendar/calendar.js"
|
||||
|
||||
|
||||
@component.register("calendar_relative")
|
||||
class CalendarRelative(component.Component):
|
||||
@register("calendar_relative")
|
||||
class CalendarRelative(Component):
|
||||
# Note that Django will look for templates inside `[your apps]/components` dir and
|
||||
# `[project root]/components` dir. To customize which template to use based on context
|
||||
# you can override def get_template_name() instead of specifying the below variable.
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
from typing import Any, Dict
|
||||
|
||||
from django_components import component
|
||||
from django_components import types as t
|
||||
from django_components import Component, register, types
|
||||
|
||||
|
||||
@component.register("greeting")
|
||||
class Greeting(component.Component):
|
||||
@register("greeting")
|
||||
class Greeting(Component):
|
||||
def get(self, request, *args, **kwargs):
|
||||
slots = {"message": "Hello, world!"}
|
||||
context = {"name": request.GET.get("name", "")}
|
||||
|
@ -14,12 +13,12 @@ class Greeting(component.Component):
|
|||
def get_context_data(self, name, *args, **kwargs) -> Dict[str, Any]:
|
||||
return {"name": name}
|
||||
|
||||
template: t.django_html = """
|
||||
template: types.django_html = """
|
||||
<div id="greeting">Hello, {{ name }}!</div>
|
||||
{% slot "message" %}{% endslot %}
|
||||
"""
|
||||
|
||||
css: t.css = """
|
||||
css: types.css = """
|
||||
#greeting {
|
||||
display: inline-block;
|
||||
color: blue;
|
||||
|
@ -27,7 +26,7 @@ class Greeting(component.Component):
|
|||
}
|
||||
"""
|
||||
|
||||
js: t.js = """
|
||||
js: types.js = """
|
||||
document.getElementById("greeting").addEventListener("click", (event) => {
|
||||
alert("Hello!");
|
||||
});
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
|
||||
@component.register("calendar_nested")
|
||||
class CalendarNested(component.Component):
|
||||
@register("calendar_nested")
|
||||
class CalendarNested(Component):
|
||||
# Note that Django will look for templates inside `[your apps]/components` dir and
|
||||
# `[project root]/components` dir. To customize which template to use based on context
|
||||
# you can override def get_template_name() instead of specifying the below variable.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
|
||||
@component.register("todo")
|
||||
class Calendar(component.Component):
|
||||
@register("todo")
|
||||
class Calendar(Component):
|
||||
# Note that Django will look for templates inside `[your apps]/components` dir and
|
||||
# `[project root]/components` dir. To customize which template to use based on context
|
||||
# you can override def get_template_name() instead of specifying the below variable.
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
# flake8: noqa F401
|
||||
import django
|
||||
|
||||
from django_components.autodiscover import autodiscover as autodiscover # NOQA
|
||||
from django_components.autodiscover import import_libraries as import_libraries # NOQA
|
||||
# Public API
|
||||
# isort: off
|
||||
from django_components.autodiscover import autodiscover as autodiscover
|
||||
from django_components.autodiscover import import_libraries as import_libraries
|
||||
from django_components.component import Component as Component
|
||||
from django_components.component_registry import AlreadyRegistered as AlreadyRegistered
|
||||
from django_components.component_registry import ComponentRegistry as ComponentRegistry
|
||||
from django_components.component_registry import NotRegistered as NotRegistered
|
||||
from django_components.component_registry import register as register
|
||||
from django_components.component_registry import registry as registry
|
||||
import django_components.types as types
|
||||
|
||||
# isort: on
|
||||
|
||||
if django.VERSION < (3, 2):
|
||||
default_app_config = "django_components.apps.ComponentsConfig"
|
||||
|
|
|
@ -15,16 +15,7 @@ from django.utils.safestring import SafeString, mark_safe
|
|||
from django.views import View
|
||||
|
||||
from django_components.component_media import ComponentMediaInput, MediaMeta
|
||||
|
||||
# Global registry var and register() function moved to separate module.
|
||||
# Defining them here made little sense, since 1) component_tags.py and component.py
|
||||
# rely on them equally, and 2) it made it difficult to avoid circularity in the
|
||||
# way the two modules depend on one another.
|
||||
from django_components.component_registry import AlreadyRegistered as AlreadyRegistered # NOQA
|
||||
from django_components.component_registry import ComponentRegistry as ComponentRegistry # NOQA
|
||||
from django_components.component_registry import NotRegistered as NotRegistered # NOQA
|
||||
from django_components.component_registry import register as register # NOQA
|
||||
from django_components.component_registry import registry # NOQA
|
||||
from django_components.component_registry import registry
|
||||
from django_components.context import (
|
||||
_FILLED_SLOTS_CONTENT_CONTEXT_KEY,
|
||||
_PARENT_COMP_CONTEXT_KEY,
|
||||
|
@ -50,6 +41,16 @@ from django_components.slots import (
|
|||
from django_components.template_parser import process_aggregate_kwargs
|
||||
from django_components.utils import gen_id
|
||||
|
||||
# TODO_DEPRECATE_V1 - REMOVE IN V1, users should use top-level import instead
|
||||
# isort: off
|
||||
from django_components.component_registry import AlreadyRegistered as AlreadyRegistered # NOQA
|
||||
from django_components.component_registry import ComponentRegistry as ComponentRegistry # NOQA
|
||||
from django_components.component_registry import NotRegistered as NotRegistered # NOQA
|
||||
from django_components.component_registry import register as register # NOQA
|
||||
from django_components.component_registry import registry as registry # NOQA
|
||||
|
||||
# isort: on
|
||||
|
||||
RENDERED_COMMENT_TEMPLATE = "<!-- _RENDERED {name} -->"
|
||||
|
||||
|
||||
|
@ -202,8 +203,10 @@ class Component(View, metaclass=ComponentMeta):
|
|||
|
||||
And given this definition of "my_comp" component:
|
||||
```py
|
||||
@component.register("my_comp")
|
||||
class MyComp(component.Component):
|
||||
from django_components import Component, register
|
||||
|
||||
@register("my_comp")
|
||||
class MyComp(Component):
|
||||
template = "hi {{ data.hello }}!"
|
||||
def get_context_data(self):
|
||||
data = self.inject("provider")
|
||||
|
|
|
@ -34,7 +34,7 @@ class MediaMeta(MediaDefiningClass):
|
|||
|
||||
1. As plain strings
|
||||
```py
|
||||
class MyComponent(component.Component):
|
||||
class MyComponent(Component):
|
||||
class Media:
|
||||
js = "path/to/script.js"
|
||||
css = "path/to/style.css"
|
||||
|
@ -42,7 +42,7 @@ class MediaMeta(MediaDefiningClass):
|
|||
|
||||
2. As lists
|
||||
```py
|
||||
class MyComponent(component.Component):
|
||||
class MyComponent(Component):
|
||||
class Media:
|
||||
js = ["path/to/script1.js", "path/to/script2.js"]
|
||||
css = ["path/to/style1.css", "path/to/style2.css"]
|
||||
|
@ -50,7 +50,7 @@ class MediaMeta(MediaDefiningClass):
|
|||
|
||||
3. [CSS ONLY] Dicts of strings
|
||||
```py
|
||||
class MyComponent(component.Component):
|
||||
class MyComponent(Component):
|
||||
class Media:
|
||||
css = {
|
||||
"all": "path/to/style1.css",
|
||||
|
@ -60,7 +60,7 @@ class MediaMeta(MediaDefiningClass):
|
|||
|
||||
4. [CSS ONLY] Dicts of lists
|
||||
```py
|
||||
class MyComponent(component.Component):
|
||||
class MyComponent(Component):
|
||||
class Media:
|
||||
css = {
|
||||
"all": ["path/to/style1.css"],
|
||||
|
@ -74,7 +74,7 @@ class MediaMeta(MediaDefiningClass):
|
|||
and `my_comp.py` looks like this:
|
||||
|
||||
```py
|
||||
class MyComponent(component.Component):
|
||||
class MyComponent(Component):
|
||||
class Media:
|
||||
js = "script.js"
|
||||
```
|
||||
|
@ -90,7 +90,7 @@ class MediaMeta(MediaDefiningClass):
|
|||
# do something
|
||||
return path
|
||||
|
||||
class MyComponent(component.Component):
|
||||
class MyComponent(Component):
|
||||
class Media:
|
||||
js = b"script.js"
|
||||
css = lazy_eval_css
|
||||
|
@ -106,7 +106,7 @@ class MediaMeta(MediaDefiningClass):
|
|||
def render_js(self):
|
||||
...
|
||||
|
||||
class MyComponent(component.Component):
|
||||
class MyComponent(Component):
|
||||
media_class = MyMedia
|
||||
def get_context_data(self):
|
||||
assert isinstance(self.media, MyMedia)
|
||||
|
|
|
@ -125,10 +125,10 @@ class Command(BaseCommand):
|
|||
with open(os.path.join(component_path, f"{name}.py"), "w") as f:
|
||||
py_content = dedent(
|
||||
f"""
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
@component.register("{name}")
|
||||
class {name.capitalize()}(component.Component):
|
||||
@register("{name}")
|
||||
class {name.capitalize()}(Component):
|
||||
template_name = "{name}/{template_filename}"
|
||||
|
||||
def get_context_data(self, value):
|
||||
|
|
|
@ -2,11 +2,11 @@ from typing import Any, Dict
|
|||
|
||||
from django.http import HttpResponse
|
||||
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
|
||||
@component.register("multi_file_component")
|
||||
class MultFileComponent(component.Component):
|
||||
@register("multi_file_component")
|
||||
class MultFileComponent(Component):
|
||||
template_name = "multi_file/multi_file.html"
|
||||
|
||||
def post(self, request, *args, **kwargs) -> HttpResponse:
|
||||
|
|
|
@ -2,11 +2,11 @@ from typing import Any, Dict
|
|||
|
||||
from django.http import HttpResponse
|
||||
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
|
||||
@component.register("relative_file_component")
|
||||
class RelativeFileComponent(component.Component):
|
||||
@register("relative_file_component")
|
||||
class RelativeFileComponent(Component):
|
||||
template_name = "relative_file.html"
|
||||
|
||||
class Media:
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import Any, Dict
|
|||
from django.templatetags.static import static
|
||||
from django.utils.html import format_html, html_safe
|
||||
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
|
||||
# Format as mentioned in https://github.com/EmilStenstrom/django-components/issues/522#issuecomment-2173577094
|
||||
|
@ -21,8 +21,8 @@ class PathObj:
|
|||
return format_html('<script type="module" src="{}"></script>', static(self.static_path))
|
||||
|
||||
|
||||
@component.register("relative_file_pathobj_component")
|
||||
class RelativeFileWithPathObjComponent(component.Component):
|
||||
@register("relative_file_pathobj_component")
|
||||
class RelativeFileWithPathObjComponent(Component):
|
||||
template_name = "relative_file_pathobj.html"
|
||||
|
||||
class Media:
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from typing import Any, Dict
|
||||
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
|
||||
# Used for testing the safer_staticfiles app in `test_safer_staticfiles.py`
|
||||
@component.register("safer_staticfiles_component")
|
||||
class RelativeFileWithPathObjComponent(component.Component):
|
||||
@register("safer_staticfiles_component")
|
||||
class RelativeFileWithPathObjComponent(Component):
|
||||
template_name = "safer_staticfiles.html"
|
||||
|
||||
class Media:
|
||||
|
|
|
@ -2,11 +2,11 @@ from typing import Any, Dict
|
|||
|
||||
from django.http import HttpResponse
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, register, types
|
||||
|
||||
|
||||
@component.register("single_file_component")
|
||||
class SingleFileComponent(component.Component):
|
||||
@register("single_file_component")
|
||||
class SingleFileComponent(Component):
|
||||
template: types.django_html = """
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.template import Context, Template, TemplateSyntaxError
|
||||
from django.utils.safestring import SafeString, mark_safe
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, register, types
|
||||
from django_components.attributes import append_attributes, attributes_to_string
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
|
@ -84,8 +84,8 @@ class HtmlAttrsTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_tag_positional_args(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div {% html_attrs attrs defaults class="added_class" class="another-class" data-id=123 %}>
|
||||
|
@ -112,8 +112,8 @@ class HtmlAttrsTests(BaseTestCase):
|
|||
self.assertNotIn("override-me", rendered)
|
||||
|
||||
def test_tag_raises_on_extra_positional_args(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div {% html_attrs attrs defaults class %}>
|
||||
|
@ -136,8 +136,8 @@ class HtmlAttrsTests(BaseTestCase):
|
|||
template.render(Context({"class_var": "padding-top-8"}))
|
||||
|
||||
def test_tag_kwargs(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div {% html_attrs attrs=attrs defaults=defaults class="added_class" class="another-class" data-id=123 %}>
|
||||
|
@ -164,8 +164,8 @@ class HtmlAttrsTests(BaseTestCase):
|
|||
self.assertNotIn("override-me", rendered)
|
||||
|
||||
def test_tag_kwargs_2(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div {% html_attrs class="added_class" class="another-class" data-id=123 defaults=defaults attrs=attrs %}>
|
||||
|
@ -192,8 +192,8 @@ class HtmlAttrsTests(BaseTestCase):
|
|||
self.assertNotIn("override-me", rendered)
|
||||
|
||||
def test_tag_aggregate_args(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div {% html_attrs attrs:class="from_agg_key" attrs:type="submit" defaults:class="override-me" class="added_class" class="another-class" data-id=123 %}>
|
||||
|
@ -219,8 +219,8 @@ class HtmlAttrsTests(BaseTestCase):
|
|||
self.assertNotIn("override-me", rendered)
|
||||
|
||||
def test_tag_raises_on_aggregate_and_positional_args_for_attrs(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div {% html_attrs attrs attrs:class="from_agg_key" defaults:class="override-me" class="added_class" class="another-class" data-id=123 %}>
|
||||
|
@ -237,8 +237,8 @@ class HtmlAttrsTests(BaseTestCase):
|
|||
template.render(Context({"class_var": "padding-top-8"}))
|
||||
|
||||
def test_tag_raises_on_aggregate_and_positional_args_for_defaults(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div {% html_attrs defaults=defaults attrs:class="from_agg_key" defaults:class="override-me" class="added_class" class="another-class" data-id=123 %}>
|
||||
|
@ -258,8 +258,8 @@ class HtmlAttrsTests(BaseTestCase):
|
|||
template.render(Context({"class_var": "padding-top-8"}))
|
||||
|
||||
def test_tag_no_attrs(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div {% html_attrs defaults:class="override-me" class="added_class" class="another-class" data-id=123 %}>
|
||||
|
@ -282,8 +282,8 @@ class HtmlAttrsTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_tag_no_defaults(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div {% html_attrs attrs class="added_class" class="another-class" data-id=123 %}>
|
||||
|
@ -312,8 +312,8 @@ class HtmlAttrsTests(BaseTestCase):
|
|||
self.assertNotIn("override-me", rendered)
|
||||
|
||||
def test_tag_no_attrs_no_defaults(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div {% html_attrs class="added_class" class="another-class" data-id=123 %}>
|
||||
|
@ -337,8 +337,8 @@ class HtmlAttrsTests(BaseTestCase):
|
|||
self.assertNotIn("override-me", rendered)
|
||||
|
||||
def test_tag_empty(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div {% html_attrs %}>
|
||||
|
|
|
@ -4,7 +4,7 @@ from unittest import TestCase, mock
|
|||
|
||||
from django.conf import settings
|
||||
|
||||
from django_components import component, component_registry
|
||||
from django_components import AlreadyRegistered, registry
|
||||
from django_components.autodiscover import _filepath_to_python_module, autodiscover, import_libraries
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
|
@ -15,14 +15,14 @@ from .django_test_setup import setup_test_config
|
|||
class _TestCase(TestCase):
|
||||
def tearDown(self) -> None:
|
||||
super().tearDown()
|
||||
component.registry.clear()
|
||||
registry.clear()
|
||||
|
||||
|
||||
class TestAutodiscover(_TestCase):
|
||||
def test_autodiscover(self):
|
||||
setup_test_config({"autodiscover": False})
|
||||
|
||||
all_components = component.registry.all().copy()
|
||||
all_components = registry.all().copy()
|
||||
self.assertNotIn("single_file_component", all_components)
|
||||
self.assertNotIn("multi_file_component", all_components)
|
||||
self.assertNotIn("relative_file_component", all_components)
|
||||
|
@ -30,7 +30,7 @@ class TestAutodiscover(_TestCase):
|
|||
|
||||
try:
|
||||
modules = autodiscover(map_module=lambda p: "tests." + p)
|
||||
except component_registry.AlreadyRegistered:
|
||||
except AlreadyRegistered:
|
||||
self.fail("Autodiscover should not raise AlreadyRegistered exception")
|
||||
|
||||
self.assertIn("tests.components.single_file", modules)
|
||||
|
@ -38,7 +38,7 @@ class TestAutodiscover(_TestCase):
|
|||
self.assertIn("tests.components.relative_file_pathobj.relative_file_pathobj", modules)
|
||||
self.assertIn("tests.components.relative_file.relative_file", modules)
|
||||
|
||||
all_components = component.registry.all().copy()
|
||||
all_components = registry.all().copy()
|
||||
self.assertIn("single_file_component", all_components)
|
||||
self.assertIn("multi_file_component", all_components)
|
||||
self.assertIn("relative_file_component", all_components)
|
||||
|
@ -56,8 +56,8 @@ class TestImportLibraries(_TestCase):
|
|||
settings.COMPONENTS["libraries"] = ["tests.components.single_file", "tests.components.multi_file.multi_file"]
|
||||
|
||||
# Ensure we start with a clean state
|
||||
component.registry.clear()
|
||||
all_components = component.registry.all().copy()
|
||||
registry.clear()
|
||||
all_components = registry.all().copy()
|
||||
self.assertNotIn("single_file_component", all_components)
|
||||
self.assertNotIn("multi_file_component", all_components)
|
||||
|
||||
|
@ -69,13 +69,13 @@ class TestImportLibraries(_TestCase):
|
|||
|
||||
try:
|
||||
modules = import_libraries()
|
||||
except component_registry.AlreadyRegistered:
|
||||
except AlreadyRegistered:
|
||||
self.fail("Autodiscover should not raise AlreadyRegistered exception")
|
||||
|
||||
self.assertIn("tests.components.single_file", modules)
|
||||
self.assertIn("tests.components.multi_file.multi_file", modules)
|
||||
|
||||
all_components = component.registry.all().copy()
|
||||
all_components = registry.all().copy()
|
||||
self.assertIn("single_file_component", all_components)
|
||||
self.assertIn("multi_file_component", all_components)
|
||||
|
||||
|
@ -91,8 +91,8 @@ class TestImportLibraries(_TestCase):
|
|||
settings.COMPONENTS["libraries"] = ["components.single_file", "components.multi_file.multi_file"]
|
||||
|
||||
# Ensure we start with a clean state
|
||||
component.registry.clear()
|
||||
all_components = component.registry.all().copy()
|
||||
registry.clear()
|
||||
all_components = registry.all().copy()
|
||||
self.assertNotIn("single_file_component", all_components)
|
||||
self.assertNotIn("multi_file_component", all_components)
|
||||
|
||||
|
@ -104,13 +104,13 @@ class TestImportLibraries(_TestCase):
|
|||
|
||||
try:
|
||||
modules = import_libraries(map_module=lambda p: "tests." + p)
|
||||
except component_registry.AlreadyRegistered:
|
||||
except AlreadyRegistered:
|
||||
self.fail("Autodiscover should not raise AlreadyRegistered exception")
|
||||
|
||||
self.assertIn("tests.components.single_file", modules)
|
||||
self.assertIn("tests.components.multi_file.multi_file", modules)
|
||||
|
||||
all_components = component.registry.all().copy()
|
||||
all_components = registry.all().copy()
|
||||
self.assertIn("single_file_component", all_components)
|
||||
self.assertIn("multi_file_component", all_components)
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.core.exceptions import ImproperlyConfigured
|
|||
from django.http import HttpResponse
|
||||
from django.template import Context, Template, TemplateSyntaxError
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, registry, types
|
||||
from django_components.slots import SlotRef
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
|
@ -19,7 +19,7 @@ setup_test_config({"autodiscover": False})
|
|||
|
||||
|
||||
class ComponentTest(BaseTestCase):
|
||||
class ParentComponent(component.Component):
|
||||
class ParentComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -39,7 +39,7 @@ class ComponentTest(BaseTestCase):
|
|||
def get_context_data(self):
|
||||
return {"shadowing_variable": "NOT SHADOWED"}
|
||||
|
||||
class VariableDisplay(component.Component):
|
||||
class VariableDisplay(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<h1>Shadowing variable = {{ shadowing_variable }}</h1>
|
||||
|
@ -56,12 +56,12 @@ class ComponentTest(BaseTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register(name="parent_component", component=self.ParentComponent)
|
||||
component.registry.register(name="variable_display", component=self.VariableDisplay)
|
||||
registry.register(name="parent_component", component=self.ParentComponent)
|
||||
registry.register(name="variable_display", component=self.VariableDisplay)
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_empty_component(self):
|
||||
class EmptyComponent(component.Component):
|
||||
class EmptyComponent(Component):
|
||||
pass
|
||||
|
||||
with self.assertRaises(ImproperlyConfigured):
|
||||
|
@ -69,7 +69,7 @@ class ComponentTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_template_string_static_inlined(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -93,7 +93,7 @@ class ComponentTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_template_string_dynamic(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
def get_template_string(self, context):
|
||||
content: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
|
@ -119,7 +119,7 @@ class ComponentTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_template_name_static(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template_name = "simple_template.html"
|
||||
|
||||
def get_context_data(self, variable=None):
|
||||
|
@ -141,7 +141,7 @@ class ComponentTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_template_name_dynamic(self):
|
||||
class SvgComponent(component.Component):
|
||||
class SvgComponent(Component):
|
||||
def get_context_data(self, name, css_class="", title="", **attrs):
|
||||
return {
|
||||
"name": name,
|
||||
|
@ -168,7 +168,7 @@ class ComponentTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_allows_to_override_get_template(self):
|
||||
class TestComponent(component.Component):
|
||||
class TestComponent(Component):
|
||||
def get_context_data(self, variable, **attrs):
|
||||
return {
|
||||
"variable": variable,
|
||||
|
@ -190,7 +190,7 @@ class ComponentTest(BaseTestCase):
|
|||
class ComponentRenderTest(BaseTestCase):
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_render_minimal(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
the_arg2: {{ the_arg2 }}
|
||||
|
@ -230,7 +230,7 @@ class ComponentRenderTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_render_full(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
the_arg: {{ the_arg }}
|
||||
|
@ -283,7 +283,7 @@ class ComponentRenderTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_render_to_response_full(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
the_arg: {{ the_arg }}
|
||||
|
@ -342,7 +342,7 @@ class ComponentRenderTest(BaseTestCase):
|
|||
def __init__(self, content: str) -> None:
|
||||
self.content = bytes(content, "utf-8")
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
response_class = MyResponse
|
||||
template: types.django_html = "HELLO"
|
||||
|
||||
|
@ -358,7 +358,7 @@ class ComponentRenderTest(BaseTestCase):
|
|||
def test_render_slot_as_func(self, context_behavior_data):
|
||||
is_isolated = context_behavior_data
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% slot "first" required data1="abc" data2:hello="world" data2:one=123 %}
|
||||
|
@ -414,7 +414,7 @@ class ComponentRenderTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_render_raises_on_missing_slot(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% slot "first" required %}
|
||||
|
@ -430,7 +430,7 @@ class ComponentRenderTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_render_with_include(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% include 'slotted_template.html' %}
|
||||
|
@ -450,7 +450,7 @@ class ComponentRenderTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_render_with_extends(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% extends 'block.html' %}
|
||||
{% block body %}
|
||||
|
@ -478,7 +478,7 @@ class ComponentRenderTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_render_can_access_instance(self):
|
||||
class TestComponent(component.Component):
|
||||
class TestComponent(Component):
|
||||
template = "Variable: <strong>{{ id }}</strong>"
|
||||
|
||||
def get_context_data(self, **attrs):
|
||||
|
@ -494,7 +494,7 @@ class ComponentRenderTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_render_to_response_can_access_instance(self):
|
||||
class TestComponent(component.Component):
|
||||
class TestComponent(Component):
|
||||
template = "Variable: <strong>{{ id }}</strong>"
|
||||
|
||||
def get_context_data(self, **attrs):
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.template import Context, Template
|
|||
from django.test import Client
|
||||
from django.urls import path
|
||||
|
||||
from django_components import component
|
||||
from django_components import Component, register
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
from .testutils import BaseTestCase, parametrize_context_behavior
|
||||
|
@ -30,8 +30,8 @@ class CustomClient(Client):
|
|||
|
||||
class TestComponentAsView(BaseTestCase):
|
||||
def test_render_component_from_template(self):
|
||||
@component.register("testcomponent")
|
||||
class MockComponentRequest(component.Component):
|
||||
@register("testcomponent")
|
||||
class MockComponentRequest(Component):
|
||||
template = """
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
@ -64,7 +64,7 @@ class TestComponentAsView(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_get_request(self):
|
||||
class MockComponentRequest(component.Component):
|
||||
class MockComponentRequest(Component):
|
||||
template = """
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
@ -88,7 +88,7 @@ class TestComponentAsView(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_post_request(self):
|
||||
class MockComponentRequest(component.Component):
|
||||
class MockComponentRequest(Component):
|
||||
template = """
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
@ -114,7 +114,7 @@ class TestComponentAsView(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_replace_slot_in_view(self):
|
||||
class MockComponentSlot(component.Component):
|
||||
class MockComponentSlot(Component):
|
||||
template = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -143,7 +143,7 @@ class TestComponentAsView(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_replace_slot_in_view_with_insecure_content(self):
|
||||
class MockInsecureComponentSlot(component.Component):
|
||||
class MockInsecureComponentSlot(Component):
|
||||
template = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -165,7 +165,7 @@ class TestComponentAsView(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_replace_context_in_view(self):
|
||||
class TestComponent(component.Component):
|
||||
class TestComponent(Component):
|
||||
template = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -186,7 +186,7 @@ class TestComponentAsView(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_replace_context_in_view_with_insecure_content(self):
|
||||
class MockInsecureComponentContext(component.Component):
|
||||
class MockInsecureComponentContext(Component):
|
||||
template = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.test import override_settings
|
|||
from django.utils.html import format_html, html_safe
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, registry, types
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
from .testutils import BaseTestCase, autodiscover_with_cleanup
|
||||
|
@ -19,7 +19,7 @@ setup_test_config()
|
|||
|
||||
class InlineComponentTest(BaseTestCase):
|
||||
def test_html(self):
|
||||
class InlineHTMLComponent(component.Component):
|
||||
class InlineHTMLComponent(Component):
|
||||
template = "<div class='inline'>Hello Inline</div>"
|
||||
|
||||
comp = InlineHTMLComponent("inline_html_component")
|
||||
|
@ -29,7 +29,7 @@ class InlineComponentTest(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_html_and_css(self):
|
||||
class HTMLCSSComponent(component.Component):
|
||||
class HTMLCSSComponent(Component):
|
||||
template = "<div class='html-css-only'>Content</div>"
|
||||
css = ".html-css-only { color: blue; }"
|
||||
|
||||
|
@ -44,7 +44,7 @@ class InlineComponentTest(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_html_and_js(self):
|
||||
class HTMLJSComponent(component.Component):
|
||||
class HTMLJSComponent(Component):
|
||||
template = "<div class='html-js-only'>Content</div>"
|
||||
js = "console.log('HTML and JS only');"
|
||||
|
||||
|
@ -59,7 +59,7 @@ class InlineComponentTest(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_html_inline_and_css_js_files(self):
|
||||
class HTMLStringFileCSSJSComponent(component.Component):
|
||||
class HTMLStringFileCSSJSComponent(Component):
|
||||
template = "<div class='html-string-file'>Content</div>"
|
||||
|
||||
class Media:
|
||||
|
@ -80,7 +80,7 @@ class InlineComponentTest(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_html_js_inline_and_css_file(self):
|
||||
class HTMLStringFileCSSJSComponent(component.Component):
|
||||
class HTMLStringFileCSSJSComponent(Component):
|
||||
template = "<div class='html-string-file'>Content</div>"
|
||||
js = "console.log('HTML and JS only');"
|
||||
|
||||
|
@ -101,7 +101,7 @@ class InlineComponentTest(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_html_css_inline_and_js_file(self):
|
||||
class HTMLStringFileCSSJSComponent(component.Component):
|
||||
class HTMLStringFileCSSJSComponent(Component):
|
||||
template = "<div class='html-string-file'>Content</div>"
|
||||
css = ".html-string-file { color: blue; }"
|
||||
|
||||
|
@ -121,7 +121,7 @@ class InlineComponentTest(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_html_variable(self):
|
||||
class VariableHTMLComponent(component.Component):
|
||||
class VariableHTMLComponent(Component):
|
||||
def get_template(self, context):
|
||||
return Template("<div class='variable-html'>{{ variable }}</div>")
|
||||
|
||||
|
@ -133,7 +133,7 @@ class InlineComponentTest(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_html_variable_filtered(self):
|
||||
class FilteredComponent(component.Component):
|
||||
class FilteredComponent(Component):
|
||||
template: types.django_html = """
|
||||
Var1: <strong>{{ var1 }}</strong>
|
||||
Var2 (uppercased): <strong>{{ var2|upper }}</strong>
|
||||
|
@ -157,7 +157,7 @@ class InlineComponentTest(BaseTestCase):
|
|||
|
||||
class ComponentMediaTests(BaseTestCase):
|
||||
def test_css_and_js(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -176,7 +176,7 @@ class ComponentMediaTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_css_only(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -194,7 +194,7 @@ class ComponentMediaTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_js_only(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -212,7 +212,7 @@ class ComponentMediaTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_empty_media(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -225,7 +225,7 @@ class ComponentMediaTests(BaseTestCase):
|
|||
self.assertHTMLEqual(comp.render_dependencies(), "")
|
||||
|
||||
def test_missing_media(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -235,7 +235,7 @@ class ComponentMediaTests(BaseTestCase):
|
|||
self.assertHTMLEqual(comp.render_dependencies(), "")
|
||||
|
||||
def test_css_js_as_lists(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
class Media:
|
||||
css = ["path/to/style.css", "path/to/style2.css"]
|
||||
js = ["path/to/script.js"]
|
||||
|
@ -251,7 +251,7 @@ class ComponentMediaTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_css_js_as_string(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
class Media:
|
||||
css = "path/to/style.css"
|
||||
js = "path/to/script.js"
|
||||
|
@ -266,7 +266,7 @@ class ComponentMediaTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_css_as_dict(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
class Media:
|
||||
css = {
|
||||
"all": "path/to/style.css",
|
||||
|
@ -295,7 +295,7 @@ class ComponentMediaTests(BaseTestCase):
|
|||
tags.append(f'<my_script_tag src="{abs_path}"></my_script_tag>')
|
||||
return tags
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
media_class = MyMedia
|
||||
|
||||
class Media:
|
||||
|
@ -320,7 +320,7 @@ class ComponentMediaTests(BaseTestCase):
|
|||
tags.append(f'<my_link href="{path}" media="{medium}" rel="stylesheet" />')
|
||||
return tags
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
media_class = MyMedia
|
||||
|
||||
class Media:
|
||||
|
@ -376,7 +376,7 @@ class MediaPathAsObjectTests(BaseTestCase):
|
|||
def __str__(self):
|
||||
return format_html('<script type="module" src="{}"></script>', static(self.static_path))
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
class Media:
|
||||
css = {
|
||||
"all": [
|
||||
|
@ -424,7 +424,7 @@ class MediaPathAsObjectTests(BaseTestCase):
|
|||
def __fspath__(self):
|
||||
return self.path
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
class Media:
|
||||
css = {
|
||||
"all": [
|
||||
|
@ -466,7 +466,7 @@ class MediaPathAsObjectTests(BaseTestCase):
|
|||
class MyStr(str):
|
||||
pass
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
class Media:
|
||||
css = {
|
||||
"all": [
|
||||
|
@ -506,7 +506,7 @@ class MediaPathAsObjectTests(BaseTestCase):
|
|||
class MyBytes(bytes):
|
||||
pass
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
class Media:
|
||||
css = {
|
||||
"all": [
|
||||
|
@ -538,7 +538,7 @@ class MediaPathAsObjectTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_function(self):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
class Media:
|
||||
css = [
|
||||
lambda: mark_safe('<link hi href="calendar/style.css" rel="stylesheet" />'), # Literal
|
||||
|
@ -573,7 +573,7 @@ class MediaPathAsObjectTests(BaseTestCase):
|
|||
def test_works_with_static(self):
|
||||
"""Test that all the different ways of defining media files works with Django's staticfiles"""
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
class Media:
|
||||
css = [
|
||||
mark_safe(f'<link hi href="{static("calendar/style.css")}" rel="stylesheet" />'), # Literal
|
||||
|
@ -636,7 +636,7 @@ class MediaStaticfilesTests(BaseTestCase):
|
|||
tags.append(f'<my_script_tag src="{abs_path}"></my_script_tag>')
|
||||
return tags
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
media_class = MyMedia
|
||||
|
||||
class Media:
|
||||
|
@ -693,7 +693,7 @@ class MediaStaticfilesTests(BaseTestCase):
|
|||
tags.append(f'<my_script_tag src="{abs_path}"></my_script_tag>')
|
||||
return tags
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
media_class = MyMedia
|
||||
|
||||
class Media:
|
||||
|
@ -714,7 +714,7 @@ class MediaStaticfilesTests(BaseTestCase):
|
|||
|
||||
|
||||
class MediaRelativePathTests(BaseTestCase):
|
||||
class ParentComponent(component.Component):
|
||||
class ParentComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -734,7 +734,7 @@ class MediaRelativePathTests(BaseTestCase):
|
|||
def get_context_data(self):
|
||||
return {"shadowing_variable": "NOT SHADOWED"}
|
||||
|
||||
class VariableDisplay(component.Component):
|
||||
class VariableDisplay(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<h1>Shadowing variable = {{ shadowing_variable }}</h1>
|
||||
|
@ -751,8 +751,8 @@ class MediaRelativePathTests(BaseTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register(name="parent_component", component=self.ParentComponent)
|
||||
component.registry.register(name="variable_display", component=self.VariableDisplay)
|
||||
registry.register(name="parent_component", component=self.ParentComponent)
|
||||
registry.register(name="variable_display", component=self.VariableDisplay)
|
||||
|
||||
# Settings required for autodiscover to work
|
||||
@override_settings(
|
||||
|
@ -771,11 +771,11 @@ class MediaRelativePathTests(BaseTestCase):
|
|||
# Make sure that only relevant components are registered:
|
||||
comps_to_remove = [
|
||||
comp_name
|
||||
for comp_name in component.registry.all()
|
||||
for comp_name in registry.all()
|
||||
if comp_name not in ["relative_file_component", "parent_component", "variable_display"]
|
||||
]
|
||||
for comp_name in comps_to_remove:
|
||||
component.registry.unregister(comp_name)
|
||||
registry.unregister(comp_name)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies %}
|
||||
|
@ -810,7 +810,7 @@ class MediaRelativePathTests(BaseTestCase):
|
|||
|
||||
# Fix the paths, since the "components" dir is nested
|
||||
with autodiscover_with_cleanup(map_module=lambda p: f"tests.{p}"):
|
||||
component.registry.unregister("relative_file_pathobj_component")
|
||||
registry.unregister("relative_file_pathobj_component")
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies %}
|
||||
|
@ -852,7 +852,7 @@ class MediaRelativePathTests(BaseTestCase):
|
|||
with autodiscover_with_cleanup(map_module=lambda p: f"tests.{p}"):
|
||||
# Mark the PathObj instances of 'relative_file_pathobj_component' so they won raise
|
||||
# error PathObj.__str__ is triggered.
|
||||
CompCls = component.registry.get("relative_file_pathobj_component")
|
||||
CompCls = registry.get("relative_file_pathobj_component")
|
||||
CompCls.Media.js[0].throw_on_calling_str = False # type: ignore
|
||||
CompCls.Media.css["all"][0].throw_on_calling_str = False # type: ignore
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.template import Context, Template
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, registry, types
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
from .testutils import BaseTestCase, parametrize_context_behavior
|
||||
|
@ -13,7 +13,7 @@ setup_test_config()
|
|||
#########################
|
||||
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -26,7 +26,7 @@ class SimpleComponent(component.Component):
|
|||
return "Variable: < strong > {} < / strong >".format(variable_value)
|
||||
|
||||
|
||||
class VariableDisplay(component.Component):
|
||||
class VariableDisplay(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<h1>Shadowing variable = {{ shadowing_variable }}</h1>
|
||||
|
@ -42,7 +42,7 @@ class VariableDisplay(component.Component):
|
|||
return context
|
||||
|
||||
|
||||
class IncrementerComponent(component.Component):
|
||||
class IncrementerComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<p class="incrementer">value={{ value }};calls={{ calls }}</p>
|
||||
|
@ -68,7 +68,7 @@ class IncrementerComponent(component.Component):
|
|||
|
||||
|
||||
class ContextTests(BaseTestCase):
|
||||
class ParentComponent(component.Component):
|
||||
class ParentComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -90,8 +90,8 @@ class ContextTests(BaseTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register(name="variable_display", component=VariableDisplay)
|
||||
component.registry.register(name="parent_component", component=self.ParentComponent)
|
||||
registry.register(name="variable_display", component=VariableDisplay)
|
||||
registry.register(name="parent_component", component=self.ParentComponent)
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_tag(
|
||||
|
@ -221,7 +221,7 @@ class ContextTests(BaseTestCase):
|
|||
|
||||
|
||||
class ParentArgsTests(BaseTestCase):
|
||||
class ParentComponentWithArgs(component.Component):
|
||||
class ParentComponentWithArgs(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -243,9 +243,9 @@ class ParentArgsTests(BaseTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register(name="incrementer", component=IncrementerComponent)
|
||||
component.registry.register(name="parent_with_args", component=self.ParentComponentWithArgs)
|
||||
component.registry.register(name="variable_display", component=VariableDisplay)
|
||||
registry.register(name="incrementer", component=IncrementerComponent)
|
||||
registry.register(name="parent_with_args", component=self.ParentComponentWithArgs)
|
||||
registry.register(name="variable_display", component=VariableDisplay)
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_parent_args_can_be_drawn_from_context(self):
|
||||
|
@ -326,7 +326,7 @@ class ParentArgsTests(BaseTestCase):
|
|||
class ContextCalledOnceTests(BaseTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register(name="incrementer", component=IncrementerComponent)
|
||||
registry.register(name="incrementer", component=IncrementerComponent)
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_one_context_call_with_simple_component(self):
|
||||
|
@ -416,7 +416,7 @@ class ContextCalledOnceTests(BaseTestCase):
|
|||
class ComponentsCanAccessOuterContext(BaseTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register(name="simple_component", component=SimpleComponent)
|
||||
registry.register(name="simple_component", component=SimpleComponent)
|
||||
|
||||
# NOTE: Second arg in tuple is expected value.
|
||||
@parametrize_context_behavior(
|
||||
|
@ -443,7 +443,7 @@ class ComponentsCanAccessOuterContext(BaseTestCase):
|
|||
class IsolatedContextTests(BaseTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register(name="simple_component", component=SimpleComponent)
|
||||
registry.register(name="simple_component", component=SimpleComponent)
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_simple_component_can_pass_outer_context_in_args(self):
|
||||
|
@ -469,7 +469,7 @@ class IsolatedContextTests(BaseTestCase):
|
|||
class IsolatedContextSettingTests(BaseTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register(name="simple_component", component=SimpleComponent)
|
||||
registry.register(name="simple_component", component=SimpleComponent)
|
||||
|
||||
@parametrize_context_behavior(["isolated"])
|
||||
def test_component_tag_includes_variable_with_isolated_context_from_settings(
|
||||
|
@ -523,7 +523,7 @@ class IsolatedContextSettingTests(BaseTestCase):
|
|||
|
||||
|
||||
class OuterContextPropertyTests(BaseTestCase):
|
||||
class OuterContextComponent(component.Component):
|
||||
class OuterContextComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -533,7 +533,7 @@ class OuterContextPropertyTests(BaseTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register(name="outer_context_component", component=self.OuterContextComponent)
|
||||
registry.register(name="outer_context_component", component=self.OuterContextComponent)
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_outer_context_property_with_component(self):
|
||||
|
@ -547,7 +547,7 @@ class OuterContextPropertyTests(BaseTestCase):
|
|||
|
||||
|
||||
class ContextVarsIsFilledTests(BaseTestCase):
|
||||
class IsFilledVarsComponent(component.Component):
|
||||
class IsFilledVarsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div class="frontmatter-component">
|
||||
|
@ -560,7 +560,7 @@ class ContextVarsIsFilledTests(BaseTestCase):
|
|||
</div>
|
||||
"""
|
||||
|
||||
class ComponentWithConditionalSlots(component.Component):
|
||||
class ComponentWithConditionalSlots(Component):
|
||||
template: types.django_html = """
|
||||
{# Example from django-components/issues/98 #}
|
||||
{% load component_tags %}
|
||||
|
@ -575,7 +575,7 @@ class ContextVarsIsFilledTests(BaseTestCase):
|
|||
</div>
|
||||
"""
|
||||
|
||||
class ComponentWithComplexConditionalSlots(component.Component):
|
||||
class ComponentWithComplexConditionalSlots(Component):
|
||||
template: types.django_html = """
|
||||
{# Example from django-components/issues/98 #}
|
||||
{% load component_tags %}
|
||||
|
@ -591,7 +591,7 @@ class ContextVarsIsFilledTests(BaseTestCase):
|
|||
</div>
|
||||
"""
|
||||
|
||||
class ComponentWithNegatedConditionalSlot(component.Component):
|
||||
class ComponentWithNegatedConditionalSlot(Component):
|
||||
template: types.django_html = """
|
||||
{# Example from django-components/issues/98 #}
|
||||
{% load component_tags %}
|
||||
|
@ -607,17 +607,17 @@ class ContextVarsIsFilledTests(BaseTestCase):
|
|||
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
component.registry.register("is_filled_vars", self.IsFilledVarsComponent)
|
||||
component.registry.register("conditional_slots", self.ComponentWithConditionalSlots)
|
||||
component.registry.register(
|
||||
registry.register("is_filled_vars", self.IsFilledVarsComponent)
|
||||
registry.register("conditional_slots", self.ComponentWithConditionalSlots)
|
||||
registry.register(
|
||||
"complex_conditional_slots",
|
||||
self.ComponentWithComplexConditionalSlots,
|
||||
)
|
||||
component.registry.register("negated_conditional_slot", self.ComponentWithNegatedConditionalSlot)
|
||||
registry.register("negated_conditional_slot", self.ComponentWithNegatedConditionalSlot)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
super().tearDown()
|
||||
component.registry.clear()
|
||||
registry.clear()
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_is_filled_vars(self):
|
||||
|
|
|
@ -4,7 +4,7 @@ from django.http import HttpResponseNotModified
|
|||
from django.template import Template
|
||||
from django.test import override_settings
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, registry, types
|
||||
from django_components.middleware import ComponentDependencyMiddleware
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
|
@ -13,7 +13,7 @@ from .testutils import BaseTestCase, create_and_process_template_response
|
|||
setup_test_config()
|
||||
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -29,7 +29,7 @@ class SimpleComponent(component.Component):
|
|||
js = "script.js"
|
||||
|
||||
|
||||
class SimpleComponentAlternate(component.Component):
|
||||
class SimpleComponentAlternate(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -42,7 +42,7 @@ class SimpleComponentAlternate(component.Component):
|
|||
js = "script2.js"
|
||||
|
||||
|
||||
class SimpleComponentWithSharedDependency(component.Component):
|
||||
class SimpleComponentWithSharedDependency(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -55,7 +55,7 @@ class SimpleComponentWithSharedDependency(component.Component):
|
|||
js = ["script.js", "script2.js"]
|
||||
|
||||
|
||||
class MultistyleComponent(component.Component):
|
||||
class MultistyleComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
"""
|
||||
|
@ -68,11 +68,11 @@ class MultistyleComponent(component.Component):
|
|||
@override_settings(COMPONENTS={"RENDER_DEPENDENCIES": True})
|
||||
class ComponentMediaRenderingTests(BaseTestCase):
|
||||
def setUp(self):
|
||||
# NOTE: component.registry is global, so need to clear before each test
|
||||
component.registry.clear()
|
||||
# NOTE: registry is global, so need to clear before each test
|
||||
registry.clear()
|
||||
|
||||
def test_no_dependencies_when_no_components_used(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies %}
|
||||
|
@ -87,7 +87,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_no_js_dependencies_when_no_components_used(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_js_dependencies %}
|
||||
|
@ -97,7 +97,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
||||
|
||||
def test_no_css_dependencies_when_no_components_used(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_css_dependencies %}
|
||||
|
@ -111,7 +111,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_preload_dependencies_render_when_no_components_used(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies preload='test' %}
|
||||
|
@ -126,7 +126,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_preload_css_dependencies_render_when_no_components_used(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_css_dependencies preload='test' %}
|
||||
|
@ -140,7 +140,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_single_component_dependencies_render_when_used(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies %}
|
||||
|
@ -156,7 +156,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||
|
||||
def test_single_component_with_dash_or_slash_in_name(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies %}
|
||||
|
@ -172,7 +172,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||
|
||||
def test_preload_dependencies_render_once_when_used(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies preload='test' %}
|
||||
|
@ -188,7 +188,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||
|
||||
def test_placeholder_removed_when_single_component_rendered(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies %}
|
||||
|
@ -199,7 +199,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
self.assertNotIn("_RENDERED", rendered)
|
||||
|
||||
def test_placeholder_removed_when_preload_rendered(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies preload='test' %}
|
||||
|
@ -209,7 +209,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
self.assertNotIn("_RENDERED", rendered)
|
||||
|
||||
def test_single_component_css_dependencies(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_css_dependencies %}
|
||||
|
@ -224,7 +224,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_single_component_js_dependencies(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_js_dependencies %}
|
||||
|
@ -237,7 +237,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
def test_all_dependencies_are_rendered_for_component_with_multiple_dependencies(
|
||||
self,
|
||||
):
|
||||
component.registry.register(name="test", component=MultistyleComponent)
|
||||
registry.register(name="test", component=MultistyleComponent)
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies %}
|
||||
{% component 'test' %}{% endcomponent %}
|
||||
|
@ -260,7 +260,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
def test_all_js_dependencies_are_rendered_for_component_with_multiple_dependencies(
|
||||
self,
|
||||
):
|
||||
component.registry.register(name="test", component=MultistyleComponent)
|
||||
registry.register(name="test", component=MultistyleComponent)
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_js_dependencies %}
|
||||
{% component 'test' %}{% endcomponent %}
|
||||
|
@ -283,7 +283,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
def test_all_css_dependencies_are_rendered_for_component_with_multiple_dependencies(
|
||||
self,
|
||||
):
|
||||
component.registry.register(name="test", component=MultistyleComponent)
|
||||
registry.register(name="test", component=MultistyleComponent)
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_css_dependencies %}
|
||||
{% component 'test' %}{% endcomponent %}
|
||||
|
@ -304,8 +304,8 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_no_dependencies_with_multiple_unused_components(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
registry.register(name="test1", component=SimpleComponent)
|
||||
registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies %}
|
||||
|
@ -326,8 +326,8 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_correct_css_dependencies_with_multiple_components(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
registry.register(name="test1", component=SimpleComponent)
|
||||
registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_css_dependencies %}
|
||||
|
@ -347,8 +347,8 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_correct_js_dependencies_with_multiple_components(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
registry.register(name="test1", component=SimpleComponent)
|
||||
registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_js_dependencies %}
|
||||
|
@ -360,8 +360,8 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
self.assertInHTML('<script src="script2.js">', rendered, count=0)
|
||||
|
||||
def test_correct_dependencies_with_multiple_components(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
registry.register(name="test1", component=SimpleComponent)
|
||||
registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies %}
|
||||
|
@ -383,9 +383,9 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_shared_dependencies_rendered_once(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
component.registry.register(name="test3", component=SimpleComponentWithSharedDependency)
|
||||
registry.register(name="test1", component=SimpleComponent)
|
||||
registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
registry.register(name="test3", component=SimpleComponentWithSharedDependency)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies %}
|
||||
|
@ -409,9 +409,9 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
)
|
||||
|
||||
def test_placeholder_removed_when_multiple_component_rendered(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
component.registry.register(name="test3", component=SimpleComponentWithSharedDependency)
|
||||
registry.register(name="test1", component=SimpleComponent)
|
||||
registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
registry.register(name="test3", component=SimpleComponentWithSharedDependency)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}{% component_dependencies %}
|
||||
|
@ -438,7 +438,7 @@ class ComponentMediaRenderingTests(BaseTestCase):
|
|||
"test_component",
|
||||
]
|
||||
for component_name in component_names:
|
||||
component.registry.register(name=component_name, component=SimpleComponent)
|
||||
registry.register(name=component_name, component=SimpleComponent)
|
||||
template_str: types.django_html = f"""
|
||||
{{% load component_tags %}}
|
||||
{{% component_js_dependencies %}}
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
import unittest
|
||||
|
||||
from django_components import component
|
||||
from django_components import AlreadyRegistered, Component, ComponentRegistry, NotRegistered, register, registry
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
|
||||
setup_test_config()
|
||||
|
||||
|
||||
class MockComponent(component.Component):
|
||||
class MockComponent(Component):
|
||||
pass
|
||||
|
||||
|
||||
class MockComponent2(component.Component):
|
||||
class MockComponent2(Component):
|
||||
pass
|
||||
|
||||
|
||||
class MockComponentView(component.Component):
|
||||
class MockComponentView(Component):
|
||||
def get(self, request, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class ComponentRegistryTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.registry = component.ComponentRegistry()
|
||||
self.registry = ComponentRegistry()
|
||||
|
||||
def test_register_class_decorator(self):
|
||||
@component.register("decorated_component")
|
||||
class TestComponent(component.Component):
|
||||
@register("decorated_component")
|
||||
class TestComponent(Component):
|
||||
pass
|
||||
|
||||
self.assertEqual(component.registry.get("decorated_component"), TestComponent)
|
||||
self.assertEqual(registry.get("decorated_component"), TestComponent)
|
||||
|
||||
def test_simple_register(self):
|
||||
self.registry.register(name="testcomponent", component=MockComponent)
|
||||
|
@ -48,14 +48,14 @@ class ComponentRegistryTest(unittest.TestCase):
|
|||
|
||||
def test_prevent_registering_different_components_with_the_same_name(self):
|
||||
self.registry.register(name="testcomponent", component=MockComponent)
|
||||
with self.assertRaises(component.AlreadyRegistered):
|
||||
with self.assertRaises(AlreadyRegistered):
|
||||
self.registry.register(name="testcomponent", component=MockComponent2)
|
||||
|
||||
def test_allow_duplicated_registration_of_the_same_component(self):
|
||||
try:
|
||||
self.registry.register(name="testcomponent", component=MockComponentView)
|
||||
self.registry.register(name="testcomponent", component=MockComponentView)
|
||||
except component.AlreadyRegistered:
|
||||
except AlreadyRegistered:
|
||||
self.fail("Should not raise AlreadyRegistered")
|
||||
|
||||
def test_simple_unregister(self):
|
||||
|
@ -64,5 +64,5 @@ class ComponentRegistryTest(unittest.TestCase):
|
|||
self.assertEqual(self.registry.all(), {})
|
||||
|
||||
def test_raises_on_failed_unregister(self):
|
||||
with self.assertRaises(component.NotRegistered):
|
||||
with self.assertRaises(NotRegistered):
|
||||
self.registry.unregister(name="testcomponent")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.template import Context, Template
|
||||
from django.template.base import Parser
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, registry, types
|
||||
from django_components.component import safe_resolve_dict, safe_resolve_list
|
||||
from django_components.template_parser import process_aggregate_kwargs
|
||||
from django_components.templatetags.component_tags import _parse_component_with_args
|
||||
|
@ -56,7 +56,7 @@ class ParserTest(BaseTestCase):
|
|||
|
||||
|
||||
class ParserComponentTest(BaseTestCase):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template: types.django_html = """
|
||||
{{ date }}
|
||||
{{ id }}
|
||||
|
@ -72,7 +72,7 @@ class ParserComponentTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_special_chars_accessible_via_kwargs(self):
|
||||
component.registry.register("test", self.SimpleComponent)
|
||||
registry.register("test", self.SimpleComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
|
|
@ -4,7 +4,7 @@ from typing import Callable
|
|||
|
||||
from django.template import Context, Template
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, register, registry, types
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
from .testutils import BaseTestCase, parametrize_context_behavior
|
||||
|
@ -12,7 +12,7 @@ from .testutils import BaseTestCase, parametrize_context_behavior
|
|||
setup_test_config()
|
||||
|
||||
|
||||
class SlottedComponent(component.Component):
|
||||
class SlottedComponent(Component):
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
|
||||
|
@ -34,11 +34,11 @@ class TemplateInstrumentationTest(BaseTestCase):
|
|||
self.saved_render_method = Template._render
|
||||
Template._render = instrumented_test_render
|
||||
|
||||
component.registry.clear()
|
||||
component.registry.register("test_component", SlottedComponent)
|
||||
registry.clear()
|
||||
registry.register("test_component", SlottedComponent)
|
||||
|
||||
@component.register("inner_component")
|
||||
class SimpleComponent(component.Component):
|
||||
@register("inner_component")
|
||||
class SimpleComponent(Component):
|
||||
template_name = "simple_template.html"
|
||||
|
||||
def get_context_data(self, variable, variable2="default"):
|
||||
|
@ -93,19 +93,19 @@ class TemplateInstrumentationTest(BaseTestCase):
|
|||
|
||||
class BlockCompatTests(BaseTestCase):
|
||||
def setUp(self):
|
||||
component.registry.clear()
|
||||
registry.clear()
|
||||
super().setUp()
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
component.registry.clear()
|
||||
registry.clear()
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slots_inside_extends(self):
|
||||
component.registry.register("slotted_component", SlottedComponent)
|
||||
registry.register("slotted_component", SlottedComponent)
|
||||
|
||||
@component.register("slot_inside_extends")
|
||||
class SlotInsideExtendsComponent(component.Component):
|
||||
@register("slot_inside_extends")
|
||||
class SlotInsideExtendsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% extends "block_in_slot_in_component.html" %}
|
||||
"""
|
||||
|
@ -135,10 +135,10 @@ class BlockCompatTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slots_inside_include(self):
|
||||
component.registry.register("slotted_component", SlottedComponent)
|
||||
registry.register("slotted_component", SlottedComponent)
|
||||
|
||||
@component.register("slot_inside_include")
|
||||
class SlotInsideIncludeComponent(component.Component):
|
||||
@register("slot_inside_include")
|
||||
class SlotInsideIncludeComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% include "block_in_slot_in_component.html" %}
|
||||
"""
|
||||
|
@ -168,7 +168,7 @@ class BlockCompatTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_component_inside_block(self):
|
||||
component.registry.register("slotted_component", SlottedComponent)
|
||||
registry.register("slotted_component", SlottedComponent)
|
||||
template: types.django_html = """
|
||||
{% extends "block.html" %}
|
||||
{% load component_tags %}
|
||||
|
@ -203,7 +203,7 @@ class BlockCompatTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_block_inside_component(self):
|
||||
component.registry.register("slotted_component", SlottedComponent)
|
||||
registry.register("slotted_component", SlottedComponent)
|
||||
|
||||
template: types.django_html = """
|
||||
{% extends "block_in_component.html" %}
|
||||
|
@ -233,10 +233,10 @@ class BlockCompatTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_block_inside_component_parent(self):
|
||||
component.registry.register("slotted_component", SlottedComponent)
|
||||
registry.register("slotted_component", SlottedComponent)
|
||||
|
||||
@component.register("block_in_component_parent")
|
||||
class BlockInCompParent(component.Component):
|
||||
@register("block_in_component_parent")
|
||||
class BlockInCompParent(Component):
|
||||
template_name = "block_in_component_parent.html"
|
||||
|
||||
template: types.django_html = """
|
||||
|
@ -266,10 +266,10 @@ class BlockCompatTests(BaseTestCase):
|
|||
Assert that when we call a component with `{% component %}`, that
|
||||
the `{% block %}` will NOT affect the inner component.
|
||||
"""
|
||||
component.registry.register("slotted_component", SlottedComponent)
|
||||
registry.register("slotted_component", SlottedComponent)
|
||||
|
||||
@component.register("block_inside_slot_v1")
|
||||
class BlockInSlotInComponent(component.Component):
|
||||
@register("block_inside_slot_v1")
|
||||
class BlockInSlotInComponent(Component):
|
||||
template_name = "block_in_slot_in_component.html"
|
||||
|
||||
template: types.django_html = """
|
||||
|
@ -301,10 +301,10 @@ class BlockCompatTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_inside_block__slot_default_block_default(self):
|
||||
component.registry.register("slotted_component", SlottedComponent)
|
||||
registry.register("slotted_component", SlottedComponent)
|
||||
|
||||
@component.register("slot_inside_block")
|
||||
class _SlotInsideBlockComponent(component.Component):
|
||||
@register("slot_inside_block")
|
||||
class _SlotInsideBlockComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% extends "slot_inside_block.html" %}
|
||||
"""
|
||||
|
@ -333,11 +333,11 @@ class BlockCompatTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_inside_block__slot_default_block_override(self):
|
||||
component.registry.clear()
|
||||
component.registry.register("slotted_component", SlottedComponent)
|
||||
registry.clear()
|
||||
registry.register("slotted_component", SlottedComponent)
|
||||
|
||||
@component.register("slot_inside_block")
|
||||
class _SlotInsideBlockComponent(component.Component):
|
||||
@register("slot_inside_block")
|
||||
class _SlotInsideBlockComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% extends "slot_inside_block.html" %}
|
||||
{% block inner %}
|
||||
|
@ -369,10 +369,10 @@ class BlockCompatTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["isolated", "django"])
|
||||
def test_slot_inside_block__slot_overriden_block_default(self):
|
||||
component.registry.register("slotted_component", SlottedComponent)
|
||||
registry.register("slotted_component", SlottedComponent)
|
||||
|
||||
@component.register("slot_inside_block")
|
||||
class _SlotInsideBlockComponent(component.Component):
|
||||
@register("slot_inside_block")
|
||||
class _SlotInsideBlockComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% extends "slot_inside_block.html" %}
|
||||
"""
|
||||
|
@ -405,10 +405,10 @@ class BlockCompatTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_inside_block__slot_overriden_block_overriden(self):
|
||||
component.registry.register("slotted_component", SlottedComponent)
|
||||
registry.register("slotted_component", SlottedComponent)
|
||||
|
||||
@component.register("slot_inside_block")
|
||||
class _SlotInsideBlockComponent(component.Component):
|
||||
@register("slot_inside_block")
|
||||
class _SlotInsideBlockComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% extends "slot_inside_block.html" %}
|
||||
{% block inner %}
|
||||
|
@ -451,10 +451,10 @@ class BlockCompatTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_inject_inside_block(self):
|
||||
component.registry.register("slotted_component", SlottedComponent)
|
||||
registry.register("slotted_component", SlottedComponent)
|
||||
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
|
|
@ -2,7 +2,7 @@ import textwrap
|
|||
|
||||
from django.template import Context, Template, TemplateSyntaxError
|
||||
|
||||
from django_components import component, component_registry, types
|
||||
from django_components import Component, NotRegistered, register, registry, types
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
from .testutils import BaseTestCase, parametrize_context_behavior
|
||||
|
@ -10,11 +10,11 @@ from .testutils import BaseTestCase, parametrize_context_behavior
|
|||
setup_test_config()
|
||||
|
||||
|
||||
class SlottedComponent(component.Component):
|
||||
class SlottedComponent(Component):
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
|
||||
class SlottedComponentWithContext(component.Component):
|
||||
class SlottedComponentWithContext(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<custom-template>
|
||||
|
@ -34,7 +34,7 @@ class SlottedComponentWithContext(component.Component):
|
|||
|
||||
|
||||
class ComponentTemplateTagTest(BaseTestCase):
|
||||
class SimpleComponent(component.Component):
|
||||
class SimpleComponent(Component):
|
||||
template_name = "simple_template.html"
|
||||
|
||||
def get_context_data(self, variable, variable2="default"):
|
||||
|
@ -48,12 +48,12 @@ class ComponentTemplateTagTest(BaseTestCase):
|
|||
js = "script.js"
|
||||
|
||||
def setUp(self):
|
||||
# NOTE: component.registry is global, so need to clear before each test
|
||||
component.registry.clear()
|
||||
# NOTE: registry is global, so need to clear before each test
|
||||
registry.clear()
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_single_component(self):
|
||||
component.registry.register(name="test", component=self.SimpleComponent)
|
||||
registry.register(name="test", component=self.SimpleComponent)
|
||||
|
||||
simple_tag_template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -74,12 +74,12 @@ class ComponentTemplateTagTest(BaseTestCase):
|
|||
"""
|
||||
|
||||
template = Template(simple_tag_template)
|
||||
with self.assertRaises(component_registry.NotRegistered):
|
||||
with self.assertRaises(NotRegistered):
|
||||
template.render(Context({}))
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_component_called_with_positional_name(self):
|
||||
component.registry.register(name="test", component=self.SimpleComponent)
|
||||
registry.register(name="test", component=self.SimpleComponent)
|
||||
|
||||
simple_tag_template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -92,8 +92,8 @@ class ComponentTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_call_component_with_two_variables(self):
|
||||
@component.register("test")
|
||||
class IffedComponent(component.Component):
|
||||
@register("test")
|
||||
class IffedComponent(Component):
|
||||
template: types.django_html = """
|
||||
Variable: <strong>{{ variable }}</strong>
|
||||
{% if variable2 != "default" %}
|
||||
|
@ -123,7 +123,7 @@ class ComponentTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_component_called_with_singlequoted_name(self):
|
||||
component.registry.register(name="test", component=self.SimpleComponent)
|
||||
registry.register(name="test", component=self.SimpleComponent)
|
||||
|
||||
simple_tag_template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -136,7 +136,7 @@ class ComponentTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_component_called_with_variable_as_name(self):
|
||||
component.registry.register(name="test", component=self.SimpleComponent)
|
||||
registry.register(name="test", component=self.SimpleComponent)
|
||||
|
||||
simple_tag_template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -151,7 +151,7 @@ class ComponentTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_component_called_with_invalid_variable_as_name(self):
|
||||
component.registry.register(name="test", component=self.SimpleComponent)
|
||||
registry.register(name="test", component=self.SimpleComponent)
|
||||
|
||||
simple_tag_template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -161,13 +161,13 @@ class ComponentTemplateTagTest(BaseTestCase):
|
|||
"""
|
||||
|
||||
template = Template(simple_tag_template)
|
||||
with self.assertRaises(component_registry.NotRegistered):
|
||||
with self.assertRaises(NotRegistered):
|
||||
template.render(Context({}))
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_component_accepts_provided_and_default_parameters(self):
|
||||
@component.register("test")
|
||||
class ComponentWithProvidedAndDefaultParameters(component.Component):
|
||||
@register("test")
|
||||
class ComponentWithProvidedAndDefaultParameters(Component):
|
||||
template: types.django_html = """
|
||||
Provided variable: <strong>{{ variable }}</strong>
|
||||
Default: <p>{{ default_param }}</p>
|
||||
|
@ -191,11 +191,11 @@ class ComponentTemplateTagTest(BaseTestCase):
|
|||
|
||||
class MultiComponentTests(BaseTestCase):
|
||||
def setUp(self):
|
||||
component.registry.clear()
|
||||
registry.clear()
|
||||
|
||||
def register_components(self):
|
||||
component.registry.register("first_component", SlottedComponent)
|
||||
component.registry.register("second_component", SlottedComponentWithContext)
|
||||
registry.register("first_component", SlottedComponent)
|
||||
registry.register("second_component", SlottedComponentWithContext)
|
||||
|
||||
def make_template(self, first_slot: str = "", second_slot: str = "") -> Template:
|
||||
template_str: types.django_html = f"""
|
||||
|
@ -266,7 +266,7 @@ class MultiComponentTests(BaseTestCase):
|
|||
|
||||
class ComponentIsolationTests(BaseTestCase):
|
||||
def setUp(self):
|
||||
class SlottedComponent(component.Component):
|
||||
class SlottedComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<custom-template>
|
||||
|
@ -276,7 +276,7 @@ class ComponentIsolationTests(BaseTestCase):
|
|||
</custom-template>
|
||||
"""
|
||||
|
||||
component.registry.register("test", SlottedComponent)
|
||||
registry.register("test", SlottedComponent)
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_instances_of_component_do_not_share_slots(self):
|
||||
|
@ -322,8 +322,8 @@ class ComponentIsolationTests(BaseTestCase):
|
|||
class AggregateInputTests(BaseTestCase):
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_agg_input_accessible_in_get_context_data(self):
|
||||
@component.register("test")
|
||||
class AttrsComponent(component.Component):
|
||||
@register("test")
|
||||
class AttrsComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -356,11 +356,11 @@ class AggregateInputTests(BaseTestCase):
|
|||
class ComponentTemplateSyntaxErrorTests(BaseTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register("test", SlottedComponent)
|
||||
registry.register("test", SlottedComponent)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
super().tearDown()
|
||||
component.registry.clear()
|
||||
registry.clear()
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_variable_outside_fill_tag_compiles_w_out_error(self):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.template import Context, Template, TemplateSyntaxError
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, register, types
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
from .testutils import BaseTestCase, parametrize_context_behavior
|
||||
|
@ -11,8 +11,8 @@ setup_test_config()
|
|||
class ProvideTemplateTagTest(BaseTestCase):
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_provide_basic(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -40,8 +40,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_provide_access_keys_in_python(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> key: {{ key }} </div>
|
||||
<div> another: {{ another }} </div>
|
||||
|
@ -74,8 +74,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_provide_access_keys_in_django(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> key: {{ my_provide.key }} </div>
|
||||
<div> another: {{ my_provide.another }} </div>
|
||||
|
@ -107,8 +107,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_provide_does_not_leak(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -138,8 +138,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
def test_provide_empty(self):
|
||||
"""Check provide tag with no kwargs"""
|
||||
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -172,8 +172,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
def test_provide_no_inject(self):
|
||||
"""Check that nothing breaks if we do NOT inject even if some data is provided"""
|
||||
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div></div>
|
||||
"""
|
||||
|
@ -203,8 +203,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_provide_key_single_quotes(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -235,8 +235,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_provide_no_key_raises(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -259,8 +259,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_provide_key_must_be_string_literal(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -283,8 +283,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_provide_key_must_be_identifier(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -308,8 +308,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_provide_aggregate_dics(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -339,8 +339,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
def test_provide_does_not_expose_kwargs_to_context(self):
|
||||
"""Check that `provide` tag doesn't assign the keys to the context like `with` tag does"""
|
||||
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -375,8 +375,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
def test_provide_nested_in_provide_same_key(self):
|
||||
"""Check that inner `provide` with same key overshadows outer `provide`"""
|
||||
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -415,8 +415,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
def test_provide_nested_in_provide_different_key(self):
|
||||
"""Check that `provide` tag with different keys don't affect each other"""
|
||||
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> first_provide: {{ first_provide|safe }} </div>
|
||||
<div> second_provide: {{ second_provide|safe }} </div>
|
||||
|
@ -452,8 +452,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_provide_in_include(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -482,8 +482,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_in_provide(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -492,8 +492,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
var = self.inject("my_provide", "default")
|
||||
return {"var": var}
|
||||
|
||||
@component.register("parent")
|
||||
class ParentComponent(component.Component):
|
||||
@register("parent")
|
||||
class ParentComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% provide "my_provide" key="hi" another=123 %}
|
||||
|
@ -523,8 +523,8 @@ class ProvideTemplateTagTest(BaseTestCase):
|
|||
class InjectTest(BaseTestCase):
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_inject_basic(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -552,8 +552,8 @@ class InjectTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_inject_missing_key_raises_without_default(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -574,8 +574,8 @@ class InjectTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_inject_missing_key_ok_with_default(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -600,8 +600,8 @@ class InjectTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_inject_empty_string(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
@ -626,8 +626,8 @@ class InjectTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_inject_raises_on_called_outside_get_context_data(self):
|
||||
@component.register("injectee")
|
||||
class InjectComponent(component.Component):
|
||||
@register("injectee")
|
||||
class InjectComponent(Component):
|
||||
template: types.django_html = """
|
||||
<div> injected: {{ var|safe }} </div>
|
||||
"""
|
||||
|
|
|
@ -2,7 +2,7 @@ from typing import Any, Dict, List, Optional
|
|||
|
||||
from django.template import Context, Template, TemplateSyntaxError
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, register, registry, types
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
from .testutils import BaseTestCase, parametrize_context_behavior
|
||||
|
@ -10,7 +10,7 @@ from .testutils import BaseTestCase, parametrize_context_behavior
|
|||
setup_test_config()
|
||||
|
||||
|
||||
class SlottedComponent(component.Component):
|
||||
class SlottedComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<custom-template>
|
||||
|
@ -33,15 +33,15 @@ class SlottedComponentWithContext(SlottedComponent):
|
|||
|
||||
class ComponentSlottedTemplateTagTest(BaseTestCase):
|
||||
def setUp(self):
|
||||
# NOTE: component.registry is global, so need to clear before each test
|
||||
component.registry.clear()
|
||||
# NOTE: registry is global, so need to clear before each test
|
||||
registry.clear()
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slotted_template_basic(self):
|
||||
component.registry.register(name="test1", component=SlottedComponent)
|
||||
registry.register(name="test1", component=SlottedComponent)
|
||||
|
||||
@component.register("test2")
|
||||
class SimpleComponent(component.Component):
|
||||
@register("test2")
|
||||
class SimpleComponent(Component):
|
||||
template = """Variable: <strong>{{ variable }}</strong>"""
|
||||
|
||||
def get_context_data(self, variable, variable2="default"):
|
||||
|
@ -82,7 +82,7 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
# NOTE: Second arg is the expected output of `{{ variable }}`
|
||||
@parametrize_context_behavior([("django", "test456"), ("isolated", "")])
|
||||
def test_slotted_template_with_context_var(self, context_behavior_data):
|
||||
component.registry.register(name="test1", component=SlottedComponentWithContext)
|
||||
registry.register(name="test1", component=SlottedComponentWithContext)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -113,7 +113,7 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slotted_template_no_slots_filled(self):
|
||||
component.registry.register(name="test", component=SlottedComponent)
|
||||
registry.register(name="test", component=SlottedComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -135,8 +135,8 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slotted_template_without_slots(self):
|
||||
@component.register("test")
|
||||
class SlottedComponentNoSlots(component.Component):
|
||||
@register("test")
|
||||
class SlottedComponentNoSlots(Component):
|
||||
template: types.django_html = """
|
||||
<custom-template></custom-template>
|
||||
"""
|
||||
|
@ -152,8 +152,8 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slotted_template_without_slots_and_single_quotes(self):
|
||||
@component.register("test")
|
||||
class SlottedComponentNoSlots(component.Component):
|
||||
@register("test")
|
||||
class SlottedComponentNoSlots(Component):
|
||||
template: types.django_html = """
|
||||
<custom-template></custom-template>
|
||||
"""
|
||||
|
@ -169,7 +169,7 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_variable_fill_name(self):
|
||||
component.registry.register(name="test", component=SlottedComponent)
|
||||
registry.register(name="test", component=SlottedComponent)
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% with slotname="header" %}
|
||||
|
@ -191,7 +191,7 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_missing_required_slot_raises_error(self):
|
||||
class Component(component.Component):
|
||||
class Comp(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div class="header-box">
|
||||
|
@ -200,7 +200,7 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
</div>
|
||||
"""
|
||||
|
||||
component.registry.register("test", Component)
|
||||
registry.register("test", Comp)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -214,8 +214,8 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_default_slot_is_fillable_by_implicit_fill_content(self):
|
||||
@component.register("test_comp")
|
||||
class ComponentWithDefaultSlot(component.Component):
|
||||
@register("test_comp")
|
||||
class ComponentWithDefaultSlot(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -241,8 +241,8 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_default_slot_is_fillable_by_explicit_fill_content(self):
|
||||
@component.register("test_comp")
|
||||
class ComponentWithDefaultSlot(component.Component):
|
||||
@register("test_comp")
|
||||
class ComponentWithDefaultSlot(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -267,8 +267,8 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_error_raised_when_default_and_required_slot_not_filled(self):
|
||||
@component.register("test_comp")
|
||||
class ComponentWithDefaultAndRequiredSlot(component.Component):
|
||||
@register("test_comp")
|
||||
class ComponentWithDefaultAndRequiredSlot(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -289,10 +289,10 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_fill_tag_can_occur_within_component_nested_in_implicit_fill(self):
|
||||
component.registry.register("slotted", SlottedComponent)
|
||||
registry.register("slotted", SlottedComponent)
|
||||
|
||||
@component.register("test_comp")
|
||||
class ComponentWithDefaultSlot(component.Component):
|
||||
@register("test_comp")
|
||||
class ComponentWithDefaultSlot(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -327,8 +327,8 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_error_from_mixed_implicit_and_explicit_fill_content(self):
|
||||
@component.register("test_comp")
|
||||
class ComponentWithDefaultSlot(component.Component):
|
||||
@register("test_comp")
|
||||
class ComponentWithDefaultSlot(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -348,8 +348,8 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_comments_permitted_inside_implicit_fill_content(self):
|
||||
@component.register("test_comp")
|
||||
class ComponentWithDefaultSlot(component.Component):
|
||||
@register("test_comp")
|
||||
class ComponentWithDefaultSlot(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -372,7 +372,7 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_component_without_default_slot_refuses_implicit_fill(self):
|
||||
component.registry.register("test_comp", SlottedComponent)
|
||||
registry.register("test_comp", SlottedComponent)
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component 'test_comp' %}
|
||||
|
@ -387,7 +387,7 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_component_template_cannot_have_multiple_default_slots(self):
|
||||
class BadComponent(component.Component):
|
||||
class BadComponent(Component):
|
||||
def get_template(self, context, template_name: Optional[str] = None) -> Template:
|
||||
template_str: types.django_html = """
|
||||
{% load django_components %}
|
||||
|
@ -405,7 +405,7 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_name_fill_typo_gives_helpful_error_message(self):
|
||||
component.registry.register(name="test1", component=SlottedComponent)
|
||||
registry.register(name="test1", component=SlottedComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -433,7 +433,7 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
# NOTE: This is relevant only for the "isolated" mode
|
||||
@parametrize_context_behavior(["isolated"])
|
||||
def test_slots_of_top_level_comps_can_access_full_outer_ctx(self):
|
||||
class SlottedComponent(component.Component):
|
||||
class SlottedComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -446,7 +446,7 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
"name": name,
|
||||
}
|
||||
|
||||
component.registry.register("test", SlottedComponent)
|
||||
registry.register("test", SlottedComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -478,13 +478,13 @@ class ComponentSlottedTemplateTagTest(BaseTestCase):
|
|||
|
||||
class SlottedTemplateRegressionTests(BaseTestCase):
|
||||
def setUp(self):
|
||||
# NOTE: component.registry is global, so need to clear before each test
|
||||
component.registry.clear()
|
||||
# NOTE: registry is global, so need to clear before each test
|
||||
registry.clear()
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slotted_template_that_uses_missing_variable(self):
|
||||
@component.register("test")
|
||||
class SlottedComponentWithMissingVariable(component.Component):
|
||||
@register("test")
|
||||
class SlottedComponentWithMissingVariable(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<custom-template>
|
||||
|
@ -517,12 +517,12 @@ class SlottedTemplateRegressionTests(BaseTestCase):
|
|||
class SlotDefaultTests(BaseTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.clear()
|
||||
component.registry.register("test", SlottedComponent)
|
||||
registry.clear()
|
||||
registry.register("test", SlottedComponent)
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
component.registry.clear()
|
||||
registry.clear()
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_basic(self):
|
||||
|
@ -654,8 +654,8 @@ class SlotDefaultTests(BaseTestCase):
|
|||
class ScopedSlotTest(BaseTestCase):
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_data(self):
|
||||
@component.register("test")
|
||||
class TestComponent(component.Component):
|
||||
@register("test")
|
||||
class TestComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -689,8 +689,8 @@ class ScopedSlotTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_data_with_flags(self):
|
||||
@component.register("test")
|
||||
class TestComponent(component.Component):
|
||||
@register("test")
|
||||
class TestComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -724,8 +724,8 @@ class ScopedSlotTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_data_with_slot_default(self):
|
||||
@component.register("test")
|
||||
class TestComponent(component.Component):
|
||||
@register("test")
|
||||
class TestComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -761,8 +761,8 @@ class ScopedSlotTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_data_raises_on_slot_data_and_slot_default_same_var(self):
|
||||
@component.register("test")
|
||||
class TestComponent(component.Component):
|
||||
@register("test")
|
||||
class TestComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -792,8 +792,8 @@ class ScopedSlotTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_data_fill_without_data(self):
|
||||
@component.register("test")
|
||||
class TestComponent(component.Component):
|
||||
@register("test")
|
||||
class TestComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -821,8 +821,8 @@ class ScopedSlotTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_data_fill_without_slot_data(self):
|
||||
@component.register("test")
|
||||
class TestComponent(component.Component):
|
||||
@register("test")
|
||||
class TestComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -844,8 +844,8 @@ class ScopedSlotTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_slot_data_no_fill(self):
|
||||
@component.register("test")
|
||||
class TestComponent(component.Component):
|
||||
@register("test")
|
||||
class TestComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -870,8 +870,8 @@ class ScopedSlotTest(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_nested_fills(self):
|
||||
@component.register("test")
|
||||
class TestComponent(component.Component):
|
||||
@register("test")
|
||||
class TestComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -917,7 +917,7 @@ class ScopedSlotTest(BaseTestCase):
|
|||
|
||||
|
||||
class DuplicateSlotTest(BaseTestCase):
|
||||
class DuplicateSlotComponent(component.Component):
|
||||
class DuplicateSlotComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<header>{% slot "header" %}Default header{% endslot %}</header>
|
||||
|
@ -931,7 +931,7 @@ class DuplicateSlotTest(BaseTestCase):
|
|||
"name": name,
|
||||
}
|
||||
|
||||
class DuplicateSlotNestedComponent(component.Component):
|
||||
class DuplicateSlotNestedComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% slot "header" %}START{% endslot %}
|
||||
|
@ -956,7 +956,7 @@ class DuplicateSlotTest(BaseTestCase):
|
|||
"items": items,
|
||||
}
|
||||
|
||||
class CalendarComponent(component.Component):
|
||||
class CalendarComponent(Component):
|
||||
"""Nested in ComponentWithNestedComponent"""
|
||||
|
||||
template: types.django_html = """
|
||||
|
@ -975,9 +975,9 @@ class DuplicateSlotTest(BaseTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register(name="duplicate_slot", component=self.DuplicateSlotComponent)
|
||||
component.registry.register(name="duplicate_slot_nested", component=self.DuplicateSlotNestedComponent)
|
||||
component.registry.register(name="calendar", component=self.CalendarComponent)
|
||||
registry.register(name="duplicate_slot", component=self.DuplicateSlotComponent)
|
||||
registry.register(name="duplicate_slot_nested", component=self.DuplicateSlotNestedComponent)
|
||||
registry.register(name="calendar", component=self.CalendarComponent)
|
||||
|
||||
# NOTE: Second arg is the input for the "name" component kwarg
|
||||
@parametrize_context_behavior(
|
||||
|
@ -1113,11 +1113,11 @@ class DuplicateSlotTest(BaseTestCase):
|
|||
class SlotFillTemplateSyntaxErrorTests(BaseTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.register("test", SlottedComponent)
|
||||
registry.register("test", SlottedComponent)
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
component.registry.clear()
|
||||
registry.clear()
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_fill_with_no_parent_is_error(self):
|
||||
|
@ -1130,8 +1130,8 @@ class SlotFillTemplateSyntaxErrorTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_isolated_slot_is_error(self):
|
||||
@component.register("broken_component")
|
||||
class BrokenComponent(component.Component):
|
||||
@register("broken_component")
|
||||
class BrokenComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% include 'slotted_template.html' with context=None only %}
|
||||
|
@ -1180,7 +1180,7 @@ class SlotBehaviorTests(BaseTestCase):
|
|||
# NOTE: This is standalone function instead of setUp, so we can configure
|
||||
# Django settings per test with `@override_settings`
|
||||
def make_template(self) -> Template:
|
||||
class SlottedComponent(component.Component):
|
||||
class SlottedComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<custom-template>
|
||||
|
@ -1195,7 +1195,7 @@ class SlotBehaviorTests(BaseTestCase):
|
|||
"name": name,
|
||||
}
|
||||
|
||||
component.registry.register("test", SlottedComponent)
|
||||
registry.register("test", SlottedComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
|
|
@ -4,7 +4,7 @@ from typing import Any, Dict, Optional
|
|||
|
||||
from django.template import Context, Template
|
||||
|
||||
from django_components import component, types
|
||||
from django_components import Component, registry, types
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
from .testutils import BaseTestCase, parametrize_context_behavior
|
||||
|
@ -12,7 +12,7 @@ from .testutils import BaseTestCase, parametrize_context_behavior
|
|||
setup_test_config()
|
||||
|
||||
|
||||
class SlottedComponent(component.Component):
|
||||
class SlottedComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<custom-template>
|
||||
|
@ -34,7 +34,7 @@ class SlottedComponentWithContext(SlottedComponent):
|
|||
|
||||
|
||||
class NestedSlotTests(BaseTestCase):
|
||||
class NestedComponent(component.Component):
|
||||
class NestedComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% slot 'outer' %}
|
||||
|
@ -44,8 +44,8 @@ class NestedSlotTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_default_slot_contents_render_correctly(self):
|
||||
component.registry.clear()
|
||||
component.registry.register("test", self.NestedComponent)
|
||||
registry.clear()
|
||||
registry.register("test", self.NestedComponent)
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component 'test' %}{% endcomponent %}
|
||||
|
@ -56,8 +56,8 @@ class NestedSlotTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_inner_slot_overriden(self):
|
||||
component.registry.clear()
|
||||
component.registry.register("test", self.NestedComponent)
|
||||
registry.clear()
|
||||
registry.register("test", self.NestedComponent)
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component 'test' %}
|
||||
|
@ -70,8 +70,8 @@ class NestedSlotTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_outer_slot_overriden(self):
|
||||
component.registry.clear()
|
||||
component.registry.register("test", self.NestedComponent)
|
||||
registry.clear()
|
||||
registry.register("test", self.NestedComponent)
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component 'test' %}{% fill 'outer' %}<p>Override</p>{% endfill %}{% endcomponent %}
|
||||
|
@ -82,8 +82,8 @@ class NestedSlotTests(BaseTestCase):
|
|||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_both_overriden_and_inner_removed(self):
|
||||
component.registry.clear()
|
||||
component.registry.register("test", self.NestedComponent)
|
||||
registry.clear()
|
||||
registry.register("test", self.NestedComponent)
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% component 'test' %}
|
||||
|
@ -100,7 +100,7 @@ class NestedSlotTests(BaseTestCase):
|
|||
# remain top-level context.
|
||||
@parametrize_context_behavior([("django", "Joe2"), ("isolated", "Jannete")])
|
||||
def test_fill_inside_fill_with_same_name(self, context_behavior_data):
|
||||
class SlottedComponent(component.Component):
|
||||
class SlottedComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<custom-template>
|
||||
|
@ -115,8 +115,8 @@ class NestedSlotTests(BaseTestCase):
|
|||
"name": name,
|
||||
}
|
||||
|
||||
component.registry.clear()
|
||||
component.registry.register("test", SlottedComponent)
|
||||
registry.clear()
|
||||
registry.register("test", SlottedComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -163,7 +163,7 @@ class NestedSlotTests(BaseTestCase):
|
|||
# NOTE: This test group are kept for backward compatibility, as the same logic
|
||||
# as provided by {% if %} tags was previously provided by this library.
|
||||
class ConditionalSlotTests(BaseTestCase):
|
||||
class ConditionalComponent(component.Component):
|
||||
class ConditionalComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% if branch == 'a' %}
|
||||
|
@ -178,12 +178,12 @@ class ConditionalSlotTests(BaseTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
component.registry.clear()
|
||||
component.registry.register("test", self.ConditionalComponent)
|
||||
registry.clear()
|
||||
registry.register("test", self.ConditionalComponent)
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
component.registry.clear()
|
||||
registry.clear()
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_no_content_if_branches_are_false(self):
|
||||
|
@ -245,7 +245,7 @@ class ConditionalSlotTests(BaseTestCase):
|
|||
class SlotIterationTest(BaseTestCase):
|
||||
"""Tests a behaviour of {% fill .. %} tag which is inside a template {% for .. %} loop."""
|
||||
|
||||
class ComponentSimpleSlotInALoop(component.Component):
|
||||
class ComponentSimpleSlotInALoop(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
{% for object in objects %}
|
||||
|
@ -261,7 +261,7 @@ class SlotIterationTest(BaseTestCase):
|
|||
}
|
||||
|
||||
def setUp(self):
|
||||
component.registry.clear()
|
||||
registry.clear()
|
||||
|
||||
# NOTE: Second arg in tuple is expected result. In isolated mode, loops should NOT leak.
|
||||
@parametrize_context_behavior(
|
||||
|
@ -271,7 +271,7 @@ class SlotIterationTest(BaseTestCase):
|
|||
]
|
||||
)
|
||||
def test_inner_slot_iteration_basic(self, context_behavior_data):
|
||||
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -296,7 +296,7 @@ class SlotIterationTest(BaseTestCase):
|
|||
]
|
||||
)
|
||||
def test_inner_slot_iteration_with_variable_from_outer_scope(self, context_behavior_data):
|
||||
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
@ -328,7 +328,7 @@ class SlotIterationTest(BaseTestCase):
|
|||
]
|
||||
)
|
||||
def test_inner_slot_iteration_nested(self, context_behavior_data):
|
||||
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
|
||||
objects = [
|
||||
{"inner": ["ITER1_OBJ1", "ITER1_OBJ2"]},
|
||||
|
@ -375,7 +375,7 @@ class SlotIterationTest(BaseTestCase):
|
|||
]
|
||||
)
|
||||
def test_inner_slot_iteration_nested_with_outer_scope_variable(self, context_behavior_data):
|
||||
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
|
||||
objects = [
|
||||
{"inner": ["ITER1_OBJ1", "ITER1_OBJ2"]},
|
||||
|
@ -417,7 +417,7 @@ class SlotIterationTest(BaseTestCase):
|
|||
]
|
||||
)
|
||||
def test_inner_slot_iteration_nested_with_slot_default(self, context_behavior_data):
|
||||
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
|
||||
objects = [
|
||||
{"inner": ["ITER1_OBJ1", "ITER1_OBJ2"]},
|
||||
|
@ -469,7 +469,7 @@ class SlotIterationTest(BaseTestCase):
|
|||
self,
|
||||
context_behavior_data,
|
||||
):
|
||||
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
|
||||
objects = [
|
||||
{"inner": ["ITER1_OBJ1", "ITER1_OBJ2"]},
|
||||
|
@ -506,7 +506,7 @@ class SlotIterationTest(BaseTestCase):
|
|||
def test_inner_slot_iteration_nested_with_slot_default_and_outer_scope_variable__isolated_2(
|
||||
self,
|
||||
):
|
||||
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||
|
||||
objects = [
|
||||
{"inner": ["ITER1_OBJ1", "ITER1_OBJ2"]},
|
||||
|
@ -559,7 +559,7 @@ class SlotIterationTest(BaseTestCase):
|
|||
|
||||
|
||||
class ComponentNestingTests(BaseTestCase):
|
||||
class CalendarComponent(component.Component):
|
||||
class CalendarComponent(Component):
|
||||
"""Nested in ComponentWithNestedComponent"""
|
||||
|
||||
template: types.django_html = """
|
||||
|
@ -576,7 +576,7 @@ class ComponentNestingTests(BaseTestCase):
|
|||
</div>
|
||||
"""
|
||||
|
||||
class DashboardComponent(component.Component):
|
||||
class DashboardComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div class="dashboard-component">
|
||||
|
@ -594,7 +594,7 @@ class ComponentNestingTests(BaseTestCase):
|
|||
</div>
|
||||
"""
|
||||
|
||||
class ComplexChildComponent(component.Component):
|
||||
class ComplexChildComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<div>
|
||||
|
@ -604,7 +604,7 @@ class ComponentNestingTests(BaseTestCase):
|
|||
</div>
|
||||
"""
|
||||
|
||||
class ComplexParentComponent(component.Component):
|
||||
class ComplexParentComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
ITEMS: {{ items|safe }}
|
||||
|
@ -622,14 +622,14 @@ class ComponentNestingTests(BaseTestCase):
|
|||
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
component.registry.register("dashboard", self.DashboardComponent)
|
||||
component.registry.register("calendar", self.CalendarComponent)
|
||||
component.registry.register("complex_child", self.ComplexChildComponent)
|
||||
component.registry.register("complex_parent", self.ComplexParentComponent)
|
||||
registry.register("dashboard", self.DashboardComponent)
|
||||
registry.register("calendar", self.CalendarComponent)
|
||||
registry.register("complex_child", self.ComplexChildComponent)
|
||||
registry.register("complex_parent", self.ComplexParentComponent)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
super().tearDown()
|
||||
component.registry.clear()
|
||||
registry.clear()
|
||||
|
||||
# NOTE: Second arg in tuple are expected names in nested fills. In "django" mode,
|
||||
# the value should be overridden by the component, while in "isolated" it should
|
||||
|
@ -638,7 +638,7 @@ class ComponentNestingTests(BaseTestCase):
|
|||
def test_component_inside_slot(self, context_behavior_data):
|
||||
first_name, second_name = context_behavior_data
|
||||
|
||||
class SlottedComponent(component.Component):
|
||||
class SlottedComponent(Component):
|
||||
template: types.django_html = """
|
||||
{% load component_tags %}
|
||||
<custom-template>
|
||||
|
@ -653,7 +653,7 @@ class ComponentNestingTests(BaseTestCase):
|
|||
"name": name,
|
||||
}
|
||||
|
||||
component.registry.register("test", SlottedComponent)
|
||||
registry.register("test", SlottedComponent)
|
||||
|
||||
template_str: types.django_html = """
|
||||
{% load component_tags %}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue