Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
dan-mm committed Nov 17, 2023
2 parents bec8dde + 594873b commit 40f1c03
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 33 deletions.
2 changes: 1 addition & 1 deletion django/forms/jinja2/django/forms/field.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% if field.use_fieldset %}
<fieldset>
<fieldset{% if field.help_text and field.auto_id and "aria-describedby" not in field.field.widget.attrs %} aria-describedby="{{ field.auto_id }}_helptext"{% endif %}>
{% if field.label %}{{ field.legend_tag() }}{% endif %}
{% else %}
{% if field.label %}{{ field.label_tag() }}{% endif %}
Expand Down
5 changes: 2 additions & 3 deletions django/forms/templates/django/forms/field.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
{% if field.use_fieldset %}
<fieldset>
<fieldset{% if field.help_text and field.auto_id and "aria-describedby" not in field.field.widget.attrs %} aria-describedby="{{ field.auto_id }}_helptext"{% endif %}>
{% if field.label %}{{ field.legend_tag }}{% endif %}
{% else %}
{% if field.label %}{{ field.label_tag }}{% endif %}
{% endif %}
{% if field.help_text %}<div class="helptext"{% if field.auto_id %} id="{{ field.auto_id }}_helptext"{% endif %}>{{ field.help_text|safe }}</div>{% endif %}
{{ field.errors }}
{{ field }}
{% if field.use_fieldset %}</fieldset>{% endif %}
{{ field }}{% if field.use_fieldset %}</fieldset>{% endif %}
9 changes: 9 additions & 0 deletions docs/faq/admin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,12 @@ Android and iOS.

Depending on feature support, there *may* be minor stylistic differences
between browsers. These are considered acceptable variations in rendering.

What assistive technologies are supported for using the admin?
==============================================================

The admin is intended to be compatible with a wide range of assistive
technologies, but there are currently many blockers. The support target is all
latest versions of major assistive technologies, including Dragon, JAWS, NVDA,
Orca, TalkBack, Voice Control, VoiceOver, Windows Contrast Themes, ZoomText,
and screen magnifiers.
11 changes: 0 additions & 11 deletions docs/howto/error-reporting.txt
Original file line number Diff line number Diff line change
Expand Up @@ -194,17 +194,6 @@ filtered out of error reports in a production environment (that is, where
def process_info(user):
...

.. warning::

Due to the machinery needed to cross the sync/async boundary,
:func:`~asgiref.sync.sync_to_async` and
:func:`~asgiref.sync.async_to_sync` are **not** compatible with
``sensitive_variables()``.

If using these adapters with sensitive variables, ensure to audit
exception reporting, and consider implementing a :ref:`custom filter
<custom-error-reports>` if necessary.

.. versionchanged:: 5.0

Support for wrapping ``async`` functions was added.
Expand Down
11 changes: 8 additions & 3 deletions docs/ref/forms/fields.txt
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,10 @@ fields. We've specified ``auto_id=False`` to simplify the output:
<div>Sender:<div class="helptext">A valid email address, please.</div><input type="email" name="sender" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself"></div>

When a field has help text and the widget is not rendered in a ``<fieldset>``,
``aria-describedby`` is added to the ``<input>`` to associate it to the
help text:
When a field has help text it is associated with its input using the
``aria-describedby`` HTML attribute. If the widget is rendered in a
``<fieldset>`` then ``aria-describedby`` is added to this element, otherwise it
is added to the widget's ``<input>``:

.. code-block:: pycon

Expand Down Expand Up @@ -325,6 +326,10 @@ inside ``aria-describedby``:

``aria-describedby`` was added to associate ``help_text`` with its input.

.. versionchanged:: 5.1

``aria-describedby`` support was added for ``<fieldset>``.

``error_messages``
------------------

Expand Down
4 changes: 3 additions & 1 deletion docs/releases/5.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ File Uploads
Forms
~~~~~

* ...
* In order to improve accessibility and enable screen readers to associate
fieldsets with their help text, the form fieldset now includes the
``aria-describedby`` HTML attribute.

Generic Views
~~~~~~~~~~~~~
Expand Down
1 change: 1 addition & 0 deletions docs/spelling_wordlist
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ OGC
OGR
ons
orderable
Orca
Orléans
orm
Outdim
Expand Down
14 changes: 0 additions & 14 deletions docs/topics/async.txt
Original file line number Diff line number Diff line change
Expand Up @@ -401,17 +401,3 @@ trigger the thread safety checks:
Rather, you should encapsulate all database access within a helper function
that can be called with ``sync_to_async()`` without relying on the connection
object in the calling code.

Use with exception reporting filters
------------------------------------

.. warning::

Due to the machinery needed to cross the sync/async boundary,
``sync_to_async()`` and ``async_to_sync()`` are **not** compatible with
:func:`~django.views.decorators.debug.sensitive_variables`, used to mask
local variables from exception reports.

If using these adapters with sensitive variables, ensure to audit exception
reporting, and consider implementing a :ref:`custom filter
<custom-error-reports>` if necessary.
94 changes: 94 additions & 0 deletions tests/forms_tests/tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3097,6 +3097,100 @@ class UserRegistration(Form):
"</span></td></tr>",
)

def test_aria_describedby_custom_widget_id(self):
class UserRegistration(Form):
username = CharField(
max_length=255,
help_text="e.g., user@example.com",
widget=TextInput(attrs={"id": "custom-id"}),
)

f = UserRegistration()
self.assertHTMLEqual(
str(f),
'<div><label for="custom-id">Username:</label>'
'<div class="helptext" id="id_username_helptext">e.g., user@example.com'
'</div><input type="text" name="username" id="custom-id" maxlength="255" '
'required aria-describedby="id_username_helptext"></div>',
)

def test_fieldset_aria_describedby(self):
class FieldsetForm(Form):
checkbox = MultipleChoiceField(
choices=[("a", "A"), ("b", "B")],
widget=CheckboxSelectMultiple,
help_text="Checkbox help text",
)
radio = MultipleChoiceField(
choices=[("a", "A"), ("b", "B")],
widget=RadioSelect,
help_text="Radio help text",
)
datetime = SplitDateTimeField(help_text="Enter Date and Time")

f = FieldsetForm()
self.assertHTMLEqual(
str(f),
'<div><fieldset aria-describedby="id_checkbox_helptext">'
"<legend>Checkbox:</legend>"
'<div class="helptext" id="id_checkbox_helptext">Checkbox help text</div>'
'<div id="id_checkbox"><div>'
'<label for="id_checkbox_0"><input type="checkbox" name="checkbox" '
'value="a" id="id_checkbox_0" /> A</label>'
"</div><div>"
'<label for="id_checkbox_1"><input type="checkbox" name="checkbox" '
'value="b" id="id_checkbox_1" /> B</label>'
"</div></div></fieldset></div>"
'<div><fieldset aria-describedby="id_radio_helptext">'
"<legend>Radio:</legend>"
'<div class="helptext" id="id_radio_helptext">Radio help text</div>'
'<div id="id_radio"><div>'
'<label for="id_radio_0"><input type="radio" name="radio" value="a" '
'required id="id_radio_0" />A</label>'
"</div><div>"
'<label for="id_radio_1"><input type="radio" name="radio" value="b" '
'required id="id_radio_1" /> B</label>'
"</div></div></fieldset></div>"
'<div><fieldset aria-describedby="id_datetime_helptext">'
"<legend>Datetime:</legend>"
'<div class="helptext" id="id_datetime_helptext">Enter Date and Time</div>'
'<input type="text" name="datetime_0" required id="id_datetime_0" />'
'<input type="text" name="datetime_1" required id="id_datetime_1" />'
"</fieldset></div>",
)
f = FieldsetForm(auto_id=False)
# aria-describedby is not included.
self.assertIn("<fieldset>", str(f))
self.assertIn('<div class="helptext">', str(f))
f = FieldsetForm(auto_id="custom_%s")
# aria-describedby uses custom auto_id.
self.assertIn('fieldset aria-describedby="custom_checkbox_helptext"', str(f))
self.assertIn('<div class="helptext" id="custom_checkbox_helptext">', str(f))

def test_fieldset_custom_aria_describedby(self):
# aria-describedby set on widget results in aria-describedby being
# added to widget and not the <fieldset>.
class FieldsetForm(Form):
checkbox = MultipleChoiceField(
choices=[("a", "A"), ("b", "B")],
widget=CheckboxSelectMultiple(attrs={"aria-describedby": "custom-id"}),
help_text="Checkbox help text",
)

f = FieldsetForm()
self.assertHTMLEqual(
str(f),
"<div><fieldset><legend>Checkbox:</legend>"
'<div class="helptext" id="id_checkbox_helptext">Checkbox help text</div>'
'<div id="id_checkbox"><div>'
'<label for="id_checkbox_0"><input type="checkbox" name="checkbox" '
'value="a" aria-describedby="custom-id" id="id_checkbox_0" />A</label>'
"</div><div>"
'<label for="id_checkbox_1"><input type="checkbox" name="checkbox" '
'value="b" aria-describedby="custom-id" id="id_checkbox_1" />B</label>'
"</div></div></fieldset></div>",
)

def test_as_widget_custom_aria_describedby(self):
class FoodForm(Form):
intl_name = CharField(help_text="The food's international name.")
Expand Down

0 comments on commit 40f1c03

Please sign in to comment.