mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-19 17:14:57 +00:00
Allow use of arrow keys to navigate FAYT results and update tests
Refs #371
This commit is contained in:
parent
68fc93ecbc
commit
a8bea1800b
@ -15,10 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export var result = {
|
const entryItem = {
|
||||||
list: {
|
|
||||||
entries: [
|
|
||||||
{
|
|
||||||
entry: {
|
entry: {
|
||||||
id: '123',
|
id: '123',
|
||||||
name: 'MyDoc',
|
name: 'MyDoc',
|
||||||
@ -33,7 +30,22 @@ export var result = {
|
|||||||
displayName: 'John Doe'
|
displayName: 'John Doe'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export var result = {
|
||||||
|
list: {
|
||||||
|
entries: [
|
||||||
|
entryItem
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export var results = {
|
||||||
|
list: {
|
||||||
|
entries: [
|
||||||
|
entryItem,
|
||||||
|
entryItem,
|
||||||
|
entryItem
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
<table data-automation-id="autocomplete_results" *ngIf="results && results.length && searchTerm"
|
<table data-automation-id="autocomplete_results" *ngIf="results && results.length && searchTerm"
|
||||||
class="mdl-data-table mdl-js-data-table mdl-shadow--2dp full-width">
|
class="mdl-data-table mdl-js-data-table mdl-shadow--2dp full-width">
|
||||||
<tbody>
|
<tbody #resultsTableBody>
|
||||||
<tr id="result_row_{{idx}}" *ngFor="let result of results; let idx = index" tabindex="0" (blur)="onRowBlur($event)" (focus)="onRowFocus($event)" (click)="onItemClick(result, $event)" (keyup.enter)="onRowEnter(result, $event)"
|
<tr id="result_row_{{idx}}" *ngFor="let result of results; let idx = index" tabindex="0"
|
||||||
|
(blur)="onRowBlur($event)" (focus)="onRowFocus($event)"
|
||||||
|
(click)="onItemClick(result)"
|
||||||
|
(keyup.enter)="onRowEnter(result)"
|
||||||
|
(keyup.arrowdown)="onRowArrowDown($event)"
|
||||||
|
(keyup.arrowup)="onRowArrowUp($event)"
|
||||||
|
(keyup.escape)="onRowEscape($event)"
|
||||||
attr.data-automation-id="autocomplete_result_for_{{result.entry.name}}">
|
attr.data-automation-id="autocomplete_result_for_{{result.entry.name}}">
|
||||||
<td class="img-td"><img src="{{getMimeTypeIcon(result)}}" alt="{{getMimeTypeKey(result)|translate}}"/></td>
|
<td class="img-td"><img src="{{getMimeTypeIcon(result)}}" alt="{{getMimeTypeKey(result)|translate}}"/></td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -19,7 +19,7 @@ import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
|||||||
import { AlfrescoSearchAutocompleteComponent } from './alfresco-search-autocomplete.component';
|
import { AlfrescoSearchAutocompleteComponent } from './alfresco-search-autocomplete.component';
|
||||||
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
||||||
import { TranslationMock } from './../assets/translation.service.mock';
|
import { TranslationMock } from './../assets/translation.service.mock';
|
||||||
import { result, folderResult, noResult, errorJson } from './../assets/alfresco-search.component.mock';
|
import { result, results, folderResult, noResult, errorJson } from './../assets/alfresco-search.component.mock';
|
||||||
import { AlfrescoSearchService } from '../services/alfresco-search.service';
|
import { AlfrescoSearchService } from '../services/alfresco-search.service';
|
||||||
import {
|
import {
|
||||||
AlfrescoApiService,
|
AlfrescoApiService,
|
||||||
@ -35,6 +35,12 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
|||||||
let fixture: ComponentFixture<AlfrescoSearchAutocompleteComponent>, element: HTMLElement;
|
let fixture: ComponentFixture<AlfrescoSearchAutocompleteComponent>, element: HTMLElement;
|
||||||
let component: AlfrescoSearchAutocompleteComponent;
|
let component: AlfrescoSearchAutocompleteComponent;
|
||||||
|
|
||||||
|
let updateSearchTerm = (newSearchTerm: string): void => {
|
||||||
|
let oldSearchTerm = component.searchTerm;
|
||||||
|
component.searchTerm = newSearchTerm;
|
||||||
|
component.ngOnChanges({searchTerm: { currentValue: newSearchTerm, previousValue: oldSearchTerm}});
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -64,20 +70,16 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
|||||||
expect(translationService.addTranslationFolder).toHaveBeenCalledWith('node_modules/ng2-alfresco-search/dist/src');
|
expect(translationService.addTranslationFolder).toHaveBeenCalledWith('node_modules/ng2-alfresco-search/dist/src');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display search results when a search term is provided', () => {
|
describe('search results', () => {
|
||||||
let searchTerm = { currentValue: 'customSearchTerm', previousValue: ''};
|
|
||||||
spyOn(component, 'displaySearchResults').and.stub();
|
let searchService;
|
||||||
component.searchTerm = 'searchTerm';
|
|
||||||
component.ngOnChanges({
|
beforeEach(() => {
|
||||||
searchTerm: searchTerm
|
searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||||
});
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(component.displaySearchResults).toHaveBeenCalledWith(searchTerm.currentValue);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear results straight away when a new search term is entered', async(() => {
|
it('should clear results straight away when a new search term is entered', async(() => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
|
||||||
spyOn(searchService, 'getSearchNodesPromise')
|
spyOn(searchService, 'getSearchNodesPromise')
|
||||||
.and.returnValue(Promise.resolve(result));
|
.and.returnValue(Promise.resolve(result));
|
||||||
|
|
||||||
@ -95,24 +97,21 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
|||||||
|
|
||||||
it('should display the returned search results', (done) => {
|
it('should display the returned search results', (done) => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
|
||||||
spyOn(searchService, 'getSearchNodesPromise')
|
spyOn(searchService, 'getSearchNodesPromise')
|
||||||
.and.returnValue(Promise.resolve(result));
|
.and.returnValue(Promise.resolve(result));
|
||||||
|
|
||||||
component.resultsEmitter.subscribe(x => {
|
component.resultsEmitter.subscribe(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect( element.querySelector('#result_user_0').innerHTML).toBe('John Doe');
|
expect( element.querySelector('#result_user_0').innerHTML).toBe('John Doe');
|
||||||
expect( element.querySelector('#result_name_0').innerHTML).toBe('<b _ngcontent-a-1="">MyDoc</b>');
|
expect( element.querySelector('#result_name_0').innerHTML).toBe('<b _ngcontent-a-1="">MyDoc</b>');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
updateSearchTerm('searchTerm');
|
||||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''} });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display the correct thumbnail for result items', (done) => {
|
it('should display the correct thumbnail for result items', (done) => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
|
||||||
spyOn(searchService, 'getSearchNodesPromise')
|
spyOn(searchService, 'getSearchNodesPromise')
|
||||||
.and.returnValue(Promise.resolve(result));
|
.and.returnValue(Promise.resolve(result));
|
||||||
|
|
||||||
@ -131,32 +130,37 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
updateSearchTerm('searchTerm');
|
||||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''} });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display no result if no result are returned', (done) => {
|
it('should display no result if no result are returned', (done) => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
|
||||||
spyOn(searchService, 'getSearchNodesPromise')
|
spyOn(searchService, 'getSearchNodesPromise')
|
||||||
.and.returnValue(Promise.resolve(noResult));
|
.and.returnValue(Promise.resolve(noResult));
|
||||||
|
|
||||||
component.resultsEmitter.subscribe(x => {
|
component.resultsEmitter.subscribe(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(element.querySelector('#search_no_result')).not.toBeNull();
|
expect(element.querySelector('#search_no_result')).not.toBeNull();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
updateSearchTerm('searchTerm');
|
||||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('errors', () => {
|
||||||
|
|
||||||
|
let searchService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||||
|
spyOn(searchService, 'getSearchNodesPromise')
|
||||||
|
.and.returnValue(Promise.reject(errorJson));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display an error if an error is encountered running the search', (done) => {
|
it('should display an error if an error is encountered running the search', (done) => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
|
||||||
spyOn(searchService, 'getSearchNodesPromise')
|
|
||||||
.and.returnValue(Promise.reject(errorJson));
|
|
||||||
|
|
||||||
component.errorEmitter.subscribe(() => {
|
component.errorEmitter.subscribe(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
let resultsEl = element.querySelector('[data-automation-id="autocomplete_results"]');
|
let resultsEl = element.querySelector('[data-automation-id="autocomplete_results"]');
|
||||||
@ -167,18 +171,12 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
updateSearchTerm('searchTerm');
|
||||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear errors straight away when a new search is performed', async(() => {
|
it('should clear errors straight away when a new search is performed', async(() => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
updateSearchTerm('searchTerm');
|
||||||
spyOn(searchService, 'getSearchNodesPromise')
|
|
||||||
.and.returnValue(Promise.reject(errorJson));
|
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
|
||||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -190,77 +188,181 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mouse interactions', () => {
|
||||||
|
|
||||||
|
let searchService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||||
|
});
|
||||||
|
|
||||||
it('should emit preview when file item clicked', (done) => {
|
it('should emit preview when file item clicked', (done) => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
|
||||||
spyOn(searchService, 'getSearchNodesPromise')
|
spyOn(searchService, 'getSearchNodesPromise')
|
||||||
.and.returnValue(Promise.resolve(result));
|
.and.returnValue(Promise.resolve(result));
|
||||||
|
|
||||||
component.resultsEmitter.subscribe(x => {
|
component.resultsEmitter.subscribe(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
(<any> element.querySelector('#result_row_0')).click();
|
(<any> element.querySelector('#result_row_0')).click();
|
||||||
});
|
});
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
updateSearchTerm('searchTerm');
|
||||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
|
||||||
|
|
||||||
component.preview.subscribe(e => {
|
component.preview.subscribe(() => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not emit preview if a non-file item is clicked', (done) => {
|
it('should not emit preview if a non-file item is clicked', (done) => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
|
||||||
spyOn(searchService, 'getSearchNodesPromise')
|
spyOn(searchService, 'getSearchNodesPromise')
|
||||||
.and.returnValue(Promise.resolve(folderResult));
|
.and.returnValue(Promise.resolve(folderResult));
|
||||||
|
|
||||||
spyOn(component.preview, 'emit');
|
spyOn(component.preview, 'emit');
|
||||||
component.resultsEmitter.subscribe(x => {
|
component.resultsEmitter.subscribe(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
(<any> element.querySelector('#result_row_0')).click();
|
(<any> element.querySelector('#result_row_0')).click();
|
||||||
expect(component.preview.emit).not.toHaveBeenCalled();
|
expect(component.preview.emit).not.toHaveBeenCalled();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
updateSearchTerm('searchTerm');
|
||||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('keyboard interactions', () => {
|
||||||
|
|
||||||
|
let searchService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||||
|
spyOn(searchService, 'getSearchNodesPromise')
|
||||||
|
.and.returnValue(Promise.resolve(results));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit preview when enter key pressed when a file item is in focus', (done) => {
|
it('should emit preview when enter key pressed when a file item is in focus', (done) => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
component.resultsEmitter.subscribe(() => {
|
||||||
spyOn(searchService, 'getSearchNodesPromise')
|
|
||||||
.and.returnValue(Promise.resolve(result));
|
|
||||||
|
|
||||||
component.resultsEmitter.subscribe(x => {
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
(<any> element.querySelector('#result_row_0')).dispatchEvent(new KeyboardEvent('keyup', {
|
(<any> element.querySelector('#result_row_0')).dispatchEvent(new KeyboardEvent('keyup', {
|
||||||
key: 'Enter'
|
key: 'Enter'
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
updateSearchTerm('searchTerm');
|
||||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
|
||||||
|
|
||||||
component.preview.subscribe(e => {
|
component.preview.subscribe(() => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit a focus event when a result comes into focus', (done) => {
|
it('should emit cancel event when escape key pressed when a result is in focus', (done) => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
component.resultsEmitter.subscribe(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
(<any> element.querySelector('#result_row_0')).dispatchEvent(new KeyboardEvent('keyup', {
|
||||||
|
key: 'Escape'
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSearchTerm('searchTerm');
|
||||||
|
|
||||||
|
component.cancelEmitter.subscribe(() => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should focus the next result when down arrow key pressed when a result is in focus', (done) => {
|
||||||
|
|
||||||
|
component.resultsEmitter.subscribe(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
let firstResult: any = element.querySelector('#result_row_0');
|
||||||
|
let secondResult: any = element.querySelector('#result_row_1');
|
||||||
|
spyOn(secondResult, 'focus');
|
||||||
|
firstResult.focus();
|
||||||
|
firstResult.dispatchEvent(new KeyboardEvent('keyup', {
|
||||||
|
key: 'ArrowDown'
|
||||||
|
}));
|
||||||
|
expect(secondResult.focus).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSearchTerm('searchTerm');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do nothing when down arrow key pressed when the last result is in focus', (done) => {
|
||||||
|
|
||||||
|
component.resultsEmitter.subscribe(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
let lastResult: any = element.querySelector('#result_row_2');
|
||||||
|
lastResult.focus();
|
||||||
|
lastResult.dispatchEvent(new KeyboardEvent('keyup', {
|
||||||
|
key: 'ArrowDown'
|
||||||
|
}));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSearchTerm('searchTerm');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should focus the previous result when up arrow key pressed when a result is in focus', (done) => {
|
||||||
|
|
||||||
|
component.resultsEmitter.subscribe(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
let firstResult: any = element.querySelector('#result_row_0');
|
||||||
|
let secondResult: any = element.querySelector('#result_row_1');
|
||||||
|
spyOn(firstResult, 'focus');
|
||||||
|
secondResult.focus();
|
||||||
|
secondResult.dispatchEvent(new KeyboardEvent('keyup', {
|
||||||
|
key: 'ArrowUp'
|
||||||
|
}));
|
||||||
|
expect(firstResult.focus).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSearchTerm('searchTerm');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit scroll back event when up arrow key pressed and the first result is in focus', (done) => {
|
||||||
|
|
||||||
|
component.resultsEmitter.subscribe(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
let firstResult: any = element.querySelector('#result_row_0');
|
||||||
|
firstResult.dispatchEvent(new KeyboardEvent('keyup', {
|
||||||
|
key: 'ArrowUp'
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
component.scrollBackEmitter.subscribe(() => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSearchTerm('searchTerm');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('changing focus', () => {
|
||||||
|
|
||||||
|
let searchService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||||
spyOn(searchService, 'getSearchNodesPromise')
|
spyOn(searchService, 'getSearchNodesPromise')
|
||||||
.and.returnValue(Promise.resolve(result));
|
.and.returnValue(Promise.resolve(result));
|
||||||
|
});
|
||||||
|
|
||||||
component.resultsEmitter.subscribe(x => {
|
it('should emit a focus event when a result comes into focus', (done) => {
|
||||||
|
|
||||||
|
component.resultsEmitter.subscribe(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
(<any> element.querySelector('#result_row_0')).dispatchEvent(new FocusEvent('focus'));
|
(<any> element.querySelector('#result_row_0')).dispatchEvent(new FocusEvent('focus'));
|
||||||
});
|
});
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
updateSearchTerm('searchTerm');
|
||||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
|
||||||
|
|
||||||
component.focusEmitter.subscribe((e: FocusEvent) => {
|
component.focusEmitter.subscribe((e: FocusEvent) => {
|
||||||
expect(e).not.toBeNull();
|
expect(e).not.toBeNull();
|
||||||
@ -271,17 +373,12 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
|||||||
|
|
||||||
it('should emit a focus event when a result loses focus', (done) => {
|
it('should emit a focus event when a result loses focus', (done) => {
|
||||||
|
|
||||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
component.resultsEmitter.subscribe(() => {
|
||||||
spyOn(searchService, 'getSearchNodesPromise')
|
|
||||||
.and.returnValue(Promise.resolve(result));
|
|
||||||
|
|
||||||
component.resultsEmitter.subscribe(x => {
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
(<any> element.querySelector('#result_row_0')).dispatchEvent(new FocusEvent('blur'));
|
(<any> element.querySelector('#result_row_0')).dispatchEvent(new FocusEvent('blur'));
|
||||||
});
|
});
|
||||||
|
|
||||||
component.searchTerm = 'searchTerm';
|
updateSearchTerm('searchTerm');
|
||||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
|
||||||
|
|
||||||
component.focusEmitter.subscribe((e: FocusEvent) => {
|
component.focusEmitter.subscribe((e: FocusEvent) => {
|
||||||
expect(e).not.toBeNull();
|
expect(e).not.toBeNull();
|
||||||
@ -290,4 +387,20 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should give focus to the first result when focusResult() is called externally', (done) => {
|
||||||
|
|
||||||
|
component.resultsEmitter.subscribe(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
let firstResult: any = element.querySelector('#result_row_0');
|
||||||
|
spyOn(firstResult, 'focus');
|
||||||
|
component.focusResult();
|
||||||
|
expect(firstResult.focus).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSearchTerm('searchTerm');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, EventEmitter, Input, OnInit, OnChanges, Output } from '@angular/core';
|
import { Component, ElementRef, EventEmitter, Input, OnInit, OnChanges, Output, ViewChild } from '@angular/core';
|
||||||
import { AlfrescoSearchService } from './../services/alfresco-search.service';
|
import { AlfrescoSearchService } from './../services/alfresco-search.service';
|
||||||
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
||||||
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||||
@ -46,12 +46,20 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
|
|||||||
@Output()
|
@Output()
|
||||||
focusEmitter: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>();
|
focusEmitter: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
cancelEmitter = new EventEmitter();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
resultsEmitter = new EventEmitter();
|
resultsEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
scrollBackEmitter = new EventEmitter();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
errorEmitter = new EventEmitter();
|
errorEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
@ViewChild('resultsTableBody', {}) resultsTableBody: ElementRef;
|
||||||
|
|
||||||
constructor(private alfrescoSearchService: AlfrescoSearchService,
|
constructor(private alfrescoSearchService: AlfrescoSearchService,
|
||||||
private translate: AlfrescoTranslationService,
|
private translate: AlfrescoTranslationService,
|
||||||
private alfrescoThumbnailService: AlfrescoThumbnailService) {
|
private alfrescoThumbnailService: AlfrescoThumbnailService) {
|
||||||
@ -119,10 +127,12 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onItemClick(node, event?: Event): void {
|
focusResult(): void {
|
||||||
if (event) {
|
let firstResult = this.resultsTableBody.nativeElement.querySelector('tr');
|
||||||
event.preventDefault();
|
(<any> firstResult).focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onItemClick(node): void {
|
||||||
if (node && node.entry) {
|
if (node && node.entry) {
|
||||||
if (node.entry.isFile) {
|
if (node.entry.isFile) {
|
||||||
this.preview.emit({
|
this.preview.emit({
|
||||||
@ -150,4 +160,32 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getNextElementSibling(node: Element): Element {
|
||||||
|
return node.nextElementSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPreviousElementSibling(node: Element): Element {
|
||||||
|
return node.previousElementSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
onRowArrowDown($event: KeyboardEvent): void {
|
||||||
|
let nextElement = this.getNextElementSibling(<Element> $event.target);
|
||||||
|
if (nextElement) {
|
||||||
|
(<any> nextElement).focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onRowArrowUp($event: KeyboardEvent): void {
|
||||||
|
let previousElement = this.getPreviousElementSibling(<Element> $event.target);
|
||||||
|
if (previousElement) {
|
||||||
|
(<any> previousElement).focus();
|
||||||
|
} else {
|
||||||
|
this.scrollBackEmitter.emit($event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onRowEscape($event: KeyboardEvent): void {
|
||||||
|
this.cancelEmitter.emit($event);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<alfresco-search-autocomplete *ngIf="autocompleteEnabled"
|
<alfresco-search-autocomplete #autocomplete *ngIf="autocompleteEnabled"
|
||||||
[searchTerm]="autocompleteSearchTerm" [ngClass]="{active: searchActive, valid: searchValid}"
|
[searchTerm]="autocompleteSearchTerm" [ngClass]="{active: searchActive, valid: searchValid}"
|
||||||
(preview)="onFileClicked($event)"
|
(preview)="onFileClicked($event)"
|
||||||
(focusEmitter)="onAutoCompleteFocus($event)"></alfresco-search-autocomplete>
|
(focusEmitter)="onAutoCompleteFocus($event)"
|
||||||
|
(scrollBackEmitter)="onAutoCompleteReturn($event)"
|
||||||
|
(cancelEmitter)="onAutoCompleteCancel($event)"></alfresco-search-autocomplete>
|
||||||
|
@ -217,6 +217,42 @@ describe('AlfrescoSearchControlComponent', () => {
|
|||||||
expect(autocomplete.classList.contains('active')).toBe(true);
|
expect(autocomplete.classList.contains('active')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should select the first result in find-as-you-type when down arrow is pressed and FAYT is visible', (done) => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
spyOn(component.autocompleteComponent, 'focusResult');
|
||||||
|
fixture.detectChanges();
|
||||||
|
inputEl.dispatchEvent(new Event('focus'));
|
||||||
|
window.setTimeout(() => { // wait for debounce() to complete
|
||||||
|
fixture.detectChanges();
|
||||||
|
inputEl.dispatchEvent(new KeyboardEvent('keyup', {
|
||||||
|
key: 'ArrowDown'
|
||||||
|
}));
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.autocompleteComponent.focusResult).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should focus input element when find-as-you-type returns control', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
spyOn(inputEl, 'focus');
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.onAutoCompleteReturn(new KeyboardEvent('keyup', {
|
||||||
|
key: 'ArrowUp'
|
||||||
|
}));
|
||||||
|
expect(inputEl.focus).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should focus input element when find-as-you-type is cancelled', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
spyOn(inputEl, 'focus');
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.onAutoCompleteCancel(new KeyboardEvent('keyup', {
|
||||||
|
key: 'ArrowUp'
|
||||||
|
}));
|
||||||
|
expect(inputEl.focus).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('should NOT display a find-as-you-type control when configured not to', () => {
|
it('should NOT display a find-as-you-type control when configured not to', () => {
|
||||||
fixture.componentInstance.autocompleteEnabled = false;
|
fixture.componentInstance.autocompleteEnabled = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
import { FormControl, Validators } from '@angular/forms';
|
import { FormControl, Validators } from '@angular/forms';
|
||||||
import { Component, Input, Output, OnInit, OnDestroy, ElementRef, EventEmitter, ViewChild } from '@angular/core';
|
import { Component, Input, Output, OnInit, OnDestroy, ElementRef, EventEmitter, ViewChild } from '@angular/core';
|
||||||
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||||
|
import { AlfrescoSearchAutocompleteComponent } from './alfresco-search-autocomplete.component';
|
||||||
import { SearchTermValidator } from './../forms/search-term-validator';
|
import { SearchTermValidator } from './../forms/search-term-validator';
|
||||||
import { Observable, Subject } from 'rxjs/Rx';
|
import { Observable, Subject } from 'rxjs/Rx';
|
||||||
|
|
||||||
@ -57,6 +58,9 @@ export class AlfrescoSearchControlComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
@ViewChild('searchInput', {}) searchInput: ElementRef;
|
@ViewChild('searchInput', {}) searchInput: ElementRef;
|
||||||
|
|
||||||
|
@ViewChild('autocomplete')
|
||||||
|
autocompleteComponent: AlfrescoSearchAutocompleteComponent;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
autocompleteEnabled = true;
|
autocompleteEnabled = true;
|
||||||
|
|
||||||
@ -94,7 +98,7 @@ export class AlfrescoSearchControlComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onSearchTermChange(value: string): void {
|
private onSearchTermChange(value: string): void {
|
||||||
this.searchActive = true;
|
this.setAutoCompleteDisplayed(true);
|
||||||
this.autocompleteSearchTerm = value;
|
this.autocompleteSearchTerm = value;
|
||||||
this.searchControl.setValue(value, true);
|
this.searchControl.setValue(value, true);
|
||||||
this.searchValid = this.searchControl.valid;
|
this.searchValid = this.searchControl.valid;
|
||||||
@ -145,6 +149,14 @@ export class AlfrescoSearchControlComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isAutoCompleteDisplayed(): boolean {
|
||||||
|
return this.searchActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAutoCompleteDisplayed(display: boolean): void {
|
||||||
|
this.searchActive = display;
|
||||||
|
}
|
||||||
|
|
||||||
onFileClicked(event): void {
|
onFileClicked(event): void {
|
||||||
this.preview.emit({
|
this.preview.emit({
|
||||||
value: event.value
|
value: event.value
|
||||||
@ -152,11 +164,11 @@ export class AlfrescoSearchControlComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSearchFocus($event): void {
|
onSearchFocus($event): void {
|
||||||
this.searchActive = true;
|
this.setAutoCompleteDisplayed(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearchBlur($event): void {
|
onSearchBlur($event): void {
|
||||||
this.searchActive = false;
|
this.setAutoCompleteDisplayed(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
onFocus($event): void {
|
onFocus($event): void {
|
||||||
@ -178,15 +190,32 @@ export class AlfrescoSearchControlComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onEscape(): void {
|
onEscape(): void {
|
||||||
this.searchActive = false;
|
this.setAutoCompleteDisplayed(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
onArrowDown(): void {
|
onArrowDown(): void {
|
||||||
this.searchActive = true;
|
if (this.isAutoCompleteDisplayed()) {
|
||||||
|
this.autocompleteComponent.focusResult();
|
||||||
|
} else {
|
||||||
|
this.setAutoCompleteDisplayed(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAutoCompleteFocus($event): void {
|
onAutoCompleteFocus($event): void {
|
||||||
this.focusSubject.next($event);
|
this.focusSubject.next($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAutoCompleteReturn($event): void {
|
||||||
|
if (this.searchInput) {
|
||||||
|
(<any> this.searchInput.nativeElement).focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAutoCompleteCancel($event): void {
|
||||||
|
if (this.searchInput) {
|
||||||
|
(<any> this.searchInput.nativeElement).focus();
|
||||||
|
}
|
||||||
|
this.setAutoCompleteDisplayed(false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user