From ab4e7323b08d29d082459c585df0f9b3be87559a Mon Sep 17 00:00:00 2001 From: Joy Serquina Date: Thu, 16 Jul 2026 11:45:52 -0700 Subject: [PATCH 1/2] fix(material/list): make disabled selection list keyboard navigable Updates Angular Components Selection List component to be able to be navigated via keyboard when it is in a disabled state to improve accessibility. Fixes b/518820148 --- src/material/list/selection-list.ts | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/material/list/selection-list.ts b/src/material/list/selection-list.ts index 3731fcbfa57c..70186b4f7cac 100644 --- a/src/material/list/selection-list.ts +++ b/src/material/list/selection-list.ts @@ -9,7 +9,7 @@ import {FocusKeyManager} from '@angular/cdk/a11y'; import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion'; import {SelectionModel} from '@angular/cdk/collections'; -import {A, ENTER, SPACE, hasModifierKey} from '@angular/cdk/keycodes'; +import {A, ENTER, SPACE, DOWN_ARROW, UP_ARROW, hasModifierKey} from '@angular/cdk/keycodes'; import {_getFocusedElementPierceShadowDom} from '@angular/cdk/platform'; import { AfterViewInit, @@ -365,6 +365,18 @@ export class MatSelectionList _handleKeydown(event: KeyboardEvent) { const activeItem = this._keyManager.activeItem; + if (activeItem && (event.keyCode === DOWN_ARROW || event.keyCode === UP_ARROW)) { + const focusedElement = _getFocusedElementPierceShadowDom(); + const isOptionFocused = this._items + .toArray() + .some(item => item._elementRef.nativeElement === focusedElement); + if (!isOptionFocused) { + event.preventDefault(); + activeItem.focus(); + return; + } + } + if ( (event.keyCode === ENTER || event.keyCode === SPACE) && !this._keyManager.isTyping() && @@ -399,10 +411,6 @@ export class MatSelectionList /** Handles focusin events within the list. */ private _handleFocusin = (event: FocusEvent) => { - if (this.disabled) { - return; - } - const activeIndex = this._items .toArray() .findIndex(item => item._elementRef.nativeElement.contains(event.target as HTMLElement)); @@ -432,7 +440,7 @@ export class MatSelectionList .withHomeAndEnd() .withTypeAhead() .withWrap() - .skipPredicate(() => this.disabled); + .skipPredicate(() => false); // Set the initial focus. this._resetActiveOption(); @@ -455,7 +463,9 @@ export class MatSelectionList * @param index Index of the active option. If set to -1, no option will be active. */ private _setActiveOption(index: number) { - this._items.forEach((item, itemIndex) => item._setTabindex(itemIndex === index ? 0 : -1)); + this._items.forEach((item, itemIndex) => { + item._setTabindex(this.disabled ? -1 : itemIndex === index ? 0 : -1); + }); this._keyManager.updateActiveItem(index); } @@ -465,7 +475,9 @@ export class MatSelectionList */ private _resetActiveOption() { if (this.disabled) { - this._setActiveOption(-1); + const activeItem = this._items.find(item => item.selected) || this._items.first; + const index = activeItem ? this._items.toArray().indexOf(activeItem) : -1; + this._setActiveOption(index); return; } From 3f99dd99d36df9c00559aa2f2d7ad6cd5cae6489 Mon Sep 17 00:00:00 2001 From: Joy Serquina Date: Thu, 16 Jul 2026 12:19:35 -0700 Subject: [PATCH 2/2] refactor(material/list): fix lint errors Updates previous changes to remove nested ternary expression. --- src/material/list/selection-list.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/material/list/selection-list.ts b/src/material/list/selection-list.ts index 70186b4f7cac..b307a394dd62 100644 --- a/src/material/list/selection-list.ts +++ b/src/material/list/selection-list.ts @@ -464,7 +464,13 @@ export class MatSelectionList */ private _setActiveOption(index: number) { this._items.forEach((item, itemIndex) => { - item._setTabindex(this.disabled ? -1 : itemIndex === index ? 0 : -1); + let tabindex = -1; + + if (!this.disabled && itemIndex === index) { + tabindex = 0; + } + + item._setTabindex(tabindex); }); this._keyManager.updateActiveItem(index); }