@@ -1022,6 +1022,171 @@ describe('MatDialog', () => {
10221022 ) ;
10231023 } ) ;
10241024
1025+ describe ( 'closePredicate option' , ( ) => {
1026+ function getDialogs ( ) {
1027+ return overlayContainerElement . querySelectorAll ( 'mat-dialog-container' ) ;
1028+ }
1029+
1030+ it ( 'should determine whether closing via the backdrop is allowed' , fakeAsync ( ( ) => {
1031+ let canClose = false ;
1032+ const closedSpy = jasmine . createSpy ( 'closed spy' ) ;
1033+ const ref = dialog . open ( PizzaMsg , {
1034+ closePredicate : ( ) => canClose ,
1035+ viewContainerRef : testViewContainerRef ,
1036+ } ) ;
1037+
1038+ ref . afterClosed ( ) . subscribe ( closedSpy ) ;
1039+ viewContainerFixture . detectChanges ( ) ;
1040+
1041+ expect ( getDialogs ( ) . length ) . toBe ( 1 ) ;
1042+
1043+ let backdrop = overlayContainerElement . querySelector ( '.cdk-overlay-backdrop' ) as HTMLElement ;
1044+ backdrop . click ( ) ;
1045+ viewContainerFixture . detectChanges ( ) ;
1046+ flush ( ) ;
1047+
1048+ expect ( getDialogs ( ) . length ) . toBe ( 1 ) ;
1049+ expect ( closedSpy ) . not . toHaveBeenCalled ( ) ;
1050+
1051+ canClose = true ;
1052+ backdrop . click ( ) ;
1053+ viewContainerFixture . detectChanges ( ) ;
1054+ flush ( ) ;
1055+
1056+ expect ( getDialogs ( ) . length ) . toBe ( 0 ) ;
1057+ expect ( closedSpy ) . toHaveBeenCalledTimes ( 1 ) ;
1058+ } ) ) ;
1059+
1060+ it ( 'should determine whether closing via the escape key is allowed' , fakeAsync ( ( ) => {
1061+ let canClose = false ;
1062+ const closedSpy = jasmine . createSpy ( 'closed spy' ) ;
1063+ const ref = dialog . open ( PizzaMsg , {
1064+ closePredicate : ( ) => canClose ,
1065+ viewContainerRef : testViewContainerRef ,
1066+ } ) ;
1067+
1068+ ref . afterClosed ( ) . subscribe ( closedSpy ) ;
1069+ viewContainerFixture . detectChanges ( ) ;
1070+
1071+ expect ( getDialogs ( ) . length ) . toBe ( 1 ) ;
1072+
1073+ dispatchKeyboardEvent ( document . body , 'keydown' , ESCAPE ) ;
1074+ viewContainerFixture . detectChanges ( ) ;
1075+ flush ( ) ;
1076+
1077+ expect ( getDialogs ( ) . length ) . toBe ( 1 ) ;
1078+ expect ( closedSpy ) . not . toHaveBeenCalled ( ) ;
1079+
1080+ canClose = true ;
1081+ dispatchKeyboardEvent ( document . body , 'keydown' , ESCAPE ) ;
1082+ viewContainerFixture . detectChanges ( ) ;
1083+ flush ( ) ;
1084+
1085+ expect ( getDialogs ( ) . length ) . toBe ( 0 ) ;
1086+ expect ( closedSpy ) . toHaveBeenCalledTimes ( 1 ) ;
1087+ } ) ) ;
1088+
1089+ it ( 'should determine whether closing via the `close` method is allowed' , fakeAsync ( ( ) => {
1090+ let canClose = false ;
1091+ const closedSpy = jasmine . createSpy ( 'closed spy' ) ;
1092+ const ref = dialog . open ( PizzaMsg , {
1093+ closePredicate : ( ) => canClose ,
1094+ viewContainerRef : testViewContainerRef ,
1095+ } ) ;
1096+
1097+ ref . afterClosed ( ) . subscribe ( closedSpy ) ;
1098+ viewContainerFixture . detectChanges ( ) ;
1099+
1100+ expect ( getDialogs ( ) . length ) . toBe ( 1 ) ;
1101+
1102+ ref . close ( ) ;
1103+ viewContainerFixture . detectChanges ( ) ;
1104+ flush ( ) ;
1105+
1106+ expect ( getDialogs ( ) . length ) . toBe ( 1 ) ;
1107+ expect ( closedSpy ) . not . toHaveBeenCalled ( ) ;
1108+
1109+ canClose = true ;
1110+ ref . close ( 'hello' ) ;
1111+ viewContainerFixture . detectChanges ( ) ;
1112+ flush ( ) ;
1113+
1114+ expect ( getDialogs ( ) . length ) . toBe ( 0 ) ;
1115+ expect ( closedSpy ) . toHaveBeenCalledTimes ( 1 ) ;
1116+ expect ( closedSpy ) . toHaveBeenCalledWith ( 'hello' ) ;
1117+ } ) ) ;
1118+
1119+ it ( 'should not be closed by `closeAll` if not allowed by the predicate' , fakeAsync ( ( ) => {
1120+ let canClose = false ;
1121+ const config = { closePredicate : ( ) => canClose } ;
1122+ const spy = jasmine . createSpy ( 'afterAllClosed spy' ) ;
1123+ dialog . open ( PizzaMsg , config ) ;
1124+ viewContainerFixture . detectChanges ( ) ;
1125+ dialog . open ( PizzaMsg , config ) ;
1126+ viewContainerFixture . detectChanges ( ) ;
1127+ dialog . open ( PizzaMsg , config ) ;
1128+ viewContainerFixture . detectChanges ( ) ;
1129+
1130+ const subscription = dialog . afterAllClosed . subscribe ( spy ) ;
1131+ expect ( getDialogs ( ) . length ) . toBe ( 3 ) ;
1132+ expect ( dialog . openDialogs . length ) . toBe ( 3 ) ;
1133+
1134+ dialog . closeAll ( ) ;
1135+ viewContainerFixture . detectChanges ( ) ;
1136+ flush ( ) ;
1137+
1138+ expect ( getDialogs ( ) . length ) . toBe ( 3 ) ;
1139+ expect ( dialog . openDialogs . length ) . toBe ( 3 ) ;
1140+ expect ( spy ) . not . toHaveBeenCalled ( ) ;
1141+
1142+ canClose = true ;
1143+ dialog . closeAll ( ) ;
1144+ viewContainerFixture . detectChanges ( ) ;
1145+ flush ( ) ;
1146+
1147+ expect ( getDialogs ( ) . length ) . toBe ( 0 ) ;
1148+ expect ( dialog . openDialogs . length ) . toBe ( 0 ) ;
1149+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
1150+
1151+ subscription . unsubscribe ( ) ;
1152+ } ) ) ;
1153+
1154+ it ( 'should recapture focus to the first tabbable element when clicking on the backdrop while the `closePredicate` is blocking the close sequence' , fakeAsync ( ( ) => {
1155+ // When testing focus, all of the elements must be in the DOM.
1156+ document . body . appendChild ( overlayContainerElement ) ;
1157+
1158+ dialog . open ( PizzaMsg , {
1159+ closePredicate : ( ) => false ,
1160+ viewContainerRef : testViewContainerRef ,
1161+ } ) ;
1162+
1163+ viewContainerFixture . detectChanges ( ) ;
1164+ flush ( ) ;
1165+ viewContainerFixture . detectChanges ( ) ;
1166+ flush ( ) ;
1167+
1168+ const backdrop = overlayContainerElement . querySelector (
1169+ '.cdk-overlay-backdrop' ,
1170+ ) as HTMLElement ;
1171+ const input = overlayContainerElement . querySelector ( 'input' ) as HTMLInputElement ;
1172+
1173+ expect ( document . activeElement )
1174+ . withContext ( 'Expected input to be focused on open' )
1175+ . toBe ( input ) ;
1176+
1177+ input . blur ( ) ; // Programmatic clicks might not move focus so we simulate it.
1178+ backdrop . click ( ) ;
1179+ viewContainerFixture . detectChanges ( ) ;
1180+ flush ( ) ;
1181+
1182+ expect ( document . activeElement )
1183+ . withContext ( 'Expected input to stay focused after click' )
1184+ . toBe ( input ) ;
1185+
1186+ overlayContainerElement . remove ( ) ;
1187+ } ) ) ;
1188+ } ) ;
1189+
10251190 it (
10261191 'should recapture focus to the first header when clicking on the backdrop with ' +
10271192 'autoFocus set to "first-heading"' ,
0 commit comments