Opened 17 months ago
Last modified 17 months ago
#34705 closed Bug
BoundField.as_widget() ignores aria-describedby in attrs argument — at Initial Version
Reported by: | Sage Abdullah | Owned by: | Sage Abdullah |
---|---|---|---|
Component: | Forms | Version: | dev |
Severity: | Release blocker | Keywords: | |
Cc: | Gregor Jerše, David Smith, Thibaud Colas | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
BoundField.as_widget()
ignores aria-describedby
that is passed in the attrs
argument.
Use case:
In Wagtail, we have our own mechanism for rendering form fields (called "Panels") that is built on top of Django's Forms API. We use BoundField.as_widget()
in our rendering process to render only the form field's widget, while its help text element is rendered separately via our Panels API with a custom markup (including HTML id
) that conforms to our design system. We've been passing additional attributes to BoundField.as_widget()
to improve accessibility, including aria-describedby
, to establish the relationship between the field widget rendered by Django and our help text element.
As of 966ecdd482167f3f6b08b00f484936c837751cb9, Django automatically adds aria-describedby
to form fields with a help_text
. The logic in build_widget_attrs()
(used by as_widget()
) checks for an existing aria-describedby
in the field's widget's attrs
before automatically generating one. However, it does not take into account the possibility of an existing aria-describedby
in the attrs
argument.
A workaround on Wagtail's side could be to modify the widget's attrs before using as_widget()
, but this is not ideal as the widget is customisable by the developer and we would have to copy the widget instance to avoid modifying the existing widget instance (which might be shared across form fields).
I believe Django should check for aria-describedby
in attrs
first, before falling back to widget.attrs
and the auto-generated value.
Test case:
class BoundFieldTests(SimpleTestCase): def test_as_widget_with_custom_aria_describedby(self): class TestForm(Form): data = CharField(help_text="Some help text") form = TestForm({"data": "some value"}) self.assertHTMLEqual( form["data"].as_widget(attrs={"aria-describedby": "custom_help_text_id"}), """ <input type="text" name="data" value="some value" aria-describedby="custom_help_text_id" required id="id_data"> """, )
Patch:
diff --git a/django/forms/boundfield.py b/django/forms/boundfield.py index deba739329..b9f29e3da5 100644 --- a/django/forms/boundfield.py +++ b/django/forms/boundfield.py @@ -290,7 +290,9 @@ class BoundField(RenderableFieldMixin): # If a custom aria-describedby attribute is given and help_text is # used, the custom aria-described by is preserved so user can set the # desired order. - if custom_aria_described_by_id := widget.attrs.get("aria-describedby"): + if custom_aria_described_by_id := attrs.get( + "aria-describedby" + ) or widget.attrs.get("aria-describedby"): attrs["aria-describedby"] = custom_aria_described_by_id elif self.field.help_text and self.id_for_label: attrs["aria-describedby"] = f"{self.id_for_label}_helptext"
Happy to submit a PR if this is accepted.
Change History (1)
by , 17 months ago
Attachment: | boundfield-as_widget-aria_describedby.patch added |
---|
Patch to fix the issue