Skip to content

Commit f738f9d

Browse files
committed
fix(tabset): prevent select call if no active tab found (#1444)
1 parent a3c838f commit f738f9d

File tree

2 files changed

+114
-4
lines changed

2 files changed

+114
-4
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { CommonModule } from '@angular/common';
2+
import { Component, QueryList, ViewChild, ViewChildren } from '@angular/core';
3+
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
4+
import { ActivatedRoute, Params } from '@angular/router';
5+
import { RouterTestingModule } from '@angular/router/testing';
6+
import { NbTabComponent, NbTabsetComponent, NbTabsetModule } from '@nebular/theme';
7+
import { BehaviorSubject } from 'rxjs';
8+
import createSpy = jasmine.createSpy;
9+
10+
@Component({
11+
template: `
12+
<nb-tabset routeParam="tab">
13+
<nb-tab *ngIf="showTabs" tabTitle="1" route="1">1</nb-tab>
14+
<nb-tab *ngIf="showTabs" tabTitle="2" route="2">2</nb-tab>
15+
<nb-tab *ngIf="showTabs" tabTitle="3" route="3" disabled>3</nb-tab>
16+
</nb-tabset>
17+
`,
18+
})
19+
export class TabsetTestComponent {
20+
showTabs = true;
21+
22+
@ViewChild(NbTabsetComponent) tabsetComponent: NbTabsetComponent;
23+
@ViewChildren(NbTabComponent) tabComponents: QueryList<NbTabComponent>;
24+
25+
getDisabledTab(): NbTabComponent {
26+
return this.tabComponents.toArray()[2];
27+
}
28+
}
29+
30+
export class ActivatedRouteStub {
31+
private subject = new BehaviorSubject<Params>(null);
32+
readonly params = this.subject.asObservable();
33+
34+
constructor(params: Params = {}) {
35+
this.subject.next(params);
36+
}
37+
38+
setParams(params?: Params) {
39+
this.subject.next(params);
40+
};
41+
}
42+
43+
describe('NbTabsetComponent', () => {
44+
let fixture: ComponentFixture<TabsetTestComponent>;
45+
let testComponent: TabsetTestComponent;
46+
let tabsetComponent: NbTabsetComponent;
47+
let activatedRouteStub: ActivatedRouteStub;
48+
49+
beforeEach(fakeAsync(() => {
50+
activatedRouteStub = new ActivatedRouteStub();
51+
52+
TestBed.configureTestingModule({
53+
declarations: [ TabsetTestComponent ],
54+
imports: [
55+
CommonModule,
56+
RouterTestingModule.withRoutes([]),
57+
NbTabsetModule,
58+
],
59+
providers: [{ provide: ActivatedRoute, useValue: activatedRouteStub }],
60+
});
61+
62+
fixture = TestBed.createComponent(TabsetTestComponent);
63+
testComponent = fixture.componentInstance;
64+
65+
fixture.detectChanges();
66+
tick();
67+
68+
tabsetComponent = testComponent.tabsetComponent;
69+
}));
70+
71+
it('should mark tab as active if selected in route param', fakeAsync(() => {
72+
const selectTabSpy = spyOn(tabsetComponent, 'selectTab');
73+
const tabToSelect: NbTabComponent = testComponent.tabComponents.first;
74+
75+
activatedRouteStub.setParams({ tab: tabToSelect.route });
76+
fixture.detectChanges();
77+
tick();
78+
79+
expect(selectTabSpy).toHaveBeenCalledTimes(1);
80+
expect(selectTabSpy).toHaveBeenCalledWith(tabToSelect);
81+
expect(tabToSelect.active).toEqual(true);
82+
}));
83+
84+
it('should not mark disabled tab as active if selected in route param', fakeAsync(() => {
85+
const changeTabSpy = createSpy('changeTabSpy');
86+
const disabledTab: NbTabComponent = testComponent.getDisabledTab();
87+
88+
activatedRouteStub.setParams({ tab: disabledTab.route });
89+
fixture.detectChanges();
90+
tick();
91+
92+
expect(changeTabSpy).not.toHaveBeenCalled();
93+
expect(disabledTab.active).toEqual(false);
94+
}));
95+
96+
it(`should not call 'selectTab' if no tabs found`, fakeAsync(() => {
97+
const selectTabSpy = spyOn(tabsetComponent, 'selectTab');
98+
99+
testComponent.showTabs = false;
100+
fixture.detectChanges();
101+
102+
activatedRouteStub.setParams({ tab: 1 });
103+
fixture.detectChanges();
104+
tick();
105+
106+
expect(selectTabSpy).not.toHaveBeenCalled();
107+
}));
108+
});

src/framework/theme/components/tabset/tabset.component.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Licensed under the MIT License. See License.txt in the project root for license information.
55
*/
66

7-
import { map, delay } from 'rxjs/operators';
7+
import { map, delay, filter } from 'rxjs/operators';
88
import {
99
Component,
1010
Input,
@@ -301,11 +301,13 @@ export class NbTabsetComponent implements AfterContentInit {
301301
this.tabs.find((tab) => this.routeParam ? tab.route === params[this.routeParam] : tab.active),
302302
),
303303
delay(0),
304+
map((tab: NbTabComponent) => tab || this.tabs.first),
305+
filter((tab: NbTabComponent) => !!tab),
304306
)
305-
.subscribe((activeTab) => {
306-
this.selectTab(activeTab || this.tabs.first);
307+
.subscribe((tabToSelect: NbTabComponent) => {
308+
this.selectTab(tabToSelect);
307309
this.changeDetectorRef.markForCheck();
308-
});
310+
});
309311
}
310312

311313
// TODO: navigate to routeParam

0 commit comments

Comments
 (0)