@@ -3,9 +3,14 @@ import {ENTER, LEFT_ARROW, RIGHT_ARROW, SPACE} from '@angular/cdk/keycodes';
3
3
import { dispatchKeyboardEvent } from '@angular/cdk/testing' ;
4
4
import { Component , DebugElement } from '@angular/core' ;
5
5
import { async , ComponentFixture , TestBed , inject } from '@angular/core/testing' ;
6
- import { FormControl , FormGroup , ReactiveFormsModule , Validators } from '@angular/forms' ;
6
+ import { AbstractControl , AsyncValidatorFn , FormControl , FormGroup , ReactiveFormsModule ,
7
+ ValidationErrors , Validators } from '@angular/forms' ;
7
8
import { By } from '@angular/platform-browser' ;
8
9
import { NoopAnimationsModule } from '@angular/platform-browser/animations' ;
10
+ import { map } from 'rxjs/operators/map' ;
11
+ import { take } from 'rxjs/operators/take' ;
12
+ import { Observable } from 'rxjs/Observable' ;
13
+ import { Subject } from 'rxjs/Subject' ;
9
14
import { MatStepperModule } from './index' ;
10
15
import { MatHorizontalStepper , MatStep , MatStepper , MatVerticalStepper } from './stepper' ;
11
16
import { MatStepperNext , MatStepperPrevious } from './stepper-button' ;
@@ -156,17 +161,25 @@ describe('MatHorizontalStepper', () => {
156
161
expect ( stepperComponent . linear ) . toBe ( true ) ;
157
162
} ) ;
158
163
159
- it ( 'should not move to next step if current step is not valid ' , ( ) => {
164
+ it ( 'should not move to next step if current step is invalid ' , ( ) => {
160
165
expect ( testComponent . oneGroup . get ( 'oneCtrl' ) ! . value ) . toBe ( '' ) ;
161
166
expect ( testComponent . oneGroup . get ( 'oneCtrl' ) ! . valid ) . toBe ( false ) ;
162
167
expect ( testComponent . oneGroup . valid ) . toBe ( false ) ;
168
+ expect ( testComponent . oneGroup . invalid ) . toBe ( true ) ;
163
169
expect ( stepperComponent . selectedIndex ) . toBe ( 0 ) ;
164
170
165
171
let stepHeaderEl = fixture . debugElement
166
172
. queryAll ( By . css ( '.mat-horizontal-stepper-header' ) ) [ 1 ] . nativeElement ;
167
173
assertLinearStepperValidity ( stepHeaderEl , testComponent , fixture ) ;
168
174
} ) ;
169
175
176
+ it ( 'should not move to next step if current step is pending' , ( ) => {
177
+ let stepHeaderEl = fixture . debugElement
178
+ . queryAll ( By . css ( '.mat-horizontal-stepper-header' ) ) [ 2 ] . nativeElement ;
179
+
180
+ assertLinearStepperPending ( stepHeaderEl , testComponent , fixture ) ;
181
+ } ) ;
182
+
170
183
it ( 'should not focus step header upon click if it is not able to be selected' , ( ) => {
171
184
assertStepHeaderBlurred ( fixture ) ;
172
185
} ) ;
@@ -317,10 +330,11 @@ describe('MatVerticalStepper', () => {
317
330
expect ( stepperComponent . linear ) . toBe ( true ) ;
318
331
} ) ;
319
332
320
- it ( 'should not move to next step if current step is not valid ' , ( ) => {
333
+ it ( 'should not move to next step if current step is invalid ' , ( ) => {
321
334
expect ( testComponent . oneGroup . get ( 'oneCtrl' ) ! . value ) . toBe ( '' ) ;
322
335
expect ( testComponent . oneGroup . get ( 'oneCtrl' ) ! . valid ) . toBe ( false ) ;
323
336
expect ( testComponent . oneGroup . valid ) . toBe ( false ) ;
337
+ expect ( testComponent . oneGroup . invalid ) . toBe ( true ) ;
324
338
expect ( stepperComponent . selectedIndex ) . toBe ( 0 ) ;
325
339
326
340
let stepHeaderEl = fixture . debugElement
@@ -329,6 +343,13 @@ describe('MatVerticalStepper', () => {
329
343
assertLinearStepperValidity ( stepHeaderEl , testComponent , fixture ) ;
330
344
} ) ;
331
345
346
+ it ( 'should not move to next step if current step is pending' , ( ) => {
347
+ let stepHeaderEl = fixture . debugElement
348
+ . queryAll ( By . css ( '.mat-vertical-stepper-header' ) ) [ 2 ] . nativeElement ;
349
+
350
+ assertLinearStepperPending ( stepHeaderEl , testComponent , fixture ) ;
351
+ } ) ;
352
+
332
353
it ( 'should not focus step header upon click if it is not able to be selected' , ( ) => {
333
354
assertStepHeaderBlurred ( fixture ) ;
334
355
} ) ;
@@ -617,6 +638,58 @@ function assertLinearStepperValidity(stepHeaderEl: HTMLElement,
617
638
expect ( stepperComponent . selectedIndex ) . toBe ( 1 ) ;
618
639
}
619
640
641
+ /** Asserts that linear stepper does not allow step selection change if current step is pending. */
642
+ function assertLinearStepperPending ( stepHeaderEl : HTMLElement ,
643
+ testComponent :
644
+ LinearMatHorizontalStepperApp |
645
+ LinearMatVerticalStepperApp ,
646
+ fixture : ComponentFixture < any > ) {
647
+ let stepperComponent = fixture . debugElement . query ( By . directive ( MatStepper ) ) . componentInstance ;
648
+ let nextButtonNativeEl = fixture . debugElement
649
+ . queryAll ( By . directive ( MatStepperNext ) ) [ 1 ] . nativeElement ;
650
+
651
+ testComponent . oneGroup . get ( 'oneCtrl' ) ! . setValue ( 'input' ) ;
652
+ testComponent . twoGroup . get ( 'twoCtrl' ) ! . setValue ( 'input' ) ;
653
+ stepperComponent . selectedIndex = 1 ;
654
+ fixture . detectChanges ( ) ;
655
+ expect ( stepperComponent . selectedIndex ) . toBe ( 1 ) ;
656
+
657
+ // Step status = PENDING
658
+ // Assert that linear stepper does not allow step selection change
659
+ expect ( testComponent . twoGroup . pending ) . toBe ( true ) ;
660
+
661
+ stepHeaderEl . click ( ) ;
662
+ fixture . detectChanges ( ) ;
663
+
664
+ expect ( stepperComponent . selectedIndex ) . toBe ( 1 ) ;
665
+
666
+ nextButtonNativeEl . click ( ) ;
667
+ fixture . detectChanges ( ) ;
668
+
669
+ expect ( stepperComponent . selectedIndex ) . toBe ( 1 ) ;
670
+
671
+ // Trigger asynchronous validation
672
+ testComponent . validationTrigger . next ( ) ;
673
+ // Asynchronous validation completed:
674
+ // Step status = VALID
675
+ expect ( testComponent . twoGroup . pending ) . toBe ( false ) ;
676
+ expect ( testComponent . twoGroup . valid ) . toBe ( true ) ;
677
+
678
+ stepHeaderEl . click ( ) ;
679
+ fixture . detectChanges ( ) ;
680
+
681
+ expect ( stepperComponent . selectedIndex ) . toBe ( 2 ) ;
682
+
683
+ stepperComponent . selectedIndex = 1 ;
684
+ fixture . detectChanges ( ) ;
685
+ expect ( stepperComponent . selectedIndex ) . toBe ( 1 ) ;
686
+
687
+ nextButtonNativeEl . click ( ) ;
688
+ fixture . detectChanges ( ) ;
689
+
690
+ expect ( stepperComponent . selectedIndex ) . toBe ( 2 ) ;
691
+ }
692
+
620
693
/** Asserts that step header focus is blurred if the step cannot be selected upon header click. */
621
694
function assertStepHeaderBlurred ( fixture : ComponentFixture < any > ) {
622
695
let stepHeaderEl = fixture . debugElement
@@ -659,6 +732,7 @@ function assertOptionalStepValidity(testComponent:
659
732
660
733
testComponent . oneGroup . get ( 'oneCtrl' ) ! . setValue ( 'input' ) ;
661
734
testComponent . twoGroup . get ( 'twoCtrl' ) ! . setValue ( 'input' ) ;
735
+ testComponent . validationTrigger . next ( ) ;
662
736
stepperComponent . selectedIndex = 2 ;
663
737
fixture . detectChanges ( ) ;
664
738
@@ -706,6 +780,18 @@ function assertCorrectStepIcon(fixture: ComponentFixture<any>,
706
780
expect ( stepperComponent . _getIndicatorType ( 0 ) ) . toBe ( icon ) ;
707
781
}
708
782
783
+ function asyncValidator ( minLength : number , validationTrigger : Observable < any > ) : AsyncValidatorFn {
784
+ return ( control : AbstractControl ) : Observable < ValidationErrors | null > => {
785
+ return validationTrigger . pipe (
786
+ map ( ( ) => {
787
+ const success = control . value && control . value . length >= minLength ;
788
+ return success ? null : { 'asyncValidation' : { } } ;
789
+ } ) ,
790
+ take ( 1 )
791
+ ) ;
792
+ } ;
793
+ }
794
+
709
795
@Component ( {
710
796
template : `
711
797
<mat-horizontal-stepper>
@@ -783,12 +869,14 @@ class LinearMatHorizontalStepperApp {
783
869
twoGroup : FormGroup ;
784
870
threeGroup : FormGroup ;
785
871
872
+ validationTrigger : Subject < any > = new Subject ( ) ;
873
+
786
874
ngOnInit ( ) {
787
875
this . oneGroup = new FormGroup ( {
788
876
oneCtrl : new FormControl ( '' , Validators . required )
789
877
} ) ;
790
878
this . twoGroup = new FormGroup ( {
791
- twoCtrl : new FormControl ( '' , Validators . required )
879
+ twoCtrl : new FormControl ( '' , Validators . required , asyncValidator ( 3 , this . validationTrigger ) )
792
880
} ) ;
793
881
this . threeGroup = new FormGroup ( {
794
882
threeCtrl : new FormControl ( '' , Validators . pattern ( VALID_REGEX ) )
@@ -873,12 +961,14 @@ class LinearMatVerticalStepperApp {
873
961
twoGroup : FormGroup ;
874
962
threeGroup : FormGroup ;
875
963
964
+ validationTrigger : Subject < any > = new Subject ( ) ;
965
+
876
966
ngOnInit ( ) {
877
967
this . oneGroup = new FormGroup ( {
878
968
oneCtrl : new FormControl ( '' , Validators . required )
879
969
} ) ;
880
970
this . twoGroup = new FormGroup ( {
881
- twoCtrl : new FormControl ( '' , Validators . required )
971
+ twoCtrl : new FormControl ( '' , Validators . required , asyncValidator ( 3 , this . validationTrigger ) )
882
972
} ) ;
883
973
this . threeGroup = new FormGroup ( {
884
974
threeCtrl : new FormControl ( '' , Validators . pattern ( VALID_REGEX ) )
0 commit comments