diff --git a/gadjo/templates/gadjo/widget.html b/gadjo/templates/gadjo/widget.html index 6bde427..6495955 100644 --- a/gadjo/templates/gadjo/widget.html +++ b/gadjo/templates/gadjo/widget.html @@ -21,7 +21,7 @@ {% block widget-attrs %}{% endblock %}> {% block widget-hint %} {% if field.help_text %} -
{{ field.help_text|safe }}
+
{{ field.help_text|safe }}
{% endif %} {% endblock %} {% block widget-control %} @@ -29,7 +29,7 @@ {% endblock %} {% block widget-error %} {% if field.errors %} -

+

{% for error in field.errors %} {{ error }}{% if not forloop.last %}
{% endif %} {% endfor %} diff --git a/gadjo/templatetags/gadjo.py b/gadjo/templatetags/gadjo.py index f951ba7..d3e33e9 100644 --- a/gadjo/templatetags/gadjo.py +++ b/gadjo/templatetags/gadjo.py @@ -108,6 +108,14 @@ def with_template(form): templates = ['gadjo/widget.html'] if hasattr(widget, 'input_type'): templates.insert(0, 'gadjo/%s-widget.html' % widget.input_type) + aria_described_by = [] + if field.field.help_text: + aria_described_by.append(f'help_text_{field.id_for_label}') + if field.errors: + aria_described_by.append(f'error_{field.id_for_label}') + field.field.widget.attrs['aria-invalid'] = 'true' + if aria_described_by: + field.field.widget.attrs['aria-describedby'] = ' '.join(aria_described_by) fields_with_templates.append( ( field, diff --git a/tests/project/settings.py b/tests/project/settings.py index 93c3e96..8b970a5 100644 --- a/tests/project/settings.py +++ b/tests/project/settings.py @@ -28,6 +28,7 @@ INSTALLED_APPS = [ "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sites", + "gadjo", ] STATIC_URL = "/static/" SITE_ID = 1 diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py index e892d39..9057188 100644 --- a/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -1,8 +1,10 @@ import html import urllib +from django import forms from django.template import Context, Template from django.test.client import RequestFactory +from pyquery import PyQuery def test_start_timestamp(): @@ -32,3 +34,40 @@ def test_querystring(): assert urllib.parse.parse_qs(urllib.parse.urlparse(html.unescape(t.render(ctx))).query) == { 'name': ['Ayers'] } + + +def test_with_template(): + class ExampleForm(forms.Form): + text = forms.CharField(label='Text', max_length=50) + + request = RequestFactory().get('/') + t = Template('{{ form|with_template }}') + ctx = Context({'request': request, 'form': ExampleForm()}) + rendered = t.render(ctx) + assert PyQuery(rendered).find('input[type=text]') + assert not PyQuery(rendered).find('input[type=text]').attr['aria-invalid'] + + ctx = Context({'request': request, 'form': ExampleForm(data=request.GET)}) + rendered = t.render(ctx) + assert ( + PyQuery(rendered).find('input[type=text][aria-describedby]').attr['aria-describedby'] + == 'error_id_text' + ) + assert PyQuery(rendered).find('input[type=text]').attr['aria-invalid'] + + class ExampleForm(forms.Form): + text = forms.CharField(label='Text', max_length=50, help_text='Help text') + + ctx = Context({'request': request, 'form': ExampleForm()}) + rendered = t.render(ctx) + assert ( + PyQuery(rendered).find('input[type=text][aria-describedby]').attr['aria-describedby'] + == 'help_text_id_text' + ) + + ctx = Context({'request': request, 'form': ExampleForm(data=request.GET)}) + rendered = t.render(ctx) + assert ( + PyQuery(rendered).find('input[type=text][aria-describedby]').attr['aria-describedby'] + == 'help_text_id_text error_id_text' + ) diff --git a/tox.ini b/tox.ini index 5ce7e44..4b63f84 100644 --- a/tox.ini +++ b/tox.ini @@ -16,6 +16,7 @@ deps = WebTest psycopg2-binary psycopg2 + pyquery codestyle: pre-commit coverage: pytest-cov pylint: pylint