docs: Syntax highlighting for mkdocs (#984)

* feat:forward context processors variables in context in ISOLATED mode

	provide context_processors_data property to Component to access those variables in Component

* refactor: internalize RequestContext and pass HttpRequest internally

* docs: document HttpRequest and context processors

* docs: use djc_py code blocks for component definitions

---------

Co-authored-by: Lilian Durey <dureylilian@gmail.com>
This commit is contained in:
Juro Oravec 2025-02-20 11:47:14 +01:00 committed by GitHub
parent 1f7e28db22
commit 314ec77d3d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 142 additions and 76 deletions

View file

@ -105,7 +105,7 @@ For live examples, see the [Community examples](../../overview/community.md#comm
It's also a good idea to have a common prefix for your components, so they can be easily distinguished from users' components. In the example below, we use the prefix `my_` / `My`. It's also a good idea to have a common prefix for your components, so they can be easily distinguished from users' components. In the example below, we use the prefix `my_` / `My`.
```py ```djc_py
from typing import Dict, NotRequired, Optional, Tuple, TypedDict from typing import Dict, NotRequired, Optional, Tuple, TypedDict
from django_components import Component, SlotFunc, register, types from django_components import Component, SlotFunc, register, types

View file

@ -106,7 +106,7 @@ Then navigate to these URLs:
### 1. Define document HTML ### 1. Define document HTML
```py title="[root]/components/demo.py" ```djc_py title="[root]/components/demo.py"
from django_components import Component, types from django_components import Component, types
# HTML into which a fragment will be loaded using HTMX # HTML into which a fragment will be loaded using HTMX
@ -141,7 +141,7 @@ class MyPage(Component):
### 2. Define fragment HTML ### 2. Define fragment HTML
```py title="[root]/components/demo.py" ```djc_py title="[root]/components/demo.py"
class Frag(Component): class Frag(Component):
def get(self, request): def get(self, request):
return self.render_to_response( return self.render_to_response(
@ -184,7 +184,7 @@ urlpatterns = [
### 1. Define document HTML ### 1. Define document HTML
```py title="[root]/components/demo.py" ```djc_py title="[root]/components/demo.py"
from django_components import Component, types from django_components import Component, types
# HTML into which a fragment will be loaded using AlpineJS # HTML into which a fragment will be loaded using AlpineJS
@ -225,7 +225,7 @@ class MyPage(Component):
### 2. Define fragment HTML ### 2. Define fragment HTML
```py title="[root]/components/demo.py" ```djc_py title="[root]/components/demo.py"
class Frag(Component): class Frag(Component):
def get(self, request): def get(self, request):
# IMPORTANT: Don't forget `type="fragment"` # IMPORTANT: Don't forget `type="fragment"`
@ -281,7 +281,7 @@ urlpatterns = [
### 1. Define document HTML ### 1. Define document HTML
```py title="[root]/components/demo.py" ```djc_py title="[root]/components/demo.py"
from django_components import Component, types from django_components import Component, types
# HTML into which a fragment will be loaded using JS # HTML into which a fragment will be loaded using JS
@ -321,7 +321,7 @@ class MyPage(Component):
### 2. Define fragment HTML ### 2. Define fragment HTML
```py title="[root]/components/demo.py" ```djc_py title="[root]/components/demo.py"
class Frag(Component): class Frag(Component):
def get(self, request): def get(self, request):
return self.render_to_response( return self.render_to_response(

View file

@ -107,7 +107,7 @@ have all the keys that were passed to the `provide` tag.
## Full example ## Full example
```py ```djc_py
@register("child") @register("child")
class ChildComponent(Component): class ChildComponent(Component):
template = """ template = """

View file

@ -27,7 +27,7 @@ the locations by inserting following Django template tags:
So if you have a component with JS and CSS: So if you have a component with JS and CSS:
```python ```djc_py
from django_components import Component, types from django_components import Component, types
class MyButton(Component): class MyButton(Component):

View file

@ -85,9 +85,9 @@ This has two modes:
Consider this example: Consider this example:
```python ```djc_py
class Outer(Component): class Outer(Component):
template = \"\"\" template = """
<div> <div>
{% component "inner" %} {% component "inner" %}
{% fill "content" %} {% fill "content" %}
@ -95,7 +95,7 @@ This has two modes:
{% endfill %} {% endfill %}
{% endcomponent %} {% endcomponent %}
</div> </div>
\"\"\" """
``` ```
- `"django"` - `my_var` has access to data from `get_context_data()` of both `Inner` and `Outer`. - `"django"` - `my_var` has access to data from `get_context_data()` of both `Inner` and `Outer`.
@ -108,7 +108,7 @@ This has two modes:
Given this template: Given this template:
```python ```djc_py
@register("root_comp") @register("root_comp")
class RootComp(Component): class RootComp(Component):
template = """ template = """
@ -148,7 +148,7 @@ all the data defined in the outer layers, like the `{% with %}` tag.
Given this template: Given this template:
```python ```djc_py
class RootComp(Component): class RootComp(Component):
template = """ template = """
{% with cheese="feta" %} {% with cheese="feta" %}

View file

@ -19,7 +19,7 @@ Components can now be used as views:
Here's an example of a calendar component defined as a view: Here's an example of a calendar component defined as a view:
```python ```djc_py
# In a file called [project root]/components/calendar.py # In a file called [project root]/components/calendar.py
from django_components import Component, ComponentView, register from django_components import Component, ComponentView, register

View file

@ -9,7 +9,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: 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 ```djc_py
class SimpleComponent(Component): class SimpleComponent(Component):
template = """ template = """
{% load component_tags %} {% load component_tags %}

View file

@ -28,7 +28,7 @@ HTML / JS / CSS with a component:
However, you can freely mix these for different languages: However, you can freely mix these for different languages:
```py ```djc_py
class MyTable(Component): class MyTable(Component):
template: types.django_html = """ template: types.django_html = """
<div class="welcome"> <div class="welcome">

View file

@ -256,7 +256,7 @@ Then:
## Full example for `html_attrs` ## Full example for `html_attrs`
```py ```djc_py
@register("my_comp") @register("my_comp")
class MyComp(Component): class MyComp(Component):
template: t.django_html = """ template: t.django_html = """

View file

@ -78,7 +78,7 @@ rendered = template.render(RequestContext(request, {}))
The data from context processors is automatically available within the component's template. The data from context processors is automatically available within the component's template.
```python ```djc_py
class MyComponent(Component): class MyComponent(Component):
template = """ template = """
<div> <div>

View file

@ -9,7 +9,7 @@ For example, here's the calendar component from
the [Getting started](../../getting_started/your_first_component.md) tutorial, the [Getting started](../../getting_started/your_first_component.md) tutorial,
defined in a single file: defined in a single file:
```python title="[project root]/components/calendar.py" ```djc_py title="[project root]/components/calendar.py"
from django_components import Component, register, types from django_components import Component, register, types
@register("calendar") @register("calendar")

View file

@ -469,7 +469,7 @@ _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: 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 ```djc_py
@register("my_comp") @register("my_comp")
class MyComp(Component): class MyComp(Component):
template = """ template = """
@ -498,7 +498,7 @@ Using scoped slots consists of two steps:
To pass the data to the `slot` tag, simply pass them as keyword attributes (`key=value`): To pass the data to the `slot` tag, simply pass them as keyword attributes (`key=value`):
```py ```djc_py
@register("my_comp") @register("my_comp")
class MyComp(Component): class MyComp(Component):
template = """ template = """
@ -649,14 +649,14 @@ So it's possible to define a `name` key on a dictionary, and then spread that on
You can dynamically pass all slots to a child component. This is similar to You can dynamically pass all slots to a child component. This is similar to
[passing all slots in Vue](https://vue-land.github.io/faq/forwarding-slots#passing-all-slots): [passing all slots in Vue](https://vue-land.github.io/faq/forwarding-slots#passing-all-slots):
```py ```djc_py
class MyTable(Component): class MyTable(Component):
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
return { return {
"slots": self.input.slots, "slots": self.input.slots,
} }
template: """ template = """
<div> <div>
{% component "child" %} {% component "child" %}
{% for slot_name in slots %} {% for slot_name in slots %}

View file

@ -25,7 +25,7 @@ inheritance follows these rules:
For example: For example:
```python ```djc_py
class BaseCard(Component): class BaseCard(Component):
template = """ template = """
<div class="card"> <div class="card">
@ -37,7 +37,7 @@ class BaseCard(Component):
border: 1px solid gray; border: 1px solid gray;
} }
""" """
js = "console.log('Base card loaded');" js = """console.log('Base card loaded');"""
# This class overrides parent's template, but inherits CSS and JS # This class overrides parent's template, but inherits CSS and JS
class SpecialCard(BaseCard): class SpecialCard(BaseCard):
@ -94,7 +94,7 @@ All other attributes and methods (including the [`Component.View`](../../referen
For example: For example:
```python ```djc_py
class BaseForm(Component): class BaseForm(Component):
template = """ template = """
<form> <form>

View file

@ -202,7 +202,7 @@ of HTML attributes (usually called `attrs`) to pass to the underlying template.
In such cases, we may want to define some HTML attributes statically, and other dynamically. In such cases, we may want to define some HTML attributes statically, and other dynamically.
But for that, we need to define this dictionary on Python side: But for that, we need to define this dictionary on Python side:
```py ```djc_py
@register("my_comp") @register("my_comp")
class MyComp(Component): class MyComp(Component):
template = """ template = """
@ -229,7 +229,7 @@ as component kwargs, so we can keep all the relevant information in the template
we prefix the key with the name of the dict and `:`. So key `class` of input `attrs` becomes we prefix the key with the name of the dict and `:`. So key `class` of input `attrs` becomes
`attrs:class`. And our example becomes: `attrs:class`. And our example becomes:
```py ```djc_py
@register("my_comp") @register("my_comp")
class MyComp(Component): class MyComp(Component):
template = """ template = """

View file

@ -50,7 +50,7 @@ class Calendar(Component):
Alternatively, you can "inline" HTML, JS, and CSS right into the component class: Alternatively, you can "inline" HTML, JS, and CSS right into the component class:
```py ```djc_py
from django_components import Component from django_components import Component
class Calendar(Component): class Calendar(Component):

View file

@ -7,7 +7,7 @@ associated with components, and how we render them.
1. First of all, when we consider a component, it has two kind of dependencies - the "inlined" JS and CSS, and additional linked JS and CSS via `Media.js/css`: 1. First of all, when we consider a component, it has two kind of dependencies - the "inlined" JS and CSS, and additional linked JS and CSS via `Media.js/css`:
```py ```djc_py
from django_components import Component, types from django_components import Component, types
class MyTable(Component): class MyTable(Component):

View file

@ -15,7 +15,7 @@
``` ```
And components that make use of `abc.html` via `include` or `extends`: And components that make use of `abc.html` via `include` or `extends`:
```py ```djc_py
from django_components import Component, register from django_components import Component, register
@register("my_comp_extends") @register("my_comp_extends")
@ -66,7 +66,7 @@
```py ```py
@register("my_comp") @register("my_comp")
class MyComp(Component): class MyComp(Component):
template_file = "abc.html" template_file = "abc.html"
``` ```
Then: Then:
@ -110,37 +110,34 @@
uses `extends`. In that case, just as you would expect, the `block inner` inside uses `extends`. In that case, just as you would expect, the `block inner` inside
`abc.html` will render `OVERRIDEN`: `abc.html` will render `OVERRIDEN`:
````py ```djc_py
@register("my_comp") @register("my_comp")
class MyComp(Component): class MyComp(Component):
template_file = """ template = """
{% extends "abc.html" %} {% extends "abc.html" %}
{% block inner %}
{% block inner %} OVERRIDEN
OVERRIDEN {% endblock %}
{% endblock %} """
""" ```
```
````
4. This is where it gets interesting (but still intuitive). You can insert even 4. This is where it gets interesting (but still intuitive). You can insert even
new `slots` inside these "overriding" blocks: new `slots` inside these "overriding" blocks:
```py ```djc_py
@register("my_comp") @register("my_comp")
class MyComp(Component): class MyComp(Component):
template_file = """ template = """
{% extends "abc.html" %} {% extends "abc.html" %}
{% load component_tags %} {% load component_tags %}
{% block "inner" %} {% block "inner" %}
OVERRIDEN OVERRIDEN
{% slot "new_slot" %} {% slot "new_slot" %}
hello hello
{% endslot %} {% endslot %}
{% endblock %} {% endblock %}
""" """
``` ```
And you can then pass fill for this `new_slot` when rendering the component: And you can then pass fill for this `new_slot` when rendering the component:

View file

@ -9,7 +9,7 @@ weight: 1
2. Next, in your component, set typings of `Component.template/css/js` to `types.django_html`, `types.css`, and `types.js` respectively. The extension will recognize these and will activate syntax highlighting. 2. Next, in your component, set typings of `Component.template/css/js` to `types.django_html`, `types.css`, and `types.js` respectively. The extension will recognize these and will activate syntax highlighting.
```python title="[project root]/components/calendar.py" ```djc_py title="[project root]/components/calendar.py"
# In a file called [project root]/components/calendar.py # In a file called [project root]/components/calendar.py
from django_components import Component, register, types from django_components import Component, register, types
@ -21,12 +21,19 @@ class Calendar(Component):
} }
template: types.django_html = """ template: types.django_html = """
<div class="calendar-component">Today's date is <span>{{ date }}</span></div> <div class="calendar-component">
Today's date is <span>{{ date }}</span>
</div>
""" """
css: types.css = """ css: types.css = """
.calendar-component { width: 200px; background: pink; } .calendar-component {
.calendar-component span { font-weight: bold; } width: 200px;
background: pink;
}
.calendar-component span {
font-weight: bold;
}
""" """
js: types.js = """ js: types.js = """
@ -43,7 +50,7 @@ class Calendar(Component):
With PyCharm (or any other editor from Jetbrains), 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). With PyCharm (or any other editor from Jetbrains), 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. You only need to write the comments `# language=<lang>` above the variables.
```python ```djc_py
from django_components import Component, register from django_components import Component, register
@register("calendar") @register("calendar")
@ -55,13 +62,20 @@ class Calendar(Component):
# language=HTML # language=HTML
template= """ template= """
<div class="calendar-component">Today's date is <span>{{ date }}</span></div> <div class="calendar-component">
Today's date is <span>{{ date }}</span>
</div>
""" """
# language=CSS # language=CSS
css = """ css = """
.calendar-component { width: 200px; background: pink; } .calendar-component {
.calendar-component span { font-weight: bold; } width: 200px;
background: pink;
}
.calendar-component span {
font-weight: bold;
}
""" """
# language=JS # language=JS
@ -73,3 +87,45 @@ class Calendar(Component):
})() })()
""" """
``` ```
## Pygments
[Pygments](https://pygments.org/) is a syntax highlighting library written in Python. It's also what's used by this documentation site ([mkdocs-material](https://squidfunk.github.io/mkdocs-material/)) to highlight code blocks.
To write code blocks with syntax highlighting, you need to install the [`pygments-djc`](https://pypi.org/project/pygments-djc/) package.
```bash
pip install pygments-djc
```
And then initialize it by importing `pygments_djc`:
```python
import pygments_djc
```
Now you can write code blocks with syntax highlighting.
```txt
\```djc_py
from django_components import Component, register
@register("calendar")
class Calendar(Component):
template= """
<div class="calendar-component">
Today's date is <span>{{ date }}</span>
</div>
"""
css = """
.calendar-component {
width: 200px;
background: pink;
}
.calendar-component span {
font-weight: bold;
}
"""
\```
```

View file

@ -88,7 +88,7 @@ Read on to learn about all the exciting details and configuration possibilities!
- Each component can include its own HTML, CSS, and JS, or additional third-party JS and CSS. - Each component can include its own HTML, CSS, and JS, or additional third-party JS and CSS.
- HTML, CSS, and JS can be defined on the component class, or loaded from files. - HTML, CSS, and JS can be defined on the component class, or loaded from files.
```python ```djc_py
from django_components import Component from django_components import Component
@register("calendar") @register("calendar")

View file

@ -14,7 +14,7 @@ the signal is triggered for each component.
Import from django as `django.test.signals.template_rendered`. Import from django as `django.test.signals.template_rendered`.
```python ```djc_py
from django.test.signals import template_rendered from django.test.signals import template_rendered
# Setup a callback function # Setup a callback function

3
docs/scripts/setup.py Normal file
View file

@ -0,0 +1,3 @@
# Allow us to use `djc_py` / `djc_python` code blocks.
# Importing this package automatically registers the `djc_py` lexer onto Pygments.
import pygments_djc # noqa: F401

View file

@ -12,7 +12,7 @@ the signal is triggered for each component.
Import from django as `django.test.signals.template_rendered`. Import from django as `django.test.signals.template_rendered`.
```python ```djc_py
from django.test.signals import template_rendered from django.test.signals import template_rendered
# Setup a callback function # Setup a callback function

View file

@ -123,6 +123,7 @@ plugins:
closing_tag: "!}" closing_tag: "!}"
- gen-files: - gen-files:
scripts: scripts:
- docs/scripts/setup.py
- docs/scripts/reference.py - docs/scripts/reference.py
- literate-nav: - literate-nav:
nav_file: SUMMARY.md nav_file: SUMMARY.md

View file

@ -11,4 +11,5 @@ mypy
playwright playwright
requests requests
types-requests types-requests
whitenoise whitenoise
pygments-djc

View file

@ -8,7 +8,7 @@ asgiref==3.8.1
# via django # via django
black==24.10.0 black==24.10.0
# via -r requirements-dev.in # via -r requirements-dev.in
cachetools==5.5.0 cachetools==5.5.1
# via tox # via tox
certifi==2024.8.30 certifi==2024.8.30
# via requests # via requests
@ -16,15 +16,15 @@ cfgv==3.4.0
# via pre-commit # via pre-commit
chardet==5.2.0 chardet==5.2.0
# via tox # via tox
charset-normalizer==3.4.0 charset-normalizer==3.4.1
# via requests # via requests
click==8.1.7 click==8.1.8
# via black # via black
colorama==0.4.6 colorama==0.4.6
# via tox # via tox
distlib==0.3.9 distlib==0.3.9
# via virtualenv # via virtualenv
django==5.1.5 django==5.1.6
# via -r requirements-dev.in # via -r requirements-dev.in
djc-core-html-parser==1.0.1 djc-core-html-parser==1.0.1
# via -r requirements-dev.in # via -r requirements-dev.in
@ -32,7 +32,7 @@ filelock==3.16.1
# via # via
# tox # tox
# virtualenv # virtualenv
flake8==7.1.1 flake8==7.1.2
# via # via
# -r requirements-dev.in # -r requirements-dev.in
# flake8-pyproject # flake8-pyproject
@ -40,7 +40,7 @@ flake8-pyproject==1.2.3
# via -r requirements-dev.in # via -r requirements-dev.in
greenlet==3.1.1 greenlet==3.1.1
# via playwright # via playwright
identify==2.6.3 identify==2.6.7
# via pre-commit # via pre-commit
idna==3.10 idna==3.10
# via requests # via requests
@ -85,6 +85,10 @@ pyee==12.0.0
# via playwright # via playwright
pyflakes==3.2.0 pyflakes==3.2.0
# via flake8 # via flake8
pygments==2.19.1
# via pygments-djc
pygments-djc==1.0.1
# via -r requirements-dev.in
pyproject-api==1.8.0 pyproject-api==1.8.0
# via tox # via tox
pytest==8.3.4 pytest==8.3.4
@ -93,7 +97,7 @@ pyyaml==6.0.2
# via pre-commit # via pre-commit
requests==2.32.3 requests==2.32.3
# via -r requirements-dev.in # via -r requirements-dev.in
sqlparse==0.5.2 sqlparse==0.5.3
# via django # via django
tox==4.24.1 tox==4.24.1
# via -r requirements-dev.in # via -r requirements-dev.in

View file

@ -59,7 +59,7 @@ cssselect2==0.7.0
# via cairosvg # via cairosvg
defusedxml==0.7.1 defusedxml==0.7.1
# via cairosvg # via cairosvg
django==5.1.5 django==5.1.6
# via hatch.envs.docs # via hatch.envs.docs
ghp-import==2.1.0 ghp-import==2.1.0
# via mkdocs # via mkdocs
@ -178,7 +178,11 @@ platformdirs==4.3.6
pycparser==2.22 pycparser==2.22
# via cffi # via cffi
pygments==2.19.1 pygments==2.19.1
# via mkdocs-material # via
# mkdocs-material
# pygments-djc
pygments-djc==1.0.1
# via -r requirements-dev.in
pymdown-extensions==10.14.3 pymdown-extensions==10.14.3
# via # via
# hatch.envs.docs # hatch.envs.docs