#37298: Admin radio_fields have an invalid aria-describedby attribute.
-----------------------------+-----------------------------------------
Reporter: David Smith | Type: Bug
Status: new | Component: contrib.admin
Version: 6.1 | Severity: Normal
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------+-----------------------------------------
#35892 added support for Widget.use_fieldset in admin forms. Refs
4187da258fe212d494cb578a0bc2b52c4979ab95
An incorrect aria-describedby attribute is generated for `radio_fields`
with `help_text` and no reference is available for errors.
Given this form:
{{{#!python
class Colour(models.Model):
name = models.CharField(max_length=100, help_text='Enter your name')
COLOUR_CHOICES = [
('red', 'Red'),
('green', 'Green'),
('blue', 'Blue'),]
favourite_colour = models.CharField(
max_length=10,
choices=COLOUR_CHOICES,
help_text='Colour of the favourite colour',
)
}}}
... and model admin:
{{{#!python
class ColourAdmin(admin.ModelAdmin):
radio_fields = {"favourite_colour": admin.VERTICAL}
model = Colour
}}}
Adding a new Colour record in the admin with no inputs gives the following
HTML for the `favourite_colour` field.
Notice how the aria-describedby in the `<fieldset>` is `_helptext`. This
id does not exist, and also there is no reference to
"id_favourite_colour_error" to reference the error list.
{{{
<fieldset aria-describedby="_helptext">
<legend class="required">Favourite colour:</legend>
<div class="flex-container errors">
<div class="help">
<div>Colour of the favourite colour</div>
</div>
<ul class="errorlist" id="id_favourite_colour_error">
<li>This field is required.</li>
</ul>
<div id="id_favourite_colour" class="radiolist">
<div>
<label for="id_favourite_colour_0">
<input type="radio" name="favourite_colour"
value="red" class="radiolist" required="" aria-invalid="true"
id="id_favourite_colour_0" /> Red
</label>
</div>
<div>
<label for="id_favourite_colour_1">
<input type="radio" name="favourite_colour"
value="green" class="radiolist" required="" aria-invalid="true"
id="id_favourite_colour_1" /> Green
</label>
</div>
<div>
<label for="id_favourite_colour_2">
<input type="radio" name="favourite_colour"
value="blue" class="radiolist" required="" aria-invalid="true"
id="id_favourite_colour_2" /> Blue
</label>
</div>
</div>
</div>
</fieldset>
}}}
Contrast that to the name field where both the help_text and errors have a
valid aria-describedby references from the `<input>`.
Using aria-describedby from the field's boundfield (field.field in this
case) is where I'd be looking to fix the issue.
{{{#!diff
diff --git a/django/contrib/admin/templates/admin/includes/fieldset.html
b/django/contrib/admin/templates/admin/includes/fieldset.html
index 70c68655c5..4ef3d01589 100644
--- a/django/contrib/admin/templates/admin/includes/fieldset.html
+++ b/django/contrib/admin/templates/admin/includes/fieldset.html
@@ -10,7 +10,7 @@
{% for line in fieldset %}
<div class="form-row{% if not line.fields|length == 1 %} flex-
container form-multiline{% endif %}{% if not line.has_visible_field %}
hidden{% endif %}{% for field in line %}{% if
field.field.name %} field-{{
field.field.name }}{% endif %}{% endfor %}">
{% for field in line %}
- {% if field.is_fieldset %}<fieldset{% if
field.field.help_text %} aria-describedby="{{ field.field.id_for_label
}}_helptext"{% endif %}>{{ field.label_tag }}{% endif %}
+ {% if field.is_fieldset %}<fieldset{% if
field.field.aria_describedby %} aria-describedby="{{
field.field.aria_describedby }}"{% endif %}>{{ field.label_tag }}{% endif
%}
<div class="flex-container{% if not field.is_readonly and
field.errors or line.fields|length == 1 and line.errors %} errors{% endif
%}{% if not line.fields|length == 1 %} fieldBox{% if
field.field.name %}
field-{{
field.field.name }}{% endif %}{% if field.field.is_hidden %}
hidden{% endif %}{% endif %}{% if field.is_checkbox %} checkbox-row{%
endif %}">
{% if field.is_checkbox %}
<div class="checkbox">
@@ -21,7 +21,7 @@
{% if not field.is_fieldset %}{{ field.label_tag
}}{% endif %}
{% endif %}
{% if field.field.help_text %}
- <div class="help{% if field.field.is_hidden %}
hidden{% endif %}"{% if field.field.id_for_label %} id="{{
field.field.id_for_label }}_helptext"{% endif %}>
+ <div class="help{% if field.field.is_hidden %}
hidden{% endif %}"{% if field.field.auto_id %} id="{{ field.field.auto_id
}}_helptext"{% endif %}>
<div>{{ field.field.help_text|safe }}</div>
</div>
{% endif %}
}}}
Then for tests I'd suggest looking at the test that was added for admin
fieldsets. Additional tests can be added to assert the correct aria-
describedby attribute for both help_text and errors is present.
https://github.com/django/django/blob/0b40210e4808937a7c0922e8b7502bff4752faa3/tests/admin_views/tests.py#L7365
--
Ticket URL: <
https://code.djangoproject.com/ticket/37298>
Django <
https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.