Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

Commit

Permalink
Fix issues with tabs that are activated via an ngIf (#1887)
Browse files Browse the repository at this point in the history
Fixed bug with tabs which were surrounded by an ngif - Resolves #1883
  • Loading branch information
Blackbaud-TrevorBurch authored Aug 15, 2018
1 parent c528532 commit 79d7288
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 57 deletions.
1 change: 1 addition & 0 deletions src/modules/tabs/fixtures/tabset.component.fixture.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*ngIf="tab3Available"
[tabHeading]="tab3Heading"
[tabHeaderCount]="tab3HeaderCount"
[tabIndex]="3"
[active]="activeTab === 2"
>
<div class="tabset-test-content-3">
Expand Down
148 changes: 93 additions & 55 deletions src/modules/tabs/tabset.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import {
ComponentFixture,
TestBed,
fakeAsync,
tick
tick,
async
} from '@angular/core/testing';

import {
Expand Down Expand Up @@ -33,7 +34,7 @@ describe('Tabset component', () => {
});
});

function validateTabSelected(el: Element, tabIndex: number) {
function validateTabSelected(el: Element, tabIndex: number, content?: string) {
let selectedCls: string;
let buttonEls: NodeListOf<Element>;
let inDropDownMode = el.querySelector('.sky-tabset-mode-dropdown');
Expand All @@ -43,7 +44,7 @@ describe('Tabset component', () => {
buttonEls = el.querySelectorAll('.sky-tab-dropdown-item');
} else {
selectedCls = 'sky-btn-tab-selected';
buttonEls = el.querySelectorAll('.sky-tab-button');
buttonEls = el.querySelectorAll('.sky-btn-tab');
}

let contentEls = el.querySelectorAll('.sky-tab');
Expand All @@ -69,6 +70,9 @@ describe('Tabset component', () => {
expect(buttonEl.getAttribute('aria-selected')).toBe(expectedHasClass.toString());
}
}
if (content) {
expect(contentEls[tabIndex]).toHaveText(content);
}
}

it('should initialize tabs in proper order', fakeAsync(() => {
Expand All @@ -82,24 +86,36 @@ describe('Tabset component', () => {
});
}));

it('should initialize tabs that are added to the tabset after init', fakeAsync(() => {
it('should initialize tabs that are added to the tabset after init', async(() => {
let fixture = TestBed.createComponent(TabsetTestComponent);
fixture.componentInstance.tab3Content = 'test content';
fixture.componentInstance.tab3Available = false;
fixture.detectChanges();
tick();
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();

let tabsetService: SkyTabsetService = (fixture.componentInstance.tabsetComponent as any).tabsetService;
expect(tabsetService.tabs.getValue().length).toBe(2);
let tabsetService: SkyTabsetService = (fixture.componentInstance.tabsetComponent as any).tabsetService;
expect(tabsetService.tabs.getValue().length).toBe(2);
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.componentInstance.tab3Available = true;
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.nativeElement.querySelectorAll('.sky-btn-tab')[2].click();

fixture.componentInstance.tab3Available = true;
fixture.detectChanges();
tick();
fixture.detectChanges();
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();

expect(tabsetService.tabs.getValue().length).toBe(3);
fixture.componentInstance.tabsetComponent.tabs.forEach((item, index) => {
expect(item).toBe(tabsetService.tabs.getValue()[index]);
validateTabSelected(fixture.nativeElement, 2, fixture.componentInstance.tab3Content);

expect(tabsetService.tabs.getValue().length).toBe(3);
fixture.componentInstance.tabsetComponent.tabs.forEach((item, index) => {
expect(item).toBe(tabsetService.tabs.value[index]);
});
});
});
});
});
}));

Expand Down Expand Up @@ -132,18 +148,20 @@ describe('Tabset component', () => {
validateTabSelected(el, 2);
}));

it('should change the active tab when the tab is clicked manually', () => {
it('should change the active tab when the tab is clicked manually', fakeAsync(() => {
let fixture = TestBed.createComponent(TabsetTestComponent);
let el = fixture.nativeElement;

fixture.detectChanges();
tick();

el.querySelectorAll('.sky-btn-tab')[1].click();

fixture.detectChanges();
tick();

validateTabSelected(el, 1);
});
}));

it('should not change the active tab when a disabled tab is clicked', () => {
let fixture = TestBed.createComponent(TabsetTestComponent);
Expand Down Expand Up @@ -246,56 +264,70 @@ describe('Tabset component', () => {
expect(closeTabSpy).toHaveBeenCalled();
});

it('should select the next tab when the active tab is closed', () => {
it('should select the next tab when the active tab is closed', fakeAsync(() => {
let fixture = TestBed.createComponent(TabsetTestComponent);
let cmp: TabsetTestComponent = fixture.componentInstance;
let el = fixture.nativeElement;
fixture.detectChanges();
tick();

cmp.activeTab = 1;
fixture.detectChanges();
tick();

cmp.tab2Available = false;
fixture.detectChanges();
tick();

expect(el.querySelectorAll('.sky-btn-tab').length).toBe(2);
validateTabSelected(el, 1);
});
}));

it(
'should select the previous tab when the last tab is closed and the last tab was active',
() => {
fakeAsync(() => {
let fixture = TestBed.createComponent(TabsetTestComponent);
let cmp: TabsetTestComponent = fixture.componentInstance;
let el = fixture.nativeElement;
fixture.detectChanges();
tick();

cmp.activeTab = 2;
fixture.detectChanges();
tick();

cmp.tab3Available = false;
fixture.detectChanges();
tick();

expect(el.querySelectorAll('.sky-btn-tab').length).toBe(2);
validateTabSelected(el, 1);
}
);
));

it(
'should maintain the currently active tab when a non-active tab is closed',
() => {
fakeAsync(() => {
let fixture = TestBed.createComponent(TabsetTestComponent);
let cmp: TabsetTestComponent = fixture.componentInstance;
let el = fixture.nativeElement;
cmp.tab3Content = 'tab 3 content';
fixture.detectChanges();
tick();

cmp.activeTab = 2;
fixture.detectChanges();
tick();
validateTabSelected(el, 2, 'tab 3 content');

cmp.tab2Available = false;
fixture.detectChanges();
tick();

expect(el.querySelectorAll('.sky-btn-tab').length).toBe(2);
validateTabSelected(el, 2);
validateTabSelected(el, 1, 'tab 3 content');
}
);
));

it(
'should display count in tab when tabHeaderCount is defined',
Expand Down Expand Up @@ -388,7 +420,7 @@ describe('Tabset component', () => {
);

it(
'should collapse into a dropdown on initialization',
'should collapse into a dropdown on initialization',
fakeAsync(() => {
let fixture = TestBed.createComponent(TabsetTestComponent);

Expand All @@ -404,7 +436,7 @@ describe('Tabset component', () => {

expect(tabEl).not.toBeNull();
}
));
));

describe('when collapsed', () => {
let fixture: ComponentFixture<TabsetTestComponent>;
Expand Down Expand Up @@ -451,33 +483,33 @@ describe('Tabset component', () => {
);

it('should allow another tab to be selected from the dropdown', fakeAsync(() => {
let el = fixture.nativeElement;
let el = fixture.nativeElement;

fixture.detectChanges();
tick();
fixture.detectChanges();
tick();

mockAdapterService.fakeOverflowChange(true);
mockAdapterService.fakeOverflowChange(true);

fixture.detectChanges();
tick();
fixture.detectChanges();
tick();

let tabEl = el.querySelector('.sky-dropdown-button-type-tab');
let tabEl = el.querySelector('.sky-dropdown-button-type-tab');

tabEl.click();
tick();
fixture.detectChanges();
tick();
tabEl.click();
tick();
fixture.detectChanges();
tick();

let dropdownTabButtons = el.querySelectorAll('.sky-tab-dropdown-item-btn');
expect(dropdownTabButtons[1]).toHaveText('Tab 2');
let dropdownTabButtons = el.querySelectorAll('.sky-tab-dropdown-item-btn');
expect(dropdownTabButtons[1]).toHaveText('Tab 2');

dropdownTabButtons[1].click();
tick();
fixture.detectChanges();
tick();
dropdownTabButtons[1].click();
tick();
fixture.detectChanges();
tick();

validateTabSelected(el, 1);
}
validateTabSelected(el, 1);
}
));

it(
Expand Down Expand Up @@ -524,7 +556,7 @@ describe('Tabset component', () => {

validateTabSelected(el, 0);
}
));
));

it(
'should notify the consumer when a tab\'s close button is clicked',
Expand Down Expand Up @@ -576,7 +608,7 @@ describe('Tabset component', () => {
fixture.detectChanges();
tick();
cmp.activeIndex = 1;
fixture.detectChanges();
fixture.detectChanges();
tick();
fixture.detectChanges();
tick();
Expand Down Expand Up @@ -625,6 +657,8 @@ describe('Tabset component', () => {
let fixture = TestBed.createComponent(TabsetActiveTestComponent);
let cmp: TabsetActiveTestComponent = fixture.componentInstance;
let el = fixture.nativeElement;
cmp.tab1Content = 'tab 1 content';
cmp.tab3Content = 'tab 3 content';

fixture.detectChanges();
tick();
Expand All @@ -642,7 +676,7 @@ describe('Tabset component', () => {
tick();
fixture.detectChanges();
tick();
validateTabSelected(el, 2);
validateTabSelected(el, 1, 'tab 3 content');

el.querySelectorAll('.sky-btn-tab')[0].click();

Expand All @@ -651,7 +685,7 @@ describe('Tabset component', () => {
fixture.detectChanges();
tick();
expect(cmp.activeIndex).toBe(0);
validateTabSelected(el, 0);
validateTabSelected(el, 0, 'tab 1 content');
}));

it('handles initialized tabs', fakeAsync(() => {
Expand Down Expand Up @@ -704,7 +738,7 @@ describe('Tabset component', () => {
});
});

describe('keyboard accessibility', () => {
describe('keyboard accessibility', () => {
let debugElement: DebugElement;
let fixture: ComponentFixture<TabsetTestComponent>;

Expand Down Expand Up @@ -775,19 +809,23 @@ describe('Tabset component', () => {
});
}));

it('should emit a click event on enter press', () => {
it('should emit a click event on enter press', fakeAsync(() => {
fixture.detectChanges();
tick();
fixture.detectChanges();
let el = debugElement.queryAll(By.css('.sky-btn-tab'))[1];
tick();
let el = debugElement.queryAll(By.css('.sky-btn-tab'))[1];

el.triggerEventHandler('keydown', { keyCode: 15});
el.triggerEventHandler('keydown', { keyCode: 15 });
fixture.detectChanges();
tick();
validateTabSelected(fixture.nativeElement, 0);

el.triggerEventHandler('keydown', { keyCode: 13});
el.triggerEventHandler('keydown', { keyCode: 13 });
fixture.detectChanges();
tick();
validateTabSelected(fixture.nativeElement, 1);
}
);
));
});
});
6 changes: 4 additions & 2 deletions src/modules/tabs/tabset.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,10 @@ export class SkyTabsetComponent
// initialize each tab's index. (in case tabs are instantiated out of order)
this.tabs.forEach(item => item.initializeTabIndex());
this.tabs.changes.subscribe((change: QueryList<SkyTabComponent>) => {
change.filter(tab => tab.tabIndex === undefined)
.forEach(item => item.initializeTabIndex());
this.tabsetService.tabs.take(1).subscribe(currentTabs => {
change.filter(tab => currentTabs.indexOf(tab) < 0)
.forEach(item => item.initializeTabIndex());
});
});

if (this.active || this.active === 0) {
Expand Down

0 comments on commit 79d7288

Please sign in to comment.