Skip to content

Commit d766b14

Browse files
authored
fix(cdk/listbox): unable to tab in if active option is removed (#28583)
Fixes that the listbox didn't allow focus back in when its active option is removed. Fixes #28557.
1 parent 296c948 commit d766b14

File tree

2 files changed

+40
-6
lines changed

2 files changed

+40
-6
lines changed

src/cdk/listbox/listbox.spec.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,26 @@ describe('CdkOption and CdkListbox', () => {
104104

105105
expect(optionEls[0].getAttribute('tabindex')).toBe('10');
106106
});
107+
108+
it('should reset the tabindex if the active option is destroyed', () => {
109+
const {fixture, listbox, listboxEl} = setupComponent(ListboxWithOptions);
110+
let options = fixture.nativeElement.querySelectorAll('.cdk-option');
111+
expect(listboxEl.getAttribute('tabindex')).toBe('0');
112+
expect(options[0].getAttribute('tabindex')).toBe('-1');
113+
114+
listbox.focus();
115+
fixture.detectChanges();
116+
117+
expect(listboxEl.getAttribute('tabindex')).toBe('-1');
118+
expect(options[0].getAttribute('tabindex')).toBe('0');
119+
120+
fixture.componentInstance.appleRendered = false;
121+
fixture.detectChanges();
122+
options = fixture.nativeElement.querySelectorAll('.cdk-option');
123+
124+
expect(listboxEl.getAttribute('tabindex')).toBe('0');
125+
expect(options[0].getAttribute('tabindex')).toBe('-1');
126+
});
107127
});
108128

109129
describe('selection', () => {
@@ -943,12 +963,14 @@ describe('CdkOption and CdkListbox', () => {
943963
[cdkListboxNavigatesDisabledOptions]="!navigationSkipsDisabled"
944964
[cdkListboxValue]="selectedValue"
945965
(cdkListboxValueChange)="onSelectionChange($event)">
946-
<div cdkOption="apple"
947-
[cdkOptionDisabled]="isAppleDisabled"
948-
[id]="appleId"
949-
[tabindex]="appleTabindex">
950-
Apple
951-
</div>
966+
@if (appleRendered) {
967+
<div cdkOption="apple"
968+
[cdkOptionDisabled]="isAppleDisabled"
969+
[id]="appleId"
970+
[tabindex]="appleTabindex">
971+
Apple
972+
</div>
973+
}
952974
<div cdkOption="orange" [cdkOptionDisabled]="isOrangeDisabled">Orange
953975
</div>
954976
<div cdkOption="banana">Banana</div>
@@ -965,6 +987,7 @@ class ListboxWithOptions {
965987
isActiveDescendant = false;
966988
navigationWraps = true;
967989
navigationSkipsDisabled = true;
990+
appleRendered = true;
968991
listboxId: string;
969992
listboxTabindex: number;
970993
appleId: string;

src/cdk/listbox/listbox.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,17 @@ export class CdkListbox<T = unknown> implements AfterContentInit, OnDestroy, Con
831831
}
832832

833833
this.listKeyManager.change.subscribe(() => this._focusActiveOption());
834+
835+
this.options.changes.pipe(takeUntil(this.destroyed)).subscribe(() => {
836+
const activeOption = this.listKeyManager.activeItem;
837+
838+
// If the active option was deleted, we need to reset
839+
// the key manager so it can allow focus back in.
840+
if (activeOption && !this.options.find(option => option === activeOption)) {
841+
this.listKeyManager.setActiveItem(-1);
842+
this.changeDetectorRef.markForCheck();
843+
}
844+
});
834845
}
835846

836847
/** Focus the active option. */

0 commit comments

Comments
 (0)