Skip to content

Commit

Permalink
fix(cdk/a11y): don't emit blurred events on the server (angular#27315)
Browse files Browse the repository at this point in the history
The `FocusMonitor` is set up to emit `null` when an element is blurred and we have some components that depend on that value to mark themselves as touched. However, it's also set up to return an rxjs observale `of(null)` on the server which means that it'll emit and complete immediately. This is problematic, because it can mark components as touched even though they haven't been.

These changes remove the parameter from `of()` so it never emits.

Fixes angular#27234.
  • Loading branch information
crisbeto authored Jun 16, 2023
1 parent 840878f commit cc52d61
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 1 deletion.
20 changes: 20 additions & 0 deletions src/cdk/a11y/focus-monitor/focus-monitor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {DOCUMENT} from '@angular/common';
import {Component, NgZone, ViewChild} from '@angular/core';
import {ComponentFixture, fakeAsync, flush, inject, TestBed, tick} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {Platform} from '@angular/cdk/platform';
import {A11yModule, CdkMonitorFocus} from '../index';
import {TOUCH_BUFFER_MS} from '../input-modality/input-modality-detector';
import {
Expand Down Expand Up @@ -824,11 +825,14 @@ describe('FocusMonitor observable stream', () => {
let fixture: ComponentFixture<PlainButton>;
let buttonElement: HTMLElement;
let focusMonitor: FocusMonitor;
let fakePlatform: Platform;

beforeEach(() => {
fakePlatform = {isBrowser: true} as Platform;
TestBed.configureTestingModule({
imports: [A11yModule],
declarations: [PlainButton],
providers: [{provide: Platform, useValue: fakePlatform}],
}).compileComponents();
});

Expand All @@ -850,6 +854,22 @@ describe('FocusMonitor observable stream', () => {
tick();
expect(spy).toHaveBeenCalledWith(true);
}));

it('should not emit on the server', fakeAsync(() => {
fakePlatform.isBrowser = false;
const emitSpy = jasmine.createSpy('emit spy');
const completeSpy = jasmine.createSpy('complete spy');

focusMonitor.monitor(buttonElement).subscribe({next: emitSpy, complete: completeSpy});
expect(emitSpy).not.toHaveBeenCalled();
expect(completeSpy).toHaveBeenCalled();

buttonElement.focus();
fixture.detectChanges();
tick();
expect(emitSpy).not.toHaveBeenCalled();
expect(completeSpy).toHaveBeenCalled();
}));
});

describe('FocusMonitor input label detection', () => {
Expand Down
3 changes: 2 additions & 1 deletion src/cdk/a11y/focus-monitor/focus-monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ export class FocusMonitor implements OnDestroy {

// Do nothing if we're not on the browser platform or the passed in node isn't an element.
if (!this._platform.isBrowser || nativeElement.nodeType !== 1) {
return observableOf(null);
// Note: we don't want the observable to emit at all so we don't pass any parameters.
return observableOf();
}

// If the element is inside the shadow DOM, we need to bind our focus/blur listeners to
Expand Down

0 comments on commit cc52d61

Please sign in to comment.