Skip to content

Commit

Permalink
Merge pull request #6240 from freedomofpress/6238-semantic-form-errors
Browse files Browse the repository at this point in the history
adds `aria-{invalid,describedby}` annotations for WTForms validation errors
  • Loading branch information
legoktm authored Mar 2, 2022
2 parents f846e89 + d72e0dc commit 2236d3d
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 9 deletions.
1 change: 1 addition & 0 deletions securedrop/journalist_app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ def _handle_http_exception(
app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat
app.jinja_env.filters['html_datetime_format'] = \
template_filters.html_datetime_format
app.jinja_env.add_extension('jinja2.ext.do')

@app.before_first_request
def expire_blacklisted_tokens() -> None:
Expand Down
44 changes: 37 additions & 7 deletions securedrop/journalist_templates/admin_add_user.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,16 @@ <h1 id="add-user-heading">Add User</h1>
<div class="container flex-end">
<div>
<label for="username">{{ gettext('Username') }}</label>
{{ form.username() }}
{% set aria_attributes = {} %}
{% if form.username.errors %}
{% do aria_attributes.update({'aria-describedby': 'username-errors', 'aria-invalid': 'true'}) %}
{% endif %}
{{ form.username(**aria_attributes) }}
<span id="username-errors">
{% for error in form.username.errors %}
<span class="form-validation-error" role="alert">{{ error }}</span>
{% endfor %}
</span>
</div>
<div>
<ul id="username-notes" class="journalist-username__notes">
Expand All @@ -26,17 +32,29 @@ <h1 id="add-user-heading">Add User</h1>
<div class="container flex-end">
<div>
<label for="first_name">{{ gettext('First name') }}</label>
{{ form.first_name() }}
{% set aria_attributes = {} %}
{% if form.first_name.errors %}
{% do aria_attributes.update({'aria-describedby': 'first-name-errors', 'aria-invalid': 'true'}) %}
{% endif %}
{{ form.first_name(**aria_attributes) }}
<span id="first-name-errors">
{% for error in form.first_name.errors %}
<span class="form-validation-error" role="alert">{{ error }}</span>
{% endfor %}
</span>
</div>
<div>
<label for="last_name">{{ gettext('Last name') }}</label>
{{ form.last_name() }}
{% set aria_attributes = {} %}
{% if form.last_name.errors %}
{% do aria_attributes.update({'aria-describedby': 'last-name-errors', 'aria-invalid': 'true'}) %}
{% endif %}
{{ form.last_name(**aria_attributes) }}
<span id="last-name-errors">
{% for error in form.last_name.errors %}
<span class="form-validation-error" role="alert">{{ error }}</span>
{% endfor %}
</span>
</div>
<div>
<ul id="name-notes" class="journalist-username__notes">
Expand All @@ -46,20 +64,32 @@ <h1 id="add-user-heading">Add User</h1>
</div>
<p>{{ gettext("The user's password will be:") }} <mark class="password" id="password">{{ password }}</mark></p>
<div>
{{ form.is_admin(id="is-admin") }}
{% set aria_attributes = {} %}
{% if form.is_admin.errors %}
{% do aria_attributes.update({'aria-describedby': 'is-admin-errors', 'aria-invalid': 'true'}) %}
{% endif %}
{{ form.is_admin(id="is-admin", **aria_attributes) }}
<label for="is-admin">{{ gettext('Is Admin') }}</label>
<span id="is-admin-errors">
{% for error in form.is_admin.errors %}
<span class="form-validation-error" role="alert">{{ error }}</span>
{% endfor %}
</span>
</div>
<div>
{{ form.is_hotp(id="is-hotp") }}
{% set aria_attributes = {} %}
{% if form.is_hotp.errors or form.otp_secret.errors %}
{% do aria_attributes.update({'aria-describedby': 'otp-errors', 'aria-invalid': 'true'}) %}
{% endif %}
{{ form.is_hotp(id="is-hotp", **aria_attributes) }}
<label for="is-hotp">{{ gettext("Is using a YubiKey [HOTP]") }} </label>
<label for="otp_secret" class="visually-hidden">{{ gettext('HOTP Secret') }}</label>
{{ form.otp_secret(placeholder=gettext('HOTP Secret'), size="60") }}
{{ form.otp_secret(placeholder=gettext('HOTP Secret'), size="60", **aria_attributes) }}
<span id="otp-errors">
{% for error in form.is_hotp.errors + form.otp_secret.errors %}
<span class="form-validation-error" role="alert">{{ error }}</span><br>
<span class="form-validation-error" role="alert">{{ error }}</span>
{% endfor %}
</span>
</div>
<button type="submit" class="icon icon-plus" aria-label="{{ gettext('Add User') }}">
{{ gettext('ADD USER') }}
Expand Down
1 change: 1 addition & 0 deletions securedrop/source_app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def handle_csrf_error(e: CSRFError) -> werkzeug.Response:
app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat
app.jinja_env.filters['html_datetime_format'] = \
template_filters.html_datetime_format
app.jinja_env.add_extension('jinja2.ext.do')

for module in [main, info, api]:
app.register_blueprint(module.make_blueprint(config)) # type: ignore
Expand Down
4 changes: 2 additions & 2 deletions securedrop/source_templates/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ <h1>{{ gettext('Enter Codename') }}</h1>
<div class="center">
{% set aria_attributes = {'aria-label': gettext('Enter your codename'), 'aria-required': 'true'} %}
{% if form.codename.errors %}
{% set _dummy = aria_attributes.update({'aria-describedby': 'flashed login-with-existing-code-name-errors', 'aria-invalid': 'true'}) %}
{% do aria_attributes.update({'aria-describedby': 'flashed login-with-existing-code-name-errors', 'aria-invalid': 'true'}) %}
{% endif %}
{{ form.codename(id='login-with-existing-codename', class='codename-box', autocomplete='off', placeholder=gettext('Enter your codename'), autofocus=True, **aria_attributes) }}
{% if form.codename.errors %}
Expand All @@ -32,4 +32,4 @@ <h1>{{ gettext('Enter Codename') }}</h1>
</div>

</form>
{% endblock %}
{% endblock %}

0 comments on commit 2236d3d

Please sign in to comment.