Skip to content

Commit dbecfa3

Browse files
committed
fix(select): Improve the a11y of select component by making overlay
always exist in the DOM
1 parent ee2732a commit dbecfa3

File tree

6 files changed

+146
-45
lines changed

6 files changed

+146
-45
lines changed

src/cdk/overlay/overlay-directives.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,18 @@ describe('Overlay directives', () => {
120120
'Expected overlay to have been detached.');
121121
});
122122

123+
it('should not close when pressing escape when changed the setting of "disableClose"', () => {
124+
fixture.componentInstance.isOpen = true;
125+
fixture.componentInstance.disableClose = true;
126+
fixture.detectChanges();
127+
128+
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
129+
fixture.detectChanges();
130+
131+
expect(overlayContainerElement.textContent!.trim()).not.toBe('',
132+
'Expected overlay to have not been detached.');
133+
});
134+
123135
it('should not depend on the order in which the `origin` and `open` are set', async(() => {
124136
fixture.destroy();
125137

@@ -467,6 +479,7 @@ describe('Overlay directives', () => {
467479
[cdkConnectedOverlayFlexibleDimensions]="flexibleDimensions"
468480
[cdkConnectedOverlayGrowAfterOpen]="growAfterOpen"
469481
[cdkConnectedOverlayPush]="push"
482+
[cdkConnectedOverlayDisableClose]="disableClose"
470483
cdkConnectedOverlayBackdropClass="mat-test-class"
471484
(backdropClick)="backdropClickHandler($event)"
472485
[cdkConnectedOverlayOffsetX]="offsetX"
@@ -498,6 +511,7 @@ class ConnectedOverlayDirectiveTest {
498511
flexibleDimensions: boolean;
499512
growAfterOpen: boolean;
500513
push: boolean;
514+
disableClose: boolean = false;
501515
backdropClickHandler = jasmine.createSpy('backdropClick handler');
502516
positionChangeHandler = jasmine.createSpy('positionChangeHandler');
503517
positionOverrides: ConnectionPositionPair[];

src/cdk/overlay/overlay-directives.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
114114
private _offsetX: number;
115115
private _offsetY: number;
116116
private _position: FlexibleConnectedPositionStrategy;
117+
private _disableClose = false;
118+
private _open = false;
117119

118120
/** Origin for the connected overlay. */
119121
@Input('cdkConnectedOverlayOrigin') origin: CdkOverlayOrigin;
@@ -165,8 +167,15 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
165167
@Input('cdkConnectedOverlayScrollStrategy') scrollStrategy: ScrollStrategy =
166168
this._scrollStrategy();
167169

170+
/** Whether the overlay closes when the user pressed the Escape key. */
171+
@Input('cdkConnectedOverlayDisableClose')
172+
get disableClose() { return this._disableClose; }
173+
set disableClose(value: any) { this._disableClose = coerceBooleanProperty(value); }
174+
168175
/** Whether the overlay is open. */
169-
@Input('cdkConnectedOverlayOpen') open: boolean = false;
176+
@Input('cdkConnectedOverlayOpen')
177+
get open() { return this._open; }
178+
set open(value: any) { this._open = coerceBooleanProperty(value); }
170179

171180
/** Whether or not the overlay should attach a backdrop. */
172181
@Input('cdkConnectedOverlayHasBackdrop')
@@ -335,7 +344,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
335344
this._createOverlay();
336345

337346
this._overlayRef!.keydownEvents().subscribe((event: KeyboardEvent) => {
338-
if (event.keyCode === ESCAPE) {
347+
if (!this.disableClose && event.keyCode === ESCAPE) {
339348
this._detachOverlay();
340349
}
341350
});
@@ -354,11 +363,9 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
354363
this.attach.emit();
355364
}
356365

357-
if (this.hasBackdrop) {
358-
this._backdropSubscription = this._overlayRef.backdropClick().subscribe(event => {
359-
this.backdropClick.emit(event);
360-
});
361-
}
366+
this._backdropSubscription = this._overlayRef.backdropClicks().subscribe(event => {
367+
this.backdropClick.emit(event);
368+
});
362369
}
363370

364371
/** Detaches the overlay and unsubscribes to backdrop clicks if backdrop exists */

src/cdk/overlay/overlay-ref.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export class OverlayRef implements PortalOutlet {
111111
this._togglePointerEvents(true);
112112

113113
if (this._config.hasBackdrop) {
114-
this._attachBackdrop();
114+
this.attachBackdrop();
115115
}
116116

117117
if (this._config.panelClass) {
@@ -200,6 +200,10 @@ export class OverlayRef implements PortalOutlet {
200200
return this._portalOutlet.hasAttached();
201201
}
202202

203+
backdropClicks() {
204+
return this._backdropClick;
205+
}
206+
203207
/** Gets an observable that emits when the backdrop has been clicked. */
204208
backdropClick(): Observable<MouseEvent> {
205209
return this._backdropClick.asObservable();
@@ -280,7 +284,7 @@ export class OverlayRef implements PortalOutlet {
280284
}
281285

282286
/** Attaches a backdrop for this overlay. */
283-
private _attachBackdrop() {
287+
attachBackdrop() {
284288
const showingClass = 'cdk-overlay-backdrop-showing';
285289

286290
this._backdropElement = this._document.createElement('div');

src/lib/select/select.html

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,31 @@
1818
<ng-template
1919
cdk-connected-overlay
2020
cdkConnectedOverlayLockPosition
21-
cdkConnectedOverlayHasBackdrop
21+
cdkConnectedOverlayOpen
22+
cdkConnectedOverlayDisableClose
2223
cdkConnectedOverlayBackdropClass="cdk-overlay-transparent-backdrop"
2324
[cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
2425
[cdkConnectedOverlayOrigin]="origin"
25-
[cdkConnectedOverlayOpen]="panelOpen"
2626
[cdkConnectedOverlayPositions]="_positions"
2727
[cdkConnectedOverlayMinWidth]="_triggerRect?.width"
2828
[cdkConnectedOverlayOffsetY]="_offsetY"
2929
(backdropClick)="close()"
3030
(attach)="_onAttached()"
3131
(detach)="close()">
32-
3332
<div
3433
#panel
3534
class="mat-select-panel {{ _getPanelTheme() }}"
35+
[class.cdk-visually-hidden]="!panelOpen"
3636
[ngClass]="panelClass"
37-
[@transformPanel]="multiple ? 'showing-multiple' : 'showing'"
37+
[@transformPanel]="_transformPanel"
3838
(@transformPanel.done)="_panelDoneAnimatingStream.next($event.toState)"
3939
[style.transformOrigin]="_transformOrigin"
4040
[class.mat-select-panel-done-animating]="_panelDoneAnimating"
4141
[style.font-size.px]="_triggerFontSize"
4242
(keydown)="_handleKeydown($event)">
43-
4443
<div
4544
class="mat-select-content"
46-
[@fadeInContent]="'showing'"
45+
[@fadeInContent]="panelOpen ? 'showing' : 'void'"
4746
(@fadeInContent.done)="_onFadeInDone()">
4847
<ng-content></ng-content>
4948
</div>

0 commit comments

Comments
 (0)