Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple corrections sur le DSFR #188

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion dsfr/enums.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
from django import VERSION
from django.db import models
from django.utils.safestring import mark_safe
from django.utils.version import PY311

if VERSION >= (4, 0):
from django.utils.version import PY311
else:
import sys

PY311 = sys.version_info >= (3, 11)

if PY311:
from enum import property as enum_property
Expand Down
10 changes: 2 additions & 8 deletions dsfr/templates/dsfr/form_field_snippets/checkbox_snippet.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
{% load widget_tweaks dsfr_tags %}
<div class="fr-checkbox-group{% if field.errors %} fr-checkbox-group--error{% endif %}{% if field.field.disabled %} fr-input-group--disabled{% endif %} fr-mb-2w">
{% if field.errors %}
{% with aria_describedby="aria-describedby:"|add:field.auto_id|add:"-desc-error" %}
{{ field|dsfr_input_class_attr|attr:"type:checkbox"|attr:aria_describedby }}
{% endwith %}
{% else %}
{{ field|dsfr_input_class_attr|attr:"type:checkbox" }}
{% endif %}
{{ field|dsfr_input_class_attr|attr:"type:checkbox" }}
<label for="{{ field.id_for_label }}" class="fr-label">
{{ field.label_tag }}
{{ field.label }}
{% if field.help_text %}
<span class="fr-hint-text">{{ field.help_text|safe }}</span>
{% endif %}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
{% load widget_tweaks dsfr_tags %}
<fieldset class="fr-fieldset{% if field.errors %} fr-fieldset--error{% endif %}"
<fieldset class="fr-fieldset{% if field.errors %} fr-fieldset--error{% endif %} fr-fieldsets"
id="checkboxes-{{ field.auto_id }}"
aria-labelledby="{{ field.auto_id }}-legend{% if field.errors %} {{ field.auto_id }}-messages{% endif %}">
<legend class="fr-fieldset__legend fr-text--regular"
<legend class="fr-fieldset__legend fr-fieldset__legend--regular"
id="{{ field.auto_id }}-legend">
{{ field.label_tag }}
{{ field.label }}
{% if field.field.required %}
*
{% endif %}
{% if field.help_text %}
<span class="fr-hint-text">{{ field.help_text|safe }}</span>
{% endif %}
</legend>
<div class="fr-fieldset__content">
<div class="fr-checkbox-group{% if field.errors %} fr-checkbox-group--error{% endif %}{% if field.field.disabled %} fr-input-group--disabled{% endif %}">
{% if field.errors %}
{% with aria_describedby="aria-describedby:"|add:field.auto_id|add:"-desc-error" %}
{{ field|dsfr_input_class_attr|attr:"type:checkbox"|attr:aria_describedby }}
{% endwith %}
{% else %}
{{ field|dsfr_input_class_attr|attr:"type:checkbox" }}
{% endif %}
{% if field.errors %}
<div id="{{ field.auto_id }}-desc-error">
{{ field.errors }}
</div>
{% endif %}
{% with field=field|dsfr_input_class_attr %}
{% for subwidget in field.subwidgets %}
<div class="fr-fieldset__element{% if field.field.widget.inline %} fr-fieldset__element--inline{% endif %}">
{{ subwidget }}
</div>
{% endfor %}
{% endwith %}
{% if field.errors %}
<div id="{{ field.auto_id }}-desc-error">
{{ field.errors }}
</div>
</div>
{% endif %}
</fieldset>
8 changes: 1 addition & 7 deletions dsfr/templates/dsfr/form_field_snippets/input_snippet.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,7 @@
{% endif %}
{# djlint:on #}
</label>
{% if field.errors %}
{% with aria_describedby="aria-describedby:"|add:field.auto_id|add:"-desc-error" %}
{{ field|dsfr_input_class_attr|attr:"aria-invalid:true"|attr:aria_describedby }}
{% endwith %}
{% else %}
{{ field|dsfr_input_class_attr }}
{% endif %}
{{ field|dsfr_input_class_attr }}
{% if field.errors %}
<div id="{{ field.auto_id }}-desc-error">
{{ field.errors }}
Expand Down
24 changes: 11 additions & 13 deletions dsfr/templates/dsfr/form_field_snippets/radioselect_snippet.html
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
{% load widget_tweaks dsfr_tags %}
<fieldset class="fr-fieldset{% if field.errors %} fr-fieldset--error{% endif %}"
id="radio-{{ field.auto_id }}"
aria-labelledby="{{ field.auto_id }}-legend{% if field.errors %} {{ field.auto_id }}-messages{% endif %}">
<legend class="fr-fieldset__legend fr-text--regular"
aria-labelledby="{{ field.auto_id }}-legend{% if field.errors %} {{ field.auto_id }}-desc-error{% endif %}">
<legend class="fr-fieldset__legend fr-fieldset__legend--regular"
id="{{ field.auto_id }}-legend">
{{ field.label_tag }}
{{ field.label }}
{% if field.field.required %}
*
{% endif %}
{% if field.help_text %}
<span class="fr-hint-text">{{ field.help_text|safe }}</span>
{% endif %}
</legend>
<div class="fr-fieldset__content">
<div class="fr-radio-group">
{% if field.field.disabled %}
{{ field|dsfr_input_class_attr|attr:"type:radio"|attr:"disabled" }}
{% else %}
{{ field|dsfr_input_class_attr|attr:"type:radio" }}
{% endif %}
</div>
</div>
{% with field=field|dsfr_input_class_attr %}
{% for subwidget in field.subwidgets %}
<div class="fr-fieldset__element{% if field.field.widget.inline %} fr-fieldset__element--inline{% endif %}">
{{ subwidget }}
</div>
{% endfor %}
{% endwith %}
{% if field.errors %}
<div class="fr-messages-group" id="{{ field.auto_id }}-messages">
<div class="fr-messages-group" id="{{ field.auto_id }}-desc-error">
{{ field.errors }}
</div>
{% endif %}
Expand Down
22 changes: 22 additions & 0 deletions dsfr/templatetags/dsfr_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -1612,6 +1612,12 @@ def dsfr_form_field(field) -> dict:

**Usage**:
`{% dsfr_form_field field %}`

You can use this in combination with filter `dsfr_inline` to inline checkboxes,
radio buttons and rich radio buttons.

**Usage**:
`{% dsfr_form_field field|dsfr_inline %}`
"""
return {"field": field}

Expand Down Expand Up @@ -1691,3 +1697,19 @@ def get_context_data(self, **kwargs):
args = (str(args),)

return format_html(format_string, *args, **kwargs)


@register.filter
def dsfr_inline(field):
"""
Sets field.widget.inline to `True` to inline form field in combination with
{% dsfr_form_field %}

**Usage**:

```django
{% dsfr_form_field form.is_aidant|dsfr_inline %}
```
"""
field.field.widget.inline = True
return field
9 changes: 8 additions & 1 deletion dsfr/test/test_enums.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
from enum import auto
from unittest import skipIf

from django import VERSION
from django.db.models import IntegerChoices
from django.test import SimpleTestCase
from django.utils.safestring import mark_safe
from django.utils.version import PY311

from dsfr.enums import ExtendedChoices

if VERSION >= (4, 0):
from django.utils.version import PY311
else:
import sys

PY311 = sys.version_info >= (3, 11)

if PY311:
from enum import property as enum_property, nonmember
else:
Expand Down
10 changes: 9 additions & 1 deletion dsfr/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ def generate_summary_items(sections_names: list) -> list:


def dsfr_input_class_attr(bf: BoundField):
if not bf.is_hidden and "class" not in bf.field.widget.attrs:
if bf.is_hidden:
return bf
if "class" not in bf.field.widget.attrs:
bf.field.label_suffix = ""
if isinstance(bf.field.widget, (widgets.Select, widgets.SelectMultiple)):
bf.field.widget.attrs["class"] = "fr-select"
Expand All @@ -130,6 +132,12 @@ def dsfr_input_class_attr(bf: BoundField):
),
):
bf.field.widget.attrs["class"] = "fr-input"

if bf.errors:
bf.field.widget.attrs.update(
{"aria-invalid": "true", "aria-describedby": f"{bf.auto_id}-desc-error"}
)

return bf


Expand Down
21 changes: 21 additions & 0 deletions example_app/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Fieldset, Field

from dsfr.templatetags.dsfr_tags import dsfr_inline
from dsfr.utils import lazy_static
from dsfr.widgets import RichRadioSelect
from example_app.models import Author, Book
Expand Down Expand Up @@ -110,6 +111,13 @@ class ExampleForm(DsfrBaseForm):
widget=forms.RadioSelect,
)

sample_radio_inline = forms.ChoiceField(
label="Boutons radio inline",
required=False,
choices=[(1, "Premier choix"), (2, "Second choix"), (3, "Troisième choix")],
widget=forms.RadioSelect,
)

sample_checkbox = forms.MultipleChoiceField(
label="Cases à cocher",
required=False,
Expand All @@ -122,6 +130,17 @@ class ExampleForm(DsfrBaseForm):
widget=forms.CheckboxSelectMultiple,
)

sample_checkbox_inline = forms.MultipleChoiceField(
label="Cases à cocher",
required=False,
choices=[
("1", "Premier choix"),
("2", "Second choix"),
("3", "Troisième choix"),
],
widget=forms.CheckboxSelectMultiple,
)

sample_rich_radio = forms.ChoiceField(
label="Cases à cocher",
required=False,
Expand Down Expand Up @@ -176,6 +195,8 @@ def clean_sample_checkbox(self):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_autofocus_on_first_error()
dsfr_inline(self["sample_radio_inline"])
dsfr_inline(self["sample_checkbox_inline"])


class AuthorCreateForm(ModelForm, DsfrBaseForm):
Expand Down
6 changes: 3 additions & 3 deletions example_app/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ def test_form_sets_autofocus_on_first_error(self):
self.assertEqual(response.status_code, HTTPStatus.OK)
self.assertContains(
response,
"""<input type="number" name="sample_number" value="-5" class="fr-input"
autofocus="" aria-describedby="id_sample_number-desc-error" aria-invalid="true"
required id="id_sample_number">""",
"""<input type="number" name="sample_number" value="-5"
aria-invalid="true" aria-describedby="id_sample_number-desc-error" class="fr-input"
autofocus="" required id="id_sample_number">""",
html=True,
)
self.assertContains(response, "Merci d’entrer un nombre positif", html=True)
Loading