Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 41c71ed

Browse files
committed
fix(fab-speed-dial): keyboard navigation issues with tab and shift+tab
- add code to handle Tab and Shift+Tab keyboard interactions - rip out old an inaccessible `tabindex` shifting code - use a more noticeable background color for raised mini fab action buttons in speed dials as compared to stand-alone raised mini fab buttons - change fab-speed-dial demo from branded SVGs with hard coded colors to Material Design icons that can be styled by color - add button demo of `md-raised md-fab` - move from old `vm` controller alias to more consistent `ctrl` Fixes #12043
1 parent e7dfcc1 commit 41c71ed

File tree

6 files changed

+48
-39
lines changed

6 files changed

+48
-39
lines changed

src/components/button/demoBasicUsage/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
<md-icon md-svg-src="img/icons/ic_people_24px.svg"></md-icon>
3737
</md-button>
3838

39-
<md-button class="md-fab md-mini" aria-label="Eat cake">
39+
<md-button class="md-fab md-raised md-mini" aria-label="Eat cake">
4040
<md-icon md-svg-src="img/icons/cake.svg"></md-icon>
4141
</md-button>
4242

src/components/fabSpeedDial/demoBasicUsage/index.html

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
</md-fab-trigger>
1616

1717
<md-fab-actions>
18-
<md-button aria-label="Twitter" class="md-fab md-raised md-mini">
19-
<md-icon md-svg-src="img/icons/twitter.svg" aria-label="Twitter"></md-icon>
18+
<md-button aria-label="Play Demo" class="md-fab md-raised md-mini">
19+
<md-icon md-svg-src="img/icons/ic_play_circle_fill_24px.svg" aria-label="Play Demo"></md-icon>
2020
</md-button>
21-
<md-button aria-label="Facebook" class="md-fab md-raised md-mini">
22-
<md-icon md-svg-src="img/icons/facebook.svg" aria-label="Facebook"></md-icon>
21+
<md-button aria-label="Video Tutorial" class="md-fab md-raised md-mini">
22+
<md-icon md-svg-src="img/icons/ic_ondemand_video_24px.svg" aria-label="Video Tutorial"></md-icon>
2323
</md-button>
24-
<md-button aria-label="Google Hangout" class="md-fab md-raised md-mini">
25-
<md-icon md-svg-src="img/icons/hangout.svg" aria-label="Google Hangout"></md-icon>
24+
<md-button aria-label="View Code" class="md-fab md-raised md-mini">
25+
<md-icon md-svg-src="img/icons/ic_code_24px.svg" aria-label="View Code"></md-icon>
2626
</md-button>
2727
</md-fab-actions>
2828
</md-fab-speed-dial>

src/components/fabSpeedDial/fabController.js

+31-30
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,20 @@
1111
// NOTE: We use async eval(s) below to avoid conflicts with any existing digest loops
1212

1313
ctrl.open = function() {
14-
$scope.$evalAsync("vm.isOpen = true");
14+
$scope.$evalAsync("ctrl.isOpen = true");
1515
};
1616

1717
ctrl.close = function() {
1818
// Async eval to avoid conflicts with existing digest loops
19-
$scope.$evalAsync("vm.isOpen = false");
19+
$scope.$evalAsync("ctrl.isOpen = false");
2020

2121
// Focus the trigger when the element closes so users can still tab to the next item
2222
$element.find('md-fab-trigger')[0].focus();
2323
};
2424

2525
// Toggle the open/close state when the trigger is clicked
2626
ctrl.toggle = function() {
27-
$scope.$evalAsync("vm.isOpen = !vm.isOpen");
27+
$scope.$evalAsync("ctrl.isOpen = !ctrl.isOpen");
2828
};
2929

3030
/*
@@ -113,7 +113,7 @@
113113

114114
function setupWatchers() {
115115
// Watch for changes to the direction and update classes/attributes
116-
$scope.$watch('vm.direction', function(newDir, oldDir) {
116+
$scope.$watch('ctrl.direction', function(newDir, oldDir) {
117117
// Add the appropriate classes so we can target the direction in the CSS
118118
$animate.removeClass($element, 'md-' + oldDir);
119119
$animate.addClass($element, 'md-' + newDir);
@@ -125,7 +125,7 @@
125125
var trigger, actions;
126126

127127
// Watch for changes to md-open
128-
$scope.$watch('vm.isOpen', function(isOpen) {
128+
$scope.$watch('ctrl.isOpen', function(isOpen) {
129129
// Reset the action index since it may have changed
130130
resetActionIndex();
131131

@@ -182,10 +182,6 @@
182182
$mdUtil.nextTick(function() {
183183
angular.element(document).on('click touchend', checkForOutsideClick);
184184
});
185-
186-
// TODO: On desktop, we should be able to reset the indexes so you cannot tab through, but
187-
// this breaks accessibility, especially on mobile, since you have no arrow keys to press
188-
// resetActionTabIndexes();
189185
}
190186

191187
function disableKeyboard() {
@@ -204,13 +200,18 @@
204200
}
205201
}
206202

203+
/**
204+
* @param {KeyboardEvent} event
205+
* @returns {boolean}
206+
*/
207207
function keyPressed(event) {
208208
switch (event.which) {
209209
case $mdConstant.KEY_CODE.ESCAPE: ctrl.close(); event.preventDefault(); return false;
210210
case $mdConstant.KEY_CODE.LEFT_ARROW: doKeyLeft(event); return false;
211211
case $mdConstant.KEY_CODE.UP_ARROW: doKeyUp(event); return false;
212212
case $mdConstant.KEY_CODE.RIGHT_ARROW: doKeyRight(event); return false;
213213
case $mdConstant.KEY_CODE.DOWN_ARROW: doKeyDown(event); return false;
214+
case $mdConstant.KEY_CODE.TAB: doShift(event); return false;
214215
}
215216
}
216217

@@ -223,33 +224,25 @@
223224
}
224225

225226
function focusAction(event, direction) {
226-
var actions = resetActionTabIndexes();
227+
var actions = getActionsElement()[0].querySelectorAll('.md-fab-action-item');
228+
var previousActionIndex = ctrl.currentActionIndex;
227229

228230
// Increment/decrement the counter with restrictions
229231
ctrl.currentActionIndex = ctrl.currentActionIndex + direction;
230232
ctrl.currentActionIndex = Math.min(actions.length - 1, ctrl.currentActionIndex);
231233
ctrl.currentActionIndex = Math.max(0, ctrl.currentActionIndex);
232234

233-
// Focus the element
234-
var focusElement = angular.element(actions[ctrl.currentActionIndex]).children()[0];
235-
angular.element(focusElement).attr('tabindex', 0);
236-
focusElement.focus();
237-
238-
// Make sure the event doesn't bubble and cause something else
239-
event.preventDefault();
240-
event.stopImmediatePropagation();
241-
}
242-
243-
function resetActionTabIndexes() {
244-
// Grab all of the actions
245-
var actions = getActionsElement()[0].querySelectorAll('.md-fab-action-item');
246-
247-
// Disable all other actions for tabbing
248-
angular.forEach(actions, function(action) {
249-
angular.element(angular.element(action).children()[0]).attr('tabindex', -1);
250-
});
235+
// Let Tab and Shift+Tab escape if we're trying to move past the start/end.
236+
if (event.which !== $mdConstant.KEY_CODE.TAB ||
237+
previousActionIndex !== ctrl.currentActionIndex) {
238+
// Focus the element
239+
var focusElement = angular.element(actions[ctrl.currentActionIndex]).children()[0];
240+
focusElement.focus();
251241

252-
return actions;
242+
// Make sure the event doesn't bubble and cause something else
243+
event.preventDefault();
244+
event.stopImmediatePropagation();
245+
}
253246
}
254247

255248
function doKeyLeft(event) {
@@ -284,6 +277,14 @@
284277
}
285278
}
286279

280+
function doShift(event) {
281+
if (event.shiftKey) {
282+
doActionPrev(event);
283+
} else {
284+
doActionNext(event);
285+
}
286+
}
287+
287288
/**
288289
* @param {Node} element
289290
* @returns {Node|null}
@@ -309,7 +310,7 @@
309310
}
310311

311312
/**
312-
* @param {MouseEvent} event
313+
* @param {MouseEvent|FocusEvent} event
313314
*/
314315
function handleItemClick(event) {
315316
var closestButton = event.target ? getClosestButton(event.target) : null;

src/components/fabSpeedDial/fabSpeedDial-theme.scss

+8
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,12 @@ md-fab-speed-dial.md-THEME_NAME-theme {
22
md-fab-trigger .md-fab.md-button[disabled] {
33
background-color: '{{foreground-4}}';
44
}
5+
md-fab-actions .md-fab-action-item {
6+
.md-button.md-fab.md-raised.md-mini {
7+
&:hover,
8+
&.md-focused {
9+
background-color: '{{background-500}}';
10+
}
11+
}
12+
}
513
}

src/components/fabSpeedDial/fabSpeedDial.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106

107107
bindToController: true,
108108
controller: 'MdFabController',
109-
controllerAs: 'vm',
109+
controllerAs: 'ctrl',
110110

111111
link: FabSpeedDialLink
112112
};

src/components/fabToolbar/fabToolbar.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686

8787
bindToController: true,
8888
controller: 'MdFabController',
89-
controllerAs: 'vm',
89+
controllerAs: 'ctrl',
9090

9191
link: link
9292
};

0 commit comments

Comments
 (0)