Skip to content

Commit

Permalink
fix(tabs): focusChange not being emitted when tabbing into a tab head…
Browse files Browse the repository at this point in the history
…er (#15094)

Fixes the `focusChange` output not emitting when the user tabs into a tab header which is different from the previously-focused one.

Fixes #14142.

(cherry picked from commit ef18b75)
  • Loading branch information
crisbeto authored and annieyw committed Feb 9, 2021
1 parent 4083b10 commit b62e248
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/material/tabs/tab-group.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
[class.mat-tab-label-active]="selectedIndex == i"
[disabled]="tab.disabled"
[matRippleDisabled]="tab.disabled || disableRipple"
(click)="_handleClick(tab, tabHeader, i)">
(click)="_handleClick(tab, tabHeader, i)"
(cdkFocusChange)="_tabFocusChanged($event, i)">


<div class="mat-tab-label-content">
Expand Down
19 changes: 19 additions & 0 deletions src/material/tabs/tab-group.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,25 @@ describe('MatTabGroup', () => {
expect(tabLabelNativeElements.every(el => el.classList.contains('mat-focus-indicator')))
.toBe(true);
});

it('should emit focusChange when a tab receives focus', () => {
spyOn(fixture.componentInstance, 'handleFocus');
fixture.detectChanges();

const tabLabels = fixture.debugElement.queryAll(By.css('.mat-tab-label'));

expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(0);

// In order to verify that the `focusChange` event also fires with the correct
// index, we focus the second tab before testing the keyboard navigation.
dispatchFakeEvent(tabLabels[1].nativeElement, 'focus');
fixture.detectChanges();

expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(1);
expect(fixture.componentInstance.handleFocus)
.toHaveBeenCalledWith(jasmine.objectContaining({index: 1}));
});

});

describe('aria labelling', () => {
Expand Down
8 changes: 8 additions & 0 deletions src/material/tabs/tab-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
ViewChild,
ViewEncapsulation,
} from '@angular/core';
import {FocusOrigin} from '@angular/cdk/a11y';
import {
CanColor,
CanColorCtor,
Expand Down Expand Up @@ -373,6 +374,13 @@ export abstract class _MatTabGroupBase extends _MatTabGroupMixinBase implements
return this.selectedIndex === idx ? 0 : -1;
}

/** Callback for when the focused state of a tab has changed. */
_tabFocusChanged(focusOrigin: FocusOrigin, index: number) {
if (focusOrigin) {
this._tabHeader.focusIndex = index;
}
}

static ngAcceptInputType_dynamicHeight: BooleanInput;
static ngAcceptInputType_animationDuration: NumberInput;
static ngAcceptInputType_selectedIndex: NumberInput;
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 @@ -58,6 +58,7 @@ export declare abstract class _MatTabGroupBase extends _MatTabGroupMixinBase imp
_handleClick(tab: MatTab, tabHeader: MatTabGroupBaseHeader, index: number): void;
_removeTabBodyWrapperHeight(): void;
_setTabBodyWrapperHeight(tabHeight: number): void;
_tabFocusChanged(focusOrigin: FocusOrigin, index: number): void;
ngAfterContentChecked(): void;
ngAfterContentInit(): void;
ngOnDestroy(): void;
Expand Down

0 comments on commit b62e248

Please sign in to comment.