public inbox for [email protected]
help / color / mirror / Atom feedFrom: Aditya Toshniwal <[email protected]>
To: pgadmin-hackers <[email protected]>
Subject: [pgAdmin][RM4772][Accessibility] Provide aria-label to an invisible label where a visible label cannot be used
Date: Tue, 3 Dec 2019 11:38:57 +0530
Message-ID: <CAM9w-_kbux3pL4JT=8XA20BLk6=L3qXWNP8d4EraeA9mdAA3vQ@mail.gmail.com> (raw)
Hi Hackers,
Attached is the patch to add label and aria-label wherever applicable. The
patch also fixes few other accessibility related errors raised by "WAVE"
extension of Chrome.
Kindly review.
--
Thanks and Regards,
Aditya Toshniwal
Sr. Software Engineer | EnterpriseDB India | Pune
"Don't Complain about Heat, Plant a TREE"
Attachments:
[application/octet-stream] RM4772.patch (35.6K, 3-RM4772.patch)
download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/servers/templates/servers/password.html b/web/pgadmin/browser/server_groups/servers/templates/servers/password.html
index 7086c383e..3bae7c1da 100644
--- a/web/pgadmin/browser/server_groups/servers/templates/servers/password.html
+++ b/web/pgadmin/browser/server_groups/servers/templates/servers/password.html
@@ -1,9 +1,9 @@
<form name="frmPassword" id="frmPassword" style="height: 100%; width: 100%" onsubmit="return false;">
<div>
- <div><b>{{ _('Please enter the password for the user \'{0}\' to connect the server - "{1}"').format(username,
- server_label) }}</b></div>
+ <div><label class="font-weight-bold" for="password">{{ _('Please enter the password for the user \'{0}\' to connect the server - "{1}"').format(username,
+ server_label) }}</label></div>
<div class="input-group row py-2">
- <label for="password" class="col-sm-2 col-form-label">Password</label>
+ <label class="col-sm-2 col-form-label" aria-hidden="true">Password</label>
<div class="col-sm-10">
<input id="password" class="form-control" name="password" type="password">
</div>
@@ -24,7 +24,7 @@
<div class="pr-2">
<i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i>
</div>
- <div class="alert-text">{{ errmsg }}</div>
+ <div class="alert-text" role="status">{{ errmsg }}</div>
</div>
</div>
</div>
diff --git a/web/pgadmin/browser/server_groups/servers/templates/servers/tunnel_password.html b/web/pgadmin/browser/server_groups/servers/templates/servers/tunnel_password.html
index 1ad1a1138..ed7df8664 100644
--- a/web/pgadmin/browser/server_groups/servers/templates/servers/tunnel_password.html
+++ b/web/pgadmin/browser/server_groups/servers/templates/servers/tunnel_password.html
@@ -2,9 +2,9 @@
<div class="m-1">
{% if prompt_tunnel_password %}
{% if tunnel_identity_file %}
- <div><b>{{ _('SSH Tunnel password for the identity file \'{0}\' to connect the server "{1}"').format(tunnel_identity_file, tunnel_host) }}</b></div>
+ <div><label class="font-weight-bold" for="tunnel_password">{{ _('SSH Tunnel password for the identity file \'{0}\' to connect the server "{1}"').format(tunnel_identity_file, tunnel_host) }}</label></div>
{% else %}
- <div><b>{{ _('SSH Tunnel password for the user \'{0}\' to connect the server "{1}"').format(tunnel_username, tunnel_host) }}</b></div>
+ <div><label class="font-weight-bold" for="tunnel_password">{{ _('SSH Tunnel password for the user \'{0}\' to connect the server "{1}"').format(tunnel_username, tunnel_host) }}</label></div>
{% endif %}
<div class="input-group py-2">
<div class="w-100">
@@ -21,7 +21,7 @@
</div>
{% endif %}
{% if prompt_password %}
- <div><b>{{ _('Database server password for the user \'{0}\' to connect the server "{1}"').format(username, server_label) }}</b></div>
+ <div><label class="font-weight-bold" for="password">{{ _('Database server password for the user \'{0}\' to connect the server "{1}"').format(username, server_label) }}</label></div>
<div class="input-group py-2">
<div class="w-100">
<input id="password" class="form-control" name="password" type="password">
@@ -44,7 +44,7 @@
<div class="pr-2">
<i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i>
</div>
- <div class="alert-text">{{ errmsg }}</div>
+ <div class="alert-text" role="status">{{ errmsg }}</div>
</div>
</div>
</div>
diff --git a/web/pgadmin/browser/static/js/browser.js b/web/pgadmin/browser/static/js/browser.js
index 205f2c122..1dc9c24e1 100644
--- a/web/pgadmin/browser/static/js/browser.js
+++ b/web/pgadmin/browser/static/js/browser.js
@@ -226,7 +226,7 @@ define('pgadmin.browser', [
width: 500,
isCloseable: false,
isPrivate: true,
- content: '<div class="sql_textarea"><textarea id="sql-textarea" name="sql-textarea"></textarea></div>',
+ content: '<div class="sql_textarea"><textarea id="sql-textarea" name="sql-textarea" title="'+gettext('SQL Code')+'"></textarea></div>',
}),
// Dependencies of the object
'dependencies': new pgAdmin.Browser.Panel({
@@ -357,7 +357,6 @@ define('pgadmin.browser', [
// enabled/disabled.
_.each([
{m: 'file', id: '#mnu_file'},
- {m: 'edit', id: '#mnu_edit'},
{m: 'management', id: '#mnu_management'},
{m: 'tools', id: '#mnu_tools'},
{m: 'help', id:'#mnu_help'}], function(o) {
@@ -827,7 +826,6 @@ define('pgadmin.browser', [
_.each([
{menu: 'file', id: '#mnu_file'},
- {menu: 'edit', id: '#mnu_edit'},
{menu: 'management', id: '#mnu_management'},
{menu: 'tools', id: '#mnu_tools'},
{menu: 'help', id:'#mnu_help'}],
diff --git a/web/pgadmin/browser/static/js/menu.js b/web/pgadmin/browser/static/js/menu.js
index de95490bc..3f3dba411 100644
--- a/web/pgadmin/browser/static/js/menu.js
+++ b/web/pgadmin/browser/static/js/menu.js
@@ -72,6 +72,7 @@ define([
'href': this.url,
'target': this.target,
'data-toggle': 'pg-menu',
+ 'role': 'menuitem',
}).data('pgMenu', {
module: this.module || pgAdmin.Browser,
cb: this.callback,
diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js
index fbce346f0..a1493cd20 100644
--- a/web/pgadmin/browser/static/js/node.js
+++ b/web/pgadmin/browser/static/js/node.js
@@ -1129,7 +1129,8 @@ define('pgadmin.browser.node', [
tmpl = _.template([
'<button tabindex="0" type="<%= type %>" ',
'class="btn <%=extraClasses.join(\' \')%>"',
- '<% if (disabled) { %> disabled="disabled"<% } %> title="<%-tooltip%>">',
+ '<% if (disabled) { %> disabled="disabled"<% } %> title="<%-tooltip%>"',
+ '<% if (label != "") {} else { %> aria-label="<%-tooltip%>"<% } %> >',
'<span class="<%= icon %>"></span><% if (label != "") { %> <%-label%><% } %></button>',
].join(' '));
if (location == 'header') {
diff --git a/web/pgadmin/browser/templates/browser/index.html b/web/pgadmin/browser/templates/browser/index.html
index 76e222f5e..b2e0a8151 100644
--- a/web/pgadmin/browser/templates/browser/index.html
+++ b/web/pgadmin/browser/templates/browser/index.html
@@ -100,8 +100,8 @@ window.onload = function(e){
</div>
<nav class="navbar fixed-top navbar-expand-lg navbar-dark pg-navbar">
<a class="navbar-brand pgadmin_header_logo" onClick="return false;" href="{{ '#' }}"
- title="{{ config.APP_NAME }} {{ _('logo') }}">
- <i class="app-icon {{ config.APP_ICON }}"></i>
+ title="{{ config.APP_NAME }} {{ _('logo') }}" aria-label="{ config.APP_NAME }} {{ _('logo') }}">
+ <i class="app-icon {{ config.APP_ICON }}" aria-hidden="true"></i>
</a>
<button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#navbar-menu" aria-controls="navbar-menu">
<span class="sr-only">{{ _('Toggle navigation') }}</span>
@@ -115,11 +115,6 @@ window.onload = function(e){
_('File') }} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu"></ul>
</li>
- <li id="mnu_edit" class="nav-item active dropdown d-none">
- <a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
- _('Edit') }} <span class="caret"></span></a>
- <ul class="dropdown-menu" role="menu"></ul>
- </li>
<li id="mnu_obj" class="nav-item active dropdown ">
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
_('Object') }} <span class="caret"></span></a>
diff --git a/web/pgadmin/dashboard/static/js/dashboard.js b/web/pgadmin/dashboard/static/js/dashboard.js
index 6e466052a..c412a5c68 100644
--- a/web/pgadmin/dashboard/static/js/dashboard.js
+++ b/web/pgadmin/dashboard/static/js/dashboard.js
@@ -43,13 +43,13 @@ define('pgadmin.dashboard', [
this.$el.html(
'<i class=\'fa fa-stop\' data-toggle=\'tooltip\' ' +
'title=\'' + gettext('Cancel the active query') +
- '\'></i>'
+ '\' aria-label=\''+ gettext('Cancel the active query') +'\'></i>'
);
} else {
this.$el.html(
'<i class=\'fa fa-times-circle text-danger\' data-toggle=\'tooltip\' ' +
'title=\'' + gettext('Terminate the session') +
- '\'></i>'
+ '\' aria-label=\''+ gettext('Terminate the session') +'\'></i>'
);
}
this.$el.attr('tabindex', 0);
@@ -147,7 +147,7 @@ define('pgadmin.dashboard', [
this.$el.html(
'<i class=\'fa fa-caret-right\' data-toggle=\'tooltip\' ' +
'title=\'' + gettext('View the active session details') +
- '\'></i>'
+ '\' aria-label=\''+ gettext('View the active session details') +'\'></i>'
);
this.delegateEvents();
if (this.grabFocus)
diff --git a/web/pgadmin/dashboard/templates/dashboard/database_dashboard.html b/web/pgadmin/dashboard/templates/dashboard/database_dashboard.html
index d27a43a08..96aa90f06 100644
--- a/web/pgadmin/dashboard/templates/dashboard/database_dashboard.html
+++ b/web/pgadmin/dashboard/templates/dashboard/database_dashboard.html
@@ -81,12 +81,12 @@
<div class="navtab-inline-controls">
<div class="input-group">
<div class="input-group-prepend">
- <span class="input-group-text fa fa-search" id="labelSearch"></span>
+ <span class="input-group-text fa fa-search" id="labelSearch" aria-label="{{ _('Search') }}"></span>
</div>
- <input type="search" class="form-control" id="txtGridSearch" placeholder="Search" aria-label="Search" aria-describedby="labelSearch">
+ <input type="search" class="form-control" id="txtGridSearch" placeholder="{{ _('Search') }}" aria-describedby="labelSearch" aria-labelledby="labelSearch">
</div>
- <button id="btn_refresh" type="button" class="btn btn-secondary btn-navtab-inline" title="{{ _('Refresh') }}">
- <span class="fa fa-refresh"></span>
+ <button id="btn_refresh" type="button" class="btn btn-secondary btn-navtab-inline" title="{{ _('Refresh') }}" aria-label="{{ _('Refresh') }}">
+ <span class="fa fa-refresh" aria-hidden="true"></span>
</button>
</div>
</div>
diff --git a/web/pgadmin/dashboard/templates/dashboard/server_dashboard.html b/web/pgadmin/dashboard/templates/dashboard/server_dashboard.html
index a7fe75a68..e1c68b2f7 100644
--- a/web/pgadmin/dashboard/templates/dashboard/server_dashboard.html
+++ b/web/pgadmin/dashboard/templates/dashboard/server_dashboard.html
@@ -89,8 +89,8 @@
</div>
<input type="search" class="form-control" id="txtGridSearch" placeholder="Search" aria-label="Search" aria-describedby="labelSearch">
</div>
- <button id="btn_refresh" type="button" class="btn btn-secondary btn-navtab-inline" title="{{ _('Refresh') }}">
- <span class="fa fa-refresh"></span>
+ <button id="btn_refresh" type="button" class="btn btn-secondary btn-navtab-inline" title="{{ _('Refresh') }}" aria-label="{{ _('Refresh') }}">
+ <span class="fa fa-refresh" aria-hidden="true"></span>
</button>
</div>
</div>
diff --git a/web/pgadmin/dashboard/templates/dashboard/welcome_dashboard.html b/web/pgadmin/dashboard/templates/dashboard/welcome_dashboard.html
index 0c9a82749..fc798ab19 100644
--- a/web/pgadmin/dashboard/templates/dashboard/welcome_dashboard.html
+++ b/web/pgadmin/dashboard/templates/dashboard/welcome_dashboard.html
@@ -5,7 +5,7 @@
<div class="card">
<div class="card-header">{{ _('Welcome') }}</div>
<div class="card-body p-2">
- <div class="welcome-logo">
+ <div class="welcome-logo" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 130">
<defs>
<style>.cls-1{stroke:#000;stroke-width:10.19px;}.cls-2{fill:#336791;}.cls-3,.cls-4,.cls-9{fill:none;}.cls-3,.cls-4,.cls-5,.cls-6{stroke:#fff;}.cls-3,.cls-4{stroke-linecap:round;stroke-width:3.4px;}.cls-3{stroke-linejoin:round;}.cls-4{stroke-linejoin:bevel;}.cls-5,.cls-6{fill:#fff;}.cls-5{stroke-width:1.13px;}.cls-6{stroke-width:0.57px;}.cls-7{fill:#2775b6;}.cls-8{fill:#333;}.cls-9{stroke:#333;stroke-width:3px;}</style>
diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js
index 8fe989126..b620ab73c 100644
--- a/web/pgadmin/static/js/backform.pgadmin.js
+++ b/web/pgadmin/static/js/backform.pgadmin.js
@@ -173,11 +173,9 @@ define([
},
template: _.template([
- '<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+ '<label class="<%=Backform.controlLabelClassName%>" for="<%=cId%>"><%=label%></label>',
'<div class="<%=Backform.controlsClassName%>">',
- ' <span class="<%=Backform.controlClassName%> uneditable-input" <%=disabled ? "disabled readonly" : ""%>>',
- ' <%-value%>',
- ' </span>',
+ ' <input class="<%=Backform.controlClassName%> uneditable-input" <%=disabled ? "disabled readonly" : ""%> id="<%=cId%>" value="<%-value%>" />',
' <% if (helpMessage && helpMessage.length) { %>',
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
' <% } %>',
@@ -239,6 +237,7 @@ define([
required: evalF(data.required, data, this.model),
});
+ data.cId = data.cId || _.uniqueId('pgC_');
// Clean up first
this.$el.removeClass(Backform.hiddenClassName);
@@ -258,6 +257,15 @@ define([
*/
_.extend(
Backform.InputControl.prototype, {
+ template: _.template([
+ '<label class="<%=Backform.controlLabelClassName%>" for="<%=cId%>"><%=label%></label>',
+ '<div class="<%=Backform.controlContainerClassName%>">',
+ ' <input type="<%=type%>" id="<%=cId%>" class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>" maxlength="<%=maxlength%>" value="<%-value%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
+ ' <% if (helpMessage && helpMessage.length) { %>',
+ ' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
+ ' <% } %>',
+ '</div>',
+ ].join('\n')),
events: {
'change input': 'onChange',
'blur input': 'onChange',
@@ -300,9 +308,9 @@ define([
'focus textarea': 'clearInvalid',
},
template: _.template([
- '<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+ '<label class="<%=Backform.controlLabelClassName%>" for="<%=cId%>"><%=label%></label>',
'<div class="<%=Backform.controlsClassName%>">',
- ' <textarea ',
+ ' <textarea id="<%=cId%>"',
' class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>"',
' <% if (maxlength) { %>',
' maxlength="<%=maxlength%>"',
@@ -365,6 +373,7 @@ define([
}
}
+ data.cId = data.cId || _.uniqueId('pgC_');
// Clean up first
this.$el.removeClass(Backform.hiddenClassName);
@@ -376,18 +385,31 @@ define([
return this;
};
+
+ Backform.SelectControl.prototype.template = _.template([
+ '<label class="<%=Backform.controlLabelClassName%>" for="<%=cId%>"><%=label%></label>',
+ '<div class="<%=Backform.controlContainerClassName%>">',
+ ' <select id="<%=cId%>" class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> >',
+ ' <% for (var i=0; i < options.length; i++) { %>',
+ ' <% var option = options[i]; %>',
+ ' <option value="<%-formatter.fromRaw(option.value)%>" <%=option.value === rawValue ? "selected=\'selected\'" : ""%> <%=option.disabled ? "disabled=\'disabled\'" : ""%>><%-option.label%></option>',
+ ' <% } %>',
+ ' </select>',
+ '</div>',
+ ].join('\n'));
+
_.extend(Backform.SelectControl.prototype.defaults, {
helpMessage: null,
});
Backform.ReadonlyOptionControl = Backform.SelectControl.extend({
template: _.template([
- '<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+ '<label class="<%=Backform.controlLabelClassName%>" for="<%=cId%>"><%=label%></label>',
'<div class="<%=Backform.controlsClassName%>">',
'<% for (var i=0; i < options.length; i++) { %>',
' <% var option = options[i]; %>',
' <% if (option.value === rawValue) { %>',
- ' <span class="<%=Backform.controlClassName%> uneditable-input" disabled readonly><%-option.label%></span>',
+ ' <input id="<%=cId%>" class="<%=Backform.controlClassName%> uneditable-input" disabled readonly value="<%-option.label%>"></span>',
' <% } %>',
'<% } %>',
'<% if (helpMessage && helpMessage.length) { %>',
@@ -529,8 +551,9 @@ define([
},
template: _.template([
'<label class="<%=controlLabelClassName%>"><%=label%></label>',
+ '<label class="sr-value sr-only" for="<%=cId%>"></label>',
'<div class="<%=controlsClassName%> <%=extraClasses.join(\' \')%>">',
- ' <input tabindex="-1" type="checkbox" data-style="quick" data-toggle="toggle"',
+ ' <input tabindex="-1" type="checkbox" aria-hidden="true" aria-label="Toggle button" data-style="quick" data-toggle="toggle"',
' data-size="<%=options.size%>" data-height="<%=options.height%>" ',
' data-on="<%=options.onText%>" data-off="<%=options.offText%>" ',
' data-onstyle="<%=options.onColor%>" data-offstyle="<%=options.offColor%>" data-width="<%=options.width%>" ',
@@ -550,10 +573,29 @@ define([
'change input': 'onChange',
'keyup': 'toggleSwitch',
},
+ setSrValue: function() {
+ let {onText, offText} = _.defaults(this.field.get('options'), this.defaults.options);
+ let label = this.field.get('label');
+
+ if(this.$el.find('.toggle.btn').hasClass('off')) {
+ this.$el.find('.sr-value').text(`
+ ${label}. ${offText}. ${gettext('Toggle button')}
+ `);
+ } else {
+ this.$el.find('.sr-value').text(`
+ ${label}. ${onText}. ${gettext('Toggle button')}
+ `);
+ }
+ },
+ onChange: function() {
+ Backform.InputControl.prototype.onChange.apply(this, arguments);
+ this.setSrValue();
+ },
toggleSwitch: function(e) {
if (e.keyCode == 32) {
this.$el.find('input[type=checkbox]').bootstrapToggle('toggle');
e.preventDefault();
+ this.setSrValue();
}
},
render: function() {
@@ -580,6 +622,7 @@ define([
required: evalF(data.required, field, this.model),
});
+ data.cId = data.cId || _.uniqueId('pgC_');
// Clean up first
this.$el.removeClass(Backform.hiddenClassName);
@@ -607,7 +650,13 @@ define([
this.$input = this.$el.find('input[type=checkbox]').first();
this.$input.bootstrapToggle();
// When disable then set tabindex value to -1
- this.$el.find('.toggle.btn').attr('tabindex', data.options.disabled ? '-1' : '0');
+ this.$el.find('.toggle.btn')
+ .attr('tabindex', data.options.disabled ? '-1' : '0')
+ .attr('id', data.cId);
+
+ this.$el.find('.toggle.btn .toggle-group .btn').attr('aria-hidden', true);
+ this.setSrValue();
+
this.updateInvalid();
return this;
@@ -1834,9 +1883,9 @@ define([
helpMessage: null,
},
template: _.template([
- '<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+ '<label for="<%=cId%>" class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
'<div class="<%=Backform.controlsClassName%>">',
- ' <input type="<%=type%>" class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>" min="<%=min%>" max="<%=max%>"maxlength="<%=maxlength%>" value="<%-value%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
+ ' <input type="<%=type%>" id="<%=cId%>" class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>" min="<%=min%>" max="<%=max%>"maxlength="<%=maxlength%>" value="<%-value%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
' <% if (helpMessage && helpMessage.length) { %>',
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
' <% } %>',
@@ -2075,9 +2124,9 @@ define([
formatter: Select2Formatter,
template: _.template([
- '<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+ '<label class="<%=Backform.controlLabelClassName%>" for="<%=cId%>"><%=label%></label>',
'<div class="<%=Backform.controlsClassName%>">',
- ' <select class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>"',
+ ' <select id="<%=cId%>" class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>"',
' name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%>',
' <%=required ? "required" : ""%><%= select2.multiple ? " multiple>" : ">" %>',
' <%=select2.first_empty ? " <option></option>" : ""%>',
@@ -2158,6 +2207,7 @@ define([
}
}
+ data.cId = data.cId || _.uniqueId('pgC_');
// Clean up first
this.$el.removeClass(Backform.hiddenClassName);
@@ -2599,12 +2649,12 @@ define([
Backform.InputControl.prototype.initialize.apply(this, arguments);
},
template: _.template([
- '<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+ '<label class="<%=Backform.controlLabelClassName%>" for="<%=cId%>"><%=label%></label>',
'<div class="<%=Backform.controlsClassName%>">',
'<div class="input-group">',
- '<input type="<%=type%>" class="form-control <%=extraClasses.join(\' \')%>" name="<%=name%>" min="<%=min%>" max="<%=max%>"maxlength="<%=maxlength%>" value="<%-value%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
+ '<input type="<%=type%>" id="<%=cId%>" class="form-control <%=extraClasses.join(\' \')%>" name="<%=name%>" min="<%=min%>" max="<%=max%>"maxlength="<%=maxlength%>" value="<%-value%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
'<div class="input-group-append">',
- '<button class="btn btn-secondary fa fa-ellipsis-h select_item" <%=disabled ? "disabled" : ""%> ></button>',
+ '<button class="btn btn-secondary fa fa-ellipsis-h select_item" <%=disabled ? "disabled" : ""%> aria-hidden="true" aria-label="Select file" title="Select file"></button>',
'</div>',
'</div>',
'<% if (helpMessage && helpMessage.length) { %>',
@@ -2853,9 +2903,9 @@ define([
defaultColor: '',
},
template: _.template([
- '<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
+ '<label class="<%=Backform.controlLabelClassName%>" for="<%=cId%>"><%=label%></label>',
'<div class="<%=Backform.controlsClassName%>">',
- ' <input class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
+ ' <input id="<%=cId%>" class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
' <% if (helpMessage && helpMessage.length) { %>',
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
' <% } %>',
@@ -2890,6 +2940,7 @@ define([
required: evalF(data.required, data, this.model),
});
+ data.cId = data.cId || _.uniqueId('pgC_');
// Clean up first
this.$el.empty();
diff --git a/web/pgadmin/static/js/sqleditor/query_tool_preferences.js b/web/pgadmin/static/js/sqleditor/query_tool_preferences.js
index 8e74e65d3..b430d654a 100644
--- a/web/pgadmin/static/js/sqleditor/query_tool_preferences.js
+++ b/web/pgadmin/static/js/sqleditor/query_tool_preferences.js
@@ -11,6 +11,7 @@ import {shortcut_key, shortcut_accesskey_title, shortcut_title}
from 'sources/keyboard_shortcuts';
import * as SqlEditorUtils from 'sources/sqleditor_utils';
import $ from 'jquery';
+import gettext from 'sources/gettext';
function updateUIPreferences(sqlEditor) {
let $el = sqlEditor.$el,
@@ -26,51 +27,63 @@ function updateUIPreferences(sqlEditor) {
/* Accessed using accesskey direct w/o ctrl,atl,shift */
$el.find('#btn-load-file')
- .attr('title', shortcut_accesskey_title('Open File',preferences.btn_open_file))
+ .attr('title', shortcut_accesskey_title(gettext('Open File'),preferences.btn_open_file))
+ .attr('aria-label', shortcut_accesskey_title(gettext('Open File'),preferences.btn_open_file))
.attr('accesskey', shortcut_key(preferences.btn_open_file));
$el.find('#btn-save-file')
- .attr('title', shortcut_accesskey_title('Save File',preferences.btn_save_file))
+ .attr('title', shortcut_accesskey_title(gettext('Save File'),preferences.btn_save_file))
+ .attr('aria-label', shortcut_accesskey_title(gettext('Save File'),preferences.btn_save_file))
.attr('accesskey', shortcut_key(preferences.btn_save_file));
$el.find('#btn-find-menu-dropdown')
- .attr('title', shortcut_accesskey_title('Find',preferences.btn_find_options))
+ .attr('title', shortcut_accesskey_title(gettext('Find'),preferences.btn_find_options))
+ .attr('aria-label',shortcut_accesskey_title(gettext('Find'),preferences.btn_find_options))
.attr('accesskey', shortcut_key(preferences.btn_find_options));
$el.find('#btn-copy-row')
- .attr('title', shortcut_accesskey_title('Copy',preferences.btn_copy_row))
+ .attr('title', shortcut_accesskey_title(gettext('Copy'),preferences.btn_copy_row))
+ .attr('aria-label', shortcut_accesskey_title(gettext('Copy'),preferences.btn_copy_row))
.attr('accesskey', shortcut_key(preferences.btn_copy_row));
$el.find('#btn-paste-row')
- .attr('title', shortcut_accesskey_title('Paste',preferences.btn_paste_row))
+ .attr('title', shortcut_accesskey_title(gettext('Paste'),preferences.btn_paste_row))
+ .attr('aria-label', shortcut_accesskey_title(gettext('Paste'),preferences.btn_paste_row))
.attr('accesskey', shortcut_key(preferences.btn_paste_row));
$el.find('#btn-delete-row')
- .attr('title', shortcut_accesskey_title('Delete',preferences.btn_delete_row))
+ .attr('title', shortcut_accesskey_title(gettext('Delete'),preferences.btn_delete_row))
+ .attr('aria-label', shortcut_accesskey_title(gettext('Delete'),preferences.btn_delete_row))
.attr('accesskey', shortcut_key(preferences.btn_delete_row));
$el.find('#btn-filter')
- .attr('title', shortcut_accesskey_title('Filter',preferences.btn_filter_dialog))
+ .attr('title', shortcut_accesskey_title(gettext('Filter'),preferences.btn_filter_dialog))
+ .attr('aria-label', shortcut_accesskey_title(gettext('Filter'),preferences.btn_filter_dialog))
.attr('accesskey', shortcut_key(preferences.btn_filter_dialog));
$el.find('#btn-filter-dropdown')
- .attr('title', shortcut_accesskey_title('Filter options',preferences.btn_filter_options))
+ .attr('title', shortcut_accesskey_title(gettext('Filter options'),preferences.btn_filter_options))
+ .attr('aria-label', shortcut_accesskey_title(gettext('Filter options'),preferences.btn_filter_options))
.attr('accesskey', shortcut_key(preferences.btn_filter_options));
$el.find('#btn-rows-limit')
- .attr('title', shortcut_accesskey_title('Rows limit',preferences.btn_rows_limit))
+ .attr('title', shortcut_accesskey_title(gettext('Rows limit'),preferences.btn_rows_limit))
+ .attr('aria-label', shortcut_accesskey_title(gettext('Rows limit'),preferences.btn_rows_limit))
.attr('accesskey', shortcut_key(preferences.btn_rows_limit));
$el.find('#btn-query-dropdown')
- .attr('title', shortcut_accesskey_title('Execute options',preferences.btn_execute_options))
+ .attr('title', shortcut_accesskey_title(gettext('Execute options'),preferences.btn_execute_options))
+ .attr('aria-label', shortcut_accesskey_title(gettext('Execute options'),preferences.btn_execute_options))
.attr('accesskey', shortcut_key(preferences.btn_execute_options));
$el.find('#btn-cancel-query')
- .attr('title', shortcut_accesskey_title('Cancel query',preferences.btn_cancel_query))
+ .attr('title', shortcut_accesskey_title(gettext('Cancel query'),preferences.btn_cancel_query))
+ .attr('aria-label', shortcut_accesskey_title(gettext('Cancel query'),preferences.btn_cancel_query))
.attr('accesskey', shortcut_key(preferences.btn_cancel_query));
$el.find('#btn-clear-dropdown')
- .attr('title', shortcut_accesskey_title('Clear',preferences.btn_clear_options))
+ .attr('title', shortcut_accesskey_title(gettext('Clear'),preferences.btn_clear_options))
+ .attr('aria-label', shortcut_accesskey_title(gettext('Clear'),preferences.btn_clear_options))
.attr('accesskey', shortcut_key(preferences.btn_clear_options));
$el.find('#btn-conn-status')
@@ -83,31 +96,45 @@ function updateUIPreferences(sqlEditor) {
/* Accessed using ctrl,atl,shift and key */
$el.find('#btn-flash')
.attr('title',
- shortcut_title('Execute/Refresh',preferences.execute_query));
+ shortcut_title(gettext('Execute/Refresh'),preferences.execute_query))
+ .attr('aria-label',
+ shortcut_title(gettext('Execute/Refresh'),preferences.execute_query));
$el.find('#btn-explain')
.attr('title',
- shortcut_title('Explain',preferences.explain_query));
+ shortcut_title(gettext('Explain'),preferences.explain_query))
+ .attr('aria-label',
+ shortcut_title(gettext('Explain'),preferences.explain_query));
$el.find('#btn-explain-analyze')
.attr('title',
- shortcut_title('Explain Analyze',preferences.explain_analyze_query));
+ shortcut_title(gettext('Explain Analyze'),preferences.explain_analyze_query))
+ .attr('aria-label',
+ shortcut_title(gettext('Explain Analyze'),preferences.explain_analyze_query));
$el.find('#btn-download')
.attr('title',
- shortcut_title('Download as CSV',preferences.download_csv));
+ shortcut_title(gettext('Download as CSV'),preferences.download_csv))
+ .attr('aria-label',
+ shortcut_title(gettext('Download as CSV'),preferences.download_csv));
$el.find('#btn-save-data')
.attr('title',
- shortcut_title('Save Data Changes',preferences.save_data));
+ shortcut_title(gettext('Save Data Changes'),preferences.save_data))
+ .attr('aria-label',
+ shortcut_title(gettext('Save Data Changes'),preferences.save_data));
$el.find('#btn-commit')
.attr('title',
- shortcut_title('Commit',preferences.commit_transaction));
+ shortcut_title(gettext('Commit'),preferences.commit_transaction))
+ .attr('aria-label',
+ shortcut_title(gettext('Commit'),preferences.commit_transaction));
$el.find('#btn-rollback')
.attr('title',
- shortcut_title('Rollback',preferences.rollback_transaction));
+ shortcut_title(gettext('Rollback'),preferences.rollback_transaction))
+ .attr('aria-label',
+ shortcut_title(gettext('Rollback'),preferences.rollback_transaction));
/* Set explain options on query editor */
if (preferences.explain_verbose){
diff --git a/web/pgadmin/static/scss/_bootstrap.overrides.scss b/web/pgadmin/static/scss/_bootstrap.overrides.scss
index 0b61787e2..b9aa5fea9 100644
--- a/web/pgadmin/static/scss/_bootstrap.overrides.scss
+++ b/web/pgadmin/static/scss/_bootstrap.overrides.scss
@@ -195,9 +195,23 @@ legend {
}
}
- & thead, & tbody {
+ & thead {
+ & tr {
+ & th {
+ &:first-of-type {
+ border-left: none;
+ }
+
+ &:last-of-type {
+ border-right: none;
+ }
+ }
+ }
+ }
+
+ & tbody {
& tr {
- & td, & th {
+ & td {
&:first-of-type {
border-left: none;
}
diff --git a/web/pgadmin/static/vendor/backform/backform.js b/web/pgadmin/static/vendor/backform/backform.js
index 1dfa5c5cb..c4d44987b 100644
--- a/web/pgadmin/static/vendor/backform/backform.js
+++ b/web/pgadmin/static/vendor/backform/backform.js
@@ -541,11 +541,13 @@
id: _.uniqueId('bf_')
},
template: _.template([
- '<label class="<%=Backform.controlLabelClassName%>"><%=controlLabel%></label>',
+ '<label class="<%=Backform.controlLabelClassName%>" for="<%=id%>"><%=controlLabel%></label>',
'<div class="<%=Backform.controlContainerClassName%>">',
' <div class="form-check">',
' <input type="<%=type%>" class="form-check-input <%=extraClasses.join(\' \')%>" id="<%=id%>" name="<%=name%>" <%=value ? "checked=\'checked\'" : ""%> <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
- ' <label class="form-check-label" for="<%=id%>"><%=label%></label>',
+ ' <% if (label && label.length) { %>',
+ ' <label class="form-check-label" for="<%=id%>"><%=label%></label>',
+ ' <% } %>',
' </div>',
'</div>'
].join("\n")),
diff --git a/web/pgadmin/static/vendor/backgrid/backgrid.js b/web/pgadmin/static/vendor/backgrid/backgrid.js
index b250ebeee..b51635bc2 100644
--- a/web/pgadmin/static/vendor/backgrid/backgrid.js
+++ b/web/pgadmin/static/vendor/backgrid/backgrid.js
@@ -2190,6 +2190,39 @@ var HeaderCell = Backgrid.HeaderCell = Backbone.View.extend({
});
+/**
+ EmptyHeaderCell is a special cell class that renders a column header cell.
+ The text is empty here and it is not sortable.
+
+ @class Backgrid.EmptyHeaderCell
+ @extends Backbone.View
+ */
+var EmptyHeaderCell = Backgrid.EmptyHeaderCell = Backbone.View.extend({
+
+ /** @property */
+ tagName: "td",
+
+ /**
+ Initializer.
+
+ @param {Object} options
+ @param {Backgrid.Column|Object} options.column
+ */
+ initialize: function (options) {
+ this.column = options.column;
+ },
+
+ /**
+ Renders a empty header cell with no events
+ */
+ render: function () {
+ this.$el.empty();
+ this.$el.addClass(this.column.get("name"));
+ this.$el.addClass("renderable");
+ return this;
+ }
+});
+
/**
HeaderRow is a controller for a row of header cells.
@@ -2215,7 +2248,13 @@ var HeaderRow = Backgrid.HeaderRow = Backgrid.Row.extend({
},
makeCell: function (column, options) {
- var headerCell = column.get("headerCell") || options.headerCell || HeaderCell;
+ var headerCell = null;
+ if(column.get("label") === "" || !column.get("label")) {
+ headerCell = column.get("headerCell") || options.headerCell || EmptyHeaderCell;
+ } else {
+ headerCell = column.get("headerCell") || options.headerCell || HeaderCell;
+ }
+
headerCell = new headerCell({
column: column,
collection: this.collection
view thread (4+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [pgAdmin][RM4772][Accessibility] Provide aria-label to an invisible label where a visible label cannot be used
In-Reply-To: <CAM9w-_kbux3pL4JT=8XA20BLk6=L3qXWNP8d4EraeA9mdAA3vQ@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox