Fixes #22531: Ensure Saved Filter selector has unique element ID (#22542)

This commit is contained in:
Jeremy Stretch 2026-06-25 12:04:56 -04:00 committed by GitHub
parent 742cd0b5b6
commit 9c73c7a8ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 27 additions and 8 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -22,7 +22,7 @@ function handleSavedFilterChange(event: Event): void {
export function initSavedFilterSelect(): void {
const divResults = document.getElementById('results');
if (isTruthy(divResults)) {
const savedFilterSelect = document.getElementById('id_filter_id');
const savedFilterSelect = document.getElementById('saved-filter-select');
if (isTruthy(savedFilterSelect)) {
savedFilterSelect.addEventListener('change', handleSavedFilterChange);
}

View File

@ -1,3 +1,4 @@
{% load form_helpers %}
{% load helpers %}
{% load i18n %}
@ -20,11 +21,11 @@
{% if filter_form %}
<div class="col-auto d-print-none">
<div class="input-group">
<label for="{{ filter_form.filter_id.id_for_label }}" class="input-group-text">
<label for="saved-filter-select" class="input-group-text">
<i class="mdi mdi-filter" title="{% trans "Saved filter" %}" aria-hidden="true"></i>
<span class="visually-hidden">{% trans "Saved filter" %}</span>
</label>
{{ filter_form.filter_id }}
{% render_field_with_aria filter_form.filter_id element_id="saved-filter-select" %}
</div>
</div>
{% endif %}

View File

@ -56,8 +56,14 @@ def widget_type(field):
@register.simple_tag
def render_field_with_aria(field, has_helptext=None):
"""Render a bound form field with aria-describedby/aria-invalid/aria-label wired up."""
def render_field_with_aria(field, has_helptext=None, element_id=None):
"""Render a bound form field with aria-describedby/aria-invalid/aria-label wired up.
Pass ``element_id`` to override the widget's HTML ``id``. This is needed when the same
field is rendered more than once on a page (e.g. the saved-filter selector, which appears
both in the list controls and the filter drawer), to keep element IDs unique so that label
associations resolve correctly for assistive technology.
"""
if has_helptext is None:
has_helptext = bool(field.help_text)
widget_attrs = field.field.widget.attrs
@ -67,6 +73,8 @@ def render_field_with_aria(field, has_helptext=None):
if has_helptext:
described_by.append(f'{field.auto_id}_helptext')
extra_attrs = {}
if element_id:
extra_attrs['id'] = element_id
if described_by:
# Merge with any aria-describedby already set on the widget so we
# append to (rather than clobber) descriptions defined elsewhere.

View File

@ -266,6 +266,16 @@ class RenderFieldWithAriaTestCase(TestCase):
self.assertIn('id_name_errors', html)
self.assertIn('id_name_helptext', html)
def test_element_id_overrides_widget_id(self):
class TestForm(forms.Form):
name = forms.CharField()
form = TestForm()
html = render_field_with_aria(form['name'], element_id='custom-id')
self.assertIn('id="custom-id"', html)
self.assertNotIn('id="id_name"', html)
@override_settings(DEBUG=True)
def test_missing_label_emits_debug_warning(self):
class TestForm(forms.Form):