siAutocomplete: active reports the last option when siAutocompleteDefaultIndex is -1

Open Beginner friendly
#3,197 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
85/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
angular, typescript

Research direction

Read projects/element-ng/autocomplete/si-autocomplete-listbox.directive.ts, especially the active getter and setActiveItem(). Check the autocomplete tests for coverage of a default index of -1, then run the relevant test suite. Done means active is null and aria-activedescendant is empty until an option becomes active.

Written by the indexing model from the issue text.

Description

Prerequisites
  • I have read the Contributing Guidelines.
  • I have not leaked any internal/restricted information like screenshots, videos, code snippets, links etc.
Affected component

siAutocomplete (SiAutocompleteListboxDirective.active), and through it si-typeahead with
typeaheadAutoSelectIndex -1

Summary

With siAutocompleteDefaultIndex -1 (no option active when the list opens), active returns the last option
although none is active. The combobox's aria-activedescendant therefore points at the last option, so a screen reader
announces it as the current one, and SiAutocompleteDirective.active hands it to the consumer.

Version
  • @siemens/element-ng 51.6.0 (also on main, 46b5bac,
    projects/element-ng/autocomplete/si-autocomplete-listbox.directive.ts)
  • Angular 22, @angular/cdk 22, Chromium
Steps to reproduce
  1. <input [siTypeahead]="options" [typeaheadAutoSelectIndex]="-1" /> with a few options.
  2. Type so that the list opens with several suggestions, and do not press an arrow key.
  3. Look at the input's aria-activedescendant (or read SiAutocompleteDirective.active).
Code reproduction example

No hosted example; the steps above reproduce it with any typeahead.

What is the current bug behavior

setActiveItem() calls keyManager.setActiveItem(siAutocompleteDefaultIndex()), so with -1 the CDK key manager has
activeItemIndex -1 and activeItem null. The getter only checks for null:

get active(): SiAutocompleteOptionDirective<T> | null {
  if (this.keyManager.activeItemIndex === null) {
    return null;
  }
  return this.options().at(this.keyManager.activeItemIndex) ?? null;
}

Array.prototype.at(-1) is the last element, so active is the last option. aria-activedescendant names its id,
while no option has the active class and Enter does not select it (onKeydown correctly uses
keyManager.activeItem).

What is the expected correct behavior

active is null and aria-activedescendant is empty until an option is made active.

Possible fix
--- a/projects/element-ng/autocomplete/si-autocomplete-listbox.directive.ts
+++ b/projects/element-ng/autocomplete/si-autocomplete-listbox.directive.ts
   get active(): SiAutocompleteOptionDirective<T> | null {
     // NOTE: We must not return `this.keyManager.activeItem` here, because its not updating
     // activeItem reference when options change.
-    if (this.keyManager.activeItemIndex === null) {
+    const index = this.keyManager.activeItemIndex;
+    if (index === null || index < 0) {
       return null;
     }
-    return this.options().at(this.keyManager.activeItemIndex) ?? null;
+    return this.options()[index] ?? null;
   }

Related: #2569 moves this active-option handling to @angular/aria; until then the current directive reports the
wrong option.

Dominant language
TypeScript
Stars
85
Forks
28
Avg merge
1d 23h
Merged PRs (30d)
162

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from siemens/element

All issues in siemens/element

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.