Skip to content

Commit

Permalink
fixup! feat(material/dialog): Switch dialog implementation to use MDC
Browse files Browse the repository at this point in the history
  • Loading branch information
mmalerba committed Aug 4, 2022
1 parent f137ced commit 5a4171c
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 200 deletions.
2 changes: 1 addition & 1 deletion src/material/dialog/testing/BUILD.bazel
Expand Up @@ -12,9 +12,9 @@ ts_library(
"//src/cdk/overlay",
"//src/cdk/testing",
"//src/material/dialog",
"//src/material/legacy-dialog/testing",
"@npm//@angular/core",
"@npm//@angular/platform-browser",
"@npm//rxjs",
],
)

Expand Down
Expand Up @@ -9,4 +9,4 @@
import {BaseHarnessFilters} from '@angular/cdk/testing';

/** A set of criteria that can be used to filter a list of `MatDialogHarness` instances. */
export interface LegacyDialogHarnessFilters extends BaseHarnessFilters {}
export interface DialogHarnessFilters extends BaseHarnessFilters {}
86 changes: 76 additions & 10 deletions src/material/dialog/testing/dialog-harness.ts
Expand Up @@ -6,11 +6,14 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ComponentHarnessConstructor, HarnessPredicate} from '@angular/cdk/testing';
import {
LegacyDialogHarnessFilters,
_MatLegacyDialogHarnessBase,
} from '@angular/material/legacy-dialog/testing';
ComponentHarnessConstructor,
ContentContainerComponentHarness,
HarnessPredicate,
TestKey,
} from '@angular/cdk/testing';
import {DialogHarnessFilters} from './dialog-harness-filters';
import {DialogRole} from '@angular/material/dialog';

/** Selectors for different sections of the mat-dialog that can contain user content. */
export const enum MatDialogSection {
Expand All @@ -19,8 +22,75 @@ export const enum MatDialogSection {
ACTIONS = '.mat-mdc-dialog-actions',
}

/** Base class for the `MatDialogHarness` implementation. */
export class _MatDialogHarnessBase
// @breaking-change 14.0.0 change generic type to MatDialogSection.
extends ContentContainerComponentHarness<MatDialogSection | string>
{
protected _title = this.locatorForOptional(MatDialogSection.TITLE);
protected _content = this.locatorForOptional(MatDialogSection.CONTENT);
protected _actions = this.locatorForOptional(MatDialogSection.ACTIONS);

/** Gets the id of the dialog. */
async getId(): Promise<string | null> {
const id = await (await this.host()).getAttribute('id');
// In case no id has been specified, the "id" property always returns
// an empty string. To make this method more explicit, we return null.
return id !== '' ? id : null;
}

/** Gets the role of the dialog. */
async getRole(): Promise<DialogRole | null> {
return (await this.host()).getAttribute('role') as Promise<DialogRole | null>;
}

/** Gets the value of the dialog's "aria-label" attribute. */
async getAriaLabel(): Promise<string | null> {
return (await this.host()).getAttribute('aria-label');
}

/** Gets the value of the dialog's "aria-labelledby" attribute. */
async getAriaLabelledby(): Promise<string | null> {
return (await this.host()).getAttribute('aria-labelledby');
}

/** Gets the value of the dialog's "aria-describedby" attribute. */
async getAriaDescribedby(): Promise<string | null> {
return (await this.host()).getAttribute('aria-describedby');
}

/**
* Closes the dialog by pressing escape.
*
* Note: this method does nothing if `disableClose` has been set to `true` for the dialog.
*/
async close(): Promise<void> {
await (await this.host()).sendKeys(TestKey.ESCAPE);
}

/** Gets te dialog's text. */
async getText() {
return (await this.host()).text();
}

/** Gets the dialog's title text. This only works if the dialog is using mat-dialog-title. */
async getTitleText() {
return (await this._title())?.text() ?? '';
}

/** Gets the dialog's content text. This only works if the dialog is using mat-dialog-content. */
async getContentText() {
return (await this._content())?.text() ?? '';
}

/** Gets the dialog's actions text. This only works if the dialog is using mat-dialog-actions. */
async getActionsText() {
return (await this._actions())?.text() ?? '';
}
}

/** Harness for interacting with a standard `MatDialog` in tests. */
export class MatDialogHarness extends _MatLegacyDialogHarnessBase {
export class MatDialogHarness extends _MatDialogHarnessBase {
/** The selector for the host element of a `MatDialog` instance. */
static hostSelector = '.mat-mdc-dialog-container';

Expand All @@ -31,12 +101,8 @@ export class MatDialogHarness extends _MatLegacyDialogHarnessBase {
*/
static with<T extends MatDialogHarness>(
this: ComponentHarnessConstructor<T>,
options: LegacyDialogHarnessFilters = {},
options: DialogHarnessFilters = {},
): HarnessPredicate<T> {
return new HarnessPredicate(this, options);
}

protected override _title = this.locatorForOptional(MatDialogSection.TITLE);
protected override _content = this.locatorForOptional(MatDialogSection.CONTENT);
protected override _actions = this.locatorForOptional(MatDialogSection.ACTIONS);
}
62 changes: 56 additions & 6 deletions src/material/dialog/testing/dialog-opener.ts
Expand Up @@ -7,15 +7,65 @@
*/

import {ComponentType} from '@angular/cdk/overlay';
import {ChangeDetectionStrategy, Component, NgModule, ViewEncapsulation} from '@angular/core';
import {_MatTestLegacyDialogOpenerBase} from '@angular/material/legacy-dialog/testing';
import {
ChangeDetectionStrategy,
Component,
Directive,
NgModule,
OnDestroy,
ViewEncapsulation,
} from '@angular/core';
import {
MatDialog,
MatDialogConfig,
MatDialogContainer,
MatDialogModule,
MatDialogConfig,
_MatDialogBase,
_MatDialogContainerBase,
MatDialogRef,
} from '@angular/material/dialog';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {Subscription} from 'rxjs';

/** Base class for a component that immediately opens a dialog when created. */
@Directive()
export class _MatTestDialogOpenerBase<C extends _MatDialogContainerBase, T, R>
implements OnDestroy
{
/** Component that should be opened with the MatDialog `open` method. */
protected static component: ComponentType<unknown> | undefined;

/** Config that should be provided to the MatDialog `open` method. */
protected static config: MatDialogConfig | undefined;

/** MatDialogRef returned from the MatDialog `open` method. */
dialogRef: MatDialogRef<T, R>;

/** Data passed to the `MatDialog` close method. */
closedResult: R | undefined;

private readonly _afterClosedSubscription: Subscription;

constructor(public dialog: _MatDialogBase<C>) {
if (!_MatTestDialogOpenerBase.component) {
throw new Error(`MatTestDialogOpener does not have a component provided.`);
}

this.dialogRef = this.dialog.open<T, R>(
_MatTestDialogOpenerBase.component as ComponentType<T>,
_MatTestDialogOpenerBase.config || {},
);
this._afterClosedSubscription = this.dialogRef.afterClosed().subscribe(result => {
this.closedResult = result;
});
}

ngOnDestroy() {
this._afterClosedSubscription.unsubscribe();
_MatTestDialogOpenerBase.component = undefined;
_MatTestDialogOpenerBase.config = undefined;
}
}

/** Test component that immediately opens a dialog when bootstrapped. */
@Component({
Expand All @@ -24,7 +74,7 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations';
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
export class MatTestDialogOpener<T = unknown, R = unknown> extends _MatTestLegacyDialogOpenerBase<
export class MatTestDialogOpener<T = unknown, R = unknown> extends _MatTestDialogOpenerBase<
MatDialogContainer,
T,
R
Expand All @@ -38,8 +88,8 @@ export class MatTestDialogOpener<T = unknown, R = unknown> extends _MatTestLegac
component: ComponentType<T>,
config?: MatDialogConfig,
) {
_MatTestLegacyDialogOpenerBase.component = component;
_MatTestLegacyDialogOpenerBase.config = config;
_MatTestDialogOpenerBase.component = component;
_MatTestDialogOpenerBase.config = config;
return MatTestDialogOpener as ComponentType<MatTestDialogOpener<T, R>>;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/material/dialog/testing/index.ts
Expand Up @@ -7,3 +7,4 @@
*/

export * from './public-api';
export {_MatDialogHarnessBase} from './dialog-harness';
2 changes: 1 addition & 1 deletion src/material/dialog/testing/public-api.ts
Expand Up @@ -6,6 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/

export {LegacyDialogHarnessFilters} from '@angular/material/legacy-dialog/testing';
export {DialogHarnessFilters} from './dialog-harness-filters';
export {MatDialogHarness, MatDialogSection} from './dialog-harness';
export * from './dialog-opener';
1 change: 1 addition & 0 deletions src/material/legacy-dialog/testing/BUILD.bazel
Expand Up @@ -12,6 +12,7 @@ ts_library(
"//src/cdk/coercion",
"//src/cdk/overlay",
"//src/cdk/testing",
"//src/material/dialog/testing",
"//src/material/legacy-dialog",
"@npm//@angular/core",
"@npm//@angular/platform-browser",
Expand Down
80 changes: 8 additions & 72 deletions src/material/legacy-dialog/testing/dialog-harness.ts
Expand Up @@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ContentContainerComponentHarness, HarnessPredicate, TestKey} from '@angular/cdk/testing';
import {LegacyDialogRole} from '@angular/material/legacy-dialog';
import {LegacyDialogHarnessFilters} from './dialog-harness-filters';
import {HarnessPredicate} from '@angular/cdk/testing';
import {_MatDialogHarnessBase, DialogHarnessFilters} from '@angular/material/dialog/testing';

/** Selectors for different sections of the mat-dialog that can contain user content. */
export const enum MatLegacyDialogSection {
Expand All @@ -17,75 +16,8 @@ export const enum MatLegacyDialogSection {
ACTIONS = '.mat-dialog-actions',
}

/** Base class for the `MatDialogHarness` implementation. */
export class _MatLegacyDialogHarnessBase
// @breaking-change 14.0.0 change generic type to MatDialogSection.
extends ContentContainerComponentHarness<MatLegacyDialogSection | string>
{
protected _title = this.locatorForOptional(MatLegacyDialogSection.TITLE);
protected _content = this.locatorForOptional(MatLegacyDialogSection.CONTENT);
protected _actions = this.locatorForOptional(MatLegacyDialogSection.ACTIONS);

/** Gets the id of the dialog. */
async getId(): Promise<string | null> {
const id = await (await this.host()).getAttribute('id');
// In case no id has been specified, the "id" property always returns
// an empty string. To make this method more explicit, we return null.
return id !== '' ? id : null;
}

/** Gets the role of the dialog. */
async getRole(): Promise<LegacyDialogRole | null> {
return (await this.host()).getAttribute('role') as Promise<LegacyDialogRole | null>;
}

/** Gets the value of the dialog's "aria-label" attribute. */
async getAriaLabel(): Promise<string | null> {
return (await this.host()).getAttribute('aria-label');
}

/** Gets the value of the dialog's "aria-labelledby" attribute. */
async getAriaLabelledby(): Promise<string | null> {
return (await this.host()).getAttribute('aria-labelledby');
}

/** Gets the value of the dialog's "aria-describedby" attribute. */
async getAriaDescribedby(): Promise<string | null> {
return (await this.host()).getAttribute('aria-describedby');
}

/**
* Closes the dialog by pressing escape.
*
* Note: this method does nothing if `disableClose` has been set to `true` for the dialog.
*/
async close(): Promise<void> {
await (await this.host()).sendKeys(TestKey.ESCAPE);
}

/** Gets te dialog's text. */
async getText() {
return (await this.host()).text();
}

/** Gets the dialog's title text. This only works if the dialog is using mat-dialog-title. */
async getTitleText() {
return (await this._title())?.text() ?? '';
}

/** Gets the dialog's content text. This only works if the dialog is using mat-dialog-content. */
async getContentText() {
return (await this._content())?.text() ?? '';
}

/** Gets the dialog's actions text. This only works if the dialog is using mat-dialog-actions. */
async getActionsText() {
return (await this._actions())?.text() ?? '';
}
}

/** Harness for interacting with a standard `MatDialog` in tests. */
export class MatLegacyDialogHarness extends _MatLegacyDialogHarnessBase {
export class MatLegacyDialogHarness extends _MatDialogHarnessBase {
// Developers can provide a custom component or template for the
// dialog. The canonical dialog parent is the "MatDialogContainer".
/** The selector for the host element of a `MatDialog` instance. */
Expand All @@ -97,7 +29,11 @@ export class MatLegacyDialogHarness extends _MatLegacyDialogHarnessBase {
* @param options Options for filtering which dialog instances are considered a match.
* @return a `HarnessPredicate` configured with the given options.
*/
static with(options: LegacyDialogHarnessFilters = {}): HarnessPredicate<MatLegacyDialogHarness> {
static with(options: DialogHarnessFilters = {}): HarnessPredicate<MatLegacyDialogHarness> {
return new HarnessPredicate(MatLegacyDialogHarness, options);
}

protected override _title = this.locatorForOptional(MatLegacyDialogSection.TITLE);
protected override _content = this.locatorForOptional(MatLegacyDialogSection.CONTENT);
protected override _actions = this.locatorForOptional(MatLegacyDialogSection.ACTIONS);
}

0 comments on commit 5a4171c

Please sign in to comment.