Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ if (!announcementModel.getAttachments().isEmpty())
{
ActionURL downloadURL = AnnouncementsController.getDownloadURL(announcementModel, d.getName());
%>
<a href="<%=h(downloadURL)%>"><img alt="" src="<%=getWebappURL(d.getFileIcon())%>">&nbsp;<%=h(d.getName())%></a>&nbsp;<%
<%=d.renderDownloadLink(downloadURL)%>&nbsp;<%
} %>
</div></td>
</tr><%
Expand Down Expand Up @@ -210,7 +210,7 @@ if (!announcementModel.getResponses().isEmpty())
{
ActionURL downloadURL = AnnouncementsController.getDownloadURL(r, rd.getName());
%>
<a href="<%=h(downloadURL)%>"><img alt="" src="<%=getWebappURL(rd.getFileIcon())%>">&nbsp;<%=h(rd.getName())%></a>&nbsp;<%
<%=rd.renderDownloadLink(downloadURL)%>&nbsp;<%
}
%>
</div></td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ for (AnnouncementModel a : bean.announcementModels)
for (Attachment d : a.getAttachments())
{
ActionURL downloadURL = AnnouncementsController.getDownloadURL(a, d.getName());
%><a href="<%=h(downloadURL)%>"><img src="<%=getWebappURL(d.getFileIcon())%>">&nbsp;<%=h(d.getName())%></a>&nbsp;<%
%><%=d.renderDownloadLink(downloadURL)%>&nbsp;<%
}
%></td></tr><%
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ for (AnnouncementModel a : bean.announcementModels)
for (Attachment d : a.getAttachments())
{
ActionURL downloadURL = AnnouncementsController.getDownloadURL(a, d.getName());
%><a href="<%=h(downloadURL)%>"><img src="<%=getWebappURL(d.getFileIcon())%>">&nbsp;<%=h(d.getName())%></a>&nbsp;<%
%><%=d.renderDownloadLink(downloadURL)%>&nbsp;<%
}
%></td></tr><%
}
Expand Down
1 change: 0 additions & 1 deletion announcements/src/org/labkey/announcements/update.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ if (settings.hasExpires())
<tbody>
<%
int x = -1;
String id;
for (Attachment att : ann.getAttachments())
{
x++;
Expand Down
29 changes: 29 additions & 0 deletions api/src/org/labkey/api/attachments/Attachment.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@
import org.labkey.api.security.User;
import org.labkey.api.security.UserManager;
import org.labkey.api.services.ServiceRegistry;
import org.labkey.api.util.DOM;
import org.labkey.api.util.HtmlString;
import org.labkey.api.util.MemTracker;
import org.labkey.api.util.MimeMap;
import org.labkey.api.util.PageFlowUtil;
import org.labkey.api.util.Path;
import org.labkey.api.view.ActionURL;
import org.labkey.api.view.ViewServlet;
import org.labkey.api.webdav.WebdavResolver;

Expand Down Expand Up @@ -350,4 +354,29 @@ public void setDocumentSize(int documentSize)
{
_documentSize = documentSize;
}

/**
* Returns an HtmlString rendering a download link: an anchor containing a file type icon and the filename.
* The icon is marked aria-hidden since it is decorative; the link text serves as the accessible name.
*/
public HtmlString renderDownloadLink(ActionURL downloadURL)
{
return renderDownloadLink(downloadURL, getName());
}

/**
* Returns an HtmlString rendering a download link: an anchor containing a file type icon and custom link text.
* Use this overload when the visible link label differs from the filename (e.g. "Study Protocol Document").
* The icon is marked aria-hidden since it is decorative; linkText serves as the accessible name.
*/
public HtmlString renderDownloadLink(ActionURL downloadURL, String linkText)
{
return DOM.createHtmlFragment(
DOM.A(DOM.at(DOM.Attribute.href, downloadURL.toString()),
DOM.IMG(DOM.at(DOM.Attribute.alt, "").at(DOM.Attribute.aria_hidden, "true").at(DOM.Attribute.src, PageFlowUtil.staticResourceUrl(getFileIcon()))),
HtmlString.NBSP,
linkText
)
);
}
}
51 changes: 49 additions & 2 deletions api/src/org/labkey/api/util/DOM.java
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,54 @@ public enum Attribute
action,
align,
alt,
aria_activedescendant,
aria_atomic,
aria_autocomplete,
aria_busy,
aria_checked,
aria_colcount,
aria_colindex,
aria_colspan,
aria_controls,
aria_current,
aria_describedby,
aria_details,
aria_disabled,
aria_dropeffect,
aria_errormessage,
aria_expanded,
aria_flowto,
aria_grabbed,
aria_haspopup,
aria_hidden,
aria_invalid,
aria_keyshortcuts,
aria_label,
aria_labelledby,
aria_level,
aria_live,
aria_modal,
aria_multiline,
aria_multiselectable,
aria_orientation,
aria_owns,
aria_placeholder,
aria_posinset,
aria_pressed,
aria_readonly,
aria_relevant,
aria_required,
aria_roledescription,
aria_rowcount,
aria_rowindex,
aria_rowspan,
aria_selected,
aria_setsize,
aria_sort,
aria_valuemax,
aria_valuemin,
aria_valuenow,
aria_valuetext,
async,
autocomplete,
autofocus,
Expand Down Expand Up @@ -570,7 +618,6 @@ public _Attributes data(boolean condition, String datakey, Object value)
}
return this;
}

public _Attributes cl(String...names)
{
if (null != names)
Expand Down Expand Up @@ -891,7 +938,7 @@ private static Appendable appendAttribute(Appendable html, Attribute key, Object
if (null==value)
return html;
html.append(" ");
html.append(key.name());
html.append(key.name().replace('_', '-'));
html.append("=\"");
// NOTE it is somewhat unusual to pass in a Renderable, but it is possible that we
// want to render HTML into an attribute. We still need to re-encode the value before trying to wrap with "".
Expand Down
4 changes: 2 additions & 2 deletions api/webapp/clientapi/dom/DataRegion.js
Original file line number Diff line number Diff line change
Expand Up @@ -1713,8 +1713,8 @@ if (!LABKEY.DataRegions) {

ct.append([
'<div class="btn-group" style="padding-left: 5px; display: inline-block">',
'<button id="' + prevId + '" class="btn btn-default"><i class="fa fa-chevron-left"></i></button>',
'<button id="' + nextId + '" class="btn btn-default"><i class="fa fa-chevron-right"></i></button>',
'<button id="' + prevId + '" class="btn btn-default" aria-label="Previous page"><i class="fa fa-chevron-left" aria-hidden="true"></i></button>',
'<button id="' + nextId + '" class="btn btn-default" aria-label="Next page"><i class="fa fa-chevron-right" aria-hidden="true"></i></button>',
'</div>'
].join(''));

Expand Down
2 changes: 1 addition & 1 deletion core/src/org/labkey/core/login/resetPassword.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
<% } %>
<div class="auth-form-body">
<p>To reset your password, type in your email address and click the Reset button.</p>
<input id="email" name="email" type="text" class="input-block" tabindex="1" autocomplete="off" value="<%=h(form.getEmail())%>">
<input id="email" aria-label="Email address" name="email" type="text" class="input-block" tabindex="1" autocomplete="off" value="<%=h(form.getEmail())%>">
<div class="auth-item">
<%= button("Reset").submit(true).name("reset")%>
<%= button("Cancel").href(urlProvider(LoginUrls.class).getLoginURL(doneURL)) %>
Expand Down
8 changes: 4 additions & 4 deletions core/src/org/labkey/core/view/template/bootstrap/header.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
</div>
</li>
<li id="global-search-xs" class="dropdown visible-xs">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-label="<%=h(SearchUtils.getPlaceholder(c))%>" role="button">
<i class="fa fa-search"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right">
Expand Down Expand Up @@ -209,7 +209,7 @@
{
%>
<li class="dropdown dropdown-rollup" id="headerProductDropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-label="Product navigation" role="button">
<i class="fa fa-th-large" style="font-size: 18px; padding-top: 2px;"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right">
Expand All @@ -223,7 +223,7 @@
{
%>
<li class="dropdown dropdown-rollup" id="headerAdminDropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-label="Admin menu" role="button">
<i class="fa fa-cog"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right">
Expand Down Expand Up @@ -260,7 +260,7 @@
{
%>
<li class="dropdown dropdown-rollup" id="headerUserDropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" >
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-label="User menu" role="button">
<i class="fa fa-user"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right" >
Expand Down
16 changes: 13 additions & 3 deletions core/src/org/labkey/core/webdav/davListing.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,21 @@

Ext4.onReady(function() {

var loginAction = new Ext4.Action({
const loginAction = new Ext4.Action({
text : 'Login',
handler : function () {
window.location = LABKEY.ActionURL.buildURL('login', 'login', null, {returnUrl: window.location});
}
});

var logoutAction = new Ext4.Action({
const logoutAction = new Ext4.Action({
text : 'Logout',
handler : function () {
LABKEY.Utils.postToAction(LABKEY.ActionURL.buildURL('login', 'logout', null, {returnUrl: window.location}));
}
});

var htmlViewAction = new Ext4.Action({
const htmlViewAction = new Ext4.Action({
text : 'HTML View',
handler : function() {
window.location = <%=q(h(resource.getLocalHref(getViewContext())+"?listing=html"))%>;
Expand Down Expand Up @@ -103,6 +103,16 @@
useServerActions: false
}],
listeners: {
afterrender: function(vp) {
// ExtJS renders a <span role="img"> for the icon slot on every button, even icon-less ones.
// Those empty spans have no accessible name, which is a WCAG violation. Hide them instead.
vp.el.select('.x4-btn-icon-el').each(function(el) {
if (el.dom.hasAttribute('role')) {
el.dom.removeAttribute('role');
el.dom.setAttribute('aria-hidden', 'true');
}
});
},
resize: function(vp) {
if (vp) {
var fb = vp.getComponent('browser');
Expand Down
18 changes: 16 additions & 2 deletions filecontent/webapp/File/panel/Actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ Ext4.define('File.panel.Action', {
renderTpl: [
'<span id="{id}-btnEl" class="iconbtn">',
'<tpl if="stacked">',
'<span class="fa-stack fa-1x labkey-fa-stacked-wrapper">',
'<span class="fa-stack fa-1x labkey-fa-stacked-wrapper" aria-hidden="true">',
'<span class="fa {fontCls} fa-stack-2x"></span>',
'<span class="fa fa-stack-1x {stackedCls}"></span>',
'</span>',
'<tpl else>',
'<span class="fa {fontCls}"></span>',
'<span class="fa {fontCls}" aria-hidden="true"></span>',
'</tpl>',
'<span id="{id}-btnInnerEl" class="iconbtn-label">',
'<tpl if="text.length &gt; 0 && !hideText">',
Expand All @@ -74,6 +74,20 @@ Ext4.define('File.panel.Action', {
hideText: config.hideText === true
};

// Add aria-label for accessibility (used by screen readers when button text is hidden)
if (config.hardText) {
var hardText = config.hardText;
var existingListeners = config.listeners || {};
var existingAfterRender = existingListeners.afterRender;
existingListeners.afterRender = function(btn) {
btn.getEl().dom.setAttribute('aria-label', hardText);
if (Ext4.isFunction(existingAfterRender)) {
existingAfterRender.call(this, btn);
}
};
config.listeners = existingListeners;
}

this.callParent([config]);
},

Expand Down
8 changes: 8 additions & 0 deletions filecontent/webapp/File/panel/Browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1604,6 +1604,11 @@ Ext4.define('File.panel.Browser', {
border: false,
listeners: {
select: this.onTreeSelect,
afterrender: function(tree) {
if (tree.body) {
tree.body.set({'tabIndex': 0});
}
},
scope: this
}
});
Expand Down Expand Up @@ -2506,6 +2511,9 @@ Ext4.define('File.panel.Browser', {
if (g.getStore().totalCount === undefined) { // totalCount is undefined until first load
g.setLoading(true);
}
if (g.body) {
g.body.set({'tabIndex': 0});
}
},
selectionchange : this.onSelection,
itemdblclick : function(g, rec) {
Expand Down
10 changes: 2 additions & 8 deletions study/src/org/labkey/study/designer/view/studySummary.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,7 @@
{
Attachment attachment = protocolDocs.get(0);
%>
<a href="<%= h(StudyController.getProtocolDocumentDownloadURL(c, attachment.getName())) %>">
<img src="<%=getWebappURL(attachment.getFileIcon())%>" alt="[<%= h(attachment.getName()) %>]">
Study Protocol Document
</a>
<%=attachment.renderDownloadLink(StudyController.getProtocolDocumentDownloadURL(c, attachment.getName()), "Study Protocol Document")%>
<%
}
else if (protocolDocs.size() > 1)
Expand All @@ -98,10 +95,7 @@
for (Attachment doc : protocolDocs)
{
%>
<br><a href="<%= h(StudyController.getProtocolDocumentDownloadURL(c, doc.getName())) %>">
<img src="<%=getWebappURL(doc.getFileIcon())%>" alt="[<%= h(doc.getName()) %>]">
<%= h(doc.getName()) %>
</a><%
<br><%=doc.renderDownloadLink(StudyController.getProtocolDocumentDownloadURL(c, doc.getName()))%><%
}
}
%>
Expand Down
10 changes: 2 additions & 8 deletions study/src/org/labkey/study/view/studySummary.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,7 @@
{
Attachment attachment = protocolDocs.get(0);
%>
<a href="<%=h(StudyController.getProtocolDocumentDownloadURL(c, attachment.getName()))%>">
<img src="<%=getWebappURL(attachment.getFileIcon())%>" alt="[<%= h(attachment.getName()) %>]">
Study Protocol Document
</a>
<%=attachment.renderDownloadLink(StudyController.getProtocolDocumentDownloadURL(c, attachment.getName()), "Study Protocol Document")%>
<%
}
else if (protocolDocs.size() > 1)
Expand All @@ -126,10 +123,7 @@
for (Attachment doc : protocolDocs)
{
%>
<br><a href="<%=h(StudyController.getProtocolDocumentDownloadURL(c, doc.getName()))%>">
<img src="<%=getWebappURL(doc.getFileIcon())%>" alt="[<%= h(doc.getName()) %>]">
<%= h(doc.getName()) %>
</a><%
<br><%=doc.renderDownloadLink(StudyController.getProtocolDocumentDownloadURL(c, doc.getName()))%><%
}
}
%>
Expand Down
2 changes: 1 addition & 1 deletion wiki/src/org/labkey/wiki/view/wiki.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ else
{
ActionURL downloadURL = WikiController.getDownloadURL(getContainer(), wiki, a.getName());

%><a href="<%=h(downloadURL)%>"><img src="<%=getWebappURL(a.getFileIcon())%>">&nbsp;<%=h(a.getName())%></a><br><%
%><%=a.renderDownloadLink(downloadURL)%><br><%
}
}
}
Expand Down
Loading