Skip to content

Commit 15c97f5

Browse files
committed
feat(tabs/bottomNav): addressing more issues and suggestions #4297
1 parent 4dbb06a commit 15c97f5

10 files changed

+188
-56
lines changed

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,27 @@ import { Component, NgModule } from '@angular/core';
33
@Component({
44
template: `This is a content from view component # 1`
55
})
6-
export class RoutingView1Component {
6+
export class BottomNavRoutingView1Component {
77
}
88

99
@Component({
1010
template: `This is a content from view component # 2`
1111
})
12-
export class RoutingView2Component {
12+
export class BottomNavRoutingView2Component {
1313
}
1414

1515
@Component({
1616
template: `This is a content from view component # 3`
1717
})
18-
export class RoutingView3Component {
18+
export class BottomNavRoutingView3Component {
1919
}
2020

2121
/**
2222
* @hidden
2323
*/
2424
@NgModule({
25-
declarations: [RoutingView1Component, RoutingView2Component, RoutingView3Component],
26-
exports: [RoutingView1Component, RoutingView2Component, RoutingView3Component],
25+
declarations: [BottomNavRoutingView1Component, BottomNavRoutingView2Component, BottomNavRoutingView3Component],
26+
exports: [BottomNavRoutingView1Component, BottomNavRoutingView2Component, BottomNavRoutingView3Component],
2727
})
28-
export class RoutingViewComponentsModule {
28+
export class BottomNavRoutingViewComponentsModule {
2929
}
Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
1-
<ng-template #componentContentTemplate>
1+
<div *ngIf="!hasContentTabs">
22
<ng-content></ng-content>
3-
</ng-template>
4-
<div>
5-
<ng-container *ngIf="!hasContentTabs">
6-
<ng-container *ngTemplateOutlet="componentContentTemplate">
7-
</ng-container>
8-
</ng-container>
93
</div>
104
<div #tablist class="{{itemStyle}}__menu {{itemStyle}}__menu--bottom" role="tablist" aria-orientation="horizontal">
115
<ng-container *ngIf="!hasContentTabs">
@@ -17,8 +11,5 @@
1711
}" [relatedPanel]="panel" (click)="panel.select()" role="tab">
1812
</igx-tab>
1913
</ng-container>
20-
<ng-container *ngIf="hasContentTabs">
21-
<ng-container *ngTemplateOutlet="componentContentTemplate">
22-
</ng-container>
23-
</ng-container>
14+
<ng-content select="igx-tab"></ng-content>
2415
</div>

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import { IgxBottomNavComponent,
88
IgxTabTemplateDirective } from './tabbar.component';
99
import { RouterTestingModule } from '@angular/router/testing';
1010
import { configureTestSuite } from '../test-utils/configure-suite';
11-
import { RoutingViewComponentsModule,
12-
RoutingView1Component,
13-
RoutingView2Component,
14-
RoutingView3Component } from './routing-view-components';
11+
import { BottomNavRoutingViewComponentsModule,
12+
BottomNavRoutingView1Component,
13+
BottomNavRoutingView2Component,
14+
BottomNavRoutingView3Component } from './routing-view-components';
1515
import { Router } from '@angular/router';
1616
import { Location } from '@angular/common';
1717

@@ -20,14 +20,14 @@ describe('TabBar', () => {
2020
beforeEach(async(() => {
2121

2222
const testRoutes = [
23-
{ path: 'view1', component: RoutingView1Component },
24-
{ path: 'view2', component: RoutingView2Component },
25-
{ path: 'view3', component: RoutingView3Component }
23+
{ path: 'view1', component: BottomNavRoutingView1Component },
24+
{ path: 'view2', component: BottomNavRoutingView2Component },
25+
{ path: 'view3', component: BottomNavRoutingView3Component }
2626
];
2727

2828
TestBed.configureTestingModule({
2929
declarations: [TabBarTestComponent, BottomTabBarTestComponent, TemplatedTabBarTestComponent, TabBarRoutingTestComponent],
30-
imports: [IgxBottomNavModule, RoutingViewComponentsModule, RouterTestingModule.withRoutes(testRoutes)]
30+
imports: [IgxBottomNavModule, BottomNavRoutingViewComponentsModule, RouterTestingModule.withRoutes(testRoutes)]
3131
})
3232
.compileComponents();
3333
}));
@@ -171,7 +171,7 @@ describe('TabBar', () => {
171171

172172
fixture.ngZone.run(() => { router.initialNavigation(); });
173173
tick();
174-
expect(location.path()).toBe('/view1');
174+
expect(location.path()).toBe('/');
175175

176176
const theTabs = bottomNav.contentTabs.toArray();
177177

@@ -197,7 +197,7 @@ describe('TabBar', () => {
197197

198198
fixture.ngZone.run(() => { router.initialNavigation(); });
199199
tick();
200-
expect(location.path()).toBe('/view1');
200+
expect(location.path()).toBe('/');
201201

202202
fixture.ngZone.run(() => { router.navigate(['/view3']); });
203203
tick();

projects/igniteui-angular/src/lib/tabs/tab-item.component.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ export class IgxTabItemComponent implements IgxTabItemBase {
190190
} else if (this._isSelected !== newValue) {
191191
this._isSelected = newValue;
192192
if (this._isSelected) {
193-
this._tabs.onTabItemSelected.emit({ tab: this, group: null });
193+
this.select();
194194
}
195195
}
196196
}
@@ -211,9 +211,34 @@ export class IgxTabItemComponent implements IgxTabItemBase {
211211
} else {
212212
this._isSelected = true;
213213
this._tabs.onTabItemSelected.emit({ tab: this, group: null });
214+
this.handleTabSelectionAnimation();
214215
}
215216
}
216217

218+
private handleTabSelectionAnimation(): void {
219+
const tabElement = this.nativeTabItem.nativeElement;
220+
221+
// Scroll to the left
222+
if (tabElement.offsetLeft < this._tabs.offset) {
223+
this._tabs.scrollElement(tabElement, false);
224+
}
225+
226+
// Scroll to the right
227+
const viewPortOffsetWidth = this._tabs.viewPort.nativeElement.offsetWidth;
228+
const delta = (tabElement.offsetLeft + tabElement.offsetWidth) - (viewPortOffsetWidth + this._tabs.offset);
229+
// Fix for IE 11, a difference is accumulated from the widths calculations
230+
if (delta > 1) {
231+
this._tabs.scrollElement(tabElement, true);
232+
}
233+
234+
this.transformIndicatorAnimation(tabElement);
235+
}
236+
237+
private transformIndicatorAnimation(element: HTMLElement): void {
238+
this._tabs.selectedIndicator.nativeElement.style.width = `${element.offsetWidth}px`;
239+
this._tabs.selectedIndicator.nativeElement.style.transform = `translate(${element.offsetLeft}px)`;
240+
}
241+
217242
private onKeyDown(isLeftArrow: boolean, index = null): void {
218243
const tabsArray = this._tabs.tabs.toArray();
219244
if (index === null) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Component, NgModule } from '@angular/core';
2+
3+
@Component({
4+
template: `This is a content from view component # 1`
5+
})
6+
export class TabsRoutingView1Component {
7+
}
8+
9+
@Component({
10+
template: `This is a content from view component # 2`
11+
})
12+
export class TabsRoutingView2Component {
13+
}
14+
15+
@Component({
16+
template: `This is a content from view component # 3`
17+
})
18+
export class TabsRoutingView3Component {
19+
}
20+
21+
/**
22+
* @hidden
23+
*/
24+
@NgModule({
25+
declarations: [TabsRoutingView1Component, TabsRoutingView2Component, TabsRoutingView3Component],
26+
exports: [TabsRoutingView1Component, TabsRoutingView2Component, TabsRoutingView3Component],
27+
})
28+
export class TabsRoutingViewComponentsModule {
29+
}
Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
<!-- TODO Remove tab container from here -->
2-
<ng-template #componentContentTemplate>
3-
<ng-content></ng-content>
4-
</ng-template>
52
<div #tabsContainer>
63
<div class="igx-tabs__header" #headerContainer>
74
<button igxRipple class="igx-tabs__header-button" igxButton="icon" (click)="scrollLeft($event)" igxLeftButtonStyle>
@@ -18,11 +15,8 @@
1815
'igx-tabs__header-menu-item--disabled': group.disabled }" [relatedGroup]="group" role="tab">
1916
</igx-tab-item>
2017
</ng-container>
21-
<ng-container *ngIf="hasContentTabs">
22-
<ng-container *ngTemplateOutlet="componentContentTemplate">
23-
</ng-container>
24-
</ng-container>
25-
<div #selectedIndicator *ngIf="groups.length > 0" class="igx-tabs__header-menu-item-indicator"></div>
18+
<ng-content select="igx-tab-item"></ng-content>
19+
<div #selectedIndicator *ngIf="groups.length > 0 || contentTabs.length > 0" class="igx-tabs__header-menu-item-indicator"></div>
2620
</div>
2721
</div>
2822
<button igxRipple class="igx-tabs__header-button" igxButton="icon" (click)="scrollRight($event)" igxRightButtonStyle>
@@ -31,10 +25,7 @@
3125
</div>
3226
<div class="igx-tabs__content-fixed">
3327
<div #contentsContainer class="igx-tabs__content-fluid">
34-
<ng-container *ngIf="!hasContentTabs">
35-
<ng-container *ngTemplateOutlet="componentContentTemplate">
36-
</ng-container>
37-
</ng-container>
28+
<ng-content></ng-content>
3829
</div>
3930
</div>
4031
</div>

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

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,29 @@ import { IgxToggleModule } from '../directives/toggle/toggle.directive';
1111
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
1212
import { By } from '@angular/platform-browser';
1313
import { UIInteractions } from '../test-utils/ui-interactions.spec';
14+
import { RouterTestingModule } from '@angular/router/testing';
15+
import { Router } from '@angular/router';
16+
import { Location } from '@angular/common';
17+
import { TabsRoutingViewComponentsModule,
18+
TabsRoutingView1Component,
19+
TabsRoutingView2Component,
20+
TabsRoutingView3Component } from './tabs-routing-view-components';
1421

1522
describe('IgxTabs', () => {
1623
configureTestSuite();
1724
beforeEach(async(() => {
25+
26+
const testRoutes = [
27+
{ path: 'view1', component: TabsRoutingView1Component },
28+
{ path: 'view2', component: TabsRoutingView2Component },
29+
{ path: 'view3', component: TabsRoutingView3Component }
30+
];
31+
1832
TestBed.configureTestingModule({
1933
declarations: [TabsTestComponent, TabsTest2Component, TemplatedTabsTestComponent,
20-
TabsTestSelectedTabComponent, TabsTestCustomStylesComponent, TabsTestBug4420Component],
21-
imports: [IgxTabsModule, IgxButtonModule, IgxDropDownModule, IgxToggleModule, BrowserAnimationsModule]
34+
TabsTestSelectedTabComponent, TabsTestCustomStylesComponent, TabsTestBug4420Component, TabsRoutingTestComponent],
35+
imports: [IgxTabsModule, IgxButtonModule, IgxDropDownModule, IgxToggleModule, BrowserAnimationsModule,
36+
TabsRoutingViewComponentsModule, RouterTestingModule.withRoutes(testRoutes)]
2237
})
2338
.compileComponents();
2439
}));
@@ -397,6 +412,65 @@ describe('IgxTabs', () => {
397412
const indicator = dom.query(By.css('.igx-tabs__header-menu-item-indicator'));
398413
expect(indicator.nativeElement.style.width).toBe('90px');
399414
}));
415+
416+
it('should navigate to the correct URL when clicking on tab buttons', fakeAsync(() => {
417+
const router = TestBed.get(Router);
418+
const location = TestBed.get(Location);
419+
const fixture = TestBed.createComponent(TabsRoutingTestComponent);
420+
const tabsComp = fixture.componentInstance.tabs;
421+
fixture.detectChanges();
422+
423+
fixture.ngZone.run(() => { router.initialNavigation(); });
424+
tick();
425+
expect(location.path()).toBe('/');
426+
427+
const theTabs = tabsComp.contentTabs.toArray();
428+
429+
fixture.ngZone.run(() => { theTabs[2].nativeTabItem.nativeElement.dispatchEvent(new Event('click')); });
430+
tick();
431+
expect(location.path()).toBe('/view3');
432+
433+
fixture.ngZone.run(() => { theTabs[1].nativeTabItem.nativeElement.dispatchEvent(new Event('click')); });
434+
tick();
435+
expect(location.path()).toBe('/view2');
436+
437+
fixture.ngZone.run(() => { theTabs[0].nativeTabItem.nativeElement.dispatchEvent(new Event('click')); });
438+
tick();
439+
expect(location.path()).toBe('/view1');
440+
}));
441+
442+
it('should select the correct tab button/panel when navigating an URL', fakeAsync(() => {
443+
const router = TestBed.get(Router);
444+
const location = TestBed.get(Location);
445+
const fixture = TestBed.createComponent(TabsRoutingTestComponent);
446+
const tabsComp = fixture.componentInstance.tabs;
447+
fixture.detectChanges();
448+
449+
fixture.ngZone.run(() => { router.initialNavigation(); });
450+
tick();
451+
expect(location.path()).toBe('/');
452+
453+
fixture.ngZone.run(() => { router.navigate(['/view3']); });
454+
tick();
455+
expect(location.path()).toBe('/view3');
456+
fixture.detectChanges();
457+
expect(tabsComp.selectedIndex).toBe(2);
458+
expect(tabsComp.contentTabs.toArray()[2].isSelected).toBe(true);
459+
460+
fixture.ngZone.run(() => { router.navigate(['/view2']); });
461+
tick();
462+
expect(location.path()).toBe('/view2');
463+
fixture.detectChanges();
464+
expect(tabsComp.selectedIndex).toBe(1);
465+
expect(tabsComp.contentTabs.toArray()[1].isSelected).toBe(true);
466+
467+
fixture.ngZone.run(() => { router.navigate(['/view1']); });
468+
tick();
469+
expect(location.path()).toBe('/view1');
470+
fixture.detectChanges();
471+
expect(tabsComp.selectedIndex).toBe(0);
472+
expect(tabsComp.contentTabs.toArray()[0].isSelected).toBe(true);
473+
}));
400474
});
401475

402476
@Component({
@@ -584,3 +658,25 @@ class TabsTestCustomStylesComponent {
584658
class TabsTestBug4420Component {
585659
@ViewChild(IgxTabsComponent) public tabs: IgxTabsComponent;
586660
}
661+
662+
@Component({
663+
template: `
664+
<div #wrapperDiv>
665+
<igx-tabs>
666+
<igx-tab-item label="Tab 1" routerLink="/view1" routerLinkActive #rla1="routerLinkActive" [isSelected]="rla1.isActive">
667+
</igx-tab-item>
668+
<igx-tab-item label="Tab 2" routerLink="/view2" routerLinkActive #rla2="routerLinkActive" [isSelected]="rla2.isActive">
669+
</igx-tab-item>
670+
<igx-tab-item label="Tab 3" routerLink="/view3" routerLinkActive #rla3="routerLinkActive" [isSelected]="rla3.isActive">
671+
</igx-tab-item>
672+
</igx-tabs>
673+
<div>
674+
<router-outlet></router-outlet>
675+
</div>
676+
</div>
677+
`
678+
})
679+
class TabsRoutingTestComponent {
680+
@ViewChild(IgxTabsComponent)
681+
public tabs: IgxTabsComponent;
682+
}

projects/igniteui-angular/src/lib/tabs/tabs.component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,8 @@ export class IgxTabsComponent implements IgxTabsBase, AfterViewInit, OnDestroy {
217217
const defaultStyle = `igx-tabs`;
218218
const fixedStyle = `igx-tabs--fixed`;
219219
const iconStyle = `igx-tabs--icons`;
220-
const iconLabelFound = this.groups.find((group) => group.icon != null && group.label != null);
220+
const iconLabelFoundInGroups = this.groups.find((group) => group.icon != null && group.label != null);
221+
const iconLabelFoundInTabs = this.contentTabs.find((tab) => tab.icon != null && tab.label != null);
221222
let css;
222223
switch (TabsType[this.tabsType.toUpperCase()]) {
223224
case TabsType.FIXED: {
@@ -231,7 +232,7 @@ export class IgxTabsComponent implements IgxTabsBase, AfterViewInit, OnDestroy {
231232
}
232233

233234
// Layout fix for items with icons
234-
if (iconLabelFound !== undefined) {
235+
if (iconLabelFoundInGroups !== undefined || iconLabelFoundInTabs !== undefined) {
235236
css = `${css} ${iconStyle}`;
236237
}
237238

src/app/bottomnav/bottomnav.sample.html

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ <h3>Tab 3 Content</h3>
5252

5353
<article class="sample-column">
5454
<div class="tabbar-wrapper" #tabbarEl>
55-
<igx-bottom-nav #tabbar (onTabSelected)="route($event)">
55+
<igx-bottom-nav #tabbar>
5656

5757
<igx-tab-panel label="Movies" icon="local_movies">
5858
<div class="tab-content">
@@ -83,7 +83,15 @@ <h3>Tab 2 Content</h3>
8383

8484
<igx-tab-panel label="watchlist" icon="list">
8585
<div class="tab-content">
86-
<router-outlet name="tabPanelOutlet"></router-outlet>
86+
<h3>Tab 3 Content</h3>
87+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer id velit rutrum, accumsan ante a,
88+
semper nunc. Phasellus ultrices tincidunt imperdiet. Nullam vulputate mauris diam. Nullam
89+
elementum, libero vel varius fermentum, lorem ex bibendum nulla, pretium lacinia erat nibh vel
90+
massa. In hendrerit, sapien ac mollis iaculis, dolor tellus malesuada sem, a accumsan lectus
91+
nisl facilisis leo. Curabitur consequat sit amet nulla at consequat. Duis volutpat tristique
92+
luctus.Vivamus vitae malesuada odio. Praesent ante lectus, porta a eleifend vel, sodales eu
93+
nisl. Vivamus sit amet purus eu lectus cursus rhoncus quis non ex. Cras ac nulla sed arcu finibus
94+
volutpat. Vivamus risus ipsum, pharetra a augue nec, euismod fringilla odio. </p>
8795
</div>
8896
</igx-tab-panel>
8997

0 commit comments

Comments
 (0)