diff --git a/components/typeahead/readme.md b/components/typeahead/readme.md index d2d77d6abb..f939c9ea7e 100644 --- a/components/typeahead/readme.md +++ b/components/typeahead/readme.md @@ -21,6 +21,7 @@ export class TypeaheadDirective implements OnInit { @Input() public typeaheadWaitMs:number; @Input() public typeaheadOptionsLimit:number; @Input() public typeaheadOptionField:string; + @Input() public typeaheadGroupField:string; @Input() public typeaheadAsync:boolean = null; @Input() public typeaheadLatinize:boolean = true; @Input() public typeaheadSingleWords:boolean = true; @@ -46,7 +47,8 @@ export class TypeaheadDirective implements OnInit { - `typeaheadMinLength` (`?number=1`) - minimal no of characters that needs to be entered before typeahead kicks-in. When set to 0, typeahead shows on focus with full list of options (limited as normal by typeaheadOptionsLimit) - `typeaheadWaitMs` (`?number=0`) - minimal wait time after last character typed before typeahead kicks-in - `typeaheadOptionsLimit` (`?number=20`) - maximum length of options items list - - `typeaheadOptionField` (`?string`) - name of field in array of states that contain options as objects, we use array item as option in case of this field is missing. Supports nested properties and methods + - `typeaheadOptionField` (`?string`) - when options source is an array of objects, the name of field that contains the options value, we use array item as option in case of this field is missing. Supports nested properties and methods. + - `typeaheadGroupField` (`?string`) - when options source is an array of objects, the name of field that contains the group value, matches are grouped by this field when set. - `typeaheadAsync` (`?boolean`) - should be used only in case of `typeahead` attribute is array. If `true` - loading of options will be async, otherwise - sync. `true` make sense if options array is large. - `typeaheadLatinize` (`?boolean=true`) - match latin symbols. If `true` the word `súper` would match `super` and vice versa. - `typeaheadSingleWords` (`?boolean=true`) - break words with spaces. If `true` the text `"exact phrase" here match` would match with `match exact phrase here` but not with `phrase here exact match` (kind of "google style"). diff --git a/components/typeahead/typeahead-container.component.spec.ts b/components/typeahead/typeahead-container.component.spec.ts new file mode 100644 index 0000000000..0d45e3715a --- /dev/null +++ b/components/typeahead/typeahead-container.component.spec.ts @@ -0,0 +1,91 @@ +import { TestBed, ComponentFixture } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { TypeaheadContainerComponent } from './typeahead-container.component'; +import { TypeaheadOptions } from './typeahead-options.class'; +import { asNativeElements } from '@angular/core'; + +describe('Component: TypeaheadContainer', () => { + let fixture:ComponentFixture; + let component:TypeaheadContainerComponent; + + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + declarations: [TypeaheadContainerComponent], + providers: [{ + provide: TypeaheadOptions, + useValue: new TypeaheadOptions({animation: false, placement: 'bottom-left', typeaheadRef: undefined}) + }] + }).createComponent(TypeaheadContainerComponent); + + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be defined', () => { + expect(component).toBeTruthy(); + }); + + it('should have an \"element\" property', () => { + expect(component.element).toBeTruthy(); + }); + + it('should have an empty \"matches\" array', () => { + expect(component.matches.length).toBe(0); + }); + + describe('dropdown-menu', () => { + let dropDown:HTMLElement; + + beforeEach(() => { + component.position(fixture.elementRef); + fixture.detectChanges(); + + dropDown = fixture.debugElement.query(By.css('.dropdown-menu')).nativeElement as HTMLElement; + }); + + it('should be rendered', () => { + expect(dropDown).toBeDefined(); + }); + + it('should have display style set', () => { + expect(dropDown.style.display).toBe('block'); + }); + + it('should have top style set', () => { + expect(dropDown.style.top).toBe('16px'); + }); + + it('should have left style set', () => { + expect(dropDown.style.left).toBe('8px'); + }); + }); + + describe('matches', () => { + let matches:HTMLLIElement[]; + + beforeEach(() => { + component.query = 'fo'; + component.matches = ['foo', 'food']; + fixture.detectChanges(); + + matches = asNativeElements(fixture.debugElement.queryAll(By.css('.dropdown-menu li'))); + }); + + it('should render 2 matches', () => { + expect(matches.length).toBe(2); + }); + + it('should highlight query for match', () => { + expect(matches[1].children[0].innerHTML).toBe('food'); + }); + + it('should set the \"active\" class on the first match', () => { + expect(matches[0].classList.contains('active')).toBeTruthy(); + }); + + it('should not set the \"active\" class on other matches', () => { + expect(matches[1].classList.contains('active')).toBeFalsy(); + }); + }); + +}); diff --git a/components/typeahead/typeahead-container.component.ts b/components/typeahead/typeahead-container.component.ts index 66d842cf6b..f057f2fee8 100644 --- a/components/typeahead/typeahead-container.component.ts +++ b/components/typeahead/typeahead-container.component.ts @@ -8,39 +8,84 @@ import { TypeaheadDirective } from './typeahead.directive'; const bs4 = ` diff --git a/demo/components/typeahead/typeahead-demo.ts b/demo/components/typeahead/typeahead-demo.ts index 5d3a6bfa00..fd2ceae234 100644 --- a/demo/components/typeahead/typeahead-demo.ts +++ b/demo/components/typeahead/typeahead-demo.ts @@ -19,6 +19,7 @@ export class TypeaheadDemoComponent { }); public customSelected:string = ''; + public groupSelected:string = ''; public selected:string = ''; public dataSource:Observable; public asyncSelected:string = ''; @@ -38,31 +39,31 @@ export class TypeaheadDemoComponent { 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming']; public statesComplex:Array = [ - {id: 1, name: 'Alabama'}, {id: 2, name: 'Alaska'}, {id: 3, name: 'Arizona'}, - {id: 4, name: 'Arkansas'}, {id: 5, name: 'California'}, - {id: 6, name: 'Colorado'}, {id: 7, name: 'Connecticut'}, - {id: 8, name: 'Delaware'}, {id: 9, name: 'Florida'}, - {id: 10, name: 'Georgia'}, {id: 11, name: 'Hawaii'}, - {id: 12, name: 'Idaho'}, {id: 13, name: 'Illinois'}, - {id: 14, name: 'Indiana'}, {id: 15, name: 'Iowa'}, - {id: 16, name: 'Kansas'}, {id: 17, name: 'Kentucky'}, - {id: 18, name: 'Louisiana'}, {id: 19, name: 'Maine'}, - {id: 21, name: 'Maryland'}, {id: 22, name: 'Massachusetts'}, - {id: 23, name: 'Michigan'}, {id: 24, name: 'Minnesota'}, - {id: 25, name: 'Mississippi'}, {id: 26, name: 'Missouri'}, - {id: 27, name: 'Montana'}, {id: 28, name: 'Nebraska'}, - {id: 29, name: 'Nevada'}, {id: 30, name: 'New Hampshire'}, - {id: 31, name: 'New Jersey'}, {id: 32, name: 'New Mexico'}, - {id: 33, name: 'New York'}, {id: 34, name: 'North Dakota'}, - {id: 35, name: 'North Carolina'}, {id: 36, name: 'Ohio'}, - {id: 37, name: 'Oklahoma'}, {id: 38, name: 'Oregon'}, - {id: 39, name: 'Pennsylvania'}, {id: 40, name: 'Rhode Island'}, - {id: 41, name: 'South Carolina'}, {id: 42, name: 'South Dakota'}, - {id: 43, name: 'Tennessee'}, {id: 44, name: 'Texas'}, - {id: 45, name: 'Utah'}, {id: 46, name: 'Vermont'}, - {id: 47, name: 'Virginia'}, {id: 48, name: 'Washington'}, - {id: 49, name: 'West Virginia'}, {id: 50, name: 'Wisconsin'}, - {id: 51, name: 'Wyoming'}]; + {id: 1, name: 'Alabama', region: 'South'}, {id: 2, name: 'Alaska', region: 'West'}, {id: 3, name: 'Arizona', region: 'West'}, + {id: 4, name: 'Arkansas', region: 'South'}, {id: 5, name: 'California', region: 'West'}, + {id: 6, name: 'Colorado', region: 'West'}, {id: 7, name: 'Connecticut', region: 'Northeast'}, + {id: 8, name: 'Delaware', region: 'South'}, {id: 9, name: 'Florida', region: 'South'}, + {id: 10, name: 'Georgia', region: 'South'}, {id: 11, name: 'Hawaii', region: 'West'}, + {id: 12, name: 'Idaho', region: 'West'}, {id: 13, name: 'Illinois', region: 'Midwest'}, + {id: 14, name: 'Indiana', region: 'Midwest'}, {id: 15, name: 'Iowa', region: 'Midwest'}, + {id: 16, name: 'Kansas', region: 'Midwest'}, {id: 17, name: 'Kentucky', region: 'South'}, + {id: 18, name: 'Louisiana', region: 'South'}, {id: 19, name: 'Maine', region: 'Northeast'}, + {id: 21, name: 'Maryland', region: 'South'}, {id: 22, name: 'Massachusetts', region: 'Northeast'}, + {id: 23, name: 'Michigan', region: 'Midwest'}, {id: 24, name: 'Minnesota', region: 'Midwest'}, + {id: 25, name: 'Mississippi', region: 'South'}, {id: 26, name: 'Missouri', region: 'Midwest'}, + {id: 27, name: 'Montana', region: 'West'}, {id: 28, name: 'Nebraska', region: 'Midwest'}, + {id: 29, name: 'Nevada', region: 'West'}, {id: 30, name: 'New Hampshire', region: 'Northeast'}, + {id: 31, name: 'New Jersey', region: 'Northeast'}, {id: 32, name: 'New Mexico', region: 'West'}, + {id: 33, name: 'New York', region: 'Northeast'}, {id: 34, name: 'North Dakota', region: 'Midwest'}, + {id: 35, name: 'North Carolina', region: 'South'}, {id: 36, name: 'Ohio', region: 'Midwest'}, + {id: 37, name: 'Oklahoma', region: 'South'}, {id: 38, name: 'Oregon', region: 'West'}, + {id: 39, name: 'Pennsylvania', region: 'Northeast'}, {id: 40, name: 'Rhode Island', region: 'Northeast'}, + {id: 41, name: 'South Carolina', region: 'South'}, {id: 42, name: 'South Dakota', region: 'Midwest'}, + {id: 43, name: 'Tennessee', region: 'South'}, {id: 44, name: 'Texas', region: 'South'}, + {id: 45, name: 'Utah', region: 'West'}, {id: 46, name: 'Vermont', region: 'Northeast'}, + {id: 47, name: 'Virginia', region: 'South'}, {id: 48, name: 'Washington', region: 'South'}, + {id: 49, name: 'West Virginia', region: 'South'}, {id: 50, name: 'Wisconsin', region: 'Midwest'}, + {id: 51, name: 'Wyoming', region: 'West'}]; public constructor() { this.dataSource = Observable.create((observer:any) => {