[ADF-1238] Search debounce click issue fix and minor style clean (#2361)

* fix search issues no result table not shown
minor issues style button

* fix issue animation search input field debouncing toggle search

* fix test search
remove expandable probeprties

* remove test expand

* ripristinate expandable properties

* fix test
This commit is contained in:
Eugenio Romano
2017-09-21 20:13:37 +01:00
committed by Denys Vuika
parent fbaf901462
commit 62f6ca0d08
20 changed files with 128 additions and 78 deletions

View File

@@ -48,9 +48,9 @@ Contains the Search and Search Results components.
| --- | --- | --- | --- |
| searchTerm | string | | Search term to pre-populate the field with |
| inputType | string | "text" | Type of the input field to render, e.g. "search" or "text" (default) |
| expandable | boolean | true | Whether to use an expanding search control, if false then a regular input is used. |
| autocomplete | boolean | true | Whether the browser should offer field auto-completion for the input field to the user. |
| highlight | boolean | false | Use the true value if you want to see the searched word highlighted. |
| expandable | boolean | true | Whether to use an expanding search control, if false then a regular input is used. |
| liveSearchEnabled | boolean | true | Whether find-as-you-type suggestions should be offered for matching content items. Set to false to disable. |
| liveSearchRoot | string | "-root-" | NodeRef or node name where the search should start. |
| liveSearchResultType | string | | Node type to filter live search results by, e.g. 'cm:content'. |
@@ -64,7 +64,6 @@ Contains the Search and Search Results components.
| searchChange | Emitted when the search term is changed. The search term is provided in the 'value' property of the returned object. If the term is less than three characters in length then the term is truncated to an empty string. |
| searchSubmit | Emitted when the search form is submitted. The search term is provided in the 'value' property of the returned object. |
| fileSelect | Emitted when a file item from the list of find-as-you-type results is selected |
| expand | Emitted when the expanded state of the control changes based on focus events and the content of the input control |
### Details

View File

@@ -1,7 +1,7 @@
<table class="full-width adf-search-result"
[@transformAutocomplete]="panelAnimationState"
(@transformAutocomplete.done)="onAnimationDone($event)">
<tbody #resultsTableBody data-automation-id="autocomplete_results" *ngIf="results && results.length && searchTerm">
<tbody id="adf-search-results" #resultsTableBody data-automation-id="autocomplete_results" *ngIf="results && results.length && searchTerm">
<tr id="result_row_{{idx}}" *ngFor="let result of results; let idx = index" tabindex="0"
(blur)="onRowBlur($event)" (focus)="onRowFocus($event)"
(click)="onItemClick(result)"
@@ -22,8 +22,7 @@
</tr>
</tbody>
<tbody id="search_no_result" data-automation-id="search_no_result_found" *ngIf="results && results.length === 0"
class="full-width adf-search-result">
<tbody id="search_no_result" data-automation-id="search_no_result_found" *ngIf="results && results.length === 0">
<tr>
<td>
<div class="truncate"><b> {{ 'SEARCH.RESULTS.NONE' | translate:{searchTerm: searchTerm} }}</b></div>
@@ -31,8 +30,7 @@
</tr>
</tbody>
<tbody data-automation-id="autocomplete_error_message" *ngIf="errorMessage"
class="full-width adf-search-result">
<tbody data-automation-id="autocomplete_error_message" *ngIf="errorMessage">
<tr>
<td>{{ 'SEARCH.RESULTS.ERROR' | translate:{errorMessage: errorMessage} }}</td>
</tr>

View File

@@ -123,7 +123,7 @@ export class SearchAutocompleteComponent implements OnChanges {
results => {
this.results = results.list.entries.slice(0, this.maxResults);
if (results && results.list && results.list.entries.length > 0) {
if (results && results.list) {
this.startAnimation();
}

View File

@@ -2,8 +2,9 @@
<div class="adf-search-container"
[@transitionMessages]="subscriptAnimationState">
<button md-button
(click)="onClickSearch()"
*ngIf="expandable"
*ngIf="expandable"
id="adf-search-button"
(click)="toggleSearchBar()"
class="adf-search-button">
<md-icon aria-label="search button">search</md-icon>
</button>

View File

@@ -4,10 +4,11 @@
.adf {
&-search-button.mat-button {
margin-right: 10px;
border-radius: 50%;
height: 32px;
min-width: 20px;
width: 32px;
min-width: 20px;
padding: 0;
overflow: hidden;
color: inherit;
@@ -31,6 +32,10 @@
background-color: mat-color($background, card);
}
.mat-form-field-underline {
background-color: mat-color($background, card);
}
.mat-input-element {
font-size: 16px;
}

View File

@@ -18,7 +18,7 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ThumbnailService } from 'ng2-alfresco-core';
import { AlfrescoTranslationService, CoreModule, SearchService } from 'ng2-alfresco-core';
import { results } from './../assets/search.component.mock';
import { noResult, results } from './../assets/search.component.mock';
import { TranslationMock } from './../assets/translation.service.mock';
import { SearchAutocompleteComponent } from './search-autocomplete.component';
import { SearchControlComponent } from './search-control.component';
@@ -103,7 +103,28 @@ describe('SearchControlComponent', () => {
fixture.detectChanges();
});
describe('Component rendering', () => {
describe('expandable option false', () => {
beforeEach(() => {
component.expandable = false;
});
afterEach(() => {
component.expandable = true;
});
it('search button should be hide', () => {
let searchButton: any = element.querySelector('#adf-search-button');
expect(searchButton).toBe(null);
});
it('should not have animation', () => {
component.ngOnInit();
expect(component.subscriptAnimationState).toBe('no-animation');
});
});
describe('component rendering', () => {
it('should display a text input field by default', () => {
fixture.detectChanges();
@@ -129,7 +150,7 @@ describe('SearchControlComponent', () => {
});
});
describe('Autocomplete list', () => {
describe('autocomplete list', () => {
let inputEl: HTMLInputElement;
@@ -159,6 +180,25 @@ describe('SearchControlComponent', () => {
window.setTimeout(() => {
fixture.detectChanges();
expect(component.liveSearchComponent.panelAnimationState).not.toBe('void');
let resultElement: Element = element.querySelector('#adf-search-results');
expect(resultElement).not.toBe(null);
done();
}, 100);
});
it('should show autocomplete list noe results cwhen search box has focus and there is search result with length 0', (done) => {
spyOn(searchService, 'getQueryNodesPromise')
.and.returnValue(Promise.resolve(noResult));
component.liveSearchTerm = 'test';
fixture.detectChanges();
inputEl.dispatchEvent(new FocusEvent('focus'));
window.setTimeout(() => {
fixture.detectChanges();
expect(component.liveSearchComponent.panelAnimationState).not.toBe('void');
let noResultElement: Element = element.querySelector('#search_no_result');
expect(noResultElement).not.toBe(null);
done();
}, 100);
});
@@ -285,45 +325,48 @@ describe('SearchControlComponent', () => {
});
describe('component focus', () => {
describe('search button', () => {
it('should fire an event when the search box receives focus', (done) => {
spyOn(component.expand, 'emit');
let inputEl: HTMLElement = element.querySelector('input');
inputEl.dispatchEvent(new FocusEvent('focus'));
window.setTimeout(() => {
expect(component.expand.emit).toHaveBeenCalledWith({
expanded: true
});
it('click on the search button should close the input box when is open', (done) => {
fixture.detectChanges();
component.subscriptAnimationState = 'active';
let searchButton: any = element.querySelector('#adf-search-button');
searchButton.click();
setTimeout(() => {
expect(component.subscriptAnimationState).toBe('inactive');
done();
}, 100);
}, 500);
});
it('should fire an event when the search box loses focus', (done) => {
spyOn(component.expand, 'emit');
let inputEl: HTMLElement = element.querySelector('input');
inputEl.dispatchEvent(new FocusEvent('blur'));
window.setTimeout(() => {
expect(component.expand.emit).toHaveBeenCalledWith({
expanded: false
});
it('click on the search button should open the input box when is close', (done) => {
fixture.detectChanges();
component.subscriptAnimationState = 'inactive';
let searchButton: any = element.querySelector('#adf-search-button');
searchButton.click();
setTimeout(() => {
expect(component.subscriptAnimationState).toBe('active');
done();
}, 100);
}, 300);
});
it('should NOT fire an event when the search box receives/loses focus but the component is not expandable',
(done) => {
spyOn(component.expand, 'emit');
component.expandable = false;
let inputEl: HTMLElement = element.querySelector('input');
inputEl.dispatchEvent(new FocusEvent('focus'));
inputEl.dispatchEvent(new FocusEvent('blur'));
window.setTimeout(() => {
expect(component.expand.emit).not.toHaveBeenCalled();
done();
}, 100);
});
it('Search button should not change the input state too often', (done) => {
fixture.detectChanges();
component.subscriptAnimationState = 'active';
let searchButton: any = element.querySelector('#adf-search-button');
searchButton.click();
searchButton.click();
setTimeout(() => {
expect(component.subscriptAnimationState).toBe('inactive');
done();
}, 400);
});
});
describe('file preview', () => {
@@ -338,13 +381,17 @@ describe('SearchControlComponent', () => {
});
});
it('should set deactivate the search after file/folder is clicked', () => {
it('should set deactivate the search after file/folder is clicked', (done) => {
component.subscriptAnimationState = 'active';
component.onFileClicked({
value: 'node12345'
});
expect(component.subscriptAnimationState).toBe('inactive');
setTimeout(() => {
expect(component.subscriptAnimationState).toBe('inactive');
done();
}, 300);
});
it('should NOT reset the search term after file/folder is clicked', () => {

View File

@@ -29,10 +29,11 @@ import { SearchAutocompleteComponent } from './search-autocomplete.component';
animations: [
trigger('transitionMessages', [
state('active', style({transform: 'translateX(0%)'})),
state('inactive', style({transform: 'translateX(89%)'})),
transition('void => active, inactive => active',
state('inactive', style({transform: 'translateX(86%)'})),
state('no-animation', style({transform: 'translateX(0%)', width: '100%'})),
transition('inactive => active',
animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)')),
transition('active => inactive, void => inactive',
transition('active => inactive',
animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)'))
])
],
@@ -64,9 +65,6 @@ export class SearchControlComponent implements OnInit, OnDestroy {
@Output()
fileSelect = new EventEmitter();
@Output()
expand = new EventEmitter();
searchControl: FormControl;
@ViewChild('searchInput', {})
@@ -96,16 +94,30 @@ export class SearchControlComponent implements OnInit, OnDestroy {
private focusSubject = new Subject<FocusEvent>();
subscriptAnimationState: string = 'inactive';
private toggleSearch = new Subject<string>();
subscriptAnimationState: string;
constructor() {
this.searchControl = new FormControl(
this.searchTerm,
Validators.compose([Validators.required, SearchTermValidator.minAlphanumericChars(3)])
);
this.toggleSearch.debounceTime(200).subscribe(() => {
if (this.expandable) {
this.subscriptAnimationState = this.subscriptAnimationState === 'inactive' ? 'active' : 'inactive';
if (this.subscriptAnimationState === 'inactive') {
this.searchTerm = '';
}
}
});
}
ngOnInit(): void {
this.subscriptAnimationState = this.expandable ? 'inactive' : 'no-animation';
this.searchControl.valueChanges.debounceTime(400).distinctUntilChanged()
.subscribe((value: string) => {
this.onSearchTermChange(value);
@@ -177,27 +189,16 @@ export class SearchControlComponent implements OnInit, OnDestroy {
}
onFocus($event): void {
if (this.expandable) {
this.expand.emit({
expanded: true
});
}
this.focusSubject.next($event);
}
onBlur($event): void {
if (this.expandable && (this.searchControl.value === '' || this.searchControl.value === undefined)) {
this.expand.emit({
expanded: false
});
}
this.focusSubject.next($event);
}
onEscape(): void {
this.hideAutocomplete();
this.toggleSearchBar();
this.searchTerm = '';
}
onArrowDown(): void {
@@ -221,11 +222,7 @@ export class SearchControlComponent implements OnInit, OnDestroy {
this.hideAutocomplete();
}
onClickSearch() {
this.subscriptAnimationState = 'active';
}
toggleSearchBar() {
this.subscriptAnimationState = this.subscriptAnimationState === 'inactive' ? 'active' : 'inactive';
this.toggleSearch.next();
}
}