Finalize accessibility improvements for the notes module (#3088)

This commit is contained in:
Thomas Bruederli 2014-06-20 17:02:51 +02:00
parent 982b673d37
commit e26c4836bc
5 changed files with 94 additions and 59 deletions

View file

@ -135,8 +135,9 @@ class kolab_notes_ui
if ($prop['class_name'])
$class .= ' '.$prop['class_name'];
$attr = $prop['virtual'] ? array('tabindex' => '0') : array('href' => $this->rc->url(array('_list' => $id)));
$items .= html::tag('li', array('id' => 'rcmliknb' . $html_id, 'class' => trim($class)),
html::span(array('class' => 'listname', 'title' => $title), $prop['listname']) .
html::a($attr + array('class' => 'listname', 'title' => $title), $prop['listname']) .
html::span(array('class' => 'count'), '')
);
}
@ -169,7 +170,7 @@ class kolab_notes_ui
$this->rc->output->add_gui_object('noteseditform', $attrib['id']);
$this->rc->output->include_script('tinymce/tinymce.min.js');
$textarea = new html_textarea(array('name' => 'content', 'id' => 'notecontent', 'cols' => 60, 'rows' => 20, 'tabindex' => 3));
$textarea = new html_textarea(array('name' => 'content', 'id' => 'notecontent', 'cols' => 60, 'rows' => 20, 'tabindex' => 0));
return html::tag('form', $attrib, $textarea->show(), array_merge(html::$common_attrib, array('action')));
}
@ -185,7 +186,7 @@ class kolab_notes_ui
$attrib += array('id' => 'rcmkolabnotestitle');
$this->rc->output->add_gui_object('noteviewtitle', $attrib['id']);
$summary = new html_inputfield(array('name' => 'summary', 'class' => 'notetitle inline-edit', 'size' => 60, 'tabindex' => 1));
$summary = new html_inputfield(array('name' => 'summary', 'class' => 'notetitle inline-edit', 'size' => 60, 'tabindex' => 0));
$html = $summary->show();
$html .= html::div(array('class' => 'tagline tagedit', 'style' => 'display:none'), ' ');
@ -247,7 +248,7 @@ class kolab_notes_ui
$form['properties']['fields']['name'] = array(
'label' => $this->plugin->gettext('listname'),
'value' => $input_name->show($list['editname'], array('disabled' => ($options['norename'] || $options['protected']))),
'id' => 'folder-name',
'id' => 'noteslist-name',
);
// prevent user from moving folder
@ -255,10 +256,11 @@ class kolab_notes_ui
$hidden_fields[] = array('name' => 'parent', 'value' => $path_imap);
}
else {
$select = kolab_storage::folder_selector('note', array('name' => 'parent'), $folder_name);
$select = kolab_storage::folder_selector('note', array('name' => 'parent', 'id' => 'parent-folder'), $folder_name);
$form['properties']['fields']['path'] = array(
'label' => $this->plugin->gettext('parentfolder'),
'value' => $select->show(strlen($folder_name) ? $path_imap : ''),
'id' => 'parent-folder',
);
}
@ -289,7 +291,6 @@ class kolab_notes_ui
if (is_array($tab['fields']) && empty($tab['content'])) {
$table = new html_table(array('cols' => 2));
foreach ($tab['fields'] as $col => $colprop) {
$colprop['id'] = '_'.$col;
$label = !empty($colprop['label']) ? $colprop['label'] : $this->plugin->gettext($col);
$table->add('title', html::label($colprop['id'], Q($label)));

View file

@ -31,6 +31,7 @@ $labels['savein'] = 'Save in';
$labels['savingdata'] = 'Saving data...';
$labels['recordnotfound'] = 'Record not found';
$labels['norecordsfound'] = 'No notes found';
$labels['nochanges'] = 'No changes to be saved';
$labels['entertitle'] = 'Please enter a title for this note!';
$labels['deletenotesconfirm'] = 'Do you really want to delete the selected notes?';
@ -46,3 +47,4 @@ $labels['arialabelnotesquicksearchbox'] = 'Notes search input';
$labels['arialabelnotessortmenu'] = 'Notes list sorting options';
$labels['arialabelnotesoptionsmenu'] = 'Notebook actions menu';
$labels['arialabelnotebookform'] = 'Notebook properties';
$labels['arialabelmessagereferences'] = 'Linked email messages';

View file

@ -40,6 +40,7 @@ function rcube_kolab_notes_ui(settings)
var search_request;
var search_query;
var tag_draghelper;
var render_no_focus;
var me = this;
/* public members */
@ -112,9 +113,16 @@ function rcube_kolab_notes_ui(settings)
}
});
$(rcmail.gui_objects.notebooks).on('click', 'li a', function(e) {
var id = String($(this).closest('li').attr('id')).replace(/^rcmliknb/, '');
notebookslist.select(id);
e.preventDefault();
return false;
});
// register dbl-click handler to open list edit dialog
$(rcmail.gui_objects.notebooks).on('dblclick', 'li:not(.virtual)', function(e){
var id = String(this.id).replace(/^rcmliknb/, '');
$(rcmail.gui_objects.notebooks).on('dblclick', 'li:not(.virtual) a', function(e) {
var id = String($(this).closest('li').attr('id')).replace(/^rcmliknb/, '');
if (me.notebooks[id] && me.notebooks[id].editable) {
list_edit_dialog(id);
}
@ -137,6 +145,7 @@ function rcube_kolab_notes_ui(settings)
noteslist = new rcube_list_widget(rcmail.gui_objects.noteslist,
{ multiselect:true, draggable:true, keyboard:true });
noteslist.addEventListener('select', function(list) {
render_no_focus = rcube_event._last_keyboard_event && $(list.list).has(rcube_event._last_keyboard_event.target);
var selection_changed = list.selection.length != 1 || !me.selected_note || list.selection[0] != me.selected_note.id;
selection_changed && warn_unsaved_changes(function(){
var note;
@ -183,7 +192,7 @@ function rcube_kolab_notes_ui(settings)
}
// click-handler on tags list
$(rcmail.gui_objects.notestagslist).on('click', function(e){
$(rcmail.gui_objects.notestagslist).on('click', 'li', function(e){
var item = e.target.nodeName == 'LI' ? $(e.target) : $(e.target).closest('li'),
tag = item.data('value');
@ -198,17 +207,17 @@ function rcube_kolab_notes_ui(settings)
if (tagsfilter.length > 1)
index = -1;
$('li', this).removeClass('selected');
$('li', rcmail.gui_objects.notestagslist).removeClass('selected').attr('aria-checked', 'false');
tagsfilter = [];
}
// add tag to filter
if (index < 0) {
item.addClass('selected');
item.addClass('selected').attr('aria-checked', 'true');
tagsfilter.push(tag);
}
else if (shift) {
item.removeClass('selected');
item.removeClass('selected').attr('aria-checked', 'false');
var a = tagsfilter.slice(0,index);
tagsfilter = a.concat(tagsfilter.slice(index+1));
}
@ -222,6 +231,11 @@ function rcube_kolab_notes_ui(settings)
e.preventDefault();
return false;
})
.on('keypress', 'li', function(e) {
if (e.keyCode == 13) {
$(this).trigger('click', { pointerType:'keyboard' });
}
})
.mousedown(function(e){
// disable content selection with the mouse
e.preventDefault();
@ -314,6 +328,7 @@ function rcube_kolab_notes_ui(settings)
//spellchecker_rpc_url: '../../../../../?_task=utils&_action=spell_html&_remote=1',
//spellchecker_language: rcmail.env.spell_lang,
accessibility_focus: false,
tabfocus_elements: [':prev','btn-save-note'],
setup: function(ed) {
// make links open on shift-click
ed.on('click', function(e) {
@ -453,7 +468,7 @@ function rcube_kolab_notes_ui(settings)
modal: true,
resizable: true,
closeOnEscape: false,
title: rcmail.gettext((list.id ? 'editlist' : 'createlist'), 'kolab_notes'),
title: rcmail.gettext((list.id ? 'editlist' : 'newnotebook'), 'kolab_notes'),
open: function() {
$dialog.parent().find('.ui-dialog-buttonset .ui-button').first().addClass('mainaction');
},
@ -707,6 +722,10 @@ function rcube_kolab_notes_ui(settings)
else if (me.selected_note && notesdata[me.selected_note.id]) {
noteslist.select(me.selected_note.id);
}
else if (!data.data.length) {
console.log(data);
rcmail.display_message(rcmail.gettext('norecordsfound','kolab_notes'), 'info');
}
}
/**
@ -738,7 +757,7 @@ function rcube_kolab_notes_ui(settings)
$.each(typeof data.categories == 'object' && data.categories.length ? data.categories : [''], function(i,val){
$('<input>')
.attr('name', 'tags[]')
.attr('tabindex', '2')
.attr('tabindex', '0')
.addClass('tag')
.val(val)
.appendTo(tagline);
@ -808,8 +827,10 @@ function rcube_kolab_notes_ui(settings)
node = editor.getContentAreaContainer().childNodes[0];
if (node) node.tabIndex = content.get(0).tabIndex;
if (me.selected_note.uid)
editor.getBody().focus();
if (me.selected_note.uid) {
if (!render_no_focus)
editor.getBody().focus();
}
else
$('.notetitle', rcmail.gui_objects.noteviewtitle).focus().select();
@ -822,6 +843,8 @@ function rcube_kolab_notes_ui(settings)
$(rcmail.gui_objects.notesdetailview).html(html).show();
}
render_no_focus = false;
// notify subscribers
rcmail.triggerEvent('kolab_notes_render', { data:data, readonly:readonly, html:is_html });
if (rcmail.is_framed())
@ -916,7 +939,9 @@ function rcube_kolab_notes_ui(settings)
// append tags to tag cloud
$.each(tags, function(i, tag){
li = $('<li>').attr('rel', tag).data('value', tag)
li = $('<li role="checkbox" aria-checked="false" tabindex="0"></li>')
.attr('rel', tag)
.data('value', tag)
.html(Q(tag) + '<span class="count"></span>')
.appendTo(widget)
.draggable({
@ -961,10 +986,10 @@ function rcube_kolab_notes_ui(settings)
else elem.removeClass('inactive');
if (tagsfilter && tagsfilter.length && $.inArray(tag, tagsfilter)) {
elem.addClass('selected');
elem.addClass('selected').attr('aria-checked', 'true');
}
else {
elem.removeClass('selected');
elem.removeClass('selected').attr('aria-checked', 'false');
}
});
}
@ -1149,7 +1174,9 @@ function rcube_kolab_notes_ui(settings)
dialogClass: 'warning',
open: function(event, ui) {
$(this).parent().find('.ui-dialog-titlebar-close').hide();
$(this).parent().find('.ui-button').first().addClass('mainaction').focus();
setTimeout(function(){
dialog.parent().find('.ui-button:visible').first().addClass('mainaction').focus();
}, 10);
}
};

View file

@ -88,16 +88,25 @@
bottom: 0px;
}
.notesview #kolabnoteslist li {
border-left: 2px solid transparent;
}
.notesview #kolabnoteslist.focus li.focused {
border-left: 2px solid #739da8;
}
.notesview #kolabnoteslist .title {
display: block;
padding: 4px 8px;
padding: 4px 6px;
overflow: hidden;
text-overflow: ellipsis;
outline: none;
}
.notesview #kolabnoteslist .date {
display: block;
padding: 0px 8px 4px 8px;
padding: 0px 6px 4px 6px;
color: #777;
font-weight: normal;
}
@ -238,6 +247,7 @@
.notesview #notedetailstitle .tagline {
position: relative;
cursor: text;
margin: 6px -2px -2px -2px;
}
.notesview #notedetailstitle .tagline .placeholder {
@ -250,6 +260,7 @@
.notesview #notedetailstitle .tagedit-list {
position: relative;
z-index: 2;
padding: 2px;
}
.notesview #notedetailstitle #tagedit-input {
@ -295,24 +306,25 @@
height: 12px;
}
.notesview #notebooks li span.listname {
.notesview #notebooks li .listname {
display: block;
position: absolute;
top: 7px;
left: 9px;
top: 1px;
left: 2px;
right: 6px;
height: 19px;
cursor: default;
padding-bottom: 2px;
padding-right: 26px;
padding: 4px 26px 2px 6px;
color: #004458;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.notesview #notebooks li.virtual span.listname {
.notesview #notebooks li.virtual .listname {
color: #aaa;
top: 3px;
top: 0;
padding: 1px 8px;
}
.notesview #notebooks li.readonly,
@ -343,24 +355,16 @@
background-position: 98% -130px;
}
.notesview #notebooks li.other.readonly span.listname,
.notesview #notebooks li.shared.readonly span.listname {
.notesview #notebooks li.other.readonly .listname,
.notesview #notebooks li.shared.readonly .listname {
padding-right: 36px;
}
.notesview #notebooks li.selected > a {
background-color: transparent;
}
.notesview .uidialog .tabbed {
margin-top: -12px;
}
.notesview .uidialog .propform fieldset.tab {
display: block;
background: #efefef;
margin-top: 0.5em;
padding: 0.5em 1em;
.notesview .uidialog .propform fieldset.ui-tabs-panel {
min-height: 290px;
}

View file

@ -31,21 +31,30 @@
<div id="sidebar">
<div id="tagsbox" class="uibox listbox">
<h2 class="boxtitle"><roundcube:label name="kolab_notes.tags" id="taglist" /></h2>
<h2 class="boxtitle" id="aria-label-tagsbox"><roundcube:label name="kolab_notes.tags" id="taglist" /></h2>
<div class="scroller">
<roundcube:object name="plugin.tagslist" id="tagslist" class="tagcloud" />
<roundcube:object name="plugin.tagslist" id="tagslist" class="tagcloud" role="region" aria-labelledby="aria-label-tagsbox" aria-controls="kolabnoteslist" />
</div>
</div>
<div id="notebooksbox" class="uibox listbox">
<h2 class="boxtitle"><roundcube:label name="kolab_notes.lists" /></h2>
<div id="notebooksbox" class="uibox listbox" role="navigation" aria-labelledby="aria-label-notebooks">
<h2 class="boxtitle" id="aria-label-notebooks"><roundcube:label name="kolab_notes.lists" /></h2>
<div class="scroller withfooter">
<roundcube:object name="plugin.notebooks" id="notebooks" class="listing" />
<roundcube:object name="plugin.notebooks" id="notebooks" class="listing treelist" />
</div>
<div class="boxfooter">
<roundcube:button command="list-create" type="link" title="kolab_notes.newnotebook" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" label="kolab_notes.addnotebook" /><roundcube:button name="notesoptionslink" id="notesoptionsmenulink" type="link" title="kolab_notes.listactions" class="listbutton groupactions" onclick="return UI.toggle_popup('notesoptionsmenu', event)" innerClass="inner" content="&#9881;" aria-haspopup="true" aria-expanded="false" aria-owns="notesoptionsmenu-menu" />
<roundcube:button command="list-create" type="link" title="kolab_notes.newnotebook" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" label="kolab_notes.addnotebook" /><roundcube:button name="notesoptionslink" id="notesoptionsmenulink" type="link" title="kolab_notes.listactions" class="listbutton groupactions" onclick="return UI.toggle_popup('notesoptionsmenu', event)" innerClass="inner" label="kolab_notes.listactions" aria-haspopup="true" aria-expanded="false" aria-owns="notesoptionsmenu-menu" />
</div>
</div>
<div id="notesoptionsmenu" class="popupmenu" aria-hidden="true">
<h3 id="aria-label-optionsmenu" class="voice"><roundcube:label name="kolab_notes.arialabelnotesoptionsmenu" /></h3>
<ul class="toolbarmenu" id="notesoptionsmenu-menu" role="menu" aria-labelledby="aria-label-optionsmenu">
<li role="menuitem"><roundcube:button command="list-edit" label="edit" classAct="active" /></li>
<li role="menuitem"><roundcube:button command="list-remove" label="delete" classAct="active" /></li>
<li role="menuitem"><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
</ul>
</div>
</div>
<div id="mainview-right">
@ -56,23 +65,24 @@
</div>
<div class="boxfooter">
<roundcube:button command="delete" type="link" title="delete" class="listbutton delete disabled" classAct="listbutton delete" innerClass="inner" label="delete" />
<roundcube:object name="plugin.recordsCountDisplay" class="countdisplay" label="fromtoshort" />
<roundcube:object name="plugin.recordsCountDisplay" class="countdisplay" label="fromtoshort" aria-live="polite" aria-relevant="text" />
</div>
<div class="boxpagenav">
<roundcube:button name="notessortmenulink" id="notessortmenulink" type="link" title="kolab_notes.sortby" class="icon sortoptions" onclick="return UI.toggle_popup('notessortmenu', event)" innerClass="inner" content="v" aria-haspopup="true" aria-expanded="false" aria-owns="notessortmenu-menu" />
</div>
</div>
<div id="notedetailsbox" class="uibox contentbox" role="complementary" aria-labelledby="aria-label-noteform">
<div id="notedetailsbox" class="uibox contentbox" role="main" aria-labelledby="aria-label-noteform">
<h3 id="aria-label-noteform" class="voice"><roundcube:label name="kolab_notes.arialabelnoteform" /></h3>
<roundcube:object name="plugin.notetitle" id="notedetailstitle" class="boxtitle" />
<roundcube:object name="plugin.editform" id="noteform" />
<roundcube:object name="plugin.detailview" id="notedetails" class="scroller" />
<div id="notereferences">
<roundcube:object name="plugin.attachments_list" id="attachment-list" class="attachmentslist" />
<h3 id="aria-label-messagereferences" class="voice"><roundcube:label name="kolab_notes.arialabelmessagereferences" /></h3>
<roundcube:object name="plugin.attachments_list" id="attachment-list" class="attachmentslist" role="region" aria-labelledby="aria-label-messagereferences" />
</div>
<div class="footerleft formbuttons">
<roundcube:button command="save" type="input" class="button mainaction" label="save" />
<roundcube:button command="save" type="input" class="button mainaction" label="save" id="btn-save-note" />
</div>
</div>
</div>
@ -82,15 +92,6 @@
<roundcube:object name="message" id="messagestack" />
<div id="notesoptionsmenu" class="popupmenu" aria-hidden="true">
<h3 id="aria-label-optionsmenu" class="voice"><roundcube:label name="kolab_notes.arialabelnotesoptionsmenu" /></h3>
<ul class="toolbarmenu" id="notesoptionsmenu-menu" role="menu" aria-labelledby="aria-label-optionsmenu">
<li role="menuitem"><roundcube:button command="list-edit" label="edit" classAct="active" /></li>
<li role="menuitem"><roundcube:button command="list-remove" label="delete" classAct="active" /></li>
<li role="menuitem"><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
</ul>
</div>
<div id="notessortmenu" class="popupmenu" aria-hidden="true">
<h3 id="aria-label-sortmenu" class="voice"><roundcube:label name="kolab_notes.arialabelnotessortmenu" /></h3>
<ul class="toolbarmenu iconized" id="notessortmenu-menu" role="menu" aria-labelledby="aria-label-sortmenu">