fix(tab-group): focus change event not firing for keyboard navigation (#12192)

This commit is contained in:
Paul Gschwendtner
2018-07-20 16:34:09 +02:00
committed by Joey Perrott
parent 944caf9158
commit 1b7b8ab35d
2 changed files with 63 additions and 10 deletions
+42 -1
View File
@@ -1,4 +1,5 @@
import {dispatchFakeEvent} from '@angular/cdk/testing';
import {LEFT_ARROW} from '@angular/cdk/keycodes';
import {dispatchFakeEvent, dispatchKeyboardEvent} from '@angular/cdk/testing';
import {Component, OnInit, QueryList, ViewChild, ViewChildren} from '@angular/core';
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
@@ -232,6 +233,46 @@ describe('MatTabGroup', () => {
expect(labels.every(label => label.getAttribute('aria-setsize') === '3')).toBe(true);
});
it('should emit focusChange event on click', () => {
spyOn(fixture.componentInstance, 'handleFocus');
fixture.detectChanges();
const tabLabels = fixture.debugElement.queryAll(By.css('.mat-tab-label'));
expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(0);
tabLabels[1].nativeElement.click();
fixture.detectChanges();
expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(1);
expect(fixture.componentInstance.handleFocus)
.toHaveBeenCalledWith(jasmine.objectContaining({index: 1}));
});
it('should emit focusChange on arrow key navigation', () => {
spyOn(fixture.componentInstance, 'handleFocus');
fixture.detectChanges();
const tabLabels = fixture.debugElement.queryAll(By.css('.mat-tab-label'));
const tabLabelContainer = fixture.debugElement
.query(By.css('.mat-tab-label-container')).nativeElement as HTMLElement;
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.
tabLabels[1].nativeElement.click();
fixture.detectChanges();
expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(1);
dispatchKeyboardEvent(tabLabelContainer, 'keydown', LEFT_ARROW);
expect(fixture.componentInstance.handleFocus).toHaveBeenCalledTimes(2);
expect(fixture.componentInstance.handleFocus)
.toHaveBeenCalledWith(jasmine.objectContaining({index: 0}));
});
});
describe('aria labelling', () => {
+21 -9
View File
@@ -28,7 +28,8 @@ import {
ViewEncapsulation,
} from '@angular/core';
import {CanDisableRipple, mixinDisableRipple} from '@angular/material/core';
import {merge, of as observableOf, Subscription} from 'rxjs';
import {merge, of as observableOf, Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
import {MatInkBar} from './ink-bar';
import {MatTabLabelWrapper} from './tab-label-wrapper';
import {FocusKeyManager} from '@angular/cdk/a11y';
@@ -87,8 +88,8 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
/** Whether the header should scroll to the selected index after the view has been checked. */
private _selectedIndexChanged = false;
/** Combines listeners that will re-align the ink bar whenever they're invoked. */
private _realignInkBar = Subscription.EMPTY;
/** Emits when the component is destroyed. */
private readonly _destroyed = new Subject<void>();
/** Whether the controls for pagination should be displayed */
_showPaginationControls = false;
@@ -201,20 +202,31 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
.withHorizontalOrientation(this._getLayoutDirection())
.withWrap();
this._keyManager.updateActiveItemIndex(0);
this._keyManager.updateActiveItem(0);
// Defer the first call in order to allow for slower browsers to lay out the elements.
// This helps in cases where the user lands directly on a page with paginated tabs.
typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame(realign) : realign();
this._realignInkBar = merge(dirChange, resize).subscribe(() => {
// On dir change or window resize, realign the ink bar and update the orientation of
// the key manager if the direction has changed.
merge(dirChange, resize).pipe(takeUntil(this._destroyed)).subscribe(() => {
realign();
this._keyManager.withHorizontalOrientation(this._getLayoutDirection());
});
// If there is a change in the focus key manager we need to emit the `indexFocused`
// event in order to provide a public event that notifies about focus changes. Also we realign
// the tabs container by scrolling the new focused tab into the visible section.
this._keyManager.change.pipe(takeUntil(this._destroyed)).subscribe(newFocusIndex => {
this.indexFocused.emit(newFocusIndex);
this._setTabFocus(newFocusIndex);
});
}
ngOnDestroy() {
this._realignInkBar.unsubscribe();
this._destroyed.next();
this._destroyed.complete();
}
/**
@@ -242,11 +254,11 @@ export class MatTabHeader extends _MatTabHeaderMixinBase
/** When the focus index is set, we must manually send focus to the correct label */
set focusIndex(value: number) {
if (!this._isValidIndex(value) || this.focusIndex == value || !this._keyManager) { return; }
if (!this._isValidIndex(value) || this.focusIndex === value || !this._keyManager) {
return;
}
this._keyManager.setActiveItem(value);
this.indexFocused.emit(value);
this._setTabFocus(value);
}
/**