Skip to content

Commit

Permalink
Merged in bugfix/SHIBUI-1471 (pull request #423)
Browse files Browse the repository at this point in the history
SHIBUI-1471 Fixed issue with autocomplete components

Approved-by: Ryan Mathis <rmathis@unicon.net>
  • Loading branch information
rmathis committed Nov 19, 2019
2 parents 0957c89 + c6890f6 commit 6522b71
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 75 deletions.
20 changes: 18 additions & 2 deletions ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"private": true,
"dependencies": {
"@angular/animations": "8.2.11",
"@angular/cdk": "^8.2.3",
"@angular/common": "8.2.11",
"@angular/compiler": "8.2.11",
"@angular/core": "8.2.11",
Expand Down Expand Up @@ -73,4 +74,4 @@
"typescript": "3.5.3",
"webpack-bundle-analyzer": "^3.3.2"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ <h3 class="tag tag-primary">
</label>
<auto-complete
id="target"
fieldId="target"
formControlName="target"
[matches]="searchResults$ | async"
[required]="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="widget form-group">
<label [for]="formProperty._canonicalPath" class="d-flex justify-content-between control-label">
<label [id]="formProperty._canonicalPath + '-label'" [for]="formProperty._canonicalPath" class="d-flex justify-content-between control-label">
<span [ngClass]="{'sr-only': formProperty.parent.schema.type === 'array'}">
<translate-i18n [key]="title">{{ title }}</translate-i18n>
<i class="fa fa-asterisk text-danger" aria-hidden="true" *ngIf="required"></i>
Expand All @@ -10,16 +10,17 @@
</label>
<span *ngIf="schema.description" class="sr-only" [translate]="schema.description">{{ schema.description }}</span>
<auto-complete
[id]="formProperty._canonicalPath"
[id]="formProperty._canonicalPath + 'autocomplete'"
[fieldId]="formProperty._canonicalPath"
[formControl]="control"
[matches]="schema.widget.data"
[matches]="data"
[required]="true"
[dropdown]="true"
[attr.placeholder]="schema.placeholder || '' | translate"
[attr.disabled]="schema.readOnly?true:null"
role="textbox"
[attr.aria-label]="title | translate">
[attr.aria-label]="title | translate"
(onChange)="getData($event)">
</auto-complete>
<small class="form-text text-danger" *ngIf="errorMessages && errorMessages.length && control.touched">
<ng-container *ngFor="let error of errorMessages; let first=first;">
Expand Down
7 changes: 7 additions & 0 deletions ui/src/app/schema-form/widget/datalist/datalist.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { HARD_CODED_REQUIRED_MSG } from '../../model/messages';
templateUrl: `./datalist.component.html`
})
export class DatalistComponent extends ControlWidget implements AfterViewInit {

data: string[] = [];

constructor(
private widgetService: SchemaService
) {
Expand All @@ -24,6 +27,10 @@ export class DatalistComponent extends ControlWidget implements AfterViewInit {
}
}

getData(query: string): void {
this.data = [...this.schema.widget.data];
}

get required(): boolean {
return this.widgetService.isRequired(this.formProperty);
}
Expand Down
106 changes: 50 additions & 56 deletions ui/src/app/shared/autocomplete/autocomplete.component.html
Original file line number Diff line number Diff line change
@@ -1,58 +1,52 @@
<div id="{{ id }}-container"
class="dropdown form-group"
(keydown)="handleKeyDown($event)"
role='combobox'
[attr.aria-expanded]="(menuIsVisible$ | async) ? 'true' : 'false'"
[attr.aria-label]="id">
<div [ngClass]="{'input-group': dropdown}">
<input
#inputField
[formControl]="input"
[disableValidation]="true"
[id]="fieldId || (id + '__input')"
class="form-control"
autoComplete="off"
type="text"
[attr.aria-activedescendant]="activeDescendant"
[placeholder]="placeholder"
attr.aria-owns="{{ id }}__listbox"
[attr.aria-labelledby]="id"
(focus)="handleInputFocus()"
(blur)="handleInputBlur()"
/>
<div class="input-group-append" *ngIf="dropdown">
<button class="btn btn-outline-secondary"
type="button"
aria-haspopup="true"
aria-expanded="false"
[attr.aria-labelledby]="id"
(click)="state.setState({menuOpen: !state.currentState.menuOpen})">
<i class="fa fa-caret-down"></i>
<span class="sr-only">Toggle Dropdown</span>
</button>
</div>
<div [ngClass]="{'input-group': dropdown}">
<input
#inputField
[formControl]="input"
[disableValidation]="true"
[id]="fieldId"
class="form-control"
autocomplete="off"
role="combobox"
type="text"
[placeholder]="placeholder"
[attr.aria-activedescendant]="activeDescendant"
aria-autocomplete="both"
aria-multiline="false"
[attr.aria-owns]="fieldId + '__listbox'"
[attr.aria-expanded]="(menuIsVisible$ | async) ? 'true' : 'false'"
(focus)="handleInputFocus()"
(blur)="handleInputBlur()"
/>
<div class="input-group-append" *ngIf="dropdown">
<button class="btn btn-outline-secondary"
type="button"
aria-haspopup="true"
[attr.aria-expanded]="state.currentState.menuOpen"
(click)="state.setState({menuOpen: !state.currentState.menuOpen})">
<i class="fa fa-caret-down"></i>
<span class="sr-only">Toggle Dropdown</span>
</button>
</div>
<ul class="dropdown-menu"
id="{{ id }}__listbox"
role="listbox"
[class.show]="(menuIsVisible$ | async) && !processing && !input.disabled">
<li *ngFor="let option of matches; let i = index;"
#matchElement
class="dropdown-item"
[class.active]="state.currentState.selected === i"
[class.focused]="state.currentState.focused === i"
[id]="this.getOptionId(i)"
(focusout)="handleOptionBlur($event, i)"
(click)="handleOptionClick(i)"
(mousedown)="handleOptionMouseDown($event)"
(mouseenter)="handleOptionMouseEnter(i)"
(mouseout)="handleOptionMouseOut()"
role="option"
tabIndex="-1"
[attr.aria-selected]="state.currentState.focused === i"
[innerHTML]="option | highlight:this.input.value"></li>
<li class="dropdown-item dropdown-item-noresults" *ngIf="matches && matches.length === 0 && !processing">
{{ noneFoundText }}
</li>
</ul>
</div>
<ul class="dropdown-menu"
[id]="fieldId + '__listbox'"
role="listbox"
[class.show]="(menuIsVisible$ | async) && !processing && !input.disabled">
<li *ngFor="let option of matches; let i = index;"
#matchElement
class="dropdown-item"
[class.active]="state.currentState.selected === i"
[class.focused]="state.currentState.focused === i"
[id]="this.getOptionId(i)"
(focusout)="handleOptionBlur($event, i)"
(click)="handleOptionClick(i)"
(mousedown)="handleOptionMouseDown($event)"
(mouseenter)="handleOptionMouseEnter(i)"
(mouseout)="handleOptionMouseOut()"
role="option"
[attr.aria-selected]="state.currentState.focused === i"
[innerHTML]="option | highlight:this.input.value"></li>
<li class="dropdown-item dropdown-item-noresults" *ngIf="matches && matches.length === 0 && !processing">
{{ noneFoundText }}
</li>
</ul>
2 changes: 2 additions & 0 deletions ui/src/app/shared/autocomplete/autocomplete.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
}

:host {
position: relative;

.btn-outline-secondary {
border-color: $input-border-color;
}
Expand Down
30 changes: 18 additions & 12 deletions ui/src/app/shared/autocomplete/autocomplete.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@ import {
EventEmitter,
OnInit,
OnDestroy,
OnChanges,
AfterViewInit,
ViewChild,
ViewChildren,
QueryList,
ElementRef,
SimpleChanges,
forwardRef,
ChangeDetectionStrategy,
OnChanges,
HostListener
HostListener,
SimpleChanges
} from '@angular/core';
import { FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable, Subject, Subscription, interval } from 'rxjs';
import { takeUntil, combineLatest, map } from 'rxjs/operators';

import { LiveAnnouncer } from '@angular/cdk/a11y';

import { keyCodes } from '../../shared/keycodes';
import { AutoCompleteStateEmitter } from './autocomplete.model';
import { NavigatorService } from '../../core/service/navigator.service';
Expand All @@ -28,7 +29,6 @@ const POLL_TIMEOUT = 1000;
const INPUT_FIELD_INDEX = -1;

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'auto-complete',
templateUrl: './autocomplete.component.html',
styleUrls: ['./autocomplete.component.scss'],
Expand All @@ -40,7 +40,7 @@ const INPUT_FIELD_INDEX = -1;
}
]
})
export class AutoCompleteComponent implements OnInit, OnDestroy, AfterViewInit, ControlValueAccessor {
export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit, ControlValueAccessor {
@Input() defaultValue = '';
@Input() matches: string[] = [];
@Input() id: string;
Expand Down Expand Up @@ -69,8 +69,6 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, AfterViewInit,
$pollSubscription: Subscription;

showMoreAvailable: boolean;

matches$: Observable<string[]>;
numMatches: number;

input: FormControl = new FormControl();
Expand All @@ -84,7 +82,7 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, AfterViewInit,
propagateChange = (_: any | null) => { };
propagateTouched = (_: any | null) => { };

constructor(private navigator: NavigatorService) {}
constructor(private navigator: NavigatorService, private live: LiveAnnouncer) {}

ngOnInit(): void {
this.$pollInput = interval(POLL_TIMEOUT);
Expand Down Expand Up @@ -116,7 +114,14 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, AfterViewInit,
}

ngAfterViewInit(): void {
this.listItems.changes.subscribe((changes) => this.setElementReferences(changes));
this.listItems.changes.subscribe((changes) => this.setElementReferences(changes) );
}

ngOnChanges(changes: SimpleChanges): void {
if (changes.matches && this.matches) {
const count = this.matches.length;
this.live.announce(count === 0 ? 'No results available' : `${count} result${count === 1 ? '' : 's'} available`);
}
}

writeValue(value: any): void {
Expand Down Expand Up @@ -242,6 +247,7 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, AfterViewInit,
});
}

@HostListener('keydown', ['$event'])
handleKeyDown(event: KeyboardEvent): void {
switch (keyCodes[event.keyCode]) {
case 'up':
Expand Down Expand Up @@ -338,7 +344,7 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, AfterViewInit,
}

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

get hasAutoselect(): boolean {
Expand Down

0 comments on commit 6522b71

Please sign in to comment.