diff --git a/.github/workflows/deploy-doc.yml b/.github/workflows/deploy-doc.yml index 7e107c9c1..ddc8c15fe 100644 --- a/.github/workflows/deploy-doc.yml +++ b/.github/workflows/deploy-doc.yml @@ -13,7 +13,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [3.9] + python-version: ["3.11"] steps: - uses: actions/checkout@v2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0f7a9e88e..241ed7e6e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: rev: 23.10.1 hooks: - id: black - language_version: python3.10 + language_version: python3.11 args: - --line-length=88 - repo: https://github.com/PyCQA/bandit diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c118451e4..a1d038bad 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ L’installation a été testée sur Ubuntu 22.04 avec Python 3.10 et poetry ins - Faire un `git clone` du projet sur votre machine et ouvrir un terminal - Installer l’environnement virtuel, les dépendances, les *pre-commit hooks* et initialiser le site d’exemple : -``` +```{ .bash } make init ``` @@ -14,7 +14,7 @@ make init Pour faire tourner les tests : -``` +```{ .bash } make test ``` @@ -24,24 +24,24 @@ Le projet utilise [Poetry](https://python-poetry.org/) pour gérer les dépendan Pour installer les dépendances du projet : -``` +```{ .bash } poetry install ``` Pour installer un nouveau paquet et l’ajouter aux dépendances : -``` +```{ .bash } poetry add ``` Pour un paquet ne servant que pour le développement, par exemple `black` : -``` +```{ .bash } poetry add --group dev ``` Pour activer l’environnement virtuel : -``` +```{ .bash } poetry shell ``` @@ -58,7 +58,7 @@ Concernant les langues : Pour vérifier son code, on peut intégrer le linter adapté à son IDE et aussi faire ceci : -``` +```{ .bash } make checkstyle ``` @@ -66,14 +66,15 @@ Une vérification automatique est faite via des *pre-commit hooks*, qui ont norm Il est possible de les mettre à jour avec la commande : -``` +```{ .bash } pre-commit update ``` ## Mise à jour du système de design -Quand une nouvelle version du système de design de l’État est publiée, il est possible de le mettre à jour automatiquement via la commande -``` +Quand une nouvelle version du système de design de l’État est publiée, il est possible de le mettre à jour automatiquement via la commande : + +```{ .bash } make update_dsfr ``` diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 000000000..94a1266a6 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,65 @@ +# Installation de django-DSFR + +## Installation basique + +- Installez le paquet + +```{ .bash } +pip install django-dsfr +``` + +- Ajoutez `widget_tweaks` et `dsfr` à `INSTALLED_APPS` dans le `settings.py` avant la ou les app avec laquelle vous voulez l’utiliser : + +```{ .python } +INSTALLED_APPS = [ + ... + "widget_tweaks" + "dsfr", + +] +``` + +- Ajouter les lignes suivantes dans la section `TEMPLATES` du `settings.py` pour faire fonctionner les formulaires : + +```{ .python } +TEMPLATES = [ + { + [...] + "DIRS": [ + os.path.join(BASE_DIR, "dsfr/templates"), + os.path.join(BASE_DIR, "templates"), + ], + }, +] +``` + +- Ajouter le `FORM_RENDERER` in `settings.py` pour faire fonctionner les formulaires : + +```{ .python } +FORM_RENDERER = "django.forms.renderers.TemplatesSetting" +``` + +- Inclure les tags dans votre fichier `base.html` (voir par exemple sur [base.html](https://github.com/numerique-gouv/django-dsfr/blob/main/example_app/templates/example_app/base.html)) + +- Lancer le serveur (`python manage.py runserver`) et aller sur [http://127.0.0.1:8000/](http://127.0.0.1:8000/) + + +## Installation avancée (optionnelle) +### Utilisation de la conf en admin +- Ajoutez le `context_processor` au fichier `settings.py` : + +```{ .python } +TEMPLATES = [ + { + [...] + "OPTIONS": { + "context_processors": [ + [...] + "dsfr.context_processors.site_config", + ], + }, + }, +] +``` + +- Créez un objet "DsfrConfig" dans le panneau d’administration diff --git a/README.rst b/README.rst index e4d703a30..675357930 100644 --- a/README.rst +++ b/README.rst @@ -29,56 +29,4 @@ Tested with Python 3.7 to 3.11 and Django 3.2 to 4.2. Per `vermin - ] - -3. In order to use forms and formsets, add to INSTALLED_APPS in your settings.py:: - - INSTALLED_APPS = [ - ... - "django.forms", - "widget_tweaks", - "crispy_forms", - ] - -4. Add the following info in the TEMPLATES section in your settings.py so that the choice forms work:: - - TEMPLATES = [ - { - [...] - "DIRS": [ - os.path.join(BASE_DIR, "dsfr/templates"), - os.path.join(BASE_DIR, "templates"), - ], - }, - ] - -5. Add the following FORM_RENDERER in settings.py so that the choice forms work:: - - FORM_RENDERER = "django.forms.renderers.TemplatesSetting" - -6. (Optional) Add the context processor to your settings.py and create an instance of "DsfrConfig" in the admin panel:: - - TEMPLATES = [ - { - [...] - "OPTIONS": { - "context_processors": [ - [...] - "dsfr.context_processors.site_config", - ], - }, - }, - ] - -7. Include the tags in your base.html file (see example file at https://github.com/numerique-gouv/django-dsfr/blob/main/example_app/templates/example_app/base.html) - -8. Start the development server and visit http://127.0.0.1:8000/ +See the `INSTALL.md `_ file. diff --git a/doc/components.md b/doc/components.md new file mode 100644 index 000000000..cf8b8adef --- /dev/null +++ b/doc/components.md @@ -0,0 +1,36 @@ +Le système de design propose [un certain nombre de composants](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants), et Django-DSFR vise à les implémenter sous forme de balises utilisables dans les templates Django, soit en passant directement les paramètres, soit en les passant depuis la vue via un dictionnaire. + +Par exemple, il est possible de créer un bouton soit en passant directement les paramètres : + +```{.django} +{% dsfr_button label="Bouton principal" onclick="alert('Vous avez cliqué sur le bouton principal')" %} +``` + +Soit en définissant un dictionnaire dans la vue : + +```{ .python } +context["data_dict"] = { + "label": "Bouton principal", + "onclick": "alert('Vous avez cliqué sur le bouton principal')", +} +``` + +et en l’appelant depuis le template : + +```{.django} +{% dsfr_button data_dict %} +``` + +L’implémentation de ces balises est un travail en cours, mais il est tout à fait possible d’utiliser directement l’ensemble du système de design de l’État en utilisant directement le code HTML tel que défini dans la documentation officielle : + +```{.html} + +``` + +Toutes ces options produisent le même résultat : + + diff --git a/doc/forms.md b/doc/forms.md new file mode 100644 index 000000000..7cc135c6c --- /dev/null +++ b/doc/forms.md @@ -0,0 +1,39 @@ +Les formulaires sont construits en se basant sur la classe `DsfrBaseForm`, par exemple : + +```{ .python } +# votre_app/forms.py + +from dsfr.forms import DsfrBaseForm + + +class ExampleForm(DsfrBaseForm): + # basic fields + user_name = forms.CharField(label="Nom d’utilisateur", max_length=100) + + user_email = forms.EmailField( + label="Adresse électronique", + help_text="Format attendu : prenom.nom@domaine.fr", + required=False, + ) +``` + +Il est possible de multi-classer : + +```{ .python } +class AuthorCreateForm(ModelForm, DsfrBaseForm): +``` + +Le formulaire ajoute la ou les classes appropriées (`fr-input`, `fr-select`, etc.) en fonction du type de champ, mais uniquement si une classe n’a pas déjà été ajoutée. + +Si c'est le cas, il faut aussi forcer manuellement les classes à utiliser : + +```{ .python } + password = forms.CharField( + label="Mot de passe", widget=forms.PasswordInput( + "autocomplete": "current-password", + "required": True, + "class": "fr-input my-custom-class" + ) + ) + +``` diff --git a/example/settings.py b/example/settings.py index 879c179fe..d9c5fac91 100644 --- a/example/settings.py +++ b/example/settings.py @@ -44,7 +44,6 @@ "dsfr", "example_app", "django_distill", - "crispy_forms", ] MIDDLEWARE = [ diff --git a/example_app/static/css/dsfr-code.css b/example_app/static/css/dsfr-code.css new file mode 100644 index 000000000..a0106b630 --- /dev/null +++ b/example_app/static/css/dsfr-code.css @@ -0,0 +1,370 @@ +.dsfr-code .hll { + background-color: var(--background-contrast-grey) +} + +.dsfr-code pre, +pre.dsfr-code { + background: var(--background-alt-grey); + border: 1px solid var(--border-default-grey); + color: var(--text-default-grey); + padding: 1rem; + margin: 2rem 0; +} + +/* Comment */ +.dsfr-code .c { + color: var(--green-emeraude-sun-425); + font-style: italic +} + +/* Error */ +.dsfr-code .err { + border: 1px solid var(--red-marianne-main-472) +} + +/* Keyword */ +.dsfr-code .k { + color: var(--green-bourgeon-main-640); + font-weight: bold +} + +/* Operator */ +.dsfr-code .o { + color: var(--beige-gris-galet-sun-407) +} + +/* Comment.Hashbang */ +.dsfr-code .ch { + color: var(--green-emeraude-sun-425); + font-style: italic +} + +/* Comment.Multiline */ +.dsfr-code .cm { + color: var(--green-emeraude-sun-425); + font-style: italic +} + +/* Comment.Preproc */ +.dsfr-code .cp { + color: var(--warning-main-525) +} + +/* Comment.PreprocFile */ +.dsfr-code .cpf { + color: var(--green-emeraude-sun-425); + font-style: italic +} + +/* Comment.Single */ +.dsfr-code .c1 { + color: var(--green-emeraude-sun-425); + font-style: italic +} + +/* Comment.Special */ +.dsfr-code .cs { + color: var(--green-emeraude-sun-425); + font-style: italic +} + +/* Generic.Deleted */ +.dsfr-code .gd { + color: var(--red-marianne-200) +} + +/* Generic.Emph */ +.dsfr-code .ge { + font-style: italic +} + +/* Generic.Error */ +.dsfr-code .gr { + color: var(--red-marianne-main-472) +} + +/* Generic.Heading */ +.dsfr-code .gh { + color: var(--blue-france-125); + font-weight: bold +} + +/* Generic.Inserted */ +.dsfr-code .gi { + color: var(--green-bourgeon-850) +} + +/* Generic.Output */ +.dsfr-code .go { + color: var(--grey-main-525) +} + +/* Generic.Prompt */ +.dsfr-code .gp { + color: var(--blue-france-125); + font-weight: bold +} + +/* Generic.Strong */ +.dsfr-code .gs { + font-weight: bold +} + +/* Generic.Subheading */ +.dsfr-code .gu { + color: var(--purple-glycine-sun-319); + font-weight: bold +} + +/* Generic.Traceback */ +.dsfr-code .gt { + color: var(--blue-cumulus-main-526) +} + +/* Keyword.Constant */ +.dsfr-code .kc { + color: var(--green-bourgeon-main-640); + font-weight: bold +} + +/* Keyword.Declaration */ +.dsfr-code .kd { + color: var(--green-bourgeon-main-640); + font-weight: bold +} + +/* Keyword.Namespace */ +.dsfr-code .kn { + color: var(--green-bourgeon-main-640); + font-weight: bold +} + +/* Keyword.Pseudo */ +.dsfr-code .kp { + color: var(--green-bourgeon-main-640) +} + +/* Keyword.Reserved */ +.dsfr-code .kr { + color: var(--green-bourgeon-main-640); + font-weight: bold +} + +/* Keyword.Type */ +.dsfr-code .kt { + color: var(--pink-tuile-200) +} + +/* Literal.Number */ +.dsfr-code .m { + color: var(--beige-gris-galet-sun-407) +} + +/* Literal.String */ +.dsfr-code .s { + color: var(--pink-tuile-main-556) +} + +/* Name.Attribute */ +.dsfr-code .na { + color: var(--green-tilleul-verveine-main-707) +} + +/* Name.Builtin */ +.dsfr-code .nb { + color: var(--green-bourgeon-main-640) +} + +/* Name.Class */ +.dsfr-code .nc { + color: var(--blue-ecume-main-400); + font-weight: bold +} + +/* Name.Constant */ +.dsfr-code .no { + color: var(--warning-200) +} + +/* Name.Decorator */ +.dsfr-code .nd { + color: var(--purple-glycine-main-494) +} + +/* Name.Entity */ +.dsfr-code .ni { + color: var(--grey-625); + font-weight: bold +} + +/* Name.Exception */ +.dsfr-code .ne { + color: var(--pink-tuile-sun-425); + font-weight: bold +} + +/* Name.Function */ +.dsfr-code .nf { + color: var(--blue-ecume-main-400) +} + +/* Name.Label */ +.dsfr-code .nl { + color: var(--green-tilleul-verveine-main-707) +} + +/* Name.Namespace */ +.dsfr-code .nn { + color: var(--blue-ecume-main-400); + font-weight: bold +} + +/* Name.Tag */ +.dsfr-code .nt { + color: var(--green-bourgeon-main-640); + font-weight: bold +} + +/* Name.Variable */ +.dsfr-code .nv { + color: var(--blue-cumulus-200) +} + +/* Operator.Word */ +.dsfr-code .ow { + color: var(--purple-glycine-main-494); + font-weight: bold +} + +/* Text.Whitespace */ +.dsfr-code .w { + color: var(--beige-gris-galet-main-702) +} + +/* Literal.Number.Bin */ +.dsfr-code .mb { + color: var(--beige-gris-galet-sun-407) +} + +/* Literal.Number.Float */ +.dsfr-code .mf { + color: var(--beige-gris-galet-sun-407) +} + +/* Literal.Number.Hex */ +.dsfr-code .mh { + color: var(--beige-gris-galet-sun-407) +} + +/* Literal.Number.Integer */ +.dsfr-code .mi { + color: var(--beige-gris-galet-sun-407) +} + +/* Literal.Number.Oct */ +.dsfr-code .mo { + color: var(--beige-gris-galet-sun-407) +} + +/* Literal.String.Affix */ +.dsfr-code .sa { + color: var(--pink-tuile-main-556) +} + +/* Literal.String.Backtick */ +.dsfr-code .sb { + color: var(--pink-tuile-main-556) +} + +/* Literal.String.Char */ +.dsfr-code .sc { + color: var(--pink-tuile-main-556) +} + +/* Literal.String.Delimiter */ +.dsfr-code .dl { + color: var(--pink-tuile-main-556) +} + +/* Literal.String.Doc */ +.dsfr-code .sd { + color: var(--pink-tuile-main-556); + font-style: italic +} + +/* Literal.String.Double */ +.dsfr-code .s2 { + color: var(--pink-tuile-main-556) +} + +/* Literal.String.Escape */ +.dsfr-code .se { + color: var(--brown-caramel-main-648); + font-weight: bold +} + +/* Literal.String.Heredoc */ +.dsfr-code .sh { + color: var(--pink-tuile-main-556) +} + +/* Literal.String.Interpol */ +.dsfr-code .si { + color: var(--purple-glycine-main-494); + font-weight: bold +} + +/* Literal.String.Other */ +.dsfr-code .sx { + color: var(--green-bourgeon-main-640) +} + +/* Literal.String.Regex */ +.dsfr-code .sr { + color: var(--purple-glycine-main-494) +} + +/* Literal.String.Single */ +.dsfr-code .s1 { + color: var(--pink-tuile-main-556) +} + +/* Literal.String.Symbol */ +.dsfr-code .ss { + color: var(--blue-cumulus-200) +} + +/* Name.Builtin.Pseudo */ +.dsfr-code .bp { + color: var(--green-bourgeon-main-640) +} + +/* Name.Function.Magic */ +.dsfr-code .fm { + color: var(--blue-ecume-main-400) +} + +/* Name.Variable.Class */ +.dsfr-code .vc { + color: var(--blue-cumulus-200) +} + +/* Name.Variable.Global */ +.dsfr-code .vg { + color: var(--blue-cumulus-200) +} + +/* Name.Variable.Instance */ +.dsfr-code .vi { + color: var(--blue-cumulus-200) +} + +/* Name.Variable.Magic */ +.dsfr-code .vm { + color: var(--blue-cumulus-200) +} + +/* Literal.Number.Integer.Long */ +.dsfr-code .il { + color: var(--beige-gris-galet-sun-407) +} diff --git a/example_app/templates/example_app/blocks/header.html b/example_app/templates/example_app/blocks/header.html index 1a6b1c9c0..e2ccccf3a 100644 --- a/example_app/templates/example_app/blocks/header.html +++ b/example_app/templates/example_app/blocks/header.html @@ -26,18 +26,62 @@ diff --git a/example_app/templates/example_app/doc_markdown.html b/example_app/templates/example_app/doc_markdown.html new file mode 100644 index 000000000..5ec09543f --- /dev/null +++ b/example_app/templates/example_app/doc_markdown.html @@ -0,0 +1,11 @@ +{% extends "example_app/base.html" %} +{% load static dsfr_tags %} + +{% block content %} + {{ documentation|safe}} +{% endblock content %} + + +{% block extra_css %} + +{% endblock extra_css %} diff --git a/example_app/templates/example_app/example_form.html b/example_app/templates/example_app/example_form.html index 5e8fd567c..c5473db8d 100644 --- a/example_app/templates/example_app/example_form.html +++ b/example_app/templates/example_app/example_form.html @@ -9,7 +9,7 @@ {% block head_form %} -

Exemple de formulaire

+

Exemple de formulaire avec formset

{% endblock head_form %} diff --git a/example_app/templates/example_app/index.html b/example_app/templates/example_app/index.html index 0c02739c1..d23c66ccc 100644 --- a/example_app/templates/example_app/index.html +++ b/example_app/templates/example_app/index.html @@ -35,7 +35,7 @@

Documentation

Installation

- Voir la section dédiée dans le README. + Voir la page Installation.

Utilisation

@@ -46,7 +46,7 @@

Utilisation

Développement

- Voir la page CONTRIBUTING.md. + Voir la page Contribuer.

Notes

diff --git a/example_app/templates/example_app/page_icons.html b/example_app/templates/example_app/page_icons.html new file mode 100644 index 000000000..c8513c0f5 --- /dev/null +++ b/example_app/templates/example_app/page_icons.html @@ -0,0 +1,23 @@ +{% extends "example_app/base.html" %} +{% load static dsfr_tags %} + +{% block content %} +

Pictogrammes

+ +

+ + Voir la page de documentation du composant sur le Système de Design de l’État Ouvre une nouvelle fenêtre + +

+ + {% for folder, items in icons.items %} +

{{ folder|title }}

+ +
+ {% for item in items %} + + {% endfor %} +
+ {% endfor %} + +{% endblock content %} diff --git a/example_app/templates/example_app/page_pictograms.html b/example_app/templates/example_app/page_pictograms.html new file mode 100644 index 000000000..3f9e7ed1e --- /dev/null +++ b/example_app/templates/example_app/page_pictograms.html @@ -0,0 +1,45 @@ +{% extends "example_app/base.html" %} +{% load static dsfr_tags %} + +{% block content %} +

Pictogrammes

+ +

+ + Voir la page de documentation du composant sur le Système de Design de l’État Ouvre une nouvelle fenêtre + +

+ + {% for folder, files in pictograms.items %} +

{{ folder|title }}

+ +
+ {% for file in files %} +
+ +
+ {% endfor %} +
+ {% endfor %} + +{% endblock content %} diff --git a/example_app/templates/example_app/page_tag.html b/example_app/templates/example_app/page_tag.html index 9a5d486eb..7d2521b9b 100644 --- a/example_app/templates/example_app/page_tag.html +++ b/example_app/templates/example_app/page_tag.html @@ -2,15 +2,16 @@ {% load static dsfr_tags %} {% block extra_css %} + {% endblock extra_css %} @@ -31,9 +32,8 @@

{{ title }}

{% endif %}

Documentation du tag

-
-      {{ tag_comment }}
-      
+ + {{ tag_comment|safe }} {% if sample_data %}

Exemples

@@ -41,7 +41,7 @@

Exemples

Données

{% with sample_data_item|pprint as raw_sample_code %} - {% with '
'|concatenate:raw_sample_code|concatenate:"
" as sample_data_code %} + {% with '
'|concatenate:raw_sample_code|concatenate:"
" as sample_data_code %} {% dsfr_accordion title="Données d’exemple" content=sample_data_code %} {% endwith %} {% endwith %} @@ -110,18 +110,18 @@

Résultat

{% dsfr_breadcrumb %} {% elif tag_name == "css" %} -
+
{% elif tag_name == "favicon" %} -
+
{% elif tag_name == "js" %} -
+
{% elif tag_name == "pagination" %} {% dsfr_pagination page_obj %} {% elif tag_name == "theme_modale" %} -
+
{% endif %} {% endif %}
diff --git a/example_app/templates/example_app/tags_index.html b/example_app/templates/example_app/tags_index.html index 296ebae1e..08ac5034a 100644 --- a/example_app/templates/example_app/tags_index.html +++ b/example_app/templates/example_app/tags_index.html @@ -3,10 +3,16 @@ {% block content %}

Composants du système de design de l’État

+
  • - +
  • +
  • +
  • @@ -17,11 +23,17 @@

    Composants du système de design de l’État

-
+
+

Documentation

+
    + {{ documentation|safe }} +
+
+

Composants implémentés

    {% for tag, data in implemented_tags.items %} @@ -42,7 +54,7 @@

    Composants encore non implémentés

-

Tags supplémentaires

+

Balises supplémentaires

    {% for tag, data in extra_tags.items %}
  • @@ -53,8 +65,12 @@

    Tags supplémentaires

    En-tête et pied de page

    Les composants « En-tête (header) » et « Pied de page (footer) » sont actuellement gérés avec - des balises include ({% verbatim %} {% include "dsfr/header.html" %} et {% include "dsfr/footer.html" %}{% endverbatim %}) - et non des tags.

    + des balises include ({% verbatim %}{% include "dsfr/header.html" %}{% endverbatim %} et {% verbatim %}{% include "dsfr/footer.html" %}{% endverbatim %}) + et non des balises dédiées.

{% endblock %} + +{% block extra_css %} + +{% endblock extra_css %} diff --git a/example_app/urls.py b/example_app/urls.py index cc9d927e6..f40dc0f05 100644 --- a/example_app/urls.py +++ b/example_app/urls.py @@ -1,6 +1,17 @@ from django_distill import distill_path -from example_app.views import index, tags_index, page_form, page_tag, AuthorCreateView +from example_app.views import ( + index, + resource_icons, + resource_pictograms, + tags_index, + page_form, + page_tag, + AuthorCreateView, + doc_contributing, + doc_install, + doc_form, +) from example_app.tag_specifics import ALL_TAGS @@ -11,18 +22,24 @@ def get_all_tags(): urlpatterns = [ distill_path("", index, name="index", distill_file="django-dsfr/index.html"), + distill_path( + "doc-contributing", + doc_contributing, + name="doc_contributing", + distill_file="django-dsfr/doc-contributing/index.html", + ), + distill_path( + "doc-install", + doc_install, + name="doc_install", + distill_file="django-dsfr/doc-install/index.html", + ), distill_path( "tags/", tags_index, name="tags_index", distill_file="django-dsfr/tags/index.html", ), - distill_path( - "form/", - page_form, - name="page_form", - distill_file="django-dsfr/form/index.html", - ), distill_path( "tags//", page_tag, @@ -30,9 +47,33 @@ def get_all_tags(): distill_func=get_all_tags, ), distill_path( - "formset/", + "form/", + doc_form, + name="doc_form", + distill_file="django-dsfr/form/index.html", + ), + distill_path( + "form/example/", + page_form, + name="page_form", + distill_file="django-dsfr/form/example/index.html", + ), + distill_path( + "form/example-formset/", AuthorCreateView.as_view(), name="form_formset", - distill_file="django-dsfr/formset/index.html", + distill_file="django-dsfr/form/example-formset/index.html", + ), + distill_path( + "resources/icons", + resource_icons, + name="resource_icons", + distill_file="django-dsfr/resources/icons/index.html", + ), + distill_path( + "resources/pictograms", + resource_pictograms, + name="resource_pictograms", + distill_file="django-dsfr/resources/pictograms/index.html", ), ] diff --git a/example_app/utils.py b/example_app/utils.py index e6fda12b4..0e3dc26b0 100644 --- a/example_app/utils.py +++ b/example_app/utils.py @@ -1,3 +1,5 @@ +import markdown +from markdown.extensions.codehilite import CodeHiliteExtension from example_app.models import Genre @@ -24,3 +26,15 @@ def populate_genre_choices(): genres_list.append(to_add) return genres_list + + +def format_markdown_from_file(filename: str) -> str: + with open(filename) as f: + content = f.read() + return markdown.markdown( + content, + extensions=[ + "markdown.extensions.fenced_code", + CodeHiliteExtension(css_class="dsfr-code"), + ], + ) diff --git a/example_app/views.py b/example_app/views.py index 338f699f6..1350657ce 100644 --- a/example_app/views.py +++ b/example_app/views.py @@ -1,5 +1,8 @@ -from django.core.paginator import Paginator +import markdown +from markdown.extensions.codehilite import CodeHiliteExtension +import os +from django.core.paginator import Paginator from django.shortcuts import render from django.urls import reverse from django.views.decorators.http import require_safe @@ -23,6 +26,7 @@ from django.http import HttpResponse from example_app.forms import AuthorCreateForm, BookCreateFormSet, BookCreateFormHelper from example_app.models import Author +from example_app.utils import format_markdown_from_file def init_payload(page_title: str, links: list = []): @@ -68,6 +72,7 @@ def index(request): @require_safe def tags_index(request): payload = init_payload("Composants") + payload["documentation"] = format_markdown_from_file("doc/components.md") payload["implemented_tags"] = dict( sorted(IMPLEMENTED_TAGS.items(), key=lambda k: k[1]["title"]) ) @@ -96,7 +101,13 @@ def page_tag(request, tag_name): payload["page_obj"] = paginator.get_page(4) module = getattr(globals()["dsfr_tags"], f"dsfr_{tag_name}") - payload["tag_comment"] = module.__doc__ + payload["tag_comment"] = markdown.markdown( + module.__doc__, + extensions=[ + "markdown.extensions.fenced_code", + CodeHiliteExtension(css_class="dsfr-code"), + ], + ) if "sample_data" in current_tag: payload["sample_data"] = current_tag["sample_data"] @@ -207,7 +218,10 @@ def page_form(request): else: form = ExampleForm() - payload = init_payload("Formulaire") + payload = init_payload( + "Formulaire basique", + links=[{"url": reverse("doc_form"), "title": "Formulaires"}], + ) payload["form"] = form return render(request, "example_app/page_form.html", payload) @@ -241,7 +255,11 @@ def get(self, request, *args, **kwargs): def get_context_data(self, **kwargs): context = super(AuthorCreateView, self).get_context_data(**kwargs) - payload = init_payload("Formulaire avec formset") + payload = init_payload( + "Formulaire avec formset", + links=[{"url": reverse("doc_form"), "title": "Formulaires"}], + ) + for key, value in payload.items(): context[key] = value @@ -314,3 +332,64 @@ def form_invalid(self, form, formset): return self.render_to_response( self.get_context_data(form=form, formset=formset) ) + + +@require_safe +def doc_contributing(request): + payload = init_payload("Contribuer à Django-DSFR") + payload["documentation"] = format_markdown_from_file("CONTRIBUTING.md") + + return render(request, "example_app/doc_markdown.html", payload) + + +@require_safe +def doc_install(request): + payload = init_payload("Installation") + payload["documentation"] = format_markdown_from_file("INSTALL.md") + + return render(request, "example_app/doc_markdown.html", payload) + + +@require_safe +def doc_form(request): + payload = init_payload("Installation") + payload["documentation"] = format_markdown_from_file("doc/forms.md") + + return render(request, "example_app/doc_markdown.html", payload) + + +@require_safe +def resource_icons(request): + payload = init_payload("Icônes") + + icons_root = "dsfr/static/dsfr/dist/icons/" + icons_folders = os.listdir(icons_root) + icons_folders.sort() + all_icons = {} + for folder in icons_folders: + files = os.listdir(os.path.join(icons_root, folder)) + files_without_extensions = [f.split(".")[0] for f in files] + files_without_extensions.sort() + all_icons[folder] = files_without_extensions + + payload["icons"] = all_icons + + return render(request, "example_app/page_icons.html", payload) + + +@require_safe +def resource_pictograms(request): + payload = init_payload("Pictogrammes") + + picto_root = "dsfr/static/dsfr/dist/artwork/pictograms/" + picto_folders = os.listdir(picto_root) + picto_folders.sort() + all_pictos = {} + for folder in picto_folders: + files = os.listdir(os.path.join(picto_root, folder)) + files.sort() + all_pictos[folder] = files + + payload["pictograms"] = all_pictos + + return render(request, "example_app/page_pictograms.html", payload) diff --git a/poetry.lock b/poetry.lock index 7fda13a06..b36f2f180 100644 --- a/poetry.lock +++ b/poetry.lock @@ -828,6 +828,24 @@ completion = ["shtab"] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +[[package]] +name = "markdown" +version = "3.5" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.5-py3-none-any.whl", hash = "sha256:4afb124395ce5fc34e6d9886dab977fd9ae987fc6e85689f08278cf0c69d4bf3"}, + {file = "Markdown-3.5.tar.gz", hash = "sha256:a807eb2e4778d9156c8f07876c6e4d50b5494c5665c4834f67b06459dfd877b3"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -1549,4 +1567,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "3e2b16453e10f6081530367003b1adca6ebc7abaab2fdb8c1b1247e88b5a1a8a" +content-hash = "b0ffc4229d6d49a181bd2f17283ddd6f6bc95a57da003723cbb8b3558a4b6290" diff --git a/pyproject.toml b/pyproject.toml index 67eacb8be..972253036 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ classifiers = [ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Software Development :: Libraries", "Topic :: Utilities", ] @@ -55,6 +56,8 @@ django-extensions = "^3.2.1" pre-commit = "^3.5.0" bandit = "^1.7.5" ruff = "^0.1.2" +markdown = "^3.5" +pygments = "^2.16.1" [build-system] build-backend = "poetry.core.masonry.api"