Skip to content

Commit d9181b5

Browse files
authored
fix(material/button-toggle): skip disabled buttons during keyboard navigation (angular#29308)
Fixes that the button toggle was selecting disabled buttons and attempting to focus them when using the arrow keys. Fixes angular#29304.
1 parent c185c53 commit d9181b5

File tree

1 file changed

+33
-24
lines changed

1 file changed

+33
-24
lines changed

src/material/button-toggle/button-toggle.ts

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -320,35 +320,33 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
320320
return toggle.buttonId === buttonId;
321321
});
322322

323-
let nextButton;
323+
let nextButton: MatButtonToggle | null = null;
324324
switch (event.keyCode) {
325325
case SPACE:
326326
case ENTER:
327-
nextButton = this._buttonToggles.get(index);
327+
nextButton = this._buttonToggles.get(index) || null;
328328
break;
329329
case UP_ARROW:
330-
nextButton = this._buttonToggles.get(this._getNextIndex(index, -1));
330+
nextButton = this._getNextButton(index, -1);
331331
break;
332332
case LEFT_ARROW:
333-
nextButton = this._buttonToggles.get(
334-
this._getNextIndex(index, this.dir === 'ltr' ? -1 : 1),
335-
);
333+
nextButton = this._getNextButton(index, this.dir === 'ltr' ? -1 : 1);
336334
break;
337335
case DOWN_ARROW:
338-
nextButton = this._buttonToggles.get(this._getNextIndex(index, 1));
336+
nextButton = this._getNextButton(index, 1);
339337
break;
340338
case RIGHT_ARROW:
341-
nextButton = this._buttonToggles.get(
342-
this._getNextIndex(index, this.dir === 'ltr' ? 1 : -1),
343-
);
339+
nextButton = this._getNextButton(index, this.dir === 'ltr' ? 1 : -1);
344340
break;
345341
default:
346342
return;
347343
}
348344

349-
event.preventDefault();
350-
nextButton?._onButtonClick();
351-
nextButton?.focus();
345+
if (nextButton) {
346+
event.preventDefault();
347+
nextButton._onButtonClick();
348+
nextButton.focus();
349+
}
352350
}
353351

354352
/** Dispatch change event with current selection and group value. */
@@ -423,22 +421,33 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
423421
});
424422
if (this.selected) {
425423
(this.selected as MatButtonToggle).tabIndex = 0;
426-
} else if (this._buttonToggles.length > 0) {
427-
this._buttonToggles.get(0)!.tabIndex = 0;
424+
} else {
425+
for (let i = 0; i < this._buttonToggles.length; i++) {
426+
const toggle = this._buttonToggles.get(i)!;
427+
428+
if (!toggle.disabled) {
429+
toggle.tabIndex = 0;
430+
break;
431+
}
432+
}
428433
}
429434
this._markButtonsForCheck();
430435
}
431436

432-
/** Obtain the subsequent index to which the focus shifts. */
433-
private _getNextIndex(index: number, offset: number): number {
434-
let nextIndex = index + offset;
435-
if (nextIndex === this._buttonToggles.length) {
436-
nextIndex = 0;
437-
}
438-
if (nextIndex === -1) {
439-
nextIndex = this._buttonToggles.length - 1;
437+
/** Obtain the subsequent toggle to which the focus shifts. */
438+
private _getNextButton(startIndex: number, offset: number): MatButtonToggle | null {
439+
const items = this._buttonToggles;
440+
441+
for (let i = 1; i <= items.length; i++) {
442+
const index = (startIndex + offset * i + items.length) % items.length;
443+
const item = items.get(index);
444+
445+
if (item && !item.disabled) {
446+
return item;
447+
}
440448
}
441-
return nextIndex;
449+
450+
return null;
442451
}
443452

444453
/** Updates the selection state of the toggles in the group based on a value. */

0 commit comments

Comments
 (0)