diff --git a/django/forms/jinja2/django/forms/field.html b/django/forms/jinja2/django/forms/field.html index 21035b72e464..642d45ddc74b 100644 --- a/django/forms/jinja2/django/forms/field.html +++ b/django/forms/jinja2/django/forms/field.html @@ -1,5 +1,5 @@ {% if field.use_fieldset %} -
+ {% if field.label %}{{ field.legend_tag() }}{% endif %} {% else %} {% if field.label %}{{ field.label_tag() }}{% endif %} diff --git a/django/forms/templates/django/forms/field.html b/django/forms/templates/django/forms/field.html index f8b0cdedd561..a4548fe54f9d 100644 --- a/django/forms/templates/django/forms/field.html +++ b/django/forms/templates/django/forms/field.html @@ -1,10 +1,9 @@ {% if field.use_fieldset %} -
+ {% if field.label %}{{ field.legend_tag }}{% endif %} {% else %} {% if field.label %}{{ field.label_tag }}{% endif %} {% endif %} {% if field.help_text %}
{{ field.help_text|safe }}
{% endif %} {{ field.errors }} -{{ field }} -{% if field.use_fieldset %}
{% endif %} +{{ field }}{% if field.use_fieldset %}
{% endif %} diff --git a/docs/faq/admin.txt b/docs/faq/admin.txt index 7c6f7a12c7e0..7043676e271c 100644 --- a/docs/faq/admin.txt +++ b/docs/faq/admin.txt @@ -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. diff --git a/docs/howto/error-reporting.txt b/docs/howto/error-reporting.txt index 74e11a6951ab..84fe3cb768d6 100644 --- a/docs/howto/error-reporting.txt +++ b/docs/howto/error-reporting.txt @@ -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 - ` if necessary. - .. versionchanged:: 5.0 Support for wrapping ``async`` functions was added. diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt index 9dff2d4007f3..ed685cfff654 100644 --- a/docs/ref/forms/fields.txt +++ b/docs/ref/forms/fields.txt @@ -283,9 +283,10 @@ fields. We've specified ``auto_id=False`` to simplify the output:
Sender:
A valid email address, please.
Cc myself:
-When a field has help text and the widget is not rendered in a ``
``, -``aria-describedby`` is added to the ```` 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 +``
`` then ``aria-describedby`` is added to this element, otherwise it +is added to the widget's ````: .. code-block:: pycon @@ -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 ``
``. + ``error_messages`` ------------------ diff --git a/docs/releases/5.1.txt b/docs/releases/5.1.txt index d6c07d9c222b..b90808be3c72 100644 --- a/docs/releases/5.1.txt +++ b/docs/releases/5.1.txt @@ -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 ~~~~~~~~~~~~~ diff --git a/docs/spelling_wordlist b/docs/spelling_wordlist index 18d01c7e7cba..c217719a8a29 100644 --- a/docs/spelling_wordlist +++ b/docs/spelling_wordlist @@ -328,6 +328,7 @@ OGC OGR ons orderable +Orca Orléans orm Outdim diff --git a/docs/topics/async.txt b/docs/topics/async.txt index 8a9857a0df2d..6c9d35b53326 100644 --- a/docs/topics/async.txt +++ b/docs/topics/async.txt @@ -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 - ` if necessary. diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py index 877571726d42..19ca978c4523 100644 --- a/tests/forms_tests/tests/test_forms.py +++ b/tests/forms_tests/tests/test_forms.py @@ -3097,6 +3097,100 @@ class UserRegistration(Form): "", ) + 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), + '
' + '
e.g., user@example.com' + '
', + ) + + 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), + '
' + "Checkbox:" + '
Checkbox help text
' + '
' + '' + "
" + '' + "
" + '
' + "Radio:" + '
Radio help text
' + '
' + '' + "
" + '' + "
" + '
' + "Datetime:" + '
Enter Date and Time
' + '' + '' + "
", + ) + f = FieldsetForm(auto_id=False) + # aria-describedby is not included. + self.assertIn("
", str(f)) + self.assertIn('
', 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('
', 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
. + 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), + "
Checkbox:" + '
Checkbox help text
' + '
' + '' + "
" + '' + "
", + ) + def test_as_widget_custom_aria_describedby(self): class FoodForm(Form): intl_name = CharField(help_text="The food's international name.")