feat(stepper): add CdkStepHeader directive and fix CdkStepper error on init (#10614)

* Adds the `CdkStepHeader` directive which is the equivalent of `MatStepHeader` and can be used by consumers when building custom steppers.
* Fixes an error that was being thrown on init when using a `cdkStepper` directly.

Fixes #10611.
This commit is contained in:
Kristiyan Kostadinov
2018-10-25 20:55:38 +02:00
committed by mmalerba
parent d2d8a1f675
commit fbbe463354
5 changed files with 63 additions and 15 deletions
+1
View File
@@ -10,3 +10,4 @@ export * from './stepper';
export * from './step-label';
export * from './stepper-button';
export * from './stepper-module';
export * from './step-header';
+26
View File
@@ -0,0 +1,26 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, ElementRef} from '@angular/core';
import {FocusableOption} from '@angular/cdk/a11y';
@Directive({
selector: '[cdkStepHeader]',
host: {
'role': 'tab',
},
})
export class CdkStepHeader implements FocusableOption {
constructor(protected _elementRef: ElementRef<HTMLElement>) {}
/** Focuses the step header. */
focus() {
this._elementRef.nativeElement.focus();
}
}
+17 -2
View File
@@ -11,11 +11,26 @@ import {CdkStepper, CdkStep} from './stepper';
import {CommonModule} from '@angular/common';
import {CdkStepLabel} from './step-label';
import {CdkStepperNext, CdkStepperPrevious} from './stepper-button';
import {CdkStepHeader} from './step-header';
import {BidiModule} from '@angular/cdk/bidi';
@NgModule({
imports: [BidiModule, CommonModule],
exports: [CdkStep, CdkStepper, CdkStepLabel, CdkStepperNext, CdkStepperPrevious],
declarations: [CdkStep, CdkStepper, CdkStepLabel, CdkStepperNext, CdkStepperPrevious]
exports: [
CdkStep,
CdkStepper,
CdkStepHeader,
CdkStepLabel,
CdkStepperNext,
CdkStepperPrevious,
],
declarations: [
CdkStep,
CdkStepper,
CdkStepHeader,
CdkStepLabel,
CdkStepperNext,
CdkStepperPrevious,
]
})
export class CdkStepperModule {}
+11 -3
View File
@@ -38,6 +38,7 @@ import {AbstractControl} from '@angular/forms';
import {CdkStepLabel} from './step-label';
import {Observable, Subject, of as obaservableOf} from 'rxjs';
import {startWith, takeUntil} from 'rxjs/operators';
import {CdkStepHeader} from './step-header';
/** Used to generate unique ID for each stepper component. */
let nextId = 0;
@@ -242,8 +243,12 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
/** The list of step components that the stepper is holding. */
@ContentChildren(CdkStep) _steps: QueryList<CdkStep>;
/** The list of step headers of the steps in the stepper. */
_stepHeader: QueryList<FocusableOption>;
/**
* The list of step headers of the steps in the stepper.
* @deprecated Type to be changed to `QueryList<CdkStepHeader>`.
* @breaking-change 8.0.0
*/
@ContentChildren(CdkStepHeader) _stepHeader: QueryList<FocusableOption>;
/** Whether the validity of previous steps should be checked or not. */
@Input()
@@ -302,7 +307,10 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
}
ngAfterViewInit() {
this._keyManager = new FocusKeyManager(this._stepHeader)
// Note that while the step headers are content children by default, any components that
// extend this one might have them as view chidren. We initialize the keyboard handling in
// AfterViewInit so we're guaranteed for both view and content children to be defined.
this._keyManager = new FocusKeyManager<FocusableOption>(this._stepHeader)
.withWrap()
.withVerticalOrientation(this._orientation === 'vertical');
+8 -10
View File
@@ -21,7 +21,8 @@ import {Subscription} from 'rxjs';
import {MatStepLabel} from './step-label';
import {MatStepperIntl} from './stepper-intl';
import {MatStepperIconContext} from './stepper-icon';
import {StepState} from '@angular/cdk/stepper';
import {CdkStepHeader, StepState} from '@angular/cdk/stepper';
@Component({
moduleId: module.id,
@@ -35,7 +36,7 @@ import {StepState} from '@angular/cdk/stepper';
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MatStepHeader implements OnDestroy {
export class MatStepHeader extends CdkStepHeader implements OnDestroy {
private _intlSubscription: Subscription;
/** State of the given step. */
@@ -65,15 +66,16 @@ export class MatStepHeader implements OnDestroy {
constructor(
public _intl: MatStepperIntl,
private _focusMonitor: FocusMonitor,
private _element: ElementRef<HTMLElement>,
_elementRef: ElementRef<HTMLElement>,
changeDetectorRef: ChangeDetectorRef) {
_focusMonitor.monitor(_element, true);
super(_elementRef);
_focusMonitor.monitor(_elementRef, true);
this._intlSubscription = _intl.changes.subscribe(() => changeDetectorRef.markForCheck());
}
ngOnDestroy() {
this._intlSubscription.unsubscribe();
this._focusMonitor.stopMonitoring(this._element);
this._focusMonitor.stopMonitoring(this._elementRef);
}
/** Returns string label of given step if it is a text label. */
@@ -88,7 +90,7 @@ export class MatStepHeader implements OnDestroy {
/** Returns the host HTML element. */
_getHostElement() {
return this._element.nativeElement;
return this._elementRef.nativeElement;
}
/** Template context variables that are exposed to the `matStepperIcon` instances. */
@@ -99,8 +101,4 @@ export class MatStepHeader implements OnDestroy {
optional: this.optional
};
}
focus() {
this._getHostElement().focus();
}
}