Skip to content

Commit 5e2d13b

Browse files
committed
fix(cdk/a11y): resolve hydration error in focus trap
Disables focus traps on the server, because they insert DOM nodes that can throw off hydration. (cherry picked from commit 10fbe36)
1 parent c317fba commit 5e2d13b

File tree

3 files changed

+19
-11
lines changed

3 files changed

+19
-11
lines changed

src/cdk/a11y/focus-trap/focus-trap.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {_getFocusedElementPierceShadowDom} from '@angular/cdk/platform';
9+
import {Platform, _getFocusedElementPierceShadowDom} from '@angular/cdk/platform';
1010
import {DOCUMENT} from '@angular/common';
1111
import {
1212
AfterContentInit,
@@ -21,6 +21,7 @@ import {
2121
SimpleChanges,
2222
OnChanges,
2323
booleanAttribute,
24+
inject,
2425
} from '@angular/core';
2526
import {take} from 'rxjs/operators';
2627
import {InteractivityChecker} from '../interactivity-checker/interactivity-checker';
@@ -415,10 +416,12 @@ export class CdkTrapFocus implements OnDestroy, AfterContentInit, OnChanges, DoC
415416
/** Whether the focus trap is active. */
416417
@Input({alias: 'cdkTrapFocus', transform: booleanAttribute})
417418
get enabled(): boolean {
418-
return this.focusTrap.enabled;
419+
return this.focusTrap?.enabled || false;
419420
}
420421
set enabled(value: boolean) {
421-
this.focusTrap.enabled = value;
422+
if (this.focusTrap) {
423+
this.focusTrap.enabled = value;
424+
}
422425
}
423426

424427
/**
@@ -436,11 +439,15 @@ export class CdkTrapFocus implements OnDestroy, AfterContentInit, OnChanges, DoC
436439
*/
437440
@Inject(DOCUMENT) _document: any,
438441
) {
439-
this.focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement, true);
442+
const platform = inject(Platform);
443+
444+
if (platform.isBrowser) {
445+
this.focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement, true);
446+
}
440447
}
441448

442449
ngOnDestroy() {
443-
this.focusTrap.destroy();
450+
this.focusTrap?.destroy();
444451

445452
// If we stored a previously focused element when using autoCapture, return focus to that
446453
// element now that the trapped region is being destroyed.
@@ -451,15 +458,15 @@ export class CdkTrapFocus implements OnDestroy, AfterContentInit, OnChanges, DoC
451458
}
452459

453460
ngAfterContentInit() {
454-
this.focusTrap.attachAnchors();
461+
this.focusTrap?.attachAnchors();
455462

456463
if (this.autoCapture) {
457464
this._captureFocus();
458465
}
459466
}
460467

461468
ngDoCheck() {
462-
if (!this.focusTrap.hasAttached()) {
469+
if (this.focusTrap && !this.focusTrap.hasAttached()) {
463470
this.focusTrap.attachAnchors();
464471
}
465472
}
@@ -471,14 +478,14 @@ export class CdkTrapFocus implements OnDestroy, AfterContentInit, OnChanges, DoC
471478
autoCaptureChange &&
472479
!autoCaptureChange.firstChange &&
473480
this.autoCapture &&
474-
this.focusTrap.hasAttached()
481+
this.focusTrap?.hasAttached()
475482
) {
476483
this._captureFocus();
477484
}
478485
}
479486

480487
private _captureFocus() {
481488
this._previouslyFocusedElement = _getFocusedElementPierceShadowDom();
482-
this.focusTrap.focusInitialElementWhenReady();
489+
this.focusTrap?.focusInitialElementWhenReady();
483490
}
484491
}

src/universal-app/kitchen-sink/kitchen-sink.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ <h2>Horizontal Stepper</h2>
449449

450450
<h2>Focus trap</h2>
451451

452-
<div cdkTrapFocus cdkTrapFocusAutoCapture>
452+
<div cdkTrapFocus [cdkTrapFocusAutoCapture]="isAutomated">
453453
<button>Oh no, I'm trapped!</button>
454454
</div>
455455

src/universal-app/kitchen-sink/kitchen-sink.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {FocusMonitor} from '@angular/cdk/a11y';
1+
import {A11yModule, FocusMonitor} from '@angular/cdk/a11y';
22
import {DragDropModule} from '@angular/cdk/drag-drop';
33
import {ScrollingModule, ViewportRuler} from '@angular/cdk/scrolling';
44
import {CdkTableModule, DataSource} from '@angular/cdk/table';
@@ -107,6 +107,7 @@ export class TestEntryComponent {}
107107
// CDK Modules
108108
CdkTableModule,
109109
DragDropModule,
110+
A11yModule,
110111

111112
// Other modules
112113
YouTubePlayerModule,

0 commit comments

Comments
 (0)