Skip to content

Commit d1504f1

Browse files
committed
fix(dropdown-list): element iterators should operate on the displayItems
1 parent 5878c2f commit d1504f1

File tree

1 file changed

+48
-29
lines changed

1 file changed

+48
-29
lines changed

src/dropdown/list/dropdown-list.component.ts

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import {
77
TemplateRef,
88
AfterViewInit,
99
ViewChild,
10-
ElementRef
10+
ElementRef,
11+
ViewChildren,
12+
QueryList
1113
} from "@angular/core";
1214

1315
import { I18n } from "../../i18n/i18n.module";
@@ -51,7 +53,9 @@ import { Observable, isObservable, Subscription } from "rxjs";
5153
role="listbox"
5254
class="bx--list-box__menu"
5355
[attr.aria-label]="ariaLabel">
54-
<li tabindex="-1"
56+
<li
57+
#listItem
58+
tabindex="-1"
5559
role="option"
5660
*ngFor="let item of displayItems; let i = index"
5761
(click)="doClick($event, item)"
@@ -116,6 +120,14 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
116120
* Event to emit selection of a list item within the `DropdownList`.
117121
*/
118122
@Output() select: EventEmitter<Object> = new EventEmitter<Object>();
123+
/**
124+
* Event to suggest a blur on the view.
125+
* Emits _after_ the first/last item has been focussed
126+
* ex.
127+
* ArrowUp -> focus first item
128+
* ArrowUp -> emit event
129+
*/
130+
@Output() blur = new EventEmitter<"top" | "bottom">();
119131
/**
120132
* Maintains a reference to the view DOM element for the unordered list of items within the `DropdownList`.
121133
*/
@@ -146,7 +158,7 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
146158
/**
147159
* An array holding the HTML list elements in the view.
148160
*/
149-
protected listElementList: HTMLElement[];
161+
@ViewChildren("listItem") protected listElementList: QueryList<ElementRef>;
150162
/**
151163
* Observable bound to keydown events to control filtering.
152164
*/
@@ -174,7 +186,6 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
174186
* Additionally, any Observables for the `DropdownList` are initialized.
175187
*/
176188
ngAfterViewInit() {
177-
this.listElementList = Array.from(this.list.nativeElement.querySelectorAll("li")) as HTMLElement[];
178189
this.index = this.getListItems().findIndex(item => item.selected);
179190
this.setupFocusObservable();
180191
}
@@ -194,9 +205,6 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
194205
updateList(items) {
195206
this._items = items.map(item => Object.assign({}, item));
196207
this.displayItems = this._items;
197-
setTimeout(() => {
198-
this.listElementList = Array.from(this.list.nativeElement.querySelectorAll("li")) as HTMLElement[];
199-
}, 0);
200208
this.index = this._items.findIndex(item => item.selected);
201209
this.setupFocusObservable();
202210
setTimeout(() => {
@@ -217,6 +225,8 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
217225
} else {
218226
this.displayItems = this.getListItems();
219227
}
228+
// reset the index since the list has changed visually
229+
this.index = 0;
220230
}
221231

222232
/**
@@ -238,18 +248,18 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
238248
* Returns the `ListItem` that is subsequent to the selected item in the `DropdownList`.
239249
*/
240250
getNextItem(): ListItem {
241-
if (this.index < this.getListItems().length - 1) {
251+
if (this.index < this.displayItems.length - 1) {
242252
this.index++;
243253
}
244-
return this.getListItems()[this.index];
254+
return this.displayItems[this.index];
245255
}
246256

247257
/**
248258
* Returns `true` if the selected item is not the last item in the `DropdownList`.
249259
* TODO: standardize
250260
*/
251261
hasNextElement(): boolean {
252-
if (this.index < this.getListItems().length - 1) {
262+
if (this.index < this.displayItems.length - 1) {
253263
return true;
254264
}
255265
return false;
@@ -259,11 +269,11 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
259269
* Returns the `HTMLElement` for the item that is subsequent to the selected item.
260270
*/
261271
getNextElement(): HTMLElement {
262-
if (this.index < this.getListItems().length - 1) {
272+
if (this.index < this.displayItems.length - 1) {
263273
this.index++;
264274
}
265-
let elem = this.listElementList[this.index];
266-
let item = this.getListItems()[this.index];
275+
let elem = this.listElementList.toArray()[this.index].nativeElement;
276+
let item = this.displayItems[this.index];
267277
if (item.disabled) {
268278
return this.getNextElement();
269279
}
@@ -277,7 +287,7 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
277287
if (this.index > 0) {
278288
this.index--;
279289
}
280-
return this.getListItems()[this.index];
290+
return this.displayItems[this.index];
281291
}
282292

283293
/**
@@ -298,8 +308,8 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
298308
if (this.index > 0) {
299309
this.index--;
300310
}
301-
let elem = this.listElementList[this.index];
302-
let item = this.getListItems()[this.index];
311+
let elem = this.listElementList.toArray()[this.index].nativeElement;
312+
let item = this.displayItems[this.index];
303313
if (item.disabled) {
304314
return this.getPrevElement();
305315
}
@@ -311,19 +321,19 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
311321
*/
312322
getCurrentItem(): ListItem {
313323
if (this.index < 0) {
314-
return this.getListItems()[0];
324+
return this.displayItems[0];
315325
}
316-
return this.getListItems()[this.index];
326+
return this.displayItems[this.index];
317327
}
318328

319329
/**
320330
* Returns the `HTMLElement` for the item that is selected within the `DropdownList`.
321331
*/
322332
getCurrentElement(): HTMLElement {
323333
if (this.index < 0) {
324-
return this.listElementList[0];
334+
return this.listElementList.first.nativeElement;
325335
}
326-
return this.listElementList[this.index];
336+
return this.listElementList.toArray()[this.index].nativeElement;
327337
}
328338

329339
/**
@@ -374,6 +384,10 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
374384
* Initializes focus in the list, effectively a wrapper for `getCurrentElement().focus()`
375385
*/
376386
initFocus() {
387+
// ensure we start at this first item if nothing is already selected
388+
if (this.index < 0) {
389+
this.index = 0;
390+
}
377391
this.getCurrentElement().focus();
378392
}
379393

@@ -389,14 +403,17 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
389403
}
390404
} else if (event.key === "ArrowDown" || event.key === "ArrowUp" || event.key === "Down" || event.key === "Up") {
391405
event.preventDefault();
392-
// this.checkScrollArrows();
393-
if ((event.key === "ArrowDown" || event.key === "Down") && this.hasNextElement()) {
394-
this.getNextElement().focus();
406+
if (event.key === "ArrowDown" || event.key === "Down") {
407+
if (this.hasNextElement()) {
408+
this.getNextElement().focus();
409+
} else {
410+
this.blur.emit("bottom");
411+
}
395412
} else if (event.key === "ArrowUp" || event.key === "Up") {
396413
if (this.hasPrevElement()) {
397414
this.getPrevElement().focus();
398-
} else if (this.getSelected()) {
399-
this.clearSelected.nativeElement.focus();
415+
} else {
416+
this.blur.emit("top");
400417
}
401418
}
402419
}
@@ -425,12 +442,14 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
425442
}
426443

427444
onItemFocus(index) {
428-
this.listElementList[index].classList.add("bx--list-box__menu-item--highlighted");
429-
this.listElementList[index].tabIndex = 0;
445+
const element = this.listElementList.toArray()[index].nativeElement;
446+
element.classList.add("bx--list-box__menu-item--highlighted");
447+
element.tabIndex = 0;
430448
}
431449

432450
onItemBlur(index) {
433-
this.listElementList[index].classList.remove("bx--list-box__menu-item--highlighted");
434-
this.listElementList[index].tabIndex = -1;
451+
const element = this.listElementList.toArray()[index].nativeElement;
452+
element.classList.remove("bx--list-box__menu-item--highlighted");
453+
element.tabIndex = -1;
435454
}
436455
}

0 commit comments

Comments
 (0)