Skip to content

Commit 5cccfd2

Browse files
author
David Monllao
committed
Merge branch 'MDL-62144-35' of git://github.com/damyon/moodle into MOODLE_35_STABLE
2 parents f9f8edc + 40a9015 commit 5cccfd2

File tree

13 files changed

+237
-14
lines changed

13 files changed

+237
-14
lines changed

lang/en/moodle.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
$string['action'] = 'Action';
2727
$string['actionchoice'] = 'What do you want to do with the file \'{$a}\'?';
2828
$string['actions'] = 'Actions';
29+
$string['actionsmenu'] = 'Actions menu';
2930
$string['active'] = 'Active';
3031
$string['activeusers'] = 'Active users';
3132
$string['activities'] = 'Activities';

lib/outputcomponents.php

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4127,6 +4127,12 @@ class action_menu implements renderable, templatable {
41274127
*/
41284128
public $actiontext = null;
41294129

4130+
/**
4131+
* The string to use for the accessible label for the menu.
4132+
* @var array
4133+
*/
4134+
public $actionlabel = null;
4135+
41304136
/**
41314137
* An icon to use for the toggling the secondary menu (dropdown).
41324138
* @var actionicon
@@ -4184,6 +4190,16 @@ public function __construct(array $actions = array()) {
41844190
}
41854191
}
41864192

4193+
/**
4194+
* Sets the label for the menu trigger.
4195+
*
4196+
* @param string $label The text
4197+
* @return null
4198+
*/
4199+
public function set_action_label($label) {
4200+
$this->actionlabel = $label;
4201+
}
4202+
41874203
/**
41884204
* Sets the menu trigger text.
41894205
*
@@ -4287,7 +4303,7 @@ public function get_primary_actions(core_renderer $output = null) {
42874303
$pixicon = '<b class="caret"></b>';
42884304
$linkclasses[] = 'textmenu';
42894305
} else {
4290-
$title = new lang_string('actions', 'moodle');
4306+
$title = new lang_string('actionsmenu', 'moodle');
42914307
$this->actionicon = new pix_icon(
42924308
't/edit_menu',
42934309
'',
@@ -4306,10 +4322,17 @@ public function get_primary_actions(core_renderer $output = null) {
43064322
if ($this->actiontext) {
43074323
$string = $this->actiontext;
43084324
}
4325+
$label = '';
4326+
if ($this->actionlabel) {
4327+
$label = $this->actionlabel;
4328+
} else {
4329+
$label = $title;
4330+
}
43094331
$actions = $this->primaryactions;
43104332
$attributes = array(
43114333
'class' => implode(' ', $linkclasses),
43124334
'title' => $title,
4335+
'aria-label' => $label,
43134336
'id' => 'action-menu-toggle-'.$this->instance,
43144337
'role' => 'menuitem'
43154338
);
@@ -4475,9 +4498,16 @@ public function export_for_template(renderer_base $output) {
44754498
if (!empty($this->menutrigger)) {
44764499
$primary->menutrigger = $this->menutrigger;
44774500
$primary->triggerextraclasses = $this->triggerextraclasses;
4501+
if ($this->actionlabel) {
4502+
$primary->title = $this->actionlabel;
4503+
} else if ($this->actiontext) {
4504+
$primary->title = $this->actiontext;
4505+
} else {
4506+
$primary->title = strip_tags($this->menutrigger);
4507+
}
44784508
} else {
4479-
$primary->title = get_string('actions');
4480-
$actionicon = new pix_icon('t/edit_menu', '', 'moodle', ['class' => 'iconsmall actionmenu', 'title' => '']);
4509+
$primary->title = get_string('actionsmenu');
4510+
$actionicon = new pix_icon('t/edit_menu', '', 'moodle', ['class' => 'iconsmall actionmenu', 'title' => $primary->title]);
44814511
}
44824512

44834513
if ($actionicon instanceof pix_icon) {

lib/outputrenderers.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3418,6 +3418,7 @@ public function user_menu($user = null, $withlinks = null) {
34183418
$am->set_menu_trigger(
34193419
$returnstr
34203420
);
3421+
$am->set_action_label(get_string('usermenu'));
34213422
$am->set_alignment(action_menu::TR, action_menu::BR);
34223423
$am->set_nowrap_on_items();
34233424
if ($withlinks) {

lib/templates/action_menu_trigger.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@
2727
"triggerextraclasses": ""
2828
}
2929
}}
30-
<a href="#" class="{{triggerextraclasses}} toggle-display {{#menutrigger}}textmenu{{/menutrigger}}" id="action-menu-toggle-{{instance}}" title="{{title}}" role="menuitem">{{{actiontext}}}{{{menutrigger}}}{{#icon}}{{#pix}}{{key}}, {{component}}, {{title}}{{/pix}}{{/icon}}{{#rawicon}}{{{.}}}{{/rawicon}}{{#menutrigger}}<b class="caret"></b>{{/menutrigger}}</a>
30+
<a tabindex="0" class="{{triggerextraclasses}} toggle-display {{#menutrigger}}textmenu{{/menutrigger}}" id="action-menu-toggle-{{instance}}" aria-label="{{title}}" role="menuitem" aria-controls="action-menu-{{instance}}-menu">{{{actiontext}}}{{{menutrigger}}}{{#icon}}{{#pix}}{{key}}, {{component}}, {{title}}{{/pix}}{{/icon}}{{#rawicon}}{{{.}}}{{/rawicon}}{{#menutrigger}}<b class="caret"></b>{{/menutrigger}}</a>

lib/tests/user_menu_test.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ public function test_custom_user_menu($data, $entrycount, $dividercount) {
8686
$this->setAdminUser();
8787
$PAGE->set_url('/');
8888
$CFG->theme = 'clean';
89+
$PAGE->reset_theme_and_output();
90+
$PAGE->initialise_theme_and_output();
8991

9092
// Set the configuration.
9193
set_config('customusermenuitems', $data);
@@ -94,7 +96,8 @@ public function test_custom_user_menu($data, $entrycount, $dividercount) {
9496
$dividercount += 2;
9597

9698
// The basic entry count will additionally include the wrapper menu, Dashboard, Profile, Logout and switch roles link.
97-
$entrycount += 4;
99+
// On clean theme only, the trigger is also a menuitem.
100+
$entrycount += 5;
98101

99102
$output = $OUTPUT->user_menu($USER);
100103
preg_match_all('/<a [^>]+role="menuitem"[^>]+>/', $output, $results);

theme/boost/amd/build/aria.min.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

theme/boost/amd/build/loader.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

theme/boost/amd/src/aria.js

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// This file is part of Moodle - http://moodle.org/
2+
//
3+
// Moodle is free software: you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License as published by
5+
// the Free Software Foundation, either version 3 of the License, or
6+
// (at your option) any later version.
7+
//
8+
// Moodle is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15+
16+
/**
17+
* Enhancements to Bootstrap components for accessibility.
18+
*
19+
* @module theme_boost/aria
20+
* @copyright 2018 Damyon Wiese <damyon@moodle.com>
21+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22+
*/
23+
define(['jquery'], function($) {
24+
return {
25+
init: function() {
26+
// Drop downs from bootstrap don't support keyboard accessibility by default.
27+
var focusEnd = false,
28+
setFocusEnd = function() {
29+
focusEnd = true;
30+
},
31+
getFocusEnd = function() {
32+
var result = focusEnd;
33+
focusEnd = false;
34+
return result;
35+
};
36+
37+
// Special handling for "up" keyboard control.
38+
$('[data-toggle="dropdown"]').keydown(function (e) {
39+
var trigger = e.which || e.keyCode,
40+
expanded;
41+
42+
// Up key opens the menu at the end.
43+
if (trigger == 38) {
44+
// Focus the end of the menu, not the beginning.
45+
setFocusEnd();
46+
}
47+
48+
// Escape key only closes the menu, it doesn't open it.
49+
if (trigger == 27) {
50+
expanded = $(e.target).attr('aria-expanded');
51+
e.preventDefault();
52+
if (expanded == "false") {
53+
$(e.target).click();
54+
}
55+
}
56+
57+
// Space key or Enter key opens the menu.
58+
if (trigger == 32 || trigger == 13) {
59+
// Cancel random scroll.
60+
e.preventDefault();
61+
// Open the menu instead.
62+
$(e.target).click();
63+
}
64+
});
65+
66+
// Special handling for navigation keys when menu is open.
67+
var shiftFocus = function(element) {
68+
var delayedFocus = function() {
69+
$(this).focus();
70+
}.bind(element);
71+
setTimeout(delayedFocus, 50);
72+
};
73+
74+
$('.dropdown').on('shown.bs.dropdown', function(e) {
75+
// We need to focus on the first menuitem.
76+
var menu = $(e.target).find('[role="menu"]'),
77+
menuItems = false,
78+
foundMenuItem = false;
79+
80+
if (menu) {
81+
menuItems = $(menu).find('[role="menuitem"]');
82+
}
83+
if (menuItems && menuItems.length > 0) {
84+
if (getFocusEnd()) {
85+
foundMenuItem = menuItems[menuItems.length - 1];
86+
} else {
87+
// The first menu entry, pretty reasonable.
88+
foundMenuItem = menuItems[0];
89+
}
90+
}
91+
if (foundMenuItem) {
92+
shiftFocus(foundMenuItem);
93+
}
94+
});
95+
// Search for menu items.
96+
$('.dropdown [role="menu"] [role="menuitem"]').keypress(function (e) {
97+
var trigger = String.fromCharCode(e.which || e.keyCode),
98+
menu = $(e.target).closest('[role="menu"]'),
99+
i = 0,
100+
menuItems = false,
101+
item;
102+
103+
if (!menu) {
104+
return;
105+
}
106+
menuItems = $(menu).find('[role="menuitem"]');
107+
if (!menuItems) {
108+
return;
109+
}
110+
111+
trigger = trigger.toLowerCase();
112+
for (i = 0; i < menuItems.length; i++) {
113+
item = $(menuItems[i]);
114+
if ((item.text().trim().indexOf(trigger) == 0) ||
115+
(item.text().trim().indexOf(trigger.toUpperCase()) == 0)) {
116+
shiftFocus(item);
117+
break;
118+
}
119+
}
120+
});
121+
122+
// Keyboard navigation for arrow keys, home and end keys.
123+
$('.dropdown [role="menu"] [role="menuitem"]').keydown(function (e) {
124+
var trigger = e.which || e.keyCode,
125+
next = false,
126+
menu = $(e.target).closest('[role="menu"]'),
127+
i = 0,
128+
menuItems = false;
129+
if (!menu) {
130+
return;
131+
}
132+
menuItems = $(menu).find('[role="menuitem"]');
133+
if (!menuItems) {
134+
return;
135+
}
136+
// Down.
137+
if (trigger == 40) {
138+
for (i = 0; i < menuItems.length - 1; i++) {
139+
if (menuItems[i] == e.target) {
140+
next = menuItems[i + 1];
141+
}
142+
}
143+
if (!next) {
144+
// Wrap to first item.
145+
trigger = 36;
146+
}
147+
}
148+
// Up.
149+
if (trigger == 38) {
150+
for (i = 1; i < menuItems.length; i++) {
151+
if (menuItems[i] == e.target) {
152+
next = menuItems[i - 1];
153+
}
154+
}
155+
if (!next) {
156+
// Wrap to last item.
157+
trigger = 35;
158+
}
159+
}
160+
// Home.
161+
if (trigger == 36) {
162+
next = menuItems[0];
163+
}
164+
// End.
165+
if (trigger == 35) {
166+
next = menuItems[menuItems.length - 1];
167+
}
168+
if (next) {
169+
e.preventDefault();
170+
shiftFocus(next);
171+
}
172+
return;
173+
});
174+
$('.dropdown').on('hidden.bs.dropdown', function(e) {
175+
// We need to focus on the menu trigger.
176+
var trigger = $(e.target).find('[data-toggle="dropdown"]');
177+
if (trigger) {
178+
shiftFocus(trigger);
179+
}
180+
});
181+
}
182+
};
183+
});

theme/boost/amd/src/loader.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ define(['jquery', './tether', 'core/event'], function(jQuery, Tether, Event) {
2828
window.jQuery = jQuery;
2929
window.Tether = Tether;
3030

31-
require(['theme_boost/util',
31+
require(['theme_boost/aria',
32+
'theme_boost/util',
3233
'theme_boost/alert',
3334
'theme_boost/button',
3435
'theme_boost/carousel',
@@ -39,7 +40,7 @@ define(['jquery', './tether', 'core/event'], function(jQuery, Tether, Event) {
3940
'theme_boost/tab',
4041
'theme_boost/tooltip',
4142
'theme_boost/popover'],
42-
function() {
43+
function(Aria) {
4344

4445
// We do twice because: https://github.com/twbs/bootstrap/issues/10547
4546
jQuery('body').popover({
@@ -63,8 +64,11 @@ define(['jquery', './tether', 'core/event'], function(jQuery, Tether, Event) {
6364
selector: '[data-toggle="popover"]',
6465
trigger: 'focus'
6566
});
67+
6668
});
6769
});
70+
71+
Aria.init();
6872
});
6973

7074

theme/boost/templates/core/action_menu.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,4 @@
5252
</div>
5353

5454
{{/primary}}
55-
</div>
55+
</div>

theme/boost/templates/core/action_menu_trigger.mustache

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
}
7979
}}
8080
<div class="dropdown">
81-
<a href="#" class="{{triggerextraclasses}} dropdown-toggle" id="dropdown-{{instance}}" title="{{title}}" role="menuitem" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
81+
<a tabindex="0" class="btn {{triggerextraclasses}} dropdown-toggle" id="dropdown-{{instance}}" aria-label="{{title}}" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" aria-controls="action-menu-{{instance}}-menu">
8282
{{{actiontext}}}
8383
{{{menutrigger}}}
8484
{{#icon}}
@@ -92,7 +92,7 @@
9292
{{/menutrigger}}
9393
</a>
9494
{{#secondary}}
95-
<div class="dropdown-menu dropdown-menu-right {{classes}}"{{#attributes}} {{name}}="{{value}}"{{/attributes}}>
95+
<div class="dropdown-menu dropdown-menu-right {{classes}}"{{#attributes}} {{name}}="{{value}}"{{/attributes}} id="dropdown-menu-{{instance}}">
9696
{{#items}}
9797
{{#actionmenulink}}
9898
<a href="{{url}}" class="dropdown-item {{classes}}" {{#attributes}}{{name}}={{#quote}}{{value}}{{/quote}} {{/attributes}}{{#showtext}}aria-labelledby="actionmenuaction-{{instance}}"{{/showtext}}>
@@ -107,7 +107,7 @@
107107
</a>
108108
{{/actionmenulink}}
109109
{{#actionmenufiller}}
110-
<div class="dropdown-divider"></div>
110+
<div class="dropdown-divider" role="presentation"><span class="filler">&nbsp;</span></div>
111111
{{/actionmenufiller}}
112112
{{^actionmenulink}}
113113
{{^actionmenufiller}}

theme/boost/templates/core/custom_menu_item.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<a class="dropdown-item" href="{{{url}}}" {{#title}}title="{{{title}}}"{{/title}}>{{{text}}}</a>
1111
{{/divider}}
1212
{{#divider}}
13-
<div class="dropdown-divider"></div>
13+
<div class="dropdown-divider" role="presentation"></div>
1414
{{/divider}}
1515
{{/children}}
1616
</div>

version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
defined('MOODLE_INTERNAL') || die();
3131

32-
$version = 2018051702.08; // 20180517 = branching date YYYYMMDD - do not modify!
32+
$version = 2018051702.09; // 20180517 = branching date YYYYMMDD - do not modify!
3333
// RR = release increments - 00 in DEV branches.
3434
// .XX = incremental changes.
3535

0 commit comments

Comments
 (0)