bug(aria/combobox): keys pressed back to back are lost; only the last one reaches the popup
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- angular, typescript
- Domain
- accessibility, frontend
Research direction
Start in the Angular ARIA combobox implementation at ComboboxPattern, where the issue says keyboardEventRelay is set and later re-dispatched to the popup control target. Trace the key handler and relay effect, then add or update a unit test for several back-to-back ArrowDown events; done means each event is handled in order and three presses move three options.
Written by the indexing model from the issue text.
Description
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
No response
Description
While the popup is open, ComboboxPattern relays navigation and typeahead keys to the popup through a signal: the key handler does this.keyboardEventRelay.set(e), and an effect later re-dispatches the event on the popup's control target. If a second key arrives before the effect runs, the signal is overwritten and the first key is never relayed.
Effects:
- Down, Down, Down pressed quickly moves the active option once.
- Typeahead loses characters: with options
item,item-2,item-3, typingitem-3with no delay between keys lands onitem; with 60 ms between keys it lands onitem-3. - Test automation and tools that send keys in a burst (and some assistive technology input) see wrong results. Seen in Chromium through Playwright, and in unit tests.
Related: #33361 (same relay signal, different symptom: a stale event replayed on reopen).
Reproduction
import { Component } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { Combobox, ComboboxPopup, ComboboxWidget } from '@angular/aria/combobox';
import { Listbox, Option } from '@angular/aria/listbox';
@Component({
imports: [Combobox, ComboboxPopup, ComboboxWidget, Listbox, Option],
template: `
<div ngCombobox #combobox="ngCombobox" tabindex="0">Pick one</div>
<ng-template ngComboboxPopup [combobox]="combobox">
<div ngListbox ngComboboxWidget #listbox="ngListbox" focusMode="activedescendant"
[activeDescendant]="listbox.activeDescendant()">
@for (o of options; track o) {
<div ngOption [value]="o" [label]="o">{{ o }}</div>
}
</div>
</ng-template>
`,
})
class Repro {
options = ['alpha', 'bravo', 'charlie', 'delta'];
}
const key = (el: Element, k: string) =>
el.dispatchEvent(new KeyboardEvent('keydown', { key: k, bubbles: true }));
it('handles each key pressed back to back', async () => {
const fixture = TestBed.createComponent(Repro);
await fixture.whenStable();
const combobox = fixture.nativeElement.querySelector('[ngCombobox]');
combobox.focus();
key(combobox, 'ArrowDown'); // open
await fixture.whenStable();
key(combobox, 'Home');
await fixture.whenStable();
key(combobox, 'ArrowDown');
key(combobox, 'ArrowDown');
key(combobox, 'ArrowDown');
await fixture.whenStable();
const id = combobox.getAttribute('aria-activedescendant');
expect(fixture.nativeElement.querySelector(`[id="${id}"]`).textContent.trim())
.toBe('delta'); // actual: 'bravo'
});
Expected Behavior
Each key event is handled, in order. Three Down presses move three options.
Actual Behavior
Only the last key since the effect last ran is relayed. Three Down presses move one option ("bravo" in place of "delta").
A possible fix: relay each event synchronously from the key handler (or queue them), not through a single-value signal.
Environment
- Angular: 22.2.1
- CDK/Material: @angular/cdk 22.2.3, @angular/aria 22.2.3 (also in 22.3.0-next.2 and
main) - Browser(s): Chromium (Playwright); Vitest with happy-dom
- Operating System (e.g. Windows, macOS, Ubuntu): macOS
- Dominant language
- TypeScript
- Stars
- 25k
- Forks
- 6.8k
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 67
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from angular/components
-
bug(aria/combobox): ComboboxPopup throws NG0950 when destroyed before its first change detectionPossibly taken A pull request linked to this issue is open or already merged. Openarea: aria/combobox gemini-triaged
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
angular/components#33972 ·
Maintainers usually reply within 1 day
-
bug(material/sort): hover hint arrow on an unsorted header shows the active sort direction, not the resulting direction after click.Possibly taken @lekhmanrus claimed this 12 days ago. Openarea: material/sort P3
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
angular/components#33933 ·
Maintainers usually reply within 1 day
-
bug(mat-date-picker): month shown twice in date pickerPossibly taken @PreethiPantangi claimed this 29 days ago. Openarea: material/datepicker P4
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
angular/components#33763 · 2 comments · 3 reactions ·
Maintainers usually reply within 1 day
-
Bug in table.scss footer cell height: unset instead of footer row height unsetPossibly taken @minwookshin claimed this 16 days ago. Openarea: material/table P4
Difficulty 1/5 Under an hour Newbie friendliness 88/100
angular/components#33709 · 1 comment ·
Maintainers usually reply within 1 day
-
docs-bug(Table): Add example with `rowspan`Possibly taken @BHUVANSH855 claimed this 97 days ago. Openarea: material/table docs P4
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
angular/components#33455 ·
Maintainers usually reply within 1 day
All issues in angular/components
Similar issues
-
area: app type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
ExpidetGames/code-reviewer#585 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 83/100
module-federation/core#5205 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
LanternOps/breeze#9392 · 1 comment ·
Maintainers usually reply within 1 day
-
engine-rs P3 review-deferral wasm
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
maniator/verticopolis#1154 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day