Skip to content

Commit

Permalink
Merged in bugfix/SHIBUI-257 (pull request #43)
Browse files Browse the repository at this point in the history
Fixed autocomplete a11y for list options

Approved-by: Ryan Mathis <rmathis@unicon.net>
  • Loading branch information
rmathis committed Mar 30, 2018
2 parents 5ce26f9 + 046af1f commit d6737d0
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 15 deletions.
13 changes: 10 additions & 3 deletions ui/src/app/shared/autocomplete/autocomplete.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
class="dropdown-item"
[class.active]="state.currentState.selected === i"
[class.focused]="state.currentState.focused === i"
id="{{ id }}__option--{{ i }}"
[id]="this.getOptionId(i)"
(focusout)="handleOptionBlur($event, i)"
(click)="handleOptionClick(i)"
(mousedown)="handleOptionMouseDown($event)"
Expand All @@ -35,8 +35,15 @@
<li class="dropdown-item dropdown-item-noresults" *ngIf="matches && matches.length === 0 && !processing">
{{ noneFoundText }}
</li>
<li class="dropdown-item" *ngIf="showMoreAvailable">
<a href="" (click)="handleViewMore($event)" (mousedown)="handleViewMore($event)">{{ showMoreText }}</a>
<li class="dropdown-item"
*ngIf="showMoreAvailable && matches"
[class.active]="state.currentState.selected === matches.length"
[class.focused]="state.currentState.focused === matches.length"
(click)="handleViewMore($event)"
(mousedown)="handleViewMore($event)"
(keydown.enter)="handleViewMore($event)"
[id]="this.getOptionId(matches.length)">
{{ showMoreText }}
</li>
</ul>
</div>
12 changes: 8 additions & 4 deletions ui/src/app/shared/autocomplete/autocomplete.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,20 +226,23 @@ describe('AutoComplete Input Component', () => {
describe('handleEnter handler', () => {
const opts = ['foo', 'bar', 'baz'];
it('should call preventDefault on the provided event if the menu is currently open', () => {
const ev = { preventDefault: jasmine.createSpy('preventDefault') };
const ev = { preventDefault: () => {} } as KeyboardEvent;
spyOn(ev, 'preventDefault');
instanceUnderTest.state.setState({ menuOpen: true});
instanceUnderTest.handleEnter(ev);
expect(ev.preventDefault).toHaveBeenCalled();
});
it('should NOT call preventDefault on the provided event if the menu is not open', () => {
const ev = { preventDefault: jasmine.createSpy('preventDefault') };
const ev = { preventDefault: () => { } } as KeyboardEvent;
spyOn(ev, 'preventDefault');
instanceUnderTest.state.setState({ menuOpen: false });
instanceUnderTest.handleEnter(ev);
expect(ev.preventDefault).not.toHaveBeenCalled();
});

it('should call componentBlur if there is no selected option and the query is not in the options', () => {
const ev = { preventDefault: jasmine.createSpy('preventDefault') };
const ev = { preventDefault: () => { } } as KeyboardEvent;
spyOn(ev, 'preventDefault');
spyOn(instanceUnderTest, 'handleComponentBlur');
instanceUnderTest.state.setState({ menuOpen: true, selected: -1 });
instanceUnderTest.handleEnter(ev);
Expand All @@ -253,7 +256,8 @@ describe('AutoComplete Input Component', () => {
it('should call handleOptionClick if there is no selected option but the query is in the options', () => {
const i = 0;
const val = opts[i];
const ev = { preventDefault: jasmine.createSpy('preventDefault') };
const ev = { preventDefault: () => { } } as KeyboardEvent;
spyOn(ev, 'preventDefault');
testHostInstance.configure({ options: opts });
instanceUnderTest.input.setValue(val);
testHostFixture.detectChanges();
Expand Down
24 changes: 16 additions & 8 deletions ui/src/app/shared/autocomplete/autocomplete.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
});
}

handleViewMore($event: MouseEvent): void {
handleViewMore($event: MouseEvent | KeyboardEvent | Event): void {
$event.preventDefault();
$event.stopPropagation();
this.handleInputBlur();
Expand Down Expand Up @@ -274,9 +274,12 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte

handleDownArrow(event: KeyboardEvent): void {
event.preventDefault();
const isNotAtBottom = this.state.currentState.selected !== this.matches.length - 1;
let isNotAtBottom = this.state.currentState.selected !== this.matches.length - 1;
if (this.showMoreAvailable) {
isNotAtBottom = this.state.currentState.selected !== this.matches.length;
}
const allowMoveDown = isNotAtBottom && this.state.currentState.menuOpen;
if (allowMoveDown || this.showMoreAvailable) {
if (allowMoveDown) {
this.handleOptionFocus(this.state.currentState.selected + 1);
}
}
Expand All @@ -289,7 +292,7 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
}
}

handleEnter(event: KeyboardEvent | { preventDefault: () => {} }): void {
handleEnter(event: KeyboardEvent): void {
let { selected, menuOpen } = this.state.currentState,
query = this.input.value;
if (menuOpen) {
Expand All @@ -300,8 +303,10 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
hasSelectedOption = queryIndex > -1;
selected = hasSelectedOption ? queryIndex : selected;
}
if (hasSelectedOption) {
if (hasSelectedOption && selected < this.matches.length) {
this.handleOptionClick(selected);
} else if (hasSelectedOption && selected === this.matches.length) {
this.handleViewMore(event as KeyboardEvent);
} else {
this.handleComponentBlur({
focused: -1,
Expand Down Expand Up @@ -333,15 +338,18 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
return !!(agent.match(/(iPod|iPhone|iPad)/g) && agent.match(/AppleWebKit/g));
}

getOptionId(index): string {
return `${this.id}__option--${index}`;
}

get hasAutoselect(): boolean {
return this.isIosDevice() ? false : this.autoSelect;
}

get activeDescendant (): string {
let state = this.state.currentState,
focused = state.focused,
let { focused } = this.state.currentState,
optionFocused = focused !== -1 && focused !== null;
return optionFocused ? `${ this.id }__option--${ focused }` : 'false';
return optionFocused ? this.getOptionId(focused) : null;
}

get displayState(): any {
Expand Down

0 comments on commit d6737d0

Please sign in to comment.