Skip to content

Commit

Permalink
refactor(multiple): switch the CDK to the inject function
Browse files Browse the repository at this point in the history
Reworks all of the CDK directives and injectables to use the `inject` function instead of constructor-based injection. All the constructors have a backwards-compatible signature for the apps that may be extending them.
  • Loading branch information
crisbeto committed Sep 12, 2024
1 parent b77d37a commit b5e3e63
Show file tree
Hide file tree
Showing 93 changed files with 904 additions and 938 deletions.
6 changes: 3 additions & 3 deletions src/cdk/a11y/a11y-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {ObserversModule} from '@angular/cdk/observers';
import {NgModule} from '@angular/core';
import {NgModule, inject} from '@angular/core';
import {CdkMonitorFocus} from './focus-monitor/focus-monitor';
import {CdkTrapFocus} from './focus-trap/focus-trap';
import {HighContrastModeDetector} from './high-contrast-mode/high-contrast-mode-detector';
Expand All @@ -18,7 +18,7 @@ import {CdkAriaLive} from './live-announcer/live-announcer';
exports: [CdkAriaLive, CdkTrapFocus, CdkMonitorFocus],
})
export class A11yModule {
constructor(highContrastModeDetector: HighContrastModeDetector) {
highContrastModeDetector._applyBodyHighContrastModeCssClasses();
constructor() {
inject(HighContrastModeDetector)._applyBodyHighContrastModeCssClasses();
}
}
6 changes: 3 additions & 3 deletions src/cdk/a11y/aria-describer/aria-describer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {A11yModule, CDK_DESCRIBEDBY_HOST_ATTRIBUTE} from '../index';
import {AriaDescriber} from './aria-describer';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {Component, ElementRef, ViewChild, Provider} from '@angular/core';
import {Component, ElementRef, ViewChild, Provider, inject} from '@angular/core';

describe('AriaDescriber', () => {
let ariaDescriber: AriaDescriber;
Expand Down Expand Up @@ -404,6 +404,8 @@ function expectMessage(el: Element, message: string) {
imports: [A11yModule],
})
class TestApp {
ariaDescriber = inject(AriaDescriber);

@ViewChild('element1') _element1: ElementRef<HTMLElement>;
get element1(): Element {
return this._element1.nativeElement;
Expand All @@ -423,6 +425,4 @@ class TestApp {
get element4(): Element {
return this._element4.nativeElement;
}

constructor(public ariaDescriber: AriaDescriber) {}
}
17 changes: 6 additions & 11 deletions src/cdk/a11y/aria-describer/aria-describer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {DOCUMENT} from '@angular/common';
import {Inject, Injectable, OnDestroy, APP_ID, inject} from '@angular/core';
import {Injectable, OnDestroy, APP_ID, inject} from '@angular/core';
import {Platform} from '@angular/cdk/platform';
import {addAriaReferencedId, getAriaReferenceIds, removeAriaReferencedId} from './aria-reference';

Expand Down Expand Up @@ -54,7 +54,8 @@ let nextId = 0;
*/
@Injectable({providedIn: 'root'})
export class AriaDescriber implements OnDestroy {
private _document: Document;
private _platform = inject(Platform);
private _document = inject(DOCUMENT);

/** Map of all registered message elements that have been placed into the document. */
private _messageRegistry = new Map<string | Element, RegisteredMessage>();
Expand All @@ -65,15 +66,9 @@ export class AriaDescriber implements OnDestroy {
/** Unique ID for the service. */
private readonly _id = `${nextId++}`;

constructor(
@Inject(DOCUMENT) _document: any,
/**
* @deprecated To be turned into a required parameter.
* @breaking-change 14.0.0
*/
private _platform?: Platform,
) {
this._document = _document;
constructor(...args: unknown[]);

constructor() {
this._id = inject(APP_ID) + '-' + nextId++;
}

Expand Down
34 changes: 18 additions & 16 deletions src/cdk/a11y/focus-monitor/focus-monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@ import {
Directive,
ElementRef,
EventEmitter,
Inject,
Injectable,
InjectionToken,
NgZone,
OnDestroy,
Optional,
Output,
AfterViewInit,
inject,
} from '@angular/core';
import {Observable, of as observableOf, Subject, Subscription} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
Expand Down Expand Up @@ -85,6 +84,10 @@ const captureEventListenerOptions = normalizePassiveListenerOptions({
/** Monitors mouse and keyboard events to determine the cause of focus events. */
@Injectable({providedIn: 'root'})
export class FocusMonitor implements OnDestroy {
private _ngZone = inject(NgZone);
private _platform = inject(Platform);
private readonly _inputModalityDetector = inject(InputModalityDetector);

/** The focus origin that the next focus event is a result of. */
private _origin: FocusOrigin = null;

Expand Down Expand Up @@ -138,20 +141,18 @@ export class FocusMonitor implements OnDestroy {
};

/** Used to reference correct document/window */
protected _document?: Document;
protected _document? = inject(DOCUMENT, {optional: true});

/** Subject for stopping our InputModalityDetector subscription. */
private readonly _stopInputModalityDetector = new Subject<void>();

constructor(
private _ngZone: NgZone,
private _platform: Platform,
private readonly _inputModalityDetector: InputModalityDetector,
/** @breaking-change 11.0.0 make document required */
@Optional() @Inject(DOCUMENT) document: any | null,
@Optional() @Inject(FOCUS_MONITOR_DEFAULT_OPTIONS) options: FocusMonitorOptions | null,
) {
this._document = document;
constructor(...args: unknown[]);

constructor() {
const options = inject<FocusMonitorOptions | null>(FOCUS_MONITOR_DEFAULT_OPTIONS, {
optional: true,
});

this._detectionMode = options?.detectionMode || FocusMonitorDetectionMode.IMMEDIATE;
}
/**
Expand Down Expand Up @@ -619,15 +620,16 @@ export class FocusMonitor implements OnDestroy {
standalone: true,
})
export class CdkMonitorFocus implements AfterViewInit, OnDestroy {
private _elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
private _focusMonitor = inject(FocusMonitor);

private _monitorSubscription: Subscription;
private _focusOrigin: FocusOrigin = null;

@Output() readonly cdkFocusChange = new EventEmitter<FocusOrigin>();

constructor(
private _elementRef: ElementRef<HTMLElement>,
private _focusMonitor: FocusMonitor,
) {}
constructor(...args: unknown[]);
constructor() {}

get focusOrigin(): FocusOrigin {
return this._focusOrigin;
Expand Down
23 changes: 12 additions & 11 deletions src/cdk/a11y/focus-trap/configurable-focus-trap-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {DOCUMENT} from '@angular/common';
import {Inject, Injectable, Injector, NgZone, Optional, inject} from '@angular/core';
import {Injectable, Injector, NgZone, inject} from '@angular/core';
import {InteractivityChecker} from '../interactivity-checker/interactivity-checker';
import {ConfigurableFocusTrap} from './configurable-focus-trap';
import {ConfigurableFocusTrapConfig} from './configurable-focus-trap-config';
Expand All @@ -18,21 +18,22 @@ import {FocusTrapManager} from './focus-trap-manager';
/** Factory that allows easy instantiation of configurable focus traps. */
@Injectable({providedIn: 'root'})
export class ConfigurableFocusTrapFactory {
private _document: Document;
private _checker = inject(InteractivityChecker);
private _ngZone = inject(NgZone);
private _focusTrapManager = inject(FocusTrapManager);

private _document = inject(DOCUMENT);
private _inertStrategy: FocusTrapInertStrategy;

private readonly _injector = inject(Injector);

constructor(
private _checker: InteractivityChecker,
private _ngZone: NgZone,
private _focusTrapManager: FocusTrapManager,
@Inject(DOCUMENT) _document: any,
@Optional() @Inject(FOCUS_TRAP_INERT_STRATEGY) _inertStrategy?: FocusTrapInertStrategy,
) {
this._document = _document;
constructor(...args: unknown[]);

constructor() {
const inertStrategy = inject(FOCUS_TRAP_INERT_STRATEGY, {optional: true});

// TODO split up the strategies into different modules, similar to DateAdapter.
this._inertStrategy = _inertStrategy || new EventListenerFocusTrapInertStrategy();
this._inertStrategy = inertStrategy || new EventListenerFocusTrapInertStrategy();
}

/**
Expand Down
14 changes: 11 additions & 3 deletions src/cdk/a11y/focus-trap/configurable-focus-trap.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import {AfterViewInit, Component, ElementRef, Type, ViewChild, Provider} from '@angular/core';
import {
AfterViewInit,
Component,
ElementRef,
Type,
ViewChild,
Provider,
inject,
} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {
A11yModule,
Expand Down Expand Up @@ -110,12 +118,12 @@ function createComponent<T>(
standalone: true,
})
class SimpleFocusTrap implements AfterViewInit {
private _focusTrapFactory = inject(ConfigurableFocusTrapFactory);

@ViewChild('focusTrapElement') focusTrapElement!: ElementRef;

focusTrap: ConfigurableFocusTrap;

constructor(private _focusTrapFactory: ConfigurableFocusTrapFactory) {}

ngAfterViewInit() {
this.focusTrap = this._focusTrapFactory.create(this.focusTrapElement.nativeElement);
}
Expand Down
14 changes: 11 additions & 3 deletions src/cdk/a11y/focus-trap/event-listener-inert-strategy.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import {AfterViewInit, Component, ElementRef, Provider, Type, ViewChild} from '@angular/core';
import {
AfterViewInit,
Component,
ElementRef,
Provider,
Type,
ViewChild,
inject,
} from '@angular/core';
import {ComponentFixture, TestBed, fakeAsync, flush} from '@angular/core/testing';
import {patchElementFocus} from '../../testing/private';
import {
Expand Down Expand Up @@ -80,6 +88,8 @@ function createComponent<T>(
standalone: true,
})
class SimpleFocusTrap implements AfterViewInit {
private _focusTrapFactory = inject(ConfigurableFocusTrapFactory);

@ViewChild('focusTrapElement') focusTrapElement!: ElementRef<HTMLElement>;
@ViewChild('outsideFocusable') outsideFocusableElement!: ElementRef<HTMLElement>;
@ViewChild('firstFocusable') firstFocusableElement!: ElementRef<HTMLElement>;
Expand All @@ -91,8 +101,6 @@ class SimpleFocusTrap implements AfterViewInit {
// the `document.activeElement`, we need to keep track of it here.
activeElement: Element | null;

constructor(private _focusTrapFactory: ConfigurableFocusTrapFactory) {}

ngAfterViewInit() {
// Ensure consistent focus timing across browsers.
[
Expand Down
5 changes: 3 additions & 2 deletions src/cdk/a11y/focus-trap/focus-trap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ViewChild,
ViewContainerRef,
ViewEncapsulation,
inject as inject_1,
} from '@angular/core';
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
Expand Down Expand Up @@ -481,8 +482,8 @@ class FocusTrapWithoutFocusableElements {
imports: [A11yModule, PortalModule],
})
class FocusTrapInsidePortal {
viewContainerRef = inject_1(ViewContainerRef);

@ViewChild('template') template: TemplateRef<any>;
@ViewChild(CdkPortalOutlet) portalOutlet: CdkPortalOutlet;

constructor(public viewContainerRef: ViewContainerRef) {}
}
30 changes: 12 additions & 18 deletions src/cdk/a11y/focus-trap/focus-trap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
Directive,
DoCheck,
ElementRef,
Inject,
Injectable,
Injector,
Input,
Expand Down Expand Up @@ -372,16 +371,14 @@ export class FocusTrap {
*/
@Injectable({providedIn: 'root'})
export class FocusTrapFactory {
private _document: Document;
private _checker = inject(InteractivityChecker);
private _ngZone = inject(NgZone);

private _document = inject(DOCUMENT);
private _injector = inject(Injector);

constructor(
private _checker: InteractivityChecker,
private _ngZone: NgZone,
@Inject(DOCUMENT) _document: any,
) {
this._document = _document;
}
constructor(...args: unknown[]);
constructor() {}

/**
* Creates a focus-trapped region around the given element.
Expand Down Expand Up @@ -409,6 +406,9 @@ export class FocusTrapFactory {
standalone: true,
})
export class CdkTrapFocus implements OnDestroy, AfterContentInit, OnChanges, DoCheck {
private _elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
private _focusTrapFactory = inject(FocusTrapFactory);

/** Underlying FocusTrap instance. */
focusTrap: FocusTrap;

Expand All @@ -432,15 +432,9 @@ export class CdkTrapFocus implements OnDestroy, AfterContentInit, OnChanges, DoC
*/
@Input({alias: 'cdkTrapFocusAutoCapture', transform: booleanAttribute}) autoCapture: boolean;

constructor(
private _elementRef: ElementRef<HTMLElement>,
private _focusTrapFactory: FocusTrapFactory,
/**
* @deprecated No longer being used. To be removed.
* @breaking-change 13.0.0
*/
@Inject(DOCUMENT) _document: any,
) {
constructor(...args: unknown[]);

constructor() {
const platform = inject(Platform);

if (platform.isBrowser) {
Expand Down
13 changes: 6 additions & 7 deletions src/cdk/a11y/high-contrast-mode/high-contrast-mode-detector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {inject, Inject, Injectable, OnDestroy} from '@angular/core';
import {inject, Injectable, OnDestroy} from '@angular/core';
import {BreakpointObserver} from '@angular/cdk/layout';
import {Platform} from '@angular/cdk/platform';
import {DOCUMENT} from '@angular/common';
Expand Down Expand Up @@ -41,20 +41,19 @@ export const HIGH_CONTRAST_MODE_ACTIVE_CSS_CLASS = 'cdk-high-contrast-active';
*/
@Injectable({providedIn: 'root'})
export class HighContrastModeDetector implements OnDestroy {
private _platform = inject(Platform);

/**
* Figuring out the high contrast mode and adding the body classes can cause
* some expensive layouts. This flag is used to ensure that we only do it once.
*/
private _hasCheckedHighContrastMode: boolean;
private _document: Document;
private _document = inject(DOCUMENT);
private _breakpointSubscription: Subscription;

constructor(
private _platform: Platform,
@Inject(DOCUMENT) document: any,
) {
this._document = document;
constructor(...args: unknown[]);

constructor() {
this._breakpointSubscription = inject(BreakpointObserver)
.observe('(forced-colors: active)')
.subscribe(() => {
Expand Down
Loading

0 comments on commit b5e3e63

Please sign in to comment.