From 3a33e02083647ea20e85cf30717d533a068b59c1 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 3 Dec 2019 13:00:02 -0700 Subject: [PATCH 1/2] SHIBUI-1663 Fixed issue with announcing list items --- .../autocomplete/autocomplete.component.html | 25 +++++++++++++------ .../autocomplete/autocomplete.component.ts | 12 +++++++-- .../shared/autocomplete/autocomplete.model.ts | 6 ++--- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/ui/src/app/shared/autocomplete/autocomplete.component.html b/ui/src/app/shared/autocomplete/autocomplete.component.html index 735b3bc48..558dcb454 100644 --- a/ui/src/app/shared/autocomplete/autocomplete.component.html +++ b/ui/src/app/shared/autocomplete/autocomplete.component.html @@ -6,32 +6,38 @@ [id]="fieldId" class="form-control" autocomplete="off" + autocorrect="off" + aria-autocomplete="list" role="combobox" type="text" [placeholder]="placeholder" [attr.aria-activedescendant]="activeDescendant" - aria-autocomplete="both" [attr.aria-owns]="fieldId + '__listbox'" [attr.aria-expanded]="(menuIsVisible$ | async) ? 'true' : 'false'" + [attr.aria-haspopup]="fieldId + '__listbox'" (focus)="handleInputFocus()" (blur)="handleInputBlur()" />
- +

+ diff --git a/ui/src/app/shared/autocomplete/autocomplete.component.ts b/ui/src/app/shared/autocomplete/autocomplete.component.ts index 28a4c0365..472db6dd5 100644 --- a/ui/src/app/shared/autocomplete/autocomplete.component.ts +++ b/ui/src/app/shared/autocomplete/autocomplete.component.ts @@ -153,6 +153,12 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte }); } + handleDropdown($event: MouseEvent | KeyboardEvent | Event): void { + const open = this.state.currentState.menuOpen; + this.state.setState({menuOpen: !open}); + this.handleOptionFocus(0); + } + handleViewMore($event: MouseEvent | KeyboardEvent | Event): void { $event.preventDefault(); $event.stopPropagation(); @@ -235,6 +241,8 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte focused: index, selected: index }); + + console.log(this.activeDescendant); } handleOptionClick(index: number): void { const selectedOption = this.matches[index]; @@ -278,7 +286,6 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte } handleDownArrow(event: KeyboardEvent): void { - event.preventDefault(); let isNotAtBottom = this.state.currentState.selected !== this.matches.length - 1; if (this.showMoreAvailable) { isNotAtBottom = this.state.currentState.selected !== this.matches.length; @@ -287,6 +294,7 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte if (allowMoveDown) { this.handleOptionFocus(this.state.currentState.selected + 1); } + event.preventDefault(); } handleSpace(event: KeyboardEvent): void { @@ -344,7 +352,7 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte } getOptionId(index): string { - return `${this.fieldId}__option--${index}`; + return `${this.fieldId}__option--${index}`.replace('/', ''); } get hasAutoselect(): boolean { diff --git a/ui/src/app/shared/autocomplete/autocomplete.model.ts b/ui/src/app/shared/autocomplete/autocomplete.model.ts index 8e96dae05..83c003d91 100644 --- a/ui/src/app/shared/autocomplete/autocomplete.model.ts +++ b/ui/src/app/shared/autocomplete/autocomplete.model.ts @@ -9,9 +9,9 @@ export const defaultState: AutoCompleteState = { }; export interface AutoCompleteState { - focused: number | null; - selected: number | null; - hovered: number | null; + focused: number; + selected: number; + hovered: number; menuOpen: boolean; } From 0a29a2136a5d368bf1dd411634031280e191983e Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 4 Dec 2019 10:07:23 -0700 Subject: [PATCH 2/2] SHIBUI-1663 fixed live announcement --- .../filter-target.component.html | 3 +- .../filter-target/filter-target.component.ts | 28 ++++++++----------- .../autocomplete/autocomplete.component.html | 3 +- .../autocomplete/autocomplete.component.ts | 14 ++++------ .../shared/autocomplete/autocomplete.model.ts | 6 ++-- 5 files changed, 24 insertions(+), 30 deletions(-) diff --git a/ui/src/app/schema-form/widget/filter-target/filter-target.component.html b/ui/src/app/schema-form/widget/filter-target/filter-target.component.html index 059d0acdd..005ae6e37 100644 --- a/ui/src/app/schema-form/widget/filter-target/filter-target.component.html +++ b/ui/src/app/schema-form/widget/filter-target/filter-target.component.html @@ -52,7 +52,8 @@ class="component-control" limit="10" [formControl]="search" - [matches]="ids" + [matches]="ids$ | async" + [count]="idCount$ | async" (keydown.enter)="onSelectValue(search.value)"> diff --git a/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts b/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts index 835e2f005..7eed2fca9 100644 --- a/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts +++ b/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts @@ -3,7 +3,7 @@ import { FormControl, Validators, AbstractControl, ValidatorFn } from '@angular/ import { ObjectWidget } from 'ngx-schema-form'; import { Store } from '@ngrx/store'; import { Observable, Subject } from 'rxjs'; -import { distinctUntilChanged, skipWhile, takeUntil, map } from 'rxjs/operators'; +import { distinctUntilChanged, skipWhile, takeUntil, map, withLatestFrom, filter, switchMap, startWith } from 'rxjs/operators'; import * as fromRoot from '../../../app.reducer'; import * as fromFilters from '../../../metadata/filter/reducer'; @@ -19,7 +19,7 @@ import { QueryEntityIds, ClearSearch } from '../../../metadata/filter/action/sea export class FilterTargetComponent extends ObjectWidget implements OnDestroy, AfterViewInit { private ngUnsubscribe: Subject = new Subject(); ids$: Observable; - ids: string[]; + idCount$: Observable; search: FormControl = new FormControl( '', @@ -40,17 +40,22 @@ export class FilterTargetComponent extends ObjectWidget implements OnDestroy, Af ) { super(); this.ids$ = this.store.select(fromFilters.getEntityCollection); - this.ids$.subscribe(ids => { - this.ids = [...ids]; - }); + + this.idCount$ = this.ids$.pipe(map(list => list.length)); this.search .valueChanges .pipe( takeUntil(this.ngUnsubscribe), - distinctUntilChanged() + distinctUntilChanged(), + withLatestFrom(this.ids$), + filter(([term, ids]) => term && term.length >= 4 && ids.indexOf(term) < 0), + map(([term]) => new QueryEntityIds({ + term, + limit: 10 + })) ) - .subscribe(query => this.searchEntityIds(query)); + .subscribe(action => this.store.dispatch(action)); this.script .valueChanges @@ -101,15 +106,6 @@ export class FilterTargetComponent extends ObjectWidget implements OnDestroy, Af }; } - searchEntityIds(term: string): void { - if (term && term.length >= 4 && this.ids.indexOf(term) < 0) { - this.store.dispatch(new QueryEntityIds({ - term, - limit: 10 - })); - } - } - getButtonConfig(id: string): any { let buttons = this.formProperty.getProperty('value').schema.buttons; return (buttons || []).map(btn => ({ diff --git a/ui/src/app/shared/autocomplete/autocomplete.component.html b/ui/src/app/shared/autocomplete/autocomplete.component.html index 2611cfc28..7800382cd 100644 --- a/ui/src/app/shared/autocomplete/autocomplete.component.html +++ b/ui/src/app/shared/autocomplete/autocomplete.component.html @@ -54,8 +54,7 @@ [attr.aria-label]="this.input.value" [innerHTML]="option | highlight:this.input.value"> - diff --git a/ui/src/app/shared/autocomplete/autocomplete.component.ts b/ui/src/app/shared/autocomplete/autocomplete.component.ts index 80e360114..cda5d6406 100644 --- a/ui/src/app/shared/autocomplete/autocomplete.component.ts +++ b/ui/src/app/shared/autocomplete/autocomplete.component.ts @@ -51,6 +51,7 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte @Input() processing = false; @Input() dropdown = false; @Input() placeholder = ''; + @Input() count = null; @Output() more: EventEmitter = new EventEmitter(); @Output() onChange: EventEmitter = new EventEmitter(); @@ -118,14 +119,16 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte } ngOnChanges(changes: SimpleChanges): void { - if (changes.matches && this.matches) { + if (changes.matches && this.matches && this.state.currentState.menuOpen) { this.announceResults(); } } announceResults(): void { const count = this.matches.length; - this.live.announce(count === 0 ? 'No results available' : `${count} result${count === 1 ? '' : 's'} available`); + this.live.announce(count === 0 ? + `${this.noneFoundText}` : + `${count} result${count === 1 ? '' : 's'} available`, 'polite', 5000); } writeValue(value: any): void { @@ -222,7 +225,6 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte handleInputChange(query: string): void { query = query || ''; - const queryEmpty = query.length === 0; const autoselect = this.hasAutoselect; const optionsAvailable = this.matches.length > 0; @@ -233,8 +235,6 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte selected: searchForOptions ? ((autoselect && optionsAvailable) ? 0 : -1) : null }); this.propagateChange(query); - - setTimeout(() => this.announceResults(), 250); } handleInputFocus(): void { @@ -247,8 +247,6 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte focused: index, selected: index }); - - console.log(this.activeDescendant); } handleOptionClick(index: number): void { const selectedOption = this.matches[index]; @@ -357,7 +355,7 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte return !!(agent.match(/(iPod|iPhone|iPad)/g) && agent.match(/AppleWebKit/g)); } - getOptionId(index): string { + getOptionId(index: string | number): string { return `${this.fieldId}__option--${index}`.replace('/', ''); } diff --git a/ui/src/app/shared/autocomplete/autocomplete.model.ts b/ui/src/app/shared/autocomplete/autocomplete.model.ts index 83c003d91..8e96dae05 100644 --- a/ui/src/app/shared/autocomplete/autocomplete.model.ts +++ b/ui/src/app/shared/autocomplete/autocomplete.model.ts @@ -9,9 +9,9 @@ export const defaultState: AutoCompleteState = { }; export interface AutoCompleteState { - focused: number; - selected: number; - hovered: number; + focused: number | null; + selected: number | null; + hovered: number | null; menuOpen: boolean; }