1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00

fix(templates): icon-only buttons, selects and delete pages have accessible names

An accessibility scan (axe-core, WCAG 2 A/AA) of every page found 87
elements without a name. Icon-only buttons and links (the navbar's
administration menu on every page, the add/create buttons, the flavor
delete and back buttons) get a title, which is also their tooltip; the
labels of the language, architecture, chipset and filter selects point
at them; the memory bar is named. The shared delete confirmation page
had an empty <title> and heading, as no view passes "title": it says
"Delete <object>". The power-off form's aria-label was misspelled.
This commit is contained in:
catborise 2026-10-19 16:07:11 +03:00
parent ea7030d2a1
commit 64057f8d05
21 changed files with 28 additions and 26 deletions

View file

@ -6,7 +6,7 @@
{% block content %}
<div class="row">
<div class="col-lg-12">
<a href="{% url 'admin:group_create' %}" class="btn btn-success btn-header float-end">
<a href="{% url 'admin:group_create' %}" class="btn btn-success btn-header float-end" title="{% trans "Create Group" %}">
{% bs_icon 'plus-circle-fill' %}
</a>
{% include 'search_block.html' %}

View file

@ -9,7 +9,7 @@
{% block page_heading %}{{ title }}{% endblock page_heading %}
{% block page_heading_extra %}
<a href="{% url 'admin:user_create' %}" class="btn btn-success btn-header float-end">
<a href="{% url 'admin:user_create' %}" class="btn btn-success btn-header float-end" title="{% trans "Create User" %}">
{% bs_icon 'plus-circle-fill' %}
</a>
{% include 'search_block.html' %}

View file

@ -39,6 +39,8 @@ class AdminTestCase(TestCase):
response = self.client.get(reverse("admin:group_delete", args=[group_id]))
self.assertEqual(response.status_code, 200)
# the shared confirmation page names what it deletes
self.assertContains(response, "<title>Delete Updated Group Test</title>", html=True)
response = self.client.post(reverse("admin:group_delete", args=[group_id]))
self.assertRedirects(response, reverse("admin:group_list"))

View file

@ -12,9 +12,9 @@
<form action="{% url 'set_language' %}" method="post" style="display:inline" aria-label="Edit language.name_local settings form">{% csrf_token %}
<div class="row mb-1">
<input name="next" type="hidden" value="{{ redirect_to }}">
<label class="col-sm-3 col-form-label">{% trans "Language" %}</label>
<label class="col-sm-3 col-form-label" for="language">{% trans "Language" %}</label>
<div class="col-sm-6">
<select name="language" class="form-control" onchange="this.form.submit()">
<select id="language" name="language" class="form-control" onchange="this.form.submit()">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}

View file

@ -11,7 +11,7 @@
{% block page_heading_extra %}
<a href="{% url 'instances:create_instance_select_type' compute.id %}"
class="btn btn-success btn-header float-end">
class="btn btn-success btn-header float-end" title="{% trans "New Instance" %}">
{% bs_icon 'plus-circle-fill' %}
</a>
{% if instances %}

View file

@ -42,7 +42,7 @@
<td class="d-none d-sm-table-cell text-right">{{ compute.ram_size|filesizeformat }}</td>
<td>
<div class="progress">
<div class="progress-bar bg-success" role="progressbar" style="width: {{ compute.ram_usage }}%"
<div class="progress-bar bg-success" role="progressbar" aria-label="{% trans "Mem Usage" %}" style="width: {{ compute.ram_usage }}%"
aria-valuenow="{{ compute.ram_usage }}" aria-valuemin="0" aria-valuemax="100">{{ compute.ram_usage }}%
</div>
</div>

View file

@ -2,7 +2,7 @@
{% load django_bootstrap5 %}
{% load bootstrap_icons %}
{% if request.user.is_superuser %}
<button href="#addFlavor" type="button" class="btn btn-success btn-header float-end" data-bs-toggle="modal">
<button href="#addFlavor" type="button" class="btn btn-success btn-header float-end" title="{% trans "Add New Flavor" %}" data-bs-toggle="modal">
<span aria-hidden="true">{% bs_icon 'plus'%} </span>
</button>

View file

@ -1,6 +1,6 @@
{% load i18n %}
{% load bootstrap_icons %}
<a href="#AddInstance" type="button" class="btn btn-success btn-header float-end" data-bs-toggle="modal">
<a href="#AddInstance" type="button" class="btn btn-success btn-header float-end" title="{% trans "New Instance" %}" data-bs-toggle="modal">
<span aria-hidden="true">{% bs_icon 'plus-circle-fill'%} </span>
</a>

View file

@ -47,7 +47,7 @@
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="select_architecture">
<form method="post" role="form" aria-label="Select instance architecture form">{% csrf_token %}
<div class="row">
<label class="col-sm-3 col-form-label">{% trans "Architecture" %}</label>
<label class="col-sm-3 col-form-label" for="select_archs">{% trans "Architecture" %}</label>
<div class="col-sm-6">
<select class="form-select" id="select_archs" name="archs" onchange="get_machine_types({{ compute_id }}, value);">
{% for hpv in hypervisors %}
@ -57,7 +57,7 @@
</div>
</div>
<div class="row">
<label class="col-sm-3 col-form-label">{% trans "Chipset" %}</label>
<label class="col-sm-3 col-form-label" for="select_chipset">{% trans "Chipset" %}</label>
<div class="col-sm-6">
<select class="form-select" id="select_chipset" name="chipset">
<!-- fill with script -->

View file

@ -41,7 +41,7 @@
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist" aria-label="Instance create method">
<li class="nav-item" role="presentation">
<button class="nav-link float-end" data-bs-toggle="tab" data-bs-target="#" type="button" role="tab" aria-controls="go_compute" onclick="goto_compute()">
<button class="nav-link float-end" data-bs-toggle="tab" data-bs-target="#" type="button" role="tab" aria-controls="go_compute" title="{% trans "Back" %}" onclick="goto_compute()">
<span>{% bs_icon 'arrow-left'%} </span>
</button>
</li>
@ -299,14 +299,14 @@
</div>
</div>
</div>
<a data-bs-toggle="modal" href="#addVMflavor{{ forloop.counter }}" class="btn btn-sm btn-secondary">
<a data-bs-toggle="modal" href="#addVMflavor{{ forloop.counter }}" class="btn btn-sm btn-secondary" title="{% trans "Create Virtual Machine" %}">
<span >{% bs_icon 'plus'%} </span>
</a>
</td>
<td style="width:5px;">
<form action="{% url 'instances:flavor_delete' flavor.id %}" method="post" role="form" aria-label="Delete flavor form">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-secondary" name="delete_flavor" onclick="return confirm('{% trans "Are you sure?" %}')">
<button type="submit" class="btn btn-sm btn-secondary" name="delete_flavor" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
{% bs_icon 'trash' %}
</button>
</form>

View file

@ -63,7 +63,7 @@
{% if instance.status == 1 or instance.status == 2 %}
<div class="tab-pane tab-pane-bordered active" id="poweroff" role="tabpanel" aria-labelledby="poweroff-tab">
<p>{% trans "This action sends an ACPI shutdown signal to the instance." %}</p>
<form action="{% url 'instances:poweroff' instance.id %}" method="post" role="form" aria-label0="Power off instance form">
<form action="{% url 'instances:poweroff' instance.id %}" method="post" role="form" aria-label="Power off instance form">
{% csrf_token %}
<input type="submit" name="poweroff" class="btn btn-lg btn-success float-end" value="{% trans "Power Off" %}">
<div class="clearfix"></div>

View file

@ -1,7 +1,7 @@
{% load i18n %}
{% load bootstrap_icons %}
{% if request.user.is_superuser %}
<a href="#AddInterface" type="button" class="btn btn-success float-end" data-bs-toggle="modal">
<a href="#AddInterface" type="button" class="btn btn-success float-end" title="{% trans "Create New Interface" %}" data-bs-toggle="modal">
<span aria-hidden="true">{% bs_icon 'plus' %} </span>
</a>

View file

@ -1,7 +1,7 @@
{% load i18n %}
{% load bootstrap_icons %}
{% if request.user.is_superuser %}
<a href="#AddNetPool" type="button" class="btn btn-success float-end" data-bs-toggle="modal">
<a href="#AddNetPool" type="button" class="btn btn-success float-end" title="{% trans "Add New Network" %}" data-bs-toggle="modal">
<span aria-hidden="true">{% bs_icon 'plus' %} </span>
</a>

View file

@ -1,7 +1,7 @@
{% load i18n %}
{% load bootstrap_icons %}
{% if request.user.is_superuser %}
<button class="btn btn-success float-end" type="button" data-bs-toggle="modal" data-bs-target="#AddNWFilterRule">
<button class="btn btn-success float-end" type="button" title="{% trans "Add New NWFilter Rule" %}" data-bs-toggle="modal" data-bs-target="#AddNWFilterRule">
<span aria-hidden="true">{% bs_icon 'plus' %} </span>
</button>

View file

@ -1,7 +1,7 @@
{% load i18n %}
{% load bootstrap_icons %}
{% if request.user.is_superuser %}
<button type="button" class="btn btn-success btn-header float-end" data-bs-toggle="modal" data-bs-target="#AddNWFilter">
<button type="button" class="btn btn-success btn-header float-end" title="{% trans "Create New NWFilter" %}" data-bs-toggle="modal" data-bs-target="#AddNWFilter">
<span aria-hidden="true">{% bs_icon 'plus' %} </span>
</button>

View file

@ -69,14 +69,14 @@
<h5 class="page-header mr-auto">{% trans "Filter References" %}
<form class="float-end" method="post" role="form" aria-label="Add NWFilter form">{% csrf_token %}
<div class="input-group">
<span class="input-group-text">{% trans "Filter" %}</span>
<label class="input-group-text" for="nwfilter_select">{% trans "Filter" %}</label>
<select id="nwfilter_select" name="nwfilters_select" class="form-select">
<option value="" selected>{% trans 'None' %}</option>
{% for nwf in nwfilters_all %}
<option value="{{ nwf.name }}">{{ nwf.name }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-success float-end" name="add_nwfilter_ref">
<button type="submit" class="btn btn-success float-end" name="add_nwfilter_ref" title="{% trans "Add" %}">
<span aria-hidden="true">{% bs_icon 'plus-circle-fill' %} </span>
</button>
</div>

View file

@ -1,7 +1,7 @@
{% load i18n %}
{% load bootstrap_icons %}
{% if request.user.is_superuser %}
<a href="#AddStgPool" type="button" class="btn btn-success float-end" data-bs-toggle="modal">
<a href="#AddStgPool" type="button" class="btn btn-success float-end" title="{% trans "Create Storage Pool" %}" data-bs-toggle="modal">
<span aria-hidden="true">{% bs_icon 'plus-circle-fill' %} </span>
</a>

View file

@ -41,7 +41,7 @@
</div> <!-- /.modal-dialog -->
</div> <!-- /.modal -->
{% else %}
<a href="#AddImage" class="btn btn-success float-end" data-bs-toggle="modal">
<a href="#AddImage" class="btn btn-success float-end" title="{% trans "Add New Volume" %}" data-bs-toggle="modal">
<span aria-hidden="true">{% bs_icon 'plus-circle-fill' %} </span>
</a>

View file

@ -3,9 +3,9 @@
{% load bootstrap_icons %}
{% load i18n %}
{% block title %}{{ title }}{% endblock %}
{% block title %}{% trans "Delete" %} {{ object }}{% endblock %}
{% block page_heading %}{{ title }}{% endblock page_heading %}
{% block page_heading %}{% trans "Delete" %} {{ object }}{% endblock page_heading %}
{% block content %}
<div class="row">

View file

@ -25,7 +25,7 @@
</li>
{% if request.user.is_superuser %}
<li class="nav-item dropdown {% app_active request 'admin' %}">
<a class="nav-link" id="administration" href="#" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<a class="nav-link" id="administration" href="#" title="{% trans "Administration" %}" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% bs_icon 'wrench-adjustable' %}
</a>
<div class="dropdown-menu" aria-labelledby="administration">

View file

@ -1,7 +1,7 @@
{% load i18n %}
{% load bootstrap_icons %}
{% if request.user.is_superuser %}
<a href="#AddSecret" type="button" class="btn btn-success float-end" data-bs-toggle="modal">
<a href="#AddSecret" type="button" class="btn btn-success float-end" title="{% trans "Create New Secret" %}" data-bs-toggle="modal">
<span aria-hidden="true">{% bs_icon 'plus-circle-fill' %} </span>
</a>