[ADF-1924] - Some issues related to search bar (#2702)

* issue #1

Because the input element gets no focus when the search button is clicked, there is no way to toggle off the search input, when user clicks outside the search bar
Added focus on search input when the user click on search input

issue #2

Increased the width of the search results container in order to be able to show more text
translated results container to the left in order to prevent it to overlap the menu items from the navigation bar

* Adding test spec for checking if search input has focus after the user clicks on search button

* removed deprecated render dependency
using this call
	" searchInput.nativeElement.focus()"
to enable input focus

* fixed broken animation on input search focus()

* Fix search control component tests

* Fix rebasing
This commit is contained in:
mihai sirghe 2017-11-28 18:24:27 +02:00 committed by Eugenio Romano
parent c763ba45e0
commit 634e65af96
4 changed files with 64 additions and 34 deletions

View File

@ -1,5 +1,5 @@
<div class="adf-search-container"> <div class="adf-search-container">
<div *ngIf="isLoggedIn()" [@transitionMessages]="subscriptAnimationState"> <div *ngIf="isLoggedIn()" [@transitionMessages]="subscriptAnimationState" (@transitionMessages.done)="applySearchFocus($event)">
<a mat-icon-button <a mat-icon-button
*ngIf="expandable" *ngIf="expandable"
id="adf-search-button" id="adf-search-button"
@ -9,8 +9,7 @@
<mat-icon aria-label="search button">search</mat-icon> <mat-icon aria-label="search button">search</mat-icon>
</a> </a>
<mat-form-field class="adf-input-form-field-divider"> <mat-form-field class="adf-input-form-field-divider">
<input matInput <input matInput #searchInput
#inputSearch
[type]="inputType" [type]="inputType"
[autocomplete]="getAutoComplete()" [autocomplete]="getAutoComplete()"
id="adf-control-input" id="adf-control-input"

View File

@ -35,8 +35,9 @@
&-search-result-autocomplete { &-search-result-autocomplete {
@include mat-menu-base(2); @include mat-menu-base(2);
transform-origin: top left; transform-origin: top left;
transform:translateX(-40px);
position: absolute; position: absolute;
max-width: 200px; max-width: 235px;
max-height: 400px; max-height: 400px;
margin-left: 45px; margin-left: 45px;
margin-top: -22px; margin-top: -22px;
@ -47,7 +48,6 @@
border-radius: $mat-menu-border-radius; border-radius: $mat-menu-border-radius;
@media screen and ($mat-small) { @media screen and ($mat-small) {
width: 160px;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;

View File

@ -16,7 +16,8 @@
*/ */
import { DebugElement } from '@angular/core'; import { DebugElement } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, discardPeriodicTasks, fakeAsync, ComponentFixture, TestBed, tick } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MaterialModule } from '../../material.module'; import { MaterialModule } from '../../material.module';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { AuthenticationService, SearchService } from '@alfresco/adf-core'; import { AuthenticationService, SearchService } from '@alfresco/adf-core';
@ -39,7 +40,8 @@ describe('SearchControlComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
MaterialModule MaterialModule,
NoopAnimationsModule
], ],
declarations: [ declarations: [
SearchControlComponent, SearchControlComponent,
@ -408,31 +410,53 @@ describe('SearchControlComponent', () => {
describe('search button', () => { describe('search button', () => {
it('should NOT display a autocomplete list control when configured not to', async(() => { it('should NOT display a autocomplete list control when configured not to', fakeAsync(() => {
fixture.detectChanges(); fixture.detectChanges();
let searchButton: DebugElement = debugElement.query(By.css('#adf-search-button')); let searchButton: DebugElement = debugElement.query(By.css('#adf-search-button'));
component.subscriptAnimationState = 'active'; component.subscriptAnimationState = 'active';
fixture.detectChanges(); fixture.detectChanges();
expect(component.subscriptAnimationState).toBe('active'); expect(component.subscriptAnimationState).toBe('active');
searchButton.triggerEventHandler('click', null); searchButton.triggerEventHandler('click', null);
window.setTimeout(() => { fixture.detectChanges();
tick(100);
fixture.detectChanges(); fixture.detectChanges();
expect(component.subscriptAnimationState).toBe('inactive'); expect(component.subscriptAnimationState).toBe('inactive');
}, 100); discardPeriodicTasks();
})); }));
it('click on the search button should open the input box when is close', (done) => { it('click on the search button should open the input box when is close', fakeAsync(() => {
fixture.detectChanges(); fixture.detectChanges();
let searchButton: DebugElement = debugElement.query(By.css('#adf-search-button')); let searchButton: DebugElement = debugElement.query(By.css('#adf-search-button'));
searchButton.triggerEventHandler('click', null); searchButton.triggerEventHandler('click', null);
window.setTimeout(() => {
tick(100);
fixture.detectChanges(); fixture.detectChanges();
expect(component.subscriptAnimationState).toBe('active'); expect(component.subscriptAnimationState).toBe('active');
done(); discardPeriodicTasks();
}, 100); }));
});
it('Search button should not change the input state too often', async(() => { it('click on the search button should apply focus on input', fakeAsync(() => {
fixture = TestBed.createComponent(SearchControlComponent);
debugElement = fixture.debugElement;
fixture.detectChanges();
let searchButton: DebugElement = debugElement.query(By.css('#adf-search-button'));
let inputDebugElement = debugElement.query(By.css('#adf-control-input'));
searchButton.triggerEventHandler('click', null);
tick(100);
fixture.detectChanges();
tick(300);
fixture.detectChanges();
expect(document.activeElement.id).toBe(inputDebugElement.nativeElement.id);
discardPeriodicTasks();
}));
it('Search button should not change the input state too often', fakeAsync(() => {
fixture.detectChanges(); fixture.detectChanges();
let searchButton: DebugElement = debugElement.query(By.css('#adf-search-button')); let searchButton: DebugElement = debugElement.query(By.css('#adf-search-button'));
component.subscriptAnimationState = 'active'; component.subscriptAnimationState = 'active';
@ -443,13 +467,13 @@ describe('SearchControlComponent', () => {
searchButton.triggerEventHandler('click', null); searchButton.triggerEventHandler('click', null);
fixture.detectChanges(); fixture.detectChanges();
window.setTimeout(() => { tick(100);
fixture.detectChanges(); fixture.detectChanges();
expect(component.subscriptAnimationState).toBe('inactive'); expect(component.subscriptAnimationState).toBe('inactive');
}, 100); discardPeriodicTasks();
})); }));
it('Search bar should close when user press ESC button', async(() => { it('Search bar should close when user press ESC button', fakeAsync(() => {
fixture.detectChanges(); fixture.detectChanges();
let inputDebugElement = debugElement.query(By.css('#adf-control-input')); let inputDebugElement = debugElement.query(By.css('#adf-control-input'));
component.subscriptAnimationState = 'active'; component.subscriptAnimationState = 'active';
@ -457,10 +481,11 @@ describe('SearchControlComponent', () => {
expect(component.subscriptAnimationState).toBe('active'); expect(component.subscriptAnimationState).toBe('active');
inputDebugElement.triggerEventHandler('keyup.escape', {}); inputDebugElement.triggerEventHandler('keyup.escape', {});
window.setTimeout(() => {
tick(100);
fixture.detectChanges(); fixture.detectChanges();
expect(component.subscriptAnimationState).toBe('inactive'); expect(component.subscriptAnimationState).toBe('inactive');
}, 100); discardPeriodicTasks();
})); }));
}); });

View File

@ -78,8 +78,8 @@ export class SearchControlComponent implements OnInit, OnDestroy {
@ViewChild(SearchComponent) @ViewChild(SearchComponent)
searchAutocomplete: SearchComponent; searchAutocomplete: SearchComponent;
@ViewChild('inputSearch') @ViewChild('searchInput')
inputSearch: ElementRef; searchInput: ElementRef;
@ViewChildren(MatListItem) @ViewChildren(MatListItem)
private listResultElement: QueryList<MatListItem>; private listResultElement: QueryList<MatListItem>;
@ -100,14 +100,20 @@ export class SearchControlComponent implements OnInit, OnDestroy {
if (this.subscriptAnimationState === 'inactive') { if (this.subscriptAnimationState === 'inactive') {
this.searchTerm = ''; this.searchTerm = '';
this.searchAutocomplete.resetResults(); this.searchAutocomplete.resetResults();
if ( document.activeElement.id === this.inputSearch.nativeElement.id) { if ( document.activeElement.id === this.searchInput.nativeElement.id) {
this.inputSearch.nativeElement.blur(); this.searchInput.nativeElement.blur();
} }
} }
} }
}); });
} }
applySearchFocus(animationDoneEvent) {
if (animationDoneEvent.toState === 'active') {
this.searchInput.nativeElement.focus();
}
}
ngOnInit() { ngOnInit() {
this.subscriptAnimationState = this.expandable ? 'inactive' : 'no-animation'; this.subscriptAnimationState = this.expandable ? 'inactive' : 'no-animation';
this.setupFocusEventHandlers(); this.setupFocusEventHandlers();
@ -205,7 +211,7 @@ export class SearchControlComponent implements OnInit, OnDestroy {
if (previousElement) { if (previousElement) {
previousElement.focus(); previousElement.focus();
}else { }else {
this.inputSearch.nativeElement.focus(); this.searchInput.nativeElement.focus();
this.focusSubject.next(new FocusEvent('focus')); this.focusSubject.next(new FocusEvent('focus'));
} }
} }