Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix dropdown missing outputs / emits on toggle, include & fix tests #1916

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/dropdown/bs-dropdown.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ export class BsDropdownDirective implements OnInit, OnDestroy {
}
}

/**
* Emits an event when isOpen change
*/
@Output() isOpenChange: EventEmitter<any>;
/**
* Emits an event when the popover is shown
*/
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -212,6 +218,7 @@ export class BsDropdownDirective implements OnInit, OnDestroy {

if (this._showInline) {
this._isInlineOpen = false;
this.onHidden.emit();
} else {
this._dropdown.hide();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `
<div dropdown>
Expand All @@ -14,6 +14,16 @@ const defaultHtml = `
</div>
`;

const htmlWithBinding = `
<div dropdown [(isOpen)]="isOpen">
<button dropdownToggle>Dropdown</button>
<ul *dropdownMenu>
<li><a href="#">One</a></li>
<li><a href="#">Two</a></li>
</ul>
</div>
`;

describe('Directive: Dropdown', () => {

it('should be closed by default', () => {
Expand All @@ -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 = `
<div dropdown [(isOpen)]="isOpen">
<button dropdownToggle>Dropdown</button>
<ul *dropdownMenu>
<li><a href="#">One</a></li>
<li><a href="#">Two</a></li>
</ul>
</div>
`;
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', () => {
Expand All @@ -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 = `
<div dropdown>
Expand All @@ -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 = `
<div dropdown>
<button dropdownToggle>Dropdown</button>
Expand All @@ -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 = `
<div dropdown [autoClose]="autoClose">
<button dropdownToggle>Dropdown</button>
Expand All @@ -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 = `
Expand All @@ -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', () => {
Expand All @@ -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', () => {
Expand All @@ -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 = `
<div dropdown>
<button dropdownToggle [isDisabled]="isDisabled">Dropdown</button>
<div dropdown [isDisabled]="isDisabled">
<button dropdownToggle>Dropdown</button>
<ul *dropdownMenu>
<li><a href="#">One</a></li>
<li><a href="#">Two</a></li>
Expand All @@ -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 = `
<div dropdown>
<button dropdownToggle>Dropdown</button>
<ul *dropdownMenu>
<li><a href="#">One</a></li>
<li><a href="#">Two</a></li>
</ul>
</div>
`;
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 = `
<div dropdown>
<button dropdownToggle [addToggleClass]="addToggleClass">Dropdown</button>
<ul *dropdownMenu>
<li><a href="#">One</a></li>
<li><a href="#">Two</a></li>
</ul>
</div>
`;
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({
Expand Down