Skip to content

Commit f7a414d

Browse files
authored
Merge pull request #7067 from IgniteUI/dmdimitrov/issue6920-8.2.x
fix(tabs): fixes on tab related attributes - 8.2.x
2 parents c9160e4 + da15d63 commit f7a414d

File tree

5 files changed

+142
-34
lines changed

5 files changed

+142
-34
lines changed

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

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,6 @@ export class IgxTabItemComponent extends IgxTabItemBase implements AfterViewInit
126126
@HostBinding('attr.tabindex')
127127
public tabindex = -1;
128128

129-
/**
130-
* @hidden @internal
131-
*/
132-
@HostBinding('attr.id')
133-
public id = 'igx-tab-item-' + this.index;
134-
135129
/**
136130
* @hidden @internal
137131
*/
@@ -150,12 +144,6 @@ export class IgxTabItemComponent extends IgxTabItemBase implements AfterViewInit
150144
@HostBinding('attr.aria-selected')
151145
public ariaSelected = this.isSelected;
152146

153-
/**
154-
* @hidden @internal
155-
*/
156-
@HostBinding('attr.aria-controls')
157-
public ariaControls = 'igx-tab-item-group-' + this.index;
158-
159147
ngAfterViewInit(): void {
160148
this._ngZone.runOutsideAngular(() => {
161149
this._resizeObserver = new ResizeObserver(() => {
@@ -356,5 +344,4 @@ export class IgxTabItemComponent extends IgxTabItemBase implements AfterViewInit
356344
public get context(): any {
357345
return this.relatedGroup ? this.relatedGroup : this;
358346
}
359-
360347
}

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

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import {
22
AfterContentInit,
3-
AfterViewChecked,
43
Component,
54
ContentChild,
65
ElementRef,
76
HostBinding,
87
Input,
98
TemplateRef,
10-
HostListener
119
} from '@angular/core';
1210

1311
import { IgxTabItemComponent } from './tab-item.component';
@@ -19,8 +17,7 @@ import { IgxTabsBase, IgxTabsGroupBase } from './tabs.common';
1917
templateUrl: 'tabs-group.component.html'
2018
})
2119

22-
export class IgxTabsGroupComponent extends IgxTabsGroupBase implements AfterContentInit, AfterViewChecked {
23-
20+
export class IgxTabsGroupComponent extends IgxTabsGroupBase implements AfterContentInit {
2421
/**
2522
* An @Input property that allows you to enable/disable the `IgxTabGroupComponent`.
2623
*```html
@@ -68,6 +65,16 @@ export class IgxTabsGroupComponent extends IgxTabsGroupBase implements AfterCont
6865
}
6966
}
7067

68+
/**
69+
* Returns the native element of the tabs-group component
70+
* ```typescript
71+
* const mytabsGroupElement: HTMLElement = tabsGroup.nativeElement;
72+
* ```
73+
*/
74+
public get nativeElement() {
75+
return this._element.nativeElement;
76+
}
77+
7178
/**
7279
* @hidden
7380
*/
@@ -87,6 +94,7 @@ export class IgxTabsGroupComponent extends IgxTabsGroupBase implements AfterCont
8794
@HostBinding('attr.role')
8895
public role = 'tabpanel';
8996

97+
9098
/**
9199
* @hidden
92100
*/
@@ -149,14 +157,6 @@ export class IgxTabsGroupComponent extends IgxTabsGroupBase implements AfterCont
149157
}
150158
}
151159

152-
/**
153-
* @hidden
154-
*/
155-
public ngAfterViewChecked() {
156-
this._element.nativeElement.setAttribute('aria-labelledby', `igx-tab-item-${this.index}`);
157-
this._element.nativeElement.setAttribute('id', `igx-tabs__group-${this.index}`);
158-
}
159-
160160
/**
161161
* A method that sets the focus on a tab.
162162
* @memberof {@link IgxTabsGroupComponent}
@@ -180,5 +180,4 @@ export class IgxTabsGroupComponent extends IgxTabsGroupBase implements AfterCont
180180
public setSelectedInternal(newValue: boolean) {
181181
this._isSelected = newValue;
182182
}
183-
184183
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
<div class="igx-tabs__header-wrapper-fixed" #viewPort>
88
<div #itemsContainer class="igx-tabs__header-wrapper-fluid">
99
<ng-container *ngIf="!hasContentTabs">
10-
<igx-tab-item igxRipple *ngFor="let group of groups" [relatedGroup]="group" [autoGenerated]="true">
10+
<igx-tab-item igxRipple
11+
*ngFor="let group of groups; let i = index"
12+
[relatedGroup]="group"
13+
[autoGenerated]="true"
14+
[id]="getTabItemId(i)"
15+
[attr.aria-controls]="getTabsGroupId(i)">
1116
</igx-tab-item>
1217
</ng-container>
1318
<ng-content select="igx-tab-item"></ng-content>

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

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,55 @@ describe('IgxTabs', () => {
4545
];
4646

4747
TestBed.configureTestingModule({
48-
declarations: [TabsTestComponent, TabsTest2Component, TemplatedTabsTestComponent, TabsRoutingDisabledTestComponent,
49-
TabsTestSelectedTabComponent, TabsTestCustomStylesComponent, TabsTestBug4420Component, TabsRoutingTestComponent,
50-
TabsTabsOnlyModeTest1Component, TabsTabsOnlyModeTest2Component, TabsDisabledTestComponent, TabsRoutingGuardTestComponent],
48+
declarations: [TabsTestHtmlAttributesComponent, TabsTestComponent, TabsTest2Component, TemplatedTabsTestComponent,
49+
TabsRoutingDisabledTestComponent, TabsTestSelectedTabComponent, TabsTestCustomStylesComponent, TabsTestBug4420Component,
50+
TabsRoutingTestComponent, TabsTabsOnlyModeTest1Component, TabsTabsOnlyModeTest2Component, TabsDisabledTestComponent,
51+
TabsRoutingGuardTestComponent],
5152
imports: [IgxTabsModule, IgxButtonModule, IgxDropDownModule, IgxToggleModule, BrowserAnimationsModule,
5253
TabsRoutingViewComponentsModule, RouterTestingModule.withRoutes(testRoutes)],
5354
providers: [TabRoutingTestGuard]
5455
}).compileComponents();
5556
}));
5657

58+
describe('IgxTabs Html Attributes', () => {
59+
let fixture;
60+
let tabs;
61+
62+
beforeEach(async(() => {
63+
fixture = TestBed.createComponent(TabsTestHtmlAttributesComponent);
64+
tabs = fixture.componentInstance.tabs;
65+
}));
66+
67+
it('should set the correct attributes on the html elements', fakeAsync(() => {
68+
fixture.detectChanges();
69+
70+
const igxTabs = document.querySelectorAll('igx-tabs');
71+
expect(igxTabs.length).toBe(2);
72+
73+
const startTabsIndex = parseInt(igxTabs[0].id.replace('igx-tabs-', ''), 10);
74+
for (let tabIndex = startTabsIndex; tabIndex < startTabsIndex + 2; tabIndex++) {
75+
const tab = igxTabs[tabIndex - startTabsIndex];
76+
expect(tab.id).toEqual(`igx-tabs-${tabIndex}`);
77+
78+
const tabHeaders = tab.querySelectorAll('igx-tab-item');
79+
const tabContents = tab.querySelectorAll('igx-tabs-group');
80+
expect(tabHeaders.length).toBe(3);
81+
expect(tabContents.length).toBe(3);
82+
83+
for (let itemIndex = 0; itemIndex < 3; itemIndex++) {
84+
const headerId = `igx-tab-item-${tabIndex}-${itemIndex}`;
85+
const contentId = `igx-tabs-group-${tabIndex}-${itemIndex}`;
86+
87+
expect(tabHeaders[itemIndex].id).toEqual(headerId);
88+
expect(tabHeaders[itemIndex].getAttribute('aria-controls')).toEqual(contentId);
89+
90+
expect(tabContents[itemIndex].id).toEqual(contentId);
91+
expect(tabContents[itemIndex].getAttribute('aria-labelledby')).toEqual(headerId);
92+
}
93+
}
94+
}));
95+
});
96+
5797
describe('IgxTabs Component with static Panels Definitions', () => {
5898
let fixture;
5999
let tabs;
@@ -1102,3 +1142,36 @@ class TabsTabsOnlyModeTest2Component {
11021142
class TabsDisabledTestComponent {
11031143
@ViewChild(IgxTabsComponent, { static: true }) public tabs: IgxTabsComponent;
11041144
}
1145+
1146+
@Component({
1147+
template: `
1148+
<div #wrapperDiv1>
1149+
<igx-tabs [selectedIndex]="0">
1150+
<igx-tabs-group label="Tab 1">
1151+
<div>Content 1</div>
1152+
</igx-tabs-group>
1153+
<igx-tabs-group label="Tab 2">
1154+
<div>Content 2</div>
1155+
</igx-tabs-group>
1156+
<igx-tabs-group label="Tab 3">
1157+
<div>Content 3</div>
1158+
</igx-tabs-group>
1159+
</igx-tabs>
1160+
</div>
1161+
<div #wrapperDiv2>
1162+
<igx-tabs [selectedIndex]="0">
1163+
<igx-tabs-group label="Tab 4">
1164+
<div>Content 4</div>
1165+
</igx-tabs-group>
1166+
<igx-tabs-group label="Tab 5">
1167+
<div>Content 5</div>
1168+
</igx-tabs-group>
1169+
<igx-tabs-group label="Tab 6">
1170+
<div>Content 6</div>
1171+
</igx-tabs-group>
1172+
</igx-tabs>
1173+
</div>`
1174+
})
1175+
class TabsTestHtmlAttributesComponent {
1176+
@ViewChild(IgxTabsComponent, { static: true }) public tabs: IgxTabsComponent;
1177+
}

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

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,17 @@ export enum TabsType {
3232
CONTENTFIT = 'contentfit'
3333
}
3434

35+
let NEXT_TABS_ID = 0;
36+
3537
@Component({
3638
selector: 'igx-tabs',
3739
templateUrl: 'tabs.component.html',
3840
providers: [{ provide: IgxTabsBase, useExisting: IgxTabsComponent }]
3941
})
4042

4143
export class IgxTabsComponent implements IgxTabsBase, AfterViewInit, OnDestroy {
44+
private _currentTabsId = NEXT_TABS_ID++;
45+
4246
/**
4347
* Provides an observable collection of all `IgxTabsGroupComponent`s.
4448
* ```typescript
@@ -107,8 +111,24 @@ export class IgxTabsComponent implements IgxTabsBase, AfterViewInit, OnDestroy {
107111
public tabsType: string | TabsType = 'contentfit';
108112

109113
/**
110-
* @hidden
111-
*/
114+
* Sets/gets the `id` of the tabs.
115+
*
116+
* @remarks
117+
* If not set, the `id` will have value `"igx-tabs-0"`.
118+
*
119+
* @example
120+
* ```html
121+
* <igx-tabs id="my-first-tabs"></igx-tabs>
122+
* ```
123+
* @memberof IgxTabsComponent
124+
*/
125+
@HostBinding('attr.id')
126+
@Input()
127+
public id = `igx-tabs-${this._currentTabsId}`;
128+
129+
/**
130+
* @hidden
131+
*/
112132
@Input()
113133
public class = '';
114134

@@ -299,8 +319,7 @@ export class IgxTabsComponent implements IgxTabsBase, AfterViewInit, OnDestroy {
299319
}
300320
}
301321

302-
constructor(private _element: ElementRef, private _ngZone: NgZone, private platformUtil: PlatformUtil) {
303-
}
322+
constructor(private _element: ElementRef, private _ngZone: NgZone, private platformUtil: PlatformUtil) { }
304323

305324
/**
306325
* @hidden
@@ -339,7 +358,9 @@ export class IgxTabsComponent implements IgxTabsBase, AfterViewInit, OnDestroy {
339358
});
340359
}
341360

361+
this.setGroupsAttributes();
342362
this._groupChanges$ = this.groups.changes.subscribe(() => {
363+
this.setGroupsAttributes();
343364
this.resetSelectionOnCollectionChanged();
344365
});
345366
}
@@ -358,6 +379,15 @@ export class IgxTabsComponent implements IgxTabsBase, AfterViewInit, OnDestroy {
358379
}
359380
}
360381

382+
private setGroupsAttributes() {
383+
const groupsArray = this.groups.toArray();
384+
for (let index = 0; index < this.groups.length; index++) {
385+
const tabsGroup = groupsArray[index] as IgxTabsGroupComponent;
386+
tabsGroup.nativeElement.setAttribute('id', this.getTabsGroupId(index));
387+
tabsGroup.nativeElement.setAttribute('aria-labelledby', this.getTabItemId(index));
388+
}
389+
}
390+
361391
private resetSelectionOnCollectionChanged(): void {
362392
requestAnimationFrame(() => {
363393
const currentTab = this.tabs.toArray()[this.selectedIndex];
@@ -460,6 +490,20 @@ export class IgxTabsComponent implements IgxTabsBase, AfterViewInit, OnDestroy {
460490
}
461491
}
462492

493+
/**
494+
* @hidden
495+
*/
496+
public getTabItemId(index: number): string {
497+
return `igx-tab-item-${this._currentTabsId}-${index}`;
498+
}
499+
500+
/**
501+
* @hidden
502+
*/
503+
public getTabsGroupId(index: number): string {
504+
return `igx-tabs-group-${this._currentTabsId}-${index}`;
505+
}
506+
463507
/**
464508
* @hidden
465509
*/

0 commit comments

Comments
 (0)