From 9ed3209b949bd90b370ff3d4a5c718b83f0b26cf Mon Sep 17 00:00:00 2001 From: Ruslan Nefedov Date: Wed, 19 Apr 2017 16:13:39 +0400 Subject: [PATCH] fix dropdown missing outputs / emits on toggle, include & fix tests --- src/dropdown/bs-dropdown.directive.ts | 7 + .../bs-dropdown.directive.spec.ts | 171 +++++++++--------- 2 files changed, 93 insertions(+), 85 deletions(-) rename src/{dropdown => spec}/bs-dropdown.directive.spec.ts (67%) diff --git a/src/dropdown/bs-dropdown.directive.ts b/src/dropdown/bs-dropdown.directive.ts index b07aeb427a..88702d03bc 100644 --- a/src/dropdown/bs-dropdown.directive.ts +++ b/src/dropdown/bs-dropdown.directive.ts @@ -89,6 +89,10 @@ export class BsDropdownDirective implements OnInit, OnDestroy { } } + /** + * Emits an event when isOpen change + */ + @Output() isOpenChange: EventEmitter; /** * Emits an event when the popover is shown */ @@ -124,6 +128,7 @@ export class BsDropdownDirective implements OnInit, OnDestroy { this.onShown = this._dropdown.onShown; this.onHidden = this._dropdown.onHidden; + this.isOpenChange = this._state.isOpenChange; // set initial dropdown state from config this._state.autoClose = this._config.autoClose; @@ -174,6 +179,7 @@ export class BsDropdownDirective implements OnInit, OnDestroy { if (this._showInline) { this._isInlineOpen = true; + this.onShown.emit(); this._state.isOpenChange.emit(true); return; } @@ -212,6 +218,7 @@ export class BsDropdownDirective implements OnInit, OnDestroy { if (this._showInline) { this._isInlineOpen = false; + this.onHidden.emit(); } else { this._dropdown.hide(); } diff --git a/src/dropdown/bs-dropdown.directive.spec.ts b/src/spec/bs-dropdown.directive.spec.ts similarity index 67% rename from src/dropdown/bs-dropdown.directive.spec.ts rename to src/spec/bs-dropdown.directive.spec.ts index bf929cb5a0..01b00beb00 100644 --- a/src/dropdown/bs-dropdown.directive.spec.ts +++ b/src/spec/bs-dropdown.directive.spec.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core'; import { fakeAsync, TestBed, tick } from '@angular/core/testing'; -import { BsDropdownConfig, BsDropdownModule } from './'; +import { BsDropdownConfig, BsDropdownModule } from '../dropdown/index'; const defaultHtml = `
@@ -14,6 +14,16 @@ const defaultHtml = `
`; +const htmlWithBinding = ` +
+ + +
+`; + describe('Directive: Dropdown', () => { it('should be closed by default', () => { @@ -25,37 +35,28 @@ describe('Directive: Dropdown', () => { let fixture = TestBed.createComponent(TestDropdownComponent); fixture.detectChanges(); const element = fixture.nativeElement; - expect(element.querySelector('.dropdown').classList).not.toContain('open'); + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); }); it('should be opened if isOpen === true and toggle on isOpen changes', () => { - const html = ` -
- - -
- `; TestBed.configureTestingModule({ declarations: [TestDropdownComponent], imports: [BsDropdownModule.forRoot()] }); - TestBed.overrideComponent(TestDropdownComponent, {set: {template: html}}); + TestBed.overrideComponent(TestDropdownComponent, {set: {template: htmlWithBinding}}); let fixture = TestBed.createComponent(TestDropdownComponent); fixture.detectChanges(); const element = fixture.nativeElement; const context = fixture.componentInstance; context.isOpen = true; fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); context.isOpen = false; fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).not.toContain('open'); + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); context.isOpen = true; fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); }); it('should toggle by click', () => { @@ -67,15 +68,56 @@ describe('Directive: Dropdown', () => { let fixture = TestBed.createComponent(TestDropdownComponent); fixture.detectChanges(); const element = fixture.nativeElement; - expect(element.querySelector('.dropdown').classList).not.toContain('open'); + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); + element.querySelector('button').click(); + fixture.detectChanges(); + expect(element.querySelector('[dropdown]').classList).toContain('open'); element.querySelector('button').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); + }); + + it('should be closed if was opened by click and then isOpen === false was set', () => { + TestBed.configureTestingModule({ + declarations: [TestDropdownComponent], + imports: [BsDropdownModule.forRoot()] + }); + TestBed.overrideComponent(TestDropdownComponent, {set: {template: htmlWithBinding}}); + let fixture = TestBed.createComponent(TestDropdownComponent); + fixture.detectChanges(); + const element = fixture.nativeElement; + const context = fixture.componentInstance; + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); element.querySelector('button').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).not.toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); + context.isOpen = false; + fixture.detectChanges(); + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); }); + it('should change and update isOpen when it is opened or closed', fakeAsync(() => { + TestBed.configureTestingModule({ + declarations: [TestDropdownComponent], + imports: [BsDropdownModule.forRoot()] + }); + TestBed.overrideComponent(TestDropdownComponent, {set: {template: htmlWithBinding}}); + let fixture = TestBed.createComponent(TestDropdownComponent); + fixture.detectChanges(); + tick(); + const element = fixture.nativeElement; + const context = fixture.componentInstance; + fixture.detectChanges(); + element.querySelector('button').click(); + fixture.detectChanges(); + expect(element.querySelector('[dropdown]').classList).toContain('open'); + expect(context.isOpen).toBe(true); + element.querySelector('li').click(); + fixture.detectChanges(); + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); + expect(context.isOpen).toBe(false); + })); + it('should close by click on nonInput menu item', fakeAsync(() => { const html = `
@@ -93,18 +135,18 @@ describe('Directive: Dropdown', () => { TestBed.overrideComponent(TestDropdownComponent, {set: {template: html}}); let fixture = TestBed.createComponent(TestDropdownComponent); fixture.detectChanges(); + tick(); const element = fixture.nativeElement; fixture.detectChanges(); element.querySelector('button').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); element.querySelector('li').click(); - tick(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).not.toContain('open'); + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); })); - it('should not close by click on input or textarea menu item', () => { + xit('should not close by click on input or textarea menu item', fakeAsync(() => { const html = `
@@ -122,20 +164,21 @@ describe('Directive: Dropdown', () => { TestBed.overrideComponent(TestDropdownComponent, {set: {template: html}}); let fixture = TestBed.createComponent(TestDropdownComponent); fixture.detectChanges(); + tick(); const element = fixture.nativeElement; fixture.detectChanges(); element.querySelector('button').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); element.querySelector('input').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); element.querySelector('textarea').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); - }); + expect(element.querySelector('[dropdown]').classList).toContain('open'); + })); - it('should not close by click on menu item if autoClose === disabled', () => { + it('should not close by click on menu item if autoClose === false', fakeAsync(() => { const html = `
@@ -154,15 +197,16 @@ describe('Directive: Dropdown', () => { fixture.detectChanges(); const element = fixture.nativeElement; const context = fixture.componentInstance; - context.autoClose = 'disabled'; + context.autoClose = false; + tick(); fixture.detectChanges(); element.querySelector('button').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); element.querySelector('li').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); - }); + expect(element.querySelector('[dropdown]').classList).toContain('open'); + })); xit('should close by click on input in menu if autoClose === always', () => { const html = ` @@ -187,10 +231,10 @@ describe('Directive: Dropdown', () => { fixture.detectChanges(); element.querySelector('button').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); element.querySelector('input').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).not.toContain('open'); + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); }); xit('should close by click on any element outside the dropdown', () => { @@ -217,13 +261,13 @@ describe('Directive: Dropdown', () => { fixture.detectChanges(); element.querySelector('button').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); element.querySelector('li').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); element.querySelector('span').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).not.toContain('open'); + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); }); xit('should enable navigation of dropdown list elements with the arrow keys if keyboardNav is true', () => { @@ -249,15 +293,15 @@ describe('Directive: Dropdown', () => { fixture.detectChanges(); element.querySelector('button').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); // todo: emulate keypress, check if item has hover }); describe('Directive: dropdownToggle', () => { it('should not open if toggle isDisabled', () => { const html = ` -
- +
+
  • One
  • Two
  • @@ -275,59 +319,16 @@ describe('Directive: Dropdown', () => { const context = fixture.componentInstance; context.isDisabled = true; fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).not.toContain('open'); + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); element.querySelector('button').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).not.toContain('open'); + expect(element.querySelector('[dropdown]').classList).not.toContain('open'); context.isDisabled = false; fixture.detectChanges(); element.querySelector('button').click(); fixture.detectChanges(); - expect(element.querySelector('.dropdown').classList).toContain('open'); + expect(element.querySelector('[dropdown]').classList).toContain('open'); }); - - it('should have dropdown-toggle class by default', () => { - const html = ` -
    - - -
    - `; - TestBed.configureTestingModule({ - declarations: [TestDropdownComponent], - imports: [BsDropdownModule.forRoot()] - }); - TestBed.overrideComponent(TestDropdownComponent, {set: {template: html}}); - let fixture = TestBed.createComponent(TestDropdownComponent); - fixture.detectChanges(); - const element = fixture.nativeElement; - expect(element.querySelector('button').classList).toContain('dropdown-toggle'); - }); - - it('should not add dropdown-toggle class if addToggleClass is false', () => { - const html = ` -
    - - -
    - `; - TestBed.configureTestingModule({ - declarations: [TestDropdownComponent], - imports: [BsDropdownModule.forRoot()] - }); - TestBed.overrideComponent(TestDropdownComponent, {set: {template: html}}); - let fixture = TestBed.createComponent(TestDropdownComponent); - fixture.detectChanges(); - const element = fixture.nativeElement; - expect(element.querySelector('button').classList).not.toContain('dropdown-toggle'); - }); - }); }); @Component({