From a9886a1191217be31b823e8520b0dade925a8157 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 10 Sep 2021 14:04:54 +0200 Subject: [PATCH] fix(cdk/a11y): complete input modality streams on destroy (#23522) Fixes that we weren't completing the event streams from the `InputModalityDetector` when it is destroyed. --- .../input-modality-detector.spec.ts | 16 +++++++++++ .../input-modality/input-modality-detector.ts | 27 ++++++++++--------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/cdk/a11y/input-modality/input-modality-detector.spec.ts b/src/cdk/a11y/input-modality/input-modality-detector.spec.ts index b582eb33223b..6cd454525c6b 100644 --- a/src/cdk/a11y/input-modality/input-modality-detector.spec.ts +++ b/src/cdk/a11y/input-modality/input-modality-detector.spec.ts @@ -194,4 +194,20 @@ describe('InputModalityDetector', () => { dispatchMouseEvent(document, 'mousedown'); expect(detector.mostRecentModality).toBe('mouse'); })); + + it('should complete the various observables on destroy', () => { + setupTest(); + + const modalityDetectedSpy = jasmine.createSpy('modalityDetected complete spy'); + const modalityChangedSpy = jasmine.createSpy('modalityChanged complete spy'); + + detector.modalityDetected.subscribe({complete: modalityDetectedSpy}); + detector.modalityChanged.subscribe({complete: modalityChangedSpy}); + + detector.ngOnDestroy(); + + expect(modalityDetectedSpy).toHaveBeenCalled(); + expect(modalityChangedSpy).toHaveBeenCalled(); + }); + }); diff --git a/src/cdk/a11y/input-modality/input-modality-detector.ts b/src/cdk/a11y/input-modality/input-modality-detector.ts index 757ca09645c3..4cd4b7321de3 100644 --- a/src/cdk/a11y/input-modality/input-modality-detector.ts +++ b/src/cdk/a11y/input-modality/input-modality-detector.ts @@ -87,7 +87,7 @@ const modalityEventListenerOptions = normalizePassiveListenerOptions({ * update the input modality to keyboard, but in general this service's behavior is largely * undefined. */ -@Injectable({ providedIn: 'root' }) +@Injectable({providedIn: 'root'}) export class InputModalityDetector implements OnDestroy { /** Emits whenever an input modality is detected. */ readonly modalityDetected: Observable; @@ -185,21 +185,22 @@ export class InputModalityDetector implements OnDestroy { // If we're not in a browser, this service should do nothing, as there's no relevant input // modality to detect. - if (!_platform.isBrowser) { return; } - - // Add the event listeners used to detect the user's input modality. - ngZone.runOutsideAngular(() => { - document.addEventListener('keydown', this._onKeydown, modalityEventListenerOptions); - document.addEventListener('mousedown', this._onMousedown, modalityEventListenerOptions); - document.addEventListener('touchstart', this._onTouchstart, modalityEventListenerOptions); - }); + if (_platform.isBrowser) { + ngZone.runOutsideAngular(() => { + document.addEventListener('keydown', this._onKeydown, modalityEventListenerOptions); + document.addEventListener('mousedown', this._onMousedown, modalityEventListenerOptions); + document.addEventListener('touchstart', this._onTouchstart, modalityEventListenerOptions); + }); + } } ngOnDestroy() { - if (!this._platform.isBrowser) { return; } + this._modality.complete(); - document.removeEventListener('keydown', this._onKeydown, modalityEventListenerOptions); - document.removeEventListener('mousedown', this._onMousedown, modalityEventListenerOptions); - document.removeEventListener('touchstart', this._onTouchstart, modalityEventListenerOptions); + if (this._platform.isBrowser) { + document.removeEventListener('keydown', this._onKeydown, modalityEventListenerOptions); + document.removeEventListener('mousedown', this._onMousedown, modalityEventListenerOptions); + document.removeEventListener('touchstart', this._onTouchstart, modalityEventListenerOptions); + } } }