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

Commit 3cf78a7

Browse files
committed
fix(radio-button): support selection using the space key
- add an incrementSelection enum - add JSDoc and improve types - fix typos Fixes #11960
1 parent 33e8bac commit 3cf78a7

File tree

1 file changed

+40
-17
lines changed

1 file changed

+40
-17
lines changed

src/components/radioButton/radio-button.js

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ angular.module('material.components.radioButton', [
99
.directive('mdRadioGroup', mdRadioGroupDirective)
1010
.directive('mdRadioButton', mdRadioButtonDirective);
1111

12+
/**
13+
* @type {Readonly<{NEXT: number, CURRENT: number, PREVIOUS: number}>}
14+
*/
15+
var incrementSelection = Object.freeze({PREVIOUS: -1, CURRENT: 0, NEXT: 1});
16+
1217
/**
1318
* @ngdoc directive
1419
* @module material.components.radioButton
@@ -106,33 +111,38 @@ function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming, $timeout) {
106111
}
107112

108113
/**
109-
* @param {KeyboardEvent} ev
114+
* @param {KeyboardEvent} keyboardEvent
110115
*/
111-
function keydownListener(ev) {
112-
var keyCode = ev.which || ev.keyCode;
116+
function keydownListener(keyboardEvent) {
117+
var keyCode = keyboardEvent.which || keyboardEvent.keyCode;
113118

114119
// Only listen to events that we originated ourselves
115120
// so that we don't trigger on things like arrow keys in inputs.
116121
if (keyCode !== $mdConstant.KEY_CODE.ENTER &&
117-
ev.currentTarget !== ev.target) {
122+
keyboardEvent.currentTarget !== keyboardEvent.target) {
118123
return;
119124
}
120125

121126
switch (keyCode) {
122127
case $mdConstant.KEY_CODE.LEFT_ARROW:
123128
case $mdConstant.KEY_CODE.UP_ARROW:
124-
ev.preventDefault();
129+
keyboardEvent.preventDefault();
125130
radioGroupController.selectPrevious();
126131
setFocus();
127132
break;
128133

129134
case $mdConstant.KEY_CODE.RIGHT_ARROW:
130135
case $mdConstant.KEY_CODE.DOWN_ARROW:
131-
ev.preventDefault();
136+
keyboardEvent.preventDefault();
132137
radioGroupController.selectNext();
133138
setFocus();
134139
break;
135140

141+
case $mdConstant.KEY_CODE.SPACE:
142+
keyboardEvent.preventDefault();
143+
radioGroupController.selectCurrent();
144+
break;
145+
136146
case $mdConstant.KEY_CODE.ENTER:
137147
var form = angular.element($mdUtil.getClosest(element[0], 'form'));
138148
if (form.length > 0) {
@@ -180,11 +190,14 @@ function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming, $timeout) {
180190
getViewValue: function() {
181191
return this._ngModelCtrl.$viewValue;
182192
},
193+
selectCurrent: function() {
194+
return changeSelectedButton(this.$element, incrementSelection.CURRENT);
195+
},
183196
selectNext: function() {
184-
return changeSelectedButton(this.$element, 1);
197+
return changeSelectedButton(this.$element, incrementSelection.NEXT);
185198
},
186199
selectPrevious: function() {
187-
return changeSelectedButton(this.$element, -1);
200+
return changeSelectedButton(this.$element, incrementSelection.PREVIOUS);
188201
},
189202
setActiveDescendant: function (radioId) {
190203
this.$element.attr('aria-activedescendant', radioId);
@@ -196,9 +209,9 @@ function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming, $timeout) {
196209
}
197210

198211
/**
199-
* Coerce all child radio buttons into an array, then wrap then in an iterator
212+
* Coerce all child radio buttons into an array, then wrap them in an iterator.
200213
* @param parent {!JQLite}
201-
* @return {{add: function(*=, *=): *, next: Function, last: function(): *, previous: Function, count: function(): (Array.length|*|number), hasNext: function(*=): (Array.length|*|number|boolean), inRange: function(*): (Array.length|*|number|boolean), remove: function(*=): void, contains: function(*=): boolean, itemAt: function(*=): *, findBy: function(*, *): Array, hasPrevious: function(*=): (Array.length|*|number|boolean), items: function(): (Array|*), indexOf: function(*=): *, first: function(): *}|Object|*|AsyncIterableIterator<OctokitTypes.OctokitResponse<PaginationResults<any>>>}
214+
* @return {{add: add, next: (function()), last: (function(): any|null), previous: (function()), count: (function(): number), hasNext: (function(*=): Array.length|*|number|boolean), inRange: (function(*): boolean), remove: remove, contains: (function(*=): *|boolean), itemAt: (function(*=): any|null), findBy: (function(*, *): *[]), hasPrevious: (function(*=): Array.length|*|number|boolean), items: (function(): *[]), indexOf: (function(*=): number), first: (function(): any|null)}}
202215
*/
203216
function getRadioButtons(parent) {
204217
return $mdUtil.iterator(parent[0].querySelectorAll('md-radio-button'), true);
@@ -207,12 +220,14 @@ function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming, $timeout) {
207220
/**
208221
* Change the radio group's selected button by a given increment.
209222
* If no button is selected, select the first button.
210-
* @param {JQLite} parent
211-
* @param {-1|1} increment select previous button if the value is negative; the next button
212-
* otherwise.
223+
* @param {JQLite} parent the md-radio-group
224+
* @param {incrementSelection} increment enum that determines whether the next or
225+
* previous button is clicked. For current, only the first button is selected, otherwise the
226+
* current selection is maintained (by doing nothing).
213227
*/
214228
function changeSelectedButton(parent, increment) {
215229
var buttons = getRadioButtons(parent);
230+
var target;
216231

217232
if (buttons.count()) {
218233
var validate = function (button) {
@@ -221,11 +236,19 @@ function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming, $timeout) {
221236
};
222237

223238
var selected = parent[0].querySelector('md-radio-button.md-checked');
224-
var target = buttons[increment < 0 ? 'previous' : 'next'](selected, validate) ||
225-
buttons.first();
239+
if (!selected) {
240+
target = buttons.first();
241+
} else if (increment === incrementSelection.PREVIOUS ||
242+
increment === incrementSelection.NEXT) {
243+
target = buttons[
244+
increment === incrementSelection.PREVIOUS ? 'previous' : 'next'
245+
](selected, validate);
246+
}
226247

227-
// Activate radioButton's click listener (triggerHandler won't create a real click event)
228-
angular.element(target).triggerHandler('click');
248+
if (target) {
249+
// Activate radioButton's click listener (triggerHandler won't create a real click event)
250+
angular.element(target).triggerHandler('click');
251+
}
229252
}
230253
}
231254
}

0 commit comments

Comments
 (0)