Skip to content

Commit ad365f4

Browse files
Merge pull request #5539 from IgniteUI/rkolev/fix-5471-master
fix(Tabs): refactoring tabs selection
2 parents 57b418b + 15b9d80 commit ad365f4

File tree

7 files changed

+203
-203
lines changed

7 files changed

+203
-203
lines changed

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

Lines changed: 20 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { IgxTabItemTemplateDirective } from './tabs.directives';
1818
templateUrl: 'tab-item.component.html'
1919
})
2020

21-
export class IgxTabItemComponent implements IgxTabItemBase {
21+
export class IgxTabItemComponent extends IgxTabItemBase {
2222

2323
/**
2424
* Gets the group associated with the tab.
@@ -84,6 +84,7 @@ export class IgxTabItemComponent implements IgxTabItemBase {
8484
private _disabled = false;
8585

8686
constructor(private _tabs: IgxTabsBase, private _element: ElementRef) {
87+
super();
8788
this._nativeTabItem = _element;
8889
}
8990

@@ -158,9 +159,7 @@ export class IgxTabItemComponent implements IgxTabItemBase {
158159
@HostListener('window:resize', ['$event'])
159160
public onResize(event) {
160161
if (this.isSelected) {
161-
this._tabs.selectedIndicator.nativeElement.style.visibility = 'visible';
162-
this._tabs.selectedIndicator.nativeElement.style.width = `${this.nativeTabItem.nativeElement.offsetWidth}px`;
163-
this._tabs.selectedIndicator.nativeElement.style.transform = `translate(${this.nativeTabItem.nativeElement.offsetLeft}px)`;
162+
this._tabs.transformIndicatorAnimation(this.nativeTabItem.nativeElement);
164163
}
165164
}
166165

@@ -241,63 +240,36 @@ export class IgxTabItemComponent implements IgxTabItemBase {
241240
return this.relatedGroup ? this.relatedGroup.isSelected : this._isSelected;
242241
}
243242
set isSelected(newValue: boolean) {
244-
if (this.relatedGroup) {
245-
this.relatedGroup.isSelected = newValue;
246-
} else if (this._isSelected !== newValue) {
247-
this._isSelected = newValue;
248-
if (this._isSelected) {
249-
this.select();
250-
}
243+
if (!this.disabled && this.isSelected !== newValue) {
244+
this._tabs.performSelectionChange(newValue ? this : null);
251245
}
252246
}
253247

254248
/**
255249
* @hidden
256250
*/
257-
get index(): number {
258-
if (this._tabs.tabs) {
259-
return this._tabs.tabs.toArray().indexOf(this);
251+
public select(): void {
252+
if (!this.disabled && !this.isSelected) {
253+
this._tabs.performSelectionChange(this);
260254
}
261255
}
262256

263257
/**
264258
* @hidden
265259
*/
266-
public select(focusDelay = 200): void {
267-
if (this.relatedGroup) {
268-
this.relatedGroup.select(focusDelay);
269-
} else {
270-
this._isSelected = true;
271-
this._tabs.onTabItemSelected.emit({ tab: this, group: null });
272-
this.handleTabSelectionAnimation();
273-
}
274-
}
275-
276-
private handleTabSelectionAnimation(): void {
277-
const tabElement = this.nativeTabItem.nativeElement;
278-
279-
// Scroll to the left
280-
if (tabElement.offsetLeft < this._tabs.offset) {
281-
this._tabs.scrollElement(tabElement, false);
282-
}
283-
284-
// Scroll to the right
285-
const viewPortOffsetWidth = this._tabs.viewPort.nativeElement.offsetWidth;
286-
const delta = (tabElement.offsetLeft + tabElement.offsetWidth) - (viewPortOffsetWidth + this._tabs.offset);
287-
// Fix for IE 11, a difference is accumulated from the widths calculations
288-
if (delta > 1) {
289-
this._tabs.scrollElement(tabElement, true);
260+
get index(): number {
261+
if (this._tabs.tabs) {
262+
return this._tabs.tabs.toArray().indexOf(this);
290263
}
291-
292-
this.transformIndicatorAnimation(tabElement);
264+
return -1;
293265
}
294266

295-
private transformIndicatorAnimation(element: HTMLElement): void {
296-
if (this._tabs && this._tabs.selectedIndicator) {
297-
this._tabs.selectedIndicator.nativeElement.style.visibility = `visible`;
298-
this._tabs.selectedIndicator.nativeElement.style.width = `${element.offsetWidth}px`;
299-
this._tabs.selectedIndicator.nativeElement.style.transform = `translate(${element.offsetLeft}px)`;
300-
}
267+
/**
268+
* @hidden
269+
*/
270+
public setSelectedInternal(newValue: boolean) {
271+
this._isSelected = newValue;
272+
this.tabindex = newValue ? 0 : -1;
301273
}
302274

303275
private onKeyDown(isLeftArrow: boolean, index = null): void {
@@ -308,7 +280,7 @@ export class IgxTabItemComponent implements IgxTabItemBase {
308280
: (this._tabs.selectedIndex === tabsArray.length - 1) ? 0 : this._tabs.selectedIndex + 1;
309281
}
310282
const tab = tabsArray[index];
311-
tab.select(200);
283+
tab.select();
312284
}
313285

314286
/**
@@ -330,4 +302,5 @@ export class IgxTabItemComponent implements IgxTabItemBase {
330302
public get context(): any {
331303
return this.relatedGroup ? this.relatedGroup : this;
332304
}
305+
333306
}

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

Lines changed: 15 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@ import { IgxTabsBase, IgxTabsGroupBase } from './tabs.common';
1919
templateUrl: 'tabs-group.component.html'
2020
})
2121

22-
export class IgxTabsGroupComponent implements IgxTabsGroupBase, AfterContentInit, AfterViewChecked {
23-
24-
/**
25-
* @hidden
26-
*/
27-
private _isSelected = false;
22+
export class IgxTabsGroupComponent extends IgxTabsGroupBase implements AfterContentInit, AfterViewChecked {
2823

2924
/**
3025
* An @Input property that allows you to enable/disable the `IgxTabGroupComponent`.
@@ -68,12 +63,8 @@ export class IgxTabsGroupComponent implements IgxTabsGroupBase, AfterContentInit
6863
return this._isSelected;
6964
}
7065
public set isSelected(newValue: boolean) {
71-
if (this._isSelected !== newValue) {
72-
if (newValue) {
73-
this.select();
74-
} else {
75-
this._isSelected = newValue;
76-
}
66+
if (!this.disabled && this.isSelected !== newValue) {
67+
this._tabs.performSelectionChange(newValue ? this.relatedTab : null);
7768
}
7869
}
7970

@@ -84,8 +75,10 @@ export class IgxTabsGroupComponent implements IgxTabsGroupBase, AfterContentInit
8475
protected tabTemplate: IgxTabItemTemplateDirective;
8576

8677
private _tabTemplate: TemplateRef<any>;
78+
private _isSelected = false;
8779

8880
constructor(private _tabs: IgxTabsBase, private _element: ElementRef) {
81+
super();
8982
}
9083

9184
/**
@@ -103,7 +96,7 @@ export class IgxTabsGroupComponent implements IgxTabsGroupBase, AfterContentInit
10396
@HostListener('window:resize', ['$event'])
10497
public onResize(event) {
10598
if (this.isSelected) {
106-
this.transformContentAnimation(0);
99+
this._tabs.transformContentAnimation(this.relatedTab, 0);
107100
}
108101
}
109102

@@ -137,6 +130,7 @@ export class IgxTabsGroupComponent implements IgxTabsGroupBase, AfterContentInit
137130
if (this._tabs.groups) {
138131
return this._tabs.groups.toArray().indexOf(this);
139132
}
133+
return -1;
140134
}
141135

142136
/**
@@ -168,12 +162,6 @@ export class IgxTabsGroupComponent implements IgxTabsGroupBase, AfterContentInit
168162
public ngAfterViewChecked() {
169163
this._element.nativeElement.setAttribute('aria-labelledby', `igx-tab-item-${this.index}`);
170164
this._element.nativeElement.setAttribute('id', `igx-tabs__group-${this.index}`);
171-
172-
if (this.isSelected) {
173-
const tabItem = this.relatedTab.nativeTabItem.nativeElement;
174-
this.transformContentAnimation(0);
175-
this.transformIndicatorAnimation(tabItem);
176-
}
177165
}
178166

179167
/**
@@ -186,53 +174,18 @@ export class IgxTabsGroupComponent implements IgxTabsGroupBase, AfterContentInit
186174
* this.tab.select();
187175
*}
188176
*```
189-
* @param focusDelay A number representing the expected delay.
190177
*/
191-
public select(focusDelay = 200): void {
192-
if (this.disabled || this.isSelected) {
193-
return;
194-
}
195-
196-
this._isSelected = true;
197-
this.relatedTab.tabindex = 0;
198-
199-
if (focusDelay !== 0) {
200-
setTimeout(() => {
201-
this.relatedTab.nativeTabItem.nativeElement.focus();
202-
}, focusDelay);
178+
public select(): void {
179+
if (!this.disabled && !this.isSelected) {
180+
this._tabs.performSelectionChange(this.relatedTab);
203181
}
204-
this.handleSelection();
205-
this._tabs.onTabItemSelected.emit({ tab: this._tabs.tabs.toArray()[this.index], group: this });
206182
}
207183

208-
private handleSelection(): void {
209-
const tabElement = this.relatedTab.nativeTabItem.nativeElement;
210-
211-
// Scroll to the left
212-
if (tabElement.offsetLeft < this._tabs.offset) {
213-
this._tabs.scrollElement(tabElement, false);
214-
}
215-
216-
// Scroll to the right
217-
const viewPortOffsetWidth = this._tabs.viewPort.nativeElement.offsetWidth;
218-
const delta = (tabElement.offsetLeft + tabElement.offsetWidth) - (viewPortOffsetWidth + this._tabs.offset);
219-
// Fix for IE 11, a difference is accumulated from the widths calculations
220-
if (delta > 1) {
221-
this._tabs.scrollElement(tabElement, true);
222-
}
223-
224-
this.transformContentAnimation(0.2);
225-
this.transformIndicatorAnimation(tabElement);
226-
}
227-
228-
private transformContentAnimation(duration: number): void {
229-
const contentOffset = this._tabs.tabsContainer.nativeElement.offsetWidth * this.index;
230-
this._tabs.contentsContainer.nativeElement.style.transitionDuration = `${duration}s`;
231-
this._tabs.contentsContainer.nativeElement.style.transform = `translate(${-contentOffset}px)`;
184+
/**
185+
* @hidden
186+
*/
187+
public setSelectedInternal(newValue: boolean) {
188+
this._isSelected = newValue;
232189
}
233190

234-
private transformIndicatorAnimation(element: HTMLElement): void {
235-
this._tabs.selectedIndicator.nativeElement.style.width = `${element.offsetWidth}px`;
236-
this._tabs.selectedIndicator.nativeElement.style.transform = `translate(${element.offsetLeft}px)`;
237-
}
238191
}

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,21 @@ export abstract class IgxTabsBase {
1515
onTabItemSelected: EventEmitter<{}>; // TODO: Define event arg interface!
1616
hasContentTabs: boolean;
1717
scrollElement(element: any, scrollRight: boolean) {}
18+
performSelectionChange(newTab: IgxTabItemBase) {}
19+
transformContentAnimation(tab: IgxTabItemBase, duration: number) {}
20+
transformIndicatorAnimation(element: HTMLElement) {}
1821
}
1922

2023
/** @hidden */
21-
export interface IgxTabItemBase {
24+
export abstract class IgxTabItemBase {
2225
nativeTabItem: ElementRef;
23-
select(focusDelay?: number);
26+
get index(): number { return 0; }
27+
select(): void {}
28+
setSelectedInternal(newValue: boolean) {}
2429
}
2530

2631
/** @hidden */
27-
export interface IgxTabsGroupBase {
28-
select(focusDelay?: number);
32+
export abstract class IgxTabsGroupBase {
33+
select(): void {}
34+
setSelectedInternal(newValue: boolean) {}
2935
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,11 +531,13 @@ describe('IgxTabs', () => {
531531
});
532532

533533
describe('Tabs-only Mode With Initial Selection Set on TabItems Tests', () => {
534+
let router;
534535
let fixture;
535536
let tabsComp;
536537
let theTabs;
537538

538539
beforeEach(async(() => {
540+
router = TestBed.get(Router);
539541
fixture = TestBed.createComponent(TabsTabsOnlyModeTest1Component);
540542
tabsComp = fixture.componentInstance.tabs;
541543
fixture.detectChanges();
@@ -576,6 +578,13 @@ describe('IgxTabs', () => {
576578
expect(theTabs[2].nativeTabItem.nativeElement.classList.contains(tabItemSelectedCssClass)).toBe(true);
577579
});
578580

581+
it('should hide the selection indicator when no tab item is selected', () => {
582+
expect(tabsComp.selectedIndicator.nativeElement.style.visibility).toBe('visible');
583+
theTabs[1].isSelected = false;
584+
fixture.detectChanges();
585+
expect(tabsComp.selectedIndicator.nativeElement.style.visibility).toBe('hidden');
586+
});
587+
579588
});
580589

581590
describe('Tabs-only Mode With Initial Selection Set on Tabs Component Tests', () => {

0 commit comments

Comments
 (0)