From 5a9e39a07ae23c86a2cfc71122b605951dc95924 Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Mon, 27 Feb 2017 23:41:03 +0000 Subject: Bug 1280363 - [a11y] Make the Actions menu button accessible for keyboard and screen readers --- extensions/BugModal/web/bug_modal.js | 101 +---------------------------------- extensions/BugModal/web/comments.js | 43 +++++++-------- extensions/BugModal/web/dropdown.css | 52 ++++++++++++++++++ extensions/BugModal/web/dropdown.js | 93 ++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 123 deletions(-) create mode 100644 extensions/BugModal/web/dropdown.css create mode 100644 extensions/BugModal/web/dropdown.js (limited to 'extensions/BugModal/web') diff --git a/extensions/BugModal/web/bug_modal.js b/extensions/BugModal/web/bug_modal.js index 894745016..f65c12be3 100644 --- a/extensions/BugModal/web/bug_modal.js +++ b/extensions/BugModal/web/bug_modal.js @@ -377,13 +377,7 @@ $(function() { } }); - // action button menu - - $.contextMenu({ - selector: '#action-menu-btn', - trigger: 'left', - items: $.contextMenu.fromMenu($('#action-menu')) - }); + // action button actions // reset $('#action-reset') @@ -1006,99 +1000,6 @@ $(function() { BUGZILLA.remaining_time = $('#remaining_time').val(); }); - // new bug button - $.contextMenu({ - selector: '#new-bug-btn', - trigger: 'left', - items: [ - { - name: 'Create a new Bug', - callback: function() { - window.open('enter_bug.cgi', '_blank'); - } - }, - { - name: '\u2026 in this product', - callback: function() { - window.open('enter_bug.cgi?product=' + encodeURIComponent($('#product').val()), '_blank'); - } - }, - { - name: '\u2026 in this component', - callback: function() { - window.open('enter_bug.cgi?' + - 'product=' + encodeURIComponent($('#product').val()) + - '&component=' + encodeURIComponent($('#component').val()), '_blank'); - } - }, - { - name: '\u2026 that blocks this bug', - callback: function() { - window.open('enter_bug.cgi?format=__default__' + - '&product=' + encodeURIComponent($('#product').val()) + - '&blocked=' + BUGZILLA.bug_id, '_blank'); - } - }, - { - name: '\u2026 that depends on this bug', - callback: function() { - window.open('enter_bug.cgi?format=__default__' + - '&product=' + encodeURIComponent($('#product').val()) + - '&dependson=' + BUGZILLA.bug_id, '_blank'); - } - }, - { - name: '\u2026 as a clone of this bug', - callback: function() { - window.open('enter_bug.cgi?format=__default__' + - '&product=' + encodeURIComponent($('#product').val()) + - '&cloned_bug_id=' + BUGZILLA.bug_id, '_blank'); - } - }, - { - name: '\u2026 as a clone, in a different product', - callback: function() { - window.open('enter_bug.cgi?format=__default__' + - '&cloned_bug_id=' + BUGZILLA.bug_id, '_blank'); - } - }, - ] - }); - - var format_items = [ - { - name: 'For Printing', - callback: function() { - window.location.href = 'show_bug.cgi?format=multiple&id=' + BUGZILLA.bug_id; - } - }, - { - name: 'XML', - callback: function() { - window.location.href = 'show_bug.cgi?ctype=xml&id=' + BUGZILLA.bug_id; - } - }, - { - name: 'Legacy', - callback: function() { - window.location.href = 'show_bug.cgi?format=default&id=' + BUGZILLA.bug_id; - } - } - ]; - if (!BUGZILLA.bug_secure) { - format_items.push({ - name: 'JSON', - callback: function() { - window.location.href = 'rest/bug/' + BUGZILLA.bug_id; - } - }); - } - $.contextMenu({ - selector: '#format-btn', - trigger: 'left', - items: format_items - }); - // "reset to default" checkboxes $('#product, #component') .change(function(event) { diff --git a/extensions/BugModal/web/comments.js b/extensions/BugModal/web/comments.js index 7eb933cfc..04894506e 100644 --- a/extensions/BugModal/web/comments.js +++ b/extensions/BugModal/web/comments.js @@ -189,12 +189,6 @@ $(function() { } }); - $.contextMenu({ - selector: '#view-menu-btn', - trigger: 'left', - items: $.contextMenu.fromMenu($('#view-menu')) - }); - function updateTagsMenu() { var tags = []; $('.comment-tags').each(function() { @@ -218,21 +212,24 @@ $(function() { } btn.show(); - var menuItems = [ - { name: 'Reset', tag: '' }, - "--" - ]; + // clear out old li items. Always leave the first one (Reset) + var $li = $('#comment-tags-menu li'); + for (var i = 1, l = $li.length; i < l; i++) { + $li.eq(i).remove(); + } + + // add new li items $.each(tagNames, function(key, value) { - menuItems.push({ name: value + ' (' + tags[value] + ')', tag: value }); + $('#comment-tags-menu') + .append($('
  • ') + .append($('') + .append(value + ' (' + tags[value] + ')'))); }); - $.contextMenu('destroy', '#comment-tags-btn'); - $.contextMenu({ - selector: '#comment-tags-btn', - trigger: 'left', - items: menuItems, - callback: function(key, opt) { - var tag = opt.commands[key].tag; + $('a[data-comment-tag]').each(function() { + $(this).click(function() { + var $that = $(this); + var tag = $that.data('comment-tag'); if (tag === '') { $('.change-spinner:visible').each(function() { toggleChange($(this), 'reset'); @@ -241,17 +238,17 @@ $(function() { } var firstComment = false; $('.change-spinner:visible').each(function() { - var that = $(this); - var commentTags = tagsFromDom(that.parents('.comment').find('.comment-tags')); + var $that = $(this); + var commentTags = tagsFromDom($that.parents('.comment').find('.comment-tags')); var hasTag = $.inArrayIn(tag, commentTags) >= 0; - toggleChange(that, hasTag ? 'show' : 'hide'); + toggleChange($that, hasTag ? 'show' : 'hide'); if (hasTag && !firstComment) { - firstComment = that; + firstComment = $that; } }); if (firstComment) $.scrollTo(firstComment); - } + }); }); } diff --git a/extensions/BugModal/web/dropdown.css b/extensions/BugModal/web/dropdown.css new file mode 100644 index 000000000..977a7a57f --- /dev/null +++ b/extensions/BugModal/web/dropdown.css @@ -0,0 +1,52 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is "Incompatible With Secondary Licenses", as + * defined by the Mozilla Public License, v. 2.0. */ + +/* The container
    - needed to position the dropdown content */ +.dropdown { + position: relative; + display: inline-block; +} + +/* Dropdown Content (Hidden by Default) */ +.dropdown-content { + position: absolute; + background-color: #eee; + min-width: 120px; + z-index: 1; + text-align: left; + margin: 0; + padding: 0; + border: 1px solid #ddd; + -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); + box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); + list-style: none; +} + +.dropdown-content.menu-up { + bottom: 100%; +} + +.dropdown-separator { + border-bottom: 1px solid #ddd; +} + +/* Links inside the dropdown */ +.dropdown-content a { + white-space: nowrap; + background-color: #eee; + color: black !important; + padding: 4px 8px; + text-decoration: none !important; + display: block; +} + +/* Change color of dropdown links on hover */ +.dropdown-content li .active { + text-decoration: none; + background-color: #39f; +} diff --git a/extensions/BugModal/web/dropdown.js b/extensions/BugModal/web/dropdown.js new file mode 100644 index 000000000..181be9f73 --- /dev/null +++ b/extensions/BugModal/web/dropdown.js @@ -0,0 +1,93 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is "Incompatible With Secondary Licenses", as + * defined by the Mozilla Public License, v. 2.0. */ + +$(function() { + 'use strict'; + + $(window).click(function(e) { + // clicking dropdown button opens or closes the dropdown content + if (!$(e.target).hasClass('dropdown-button')) { + $('.dropdown-button').each(function() { + toggleDropDown(e, $(this), $('#' + $(this).attr('aria-controls')), 1); + }); + } + }).keydown(function(e) { + // Escape key hides the dropdown if visible + if (e.keyCode == 27) { + $('.dropdown-button').each(function() { + var $button = $(this); + if ($button.siblings('.dropdown-content').is(':visible')) { + toggleDropDown(e, $button, $('#' + $button.attr('aria-controls')), 1); + $button.focus(); + } + }); + } + // allow arrow up and down keys to choose one of the dropdown items if menu visible + if (e.keyCode == 38 || e.keyCode == 40) { + $('.dropdown-content').each(function() { + var $content = $(this); + if ($content.is(':visible')) { + e.preventDefault(); + e.stopPropagation(); + var $li = $content.find('li'); + // if none focused select the first or last + var $any_focused = $content.find('a:focus'); + if ($any_focused.length == 0) { + var index = e.keyCode == 40 ? 0 : $li.length - 1; + var $link = $li.eq(index).find('a'); + $link.addClass('active').focus(); + return; + } + // otherwise move up or down the list based on arrow key pressed + var inc = e.keyCode == 40 ? 1 : -1; + var move = $content.find('a:focus').parent('li').index() + inc; + var $link = $li.eq(move % $li.length).find('a'); + $content.find('a').removeClass('active'); + $link.addClass('active').focus(); + } + }); + } + + // enter clicks on a link + if (e.keyCode == 13) { + $('.dropdown-content:visible a.active').trigger('click'); + } + }); + + $('.dropdown-content a').hover( + function(){ $(this).addClass('active') }, + function(){ $(this).removeClass('active') } + ); + + $('.dropdown').each(function() { + var $div = $(this); + var $button = $div.find('.dropdown-button'); + var $content = $div.find('.dropdown-content'); + $button.click(function(e) { + toggleDropDown(e, $button, $content); + }).keydown(function(e) { + // allow enter to toggle menu + if (e.keyCode == 13) { + toggleDropDown(e, $button, $content); + } + }); + }); + + function toggleDropDown(e, $button, $content, hide_only) { + // clear all active links + $content.find('a').removeClass('active'); + if ($content.is(':visible')) { + $content.hide(); + $button.attr('aria-expanded', false); + } + // if not using Escape or clicking outside the dropdown div, then we are hiding + else if (!hide_only) { + $content.show(); + $button.attr('aria-expanded', true); + } + } +}); -- cgit v1.2.3-24-g4f1b