Skip to content

Commit

Permalink
feat(material/tabs): add method for programmatically setting focus (#…
Browse files Browse the repository at this point in the history
…15228)

Adds a method that allows for focus to be moved to a particular tab. This is usually
tricky, because all of the DOM elements are hidden away inside the tab group template.

Fixes #15007.
  • Loading branch information
crisbeto committed Mar 5, 2021
1 parent bd2c324 commit d869d79
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 2 deletions.
24 changes: 23 additions & 1 deletion src/material-experimental/mdc-tabs/tab-group.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ import {By} from '@angular/platform-browser';
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
import {CommonModule} from '@angular/common';
import {Observable} from 'rxjs';
import {MAT_TABS_CONFIG, MatTab, MatTabGroup, MatTabHeaderPosition, MatTabsModule} from './index';
import {
MAT_TABS_CONFIG,
MatTab,
MatTabGroup,
MatTabHeaderPosition,
MatTabsModule,
MatTabHeader,
} from './index';


describe('MDC-based MatTabGroup', () => {
Expand Down Expand Up @@ -327,6 +334,21 @@ describe('MDC-based MatTabGroup', () => {
.toHaveBeenCalledWith(jasmine.objectContaining({index: 2}));
}));

it('should be able to programmatically focus a particular tab', () => {
fixture.detectChanges();
const tabGroup: MatTabGroup =
fixture.debugElement.query(By.css('mat-tab-group')).componentInstance;
const tabHeader: MatTabHeader =
fixture.debugElement.query(By.css('mat-tab-header')).componentInstance;

expect(tabHeader.focusIndex).not.toBe(3);

tabGroup.focusTab(3);
fixture.detectChanges();

expect(tabHeader.focusIndex).not.toBe(3);
});

});

describe('aria labelling', () => {
Expand Down
24 changes: 23 additions & 1 deletion src/material/tabs/tab-group.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ import {By} from '@angular/platform-browser';
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
import {CommonModule} from '@angular/common';
import {Observable} from 'rxjs';
import {MatTab, MatTabGroup, MatTabHeaderPosition, MatTabsModule, MAT_TABS_CONFIG} from './index';
import {
MatTab,
MatTabGroup,
MatTabHeader,
MatTabHeaderPosition,
MatTabsModule,
MAT_TABS_CONFIG
} from './index';


describe('MatTabGroup', () => {
Expand Down Expand Up @@ -326,6 +333,21 @@ describe('MatTabGroup', () => {
.toHaveBeenCalledWith(jasmine.objectContaining({index: 2}));
}));

it('should be able to programmatically focus a particular tab', () => {
fixture.detectChanges();
const tabGroup: MatTabGroup =
fixture.debugElement.query(By.css('mat-tab-group')).componentInstance;
const tabHeader: MatTabHeader =
fixture.debugElement.query(By.css('mat-tab-header')).componentInstance;

expect(tabHeader.focusIndex).not.toBe(3);

tabGroup.focusTab(3);
fixture.detectChanges();

expect(tabHeader.focusIndex).not.toBe(3);
});

});

describe('aria labelling', () => {
Expand Down
12 changes: 12 additions & 0 deletions src/material/tabs/tab-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,18 @@ export abstract class _MatTabGroupBase extends _MatTabGroupMixinBase implements
}
}

/**
* Sets focus to a particular tab.
* @param index Index of the tab to be focused.
*/
focusTab(index: number) {
const header = this._tabHeader;

if (header) {
header.focusIndex = index;
}
}

_focusChanged(index: number) {
this.focusChange.emit(this._createChangeEvent(index));
}
Expand Down
1 change: 1 addition & 0 deletions tools/public_api_guard/material/tabs.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export declare abstract class _MatTabGroupBase extends _MatTabGroupMixinBase imp
_removeTabBodyWrapperHeight(): void;
_setTabBodyWrapperHeight(tabHeight: number): void;
_tabFocusChanged(focusOrigin: FocusOrigin, index: number): void;
focusTab(index: number): void;
ngAfterContentChecked(): void;
ngAfterContentInit(): void;
ngOnDestroy(): void;
Expand Down

0 comments on commit d869d79

Please sign in to comment.