a11y: add aria attributes for help and errors (#75681)
gitea/gadjo/pipeline/head This commit looks good Details

This commit is contained in:
Frédéric Péters 2023-05-07 16:42:07 +02:00
parent 246b6b3c5c
commit 75f38ce67a
5 changed files with 51 additions and 2 deletions

View File

@ -21,7 +21,7 @@
{% block widget-attrs %}{% endblock %}> {% block widget-attrs %}{% endblock %}>
{% block widget-hint %} {% block widget-hint %}
{% if field.help_text %} {% if field.help_text %}
<div class="hint">{{ field.help_text|safe }}</div> <div class="hint" id="help_text_{{field.id_for_label}}">{{ field.help_text|safe }}</div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block widget-control %} {% block widget-control %}
@ -29,7 +29,7 @@
{% endblock %} {% endblock %}
{% block widget-error %} {% block widget-error %}
{% if field.errors %} {% if field.errors %}
<div class="error"><p> <div class="error" id="error_{{field.id_for_label}}"><p>
{% for error in field.errors %} {% for error in field.errors %}
{{ error }}{% if not forloop.last %}<br>{% endif %} {{ error }}{% if not forloop.last %}<br>{% endif %}
{% endfor %} {% endfor %}

View File

@ -108,6 +108,14 @@ def with_template(form):
templates = ['gadjo/widget.html'] templates = ['gadjo/widget.html']
if hasattr(widget, 'input_type'): if hasattr(widget, 'input_type'):
templates.insert(0, 'gadjo/%s-widget.html' % 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( fields_with_templates.append(
( (
field, field,

View File

@ -28,6 +28,7 @@ INSTALLED_APPS = [
"django.contrib.auth", "django.contrib.auth",
"django.contrib.contenttypes", "django.contrib.contenttypes",
"django.contrib.sites", "django.contrib.sites",
"gadjo",
] ]
STATIC_URL = "/static/" STATIC_URL = "/static/"
SITE_ID = 1 SITE_ID = 1

View File

@ -1,8 +1,10 @@
import html import html
import urllib import urllib
from django import forms
from django.template import Context, Template from django.template import Context, Template
from django.test.client import RequestFactory from django.test.client import RequestFactory
from pyquery import PyQuery
def test_start_timestamp(): 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) == { assert urllib.parse.parse_qs(urllib.parse.urlparse(html.unescape(t.render(ctx))).query) == {
'name': ['Ayers'] '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'
)

View File

@ -16,6 +16,7 @@ deps =
WebTest WebTest
psycopg2-binary psycopg2-binary
psycopg2 psycopg2
pyquery
codestyle: pre-commit codestyle: pre-commit
coverage: pytest-cov coverage: pytest-cov
pylint: pylint pylint: pylint