Skip to content

Commit 008bdd1

Browse files
Merge pull request #7094 from IgniteUI/dmdimitrov/issue7009-master
fix(bottom-nav): fixes on routing and tab related attributes
2 parents 238d326 + 0c8abdf commit 008bdd1

File tree

5 files changed

+239
-68
lines changed

5 files changed

+239
-68
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Injectable } from '@angular/core';
2+
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
3+
4+
@Injectable()
5+
export class BottomNavRoutingTestGuard implements CanActivate {
6+
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
7+
if (state.url === '/view5') {
8+
return false;
9+
} else {
10+
return true;
11+
}
12+
}
13+
}

projects/igniteui-angular/src/lib/tabbar/routing-view-components.spec.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,26 @@ export class BottomNavRoutingView2Component {
1818
export class BottomNavRoutingView3Component {
1919
}
2020

21+
@Component({
22+
template: `This is a content from view component # 4`
23+
})
24+
export class BottomNavRoutingView4Component {
25+
}
26+
27+
@Component({
28+
template: `This is a content from view component # 5`
29+
})
30+
export class BottomNavRoutingView5Component {
31+
}
32+
2133
/**
2234
* @hidden
2335
*/
2436
@NgModule({
25-
declarations: [BottomNavRoutingView1Component, BottomNavRoutingView2Component, BottomNavRoutingView3Component],
26-
exports: [BottomNavRoutingView1Component, BottomNavRoutingView2Component, BottomNavRoutingView3Component],
37+
declarations: [BottomNavRoutingView1Component, BottomNavRoutingView2Component, BottomNavRoutingView3Component,
38+
BottomNavRoutingView4Component, BottomNavRoutingView5Component],
39+
exports: [BottomNavRoutingView1Component, BottomNavRoutingView2Component, BottomNavRoutingView3Component,
40+
BottomNavRoutingView4Component, BottomNavRoutingView5Component]
2741
})
2842
export class BottomNavRoutingViewComponentsModule {
2943
}

projects/igniteui-angular/src/lib/tabbar/tab-bar-content.component.html

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
<ng-content></ng-content>
33
</div>
44
<div #tablist class="{{itemStyle}}__menu {{itemStyle}}__menu--bottom" role="tablist" aria-orientation="horizontal">
5-
<ng-container *ngIf="!hasContentTabs">
6-
<igx-tab *ngFor="let panel of panels" [relatedPanel]="panel">
5+
<ng-container *ngIf="!hasContentTabs">
6+
<igx-tab *ngFor="let panel of panels; let i = index"
7+
[relatedPanel]="panel"
8+
[autoGenerated]="true"
9+
[id]="getTabId(i)"
10+
[attr.aria-controls]="getTabPanelId(i)">>
711
</igx-tab>
812
</ng-container>
913
<ng-content select="igx-tab"></ng-content>
10-
</div>
14+
</div>

projects/igniteui-angular/src/lib/tabbar/tabbar.component.spec.ts

Lines changed: 138 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ import { configureTestSuite } from '../test-utils/configure-suite';
99
import { BottomNavRoutingViewComponentsModule,
1010
BottomNavRoutingView1Component,
1111
BottomNavRoutingView2Component,
12-
BottomNavRoutingView3Component } from './routing-view-components.spec';
12+
BottomNavRoutingView3Component,
13+
BottomNavRoutingView4Component,
14+
BottomNavRoutingView5Component} from './routing-view-components.spec';
1315
import { Router } from '@angular/router';
1416
import { Location } from '@angular/common';
1517
import { UIInteractions } from '../test-utils/ui-interactions.spec';
18+
import { BottomNavRoutingTestGuard } from './bottom-nav-routing-test-guard.spec';
1619

1720
describe('IgxBottomNav', () => {
1821
configureTestSuite();
@@ -22,19 +25,61 @@ describe('IgxBottomNav', () => {
2225

2326
beforeAll(async(() => {
2427
const testRoutes = [
25-
{ path: 'view1', component: BottomNavRoutingView1Component },
26-
{ path: 'view2', component: BottomNavRoutingView2Component },
27-
{ path: 'view3', component: BottomNavRoutingView3Component }
28+
{ path: 'view1', component: BottomNavRoutingView1Component, canActivate: [BottomNavRoutingTestGuard] },
29+
{ path: 'view2', component: BottomNavRoutingView2Component, canActivate: [BottomNavRoutingTestGuard] },
30+
{ path: 'view3', component: BottomNavRoutingView3Component, canActivate: [BottomNavRoutingTestGuard] },
31+
{ path: 'view4', component: BottomNavRoutingView4Component, canActivate: [BottomNavRoutingTestGuard] },
32+
{ path: 'view5', component: BottomNavRoutingView5Component, canActivate: [BottomNavRoutingTestGuard] },
2833
];
2934

3035
TestBed.configureTestingModule({
3136
declarations: [TabBarTestComponent, BottomTabBarTestComponent, TemplatedTabBarTestComponent, TabBarRoutingTestComponent,
32-
TabBarTabsOnlyModeTestComponent],
33-
imports: [IgxBottomNavModule, BottomNavRoutingViewComponentsModule, RouterTestingModule.withRoutes(testRoutes)]
37+
TabBarTabsOnlyModeTestComponent, BottomNavRoutingGuardTestComponent, BottomNavTestHtmlAttributesComponent],
38+
imports: [IgxBottomNavModule, BottomNavRoutingViewComponentsModule, RouterTestingModule.withRoutes(testRoutes)],
39+
providers: [BottomNavRoutingTestGuard]
3440
}).compileComponents();
3541
}));
3642

37-
describe('IgxBottomNav Component with Panels Definitions', () => {
43+
describe('Html Attributes', () => {
44+
let fixture;
45+
let tabbar;
46+
47+
beforeEach(async(() => {
48+
fixture = TestBed.createComponent(BottomNavTestHtmlAttributesComponent);
49+
tabbar = fixture.componentInstance.tabbar;
50+
}));
51+
52+
it('should set the correct attributes on the html elements', () => {
53+
fixture.detectChanges();
54+
55+
const igxBottomNavs = document.querySelectorAll('igx-bottom-nav');
56+
expect(igxBottomNavs.length).toBe(2);
57+
58+
const startIndex = parseInt(igxBottomNavs[0].id.replace('igx-bottom-nav-', ''), 10);
59+
for (let tabIndex = startIndex; tabIndex < startIndex + 2; tabIndex++) {
60+
const tab = igxBottomNavs[tabIndex - startIndex];
61+
expect(tab.id).toEqual(`igx-bottom-nav-${tabIndex}`);
62+
63+
const headers = tab.querySelectorAll('igx-tab');
64+
const contents = tab.querySelectorAll('igx-tab-panel');
65+
expect(headers.length).toBe(3);
66+
expect(contents.length).toBe(3);
67+
68+
for (let itemIndex = 0; itemIndex < 3; itemIndex++) {
69+
const headerId = `igx-tab-${tabIndex}-${itemIndex}`;
70+
const contentId = `igx-tab-panel-${tabIndex}-${itemIndex}`;
71+
72+
expect(headers[itemIndex].id).toEqual(headerId);
73+
expect(headers[itemIndex].getAttribute('aria-controls')).toEqual(contentId);
74+
75+
expect(contents[itemIndex].id).toEqual(contentId);
76+
expect(contents[itemIndex].getAttribute('aria-labelledby')).toEqual(headerId);
77+
}
78+
}
79+
});
80+
});
81+
82+
describe('Component with Panels Definitions', () => {
3883
let fixture;
3984
let tabbar;
4085

@@ -187,7 +232,7 @@ describe('IgxBottomNav', () => {
187232

188233
});
189234

190-
describe('BottomNav Component with Custom Template', () => {
235+
describe('Component with Custom Template', () => {
191236
let fixture;
192237
let tabbar;
193238

@@ -271,6 +316,33 @@ describe('IgxBottomNav', () => {
271316
expect(theTabs[2].isSelected).toBe(false);
272317
}));
273318

319+
it('should not navigate to an URL blocked by activate guard', fakeAsync(() => {
320+
fixture = TestBed.createComponent(BottomNavRoutingGuardTestComponent);
321+
bottomNav = fixture.componentInstance.bottomNavComp;
322+
fixture.detectChanges();
323+
theTabs = bottomNav.contentTabs.toArray();
324+
325+
fixture.ngZone.run(() => { router.initialNavigation(); });
326+
tick();
327+
expect(location.path()).toBe('/');
328+
329+
fixture.ngZone.run(() => { UIInteractions.clickElement(theTabs[0].elementRef()); });
330+
tick();
331+
expect(location.path()).toBe('/view1');
332+
fixture.detectChanges();
333+
expect(bottomNav.selectedIndex).toBe(0);
334+
expect(theTabs[0].isSelected).toBe(true);
335+
expect(theTabs[1].isSelected).toBe(false);
336+
337+
fixture.ngZone.run(() => { UIInteractions.clickElement(theTabs[1].elementRef()); });
338+
tick();
339+
expect(location.path()).toBe('/view1');
340+
fixture.detectChanges();
341+
expect(bottomNav.selectedIndex).toBe(0);
342+
expect(theTabs[0].isSelected).toBe(true);
343+
expect(theTabs[1].isSelected).toBe(false);
344+
}));
345+
274346
});
275347

276348
describe('Tabs-only Mode Tests', () => {
@@ -295,38 +367,7 @@ describe('IgxBottomNav', () => {
295367
expect(theTabs[2].isSelected).toBe(false);
296368
expect(theTabs[2].elementRef().nativeElement.classList.contains(tabItemNormalCssClass)).toBe(true);
297369
});
298-
299-
it('should have the correct selection set even when no active link is present on the tabs', () => {
300-
expect(theTabs[0].isSelected).toBe(false);
301-
expect(theTabs[0].elementRef().nativeElement.classList.contains(tabItemNormalCssClass)).toBe(true);
302-
expect(theTabs[1].isSelected).toBe(true);
303-
expect(theTabs[1].elementRef().nativeElement.classList.contains(tabItemSelectedCssClass)).toBe(true);
304-
expect(theTabs[2].isSelected).toBe(false);
305-
expect(theTabs[2].elementRef().nativeElement.classList.contains(tabItemNormalCssClass)).toBe(true);
306-
307-
theTabs[0].elementRef().nativeElement.dispatchEvent(new Event('click'));
308-
fixture.detectChanges();
309-
310-
expect(theTabs[0].isSelected).toBe(true);
311-
expect(theTabs[0].elementRef().nativeElement.classList.contains(tabItemSelectedCssClass)).toBe(true);
312-
expect(theTabs[1].isSelected).toBe(false);
313-
expect(theTabs[1].elementRef().nativeElement.classList.contains(tabItemNormalCssClass)).toBe(true);
314-
expect(theTabs[2].isSelected).toBe(false);
315-
expect(theTabs[2].elementRef().nativeElement.classList.contains(tabItemNormalCssClass)).toBe(true);
316-
317-
theTabs[2].elementRef().nativeElement.dispatchEvent(new Event('click'));
318-
fixture.detectChanges();
319-
320-
expect(theTabs[0].isSelected).toBe(false);
321-
expect(theTabs[0].elementRef().nativeElement.classList.contains(tabItemNormalCssClass)).toBe(true);
322-
expect(theTabs[1].isSelected).toBe(false);
323-
expect(theTabs[1].elementRef().nativeElement.classList.contains(tabItemNormalCssClass)).toBe(true);
324-
expect(theTabs[2].isSelected).toBe(true);
325-
expect(theTabs[2].elementRef().nativeElement.classList.contains(tabItemSelectedCssClass)).toBe(true);
326-
});
327-
328370
});
329-
330371
});
331372

332373
@Component({
@@ -478,3 +519,61 @@ class TabBarTabsOnlyModeTestComponent {
478519
@ViewChild(IgxBottomNavComponent, { static: true })
479520
public bottomNavComp: IgxBottomNavComponent;
480521
}
522+
523+
@Component({
524+
template: `
525+
<div #wrapperDiv>
526+
<div>
527+
<router-outlet></router-outlet>
528+
</div>
529+
<igx-bottom-nav>
530+
<igx-tab label="Tab 1" icon="library_music"
531+
routerLink="/view1" routerLinkActive #rla1="routerLinkActive" [isSelected]="rla1.isActive">
532+
</igx-tab>
533+
<igx-tab label="Tab 5" icon="library_books"
534+
routerLink="/view5" routerLinkActive #rlaX="routerLinkActive" [isSelected]="rlaX.isActive">
535+
</igx-tab>
536+
</igx-bottom-nav>
537+
</div>
538+
`
539+
})
540+
class BottomNavRoutingGuardTestComponent {
541+
@ViewChild(IgxBottomNavComponent, { static: true })
542+
public bottomNavComp: IgxBottomNavComponent;
543+
}
544+
545+
@Component({
546+
template: `
547+
<div>
548+
<div>
549+
<igx-bottom-nav>
550+
<igx-tab-panel label="Tab 1">
551+
<div>Content 1</div>
552+
</igx-tab-panel>
553+
<igx-tab-panel label="Tab 2">
554+
<div>Content 2</div>
555+
</igx-tab-panel>
556+
<igx-tab-panel label="Tab 3">
557+
<div>Content 3</div>
558+
</igx-tab-panel>
559+
</igx-bottom-nav>
560+
</div>
561+
<div>
562+
<igx-bottom-nav>
563+
<igx-tab-panel label="Tab 4">
564+
<div>Content 4</div>
565+
</igx-tab-panel>
566+
<igx-tab-panel label="Tab 5">
567+
<div>Content 5</div>
568+
</igx-tab-panel>
569+
<igx-tab-panel label="Tab 6">
570+
<div>Content 6</div>
571+
</igx-tab-panel>
572+
</igx-bottom-nav>
573+
</div>
574+
</div>
575+
`
576+
})
577+
class BottomNavTestHtmlAttributesComponent {
578+
@ViewChild(IgxBottomNavComponent, { static: true }) public tabbar: IgxBottomNavComponent;
579+
}

0 commit comments

Comments
 (0)