From c381a6c5ae1bd0b25e91384bc8bf03198064aa2b Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 15 Nov 2021 18:28:44 +0100 Subject: [PATCH 001/561] feat(cdk/bidi): support `auto` direction value (#23906) Adds support for the `auto` value of the `dir` attribute. The value gets resolved to `ltr` or `rtl` based on browser's locale. Fixes #10186. --- src/cdk/bidi/bidi.md | 26 +++++++++++++++++--------- src/cdk/bidi/dir.ts | 14 ++++++++------ src/cdk/bidi/directionality.ts | 22 ++++++++++++++++------ tools/public_api_guard/cdk/bidi.md | 2 +- 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/cdk/bidi/bidi.md b/src/cdk/bidi/bidi.md index 272c92e1b466..3c79bdf5709d 100644 --- a/src/cdk/bidi/bidi.md +++ b/src/cdk/bidi/bidi.md @@ -1,34 +1,34 @@ The `bidi` package provides a common system for components to get and respond to change in the -application's LTR/RTL layout direction. +application's LTR/RTL layout direction. ### Directionality - + When including the CDK's `BidiModule`, components can inject `Directionality` to get the current text direction (RTL or LTR); #### Example ```ts -@Component({ ... }) +@Component({ ... }) export class MyWidget implements OnDestroy { /** Whether the widget is in RTL mode or not. */ private isRtl: boolean; - + /** Subscription to the Directionality change EventEmitter. */ - private _dirChangeSubscription = Subscription.EMPTY; - + private _dirChangeSubscription = Subscription.EMPTY; + constructor(dir: Directionality) { this.isRtl = dir.value === 'rtl'; - + this._dirChangeSubscription = dir.change.subscribe(() => { this.flipDirection(); }); } - + ngOnDestroy() { this._dirChangeSubscription.unsubscribe(); } -} +} ``` ### The `Dir` directive @@ -36,3 +36,11 @@ The `BidiModule` also includes a directive that matches any elements with a `dir directive has the same API as Directionality and provides itself _as_ `Directionality`. By doing this, any component that injects `Directionality` will get the closest ancestor layout direction context. + +### Interpreting the `auto` value +The CDK also supports the native `auto` value for the `dir` attribute, however there's a difference +in how it is interpreted. Some parts of the CDK, like overlays and keyboard navigation, need to know +if the element is in an RTL or LTR layout in order to work correctly. For performance reasons, we +resolve the `auto` value by looking at the browser's language (`navigator.language`) and matching +it against a set of known RTL locales. This differs from the way the browser handles it, which is +based on the text content of the element. diff --git a/src/cdk/bidi/dir.ts b/src/cdk/bidi/dir.ts index 1ce6b20802a3..249fab27b467 100644 --- a/src/cdk/bidi/dir.ts +++ b/src/cdk/bidi/dir.ts @@ -8,7 +8,7 @@ import {Directive, Output, Input, EventEmitter, AfterContentInit, OnDestroy} from '@angular/core'; -import {Direction, Directionality} from './directionality'; +import {Direction, Directionality, _resolveDirectionality} from './directionality'; /** * Directive to listen for changes of direction of part of the DOM. @@ -40,14 +40,16 @@ export class Dir implements Directionality, AfterContentInit, OnDestroy { get dir(): Direction { return this._dir; } - set dir(value: Direction) { - const old = this._dir; - const normalizedValue = value ? value.toLowerCase() : value; + set dir(value: Direction | 'auto') { + const previousValue = this._dir; + // Note: `_resolveDirectionality` resolves the language based on the browser's language, + // whereas the browser does it based on the content of the element. Since doing so based + // on the content can be expensive, for now we're doing the simpler matching. + this._dir = _resolveDirectionality(value); this._rawDir = value; - this._dir = normalizedValue === 'ltr' || normalizedValue === 'rtl' ? normalizedValue : 'ltr'; - if (old !== this._dir && this._isInitialized) { + if (previousValue !== this._dir && this._isInitialized) { this.change.emit(this._dir); } } diff --git a/src/cdk/bidi/directionality.ts b/src/cdk/bidi/directionality.ts index 91d0fa358443..3eda68cfc6b1 100644 --- a/src/cdk/bidi/directionality.ts +++ b/src/cdk/bidi/directionality.ts @@ -11,6 +11,21 @@ import {DIR_DOCUMENT} from './dir-document-token'; export type Direction = 'ltr' | 'rtl'; +/** Regex that matches locales with an RTL script. Taken from `goog.i18n.bidi.isRtlLanguage`. */ +const RTL_LOCALE_PATTERN = + /^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_](Adlm|Arab|Hebr|Nkoo|Rohg|Thaa))(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)/i; + +/** Resolves a string value to a specific direction. */ +export function _resolveDirectionality(rawValue: string): Direction { + const value = rawValue?.toLowerCase() || ''; + + if (value === 'auto' && typeof navigator !== 'undefined' && navigator?.language) { + return RTL_LOCALE_PATTERN.test(navigator.language) ? 'rtl' : 'ltr'; + } + + return value === 'rtl' ? 'rtl' : 'ltr'; +} + /** * The directionality (LTR / RTL) context for the application (or a subtree of it). * Exposes the current direction and a stream of direction changes. @@ -25,14 +40,9 @@ export class Directionality implements OnDestroy { constructor(@Optional() @Inject(DIR_DOCUMENT) _document?: any) { if (_document) { - // TODO: handle 'auto' value - - // We still need to account for dir="auto". - // It looks like HTMLElemenet.dir is also "auto" when that's set to the attribute, - // but getComputedStyle return either "ltr" or "rtl". avoiding getComputedStyle for now const bodyDir = _document.body ? _document.body.dir : null; const htmlDir = _document.documentElement ? _document.documentElement.dir : null; - const value = bodyDir || htmlDir; - this.value = value === 'ltr' || value === 'rtl' ? value : 'ltr'; + this.value = _resolveDirectionality(bodyDir || htmlDir || 'ltr'); } } diff --git a/tools/public_api_guard/cdk/bidi.md b/tools/public_api_guard/cdk/bidi.md index fb394856c8e3..3529507f406e 100644 --- a/tools/public_api_guard/cdk/bidi.md +++ b/tools/public_api_guard/cdk/bidi.md @@ -24,7 +24,7 @@ export class BidiModule { export class Dir implements Directionality, AfterContentInit, OnDestroy { readonly change: EventEmitter; get dir(): Direction; - set dir(value: Direction); + set dir(value: Direction | 'auto'); ngAfterContentInit(): void; // (undocumented) ngOnDestroy(): void; From 6f1a1b75201a6eeacf9b02f4ba35672c1e9b66b7 Mon Sep 17 00:00:00 2001 From: ByzantineFailure Date: Mon, 15 Nov 2021 11:26:23 -0800 Subject: [PATCH 002/561] fix(material/datepicker): Add aria-current="date" to current date (#23714) --- src/material/datepicker/calendar-body.html | 1 + src/material/datepicker/calendar-body.spec.ts | 38 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/material/datepicker/calendar-body.html b/src/material/datepicker/calendar-body.html index 14a60fd862d2..a59bd39774d8 100644 --- a/src/material/datepicker/calendar-body.html +++ b/src/material/datepicker/calendar-body.html @@ -49,6 +49,7 @@ [attr.aria-label]="item.ariaLabel" [attr.aria-disabled]="!item.enabled || null" [attr.aria-selected]="_isSelected(item.compareValue)" + [attr.aria-current]="todayValue === item.compareValue ? 'date' : null" (click)="_cellClicked(item, $event)" [style.width]="_cellWidth" [style.paddingTop]="_cellPadding" diff --git a/src/material/datepicker/calendar-body.spec.ts b/src/material/datepicker/calendar-body.spec.ts index 7237b8ab6841..df10b757c081 100644 --- a/src/material/datepicker/calendar-body.spec.ts +++ b/src/material/datepicker/calendar-body.spec.ts @@ -47,9 +47,43 @@ describe('MatCalendarBody', () => { }); it('highlights today', () => { - const todayCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-today')!; + const todayCells = calendarBodyNativeElement.querySelectorAll('.mat-calendar-body-today')!; + expect(todayCells.length).toBe(1); + + const todayCell = todayCells[0]; + expect(todayCell).not.toBeNull(); - expect(todayCell.innerHTML.trim()).toBe('3'); + expect(todayCell.textContent!.trim()).toBe('3'); + }); + + it('sets aria-current="date" on today', () => { + const todayCells = calendarBodyNativeElement.querySelectorAll( + '[aria-current="date"] .mat-calendar-body-today', + )!; + expect(todayCells.length).toBe(1); + + const todayCell = todayCells[0]; + + expect(todayCell).not.toBeNull(); + expect(todayCell.textContent!.trim()).toBe('3'); + }); + + it('does not highlight today if today is not within the scope', () => { + testComponent.todayValue = 100000; + fixture.detectChanges(); + + const todayCell = calendarBodyNativeElement.querySelector('.mat-calendar-body-today')!; + expect(todayCell).toBeNull(); + }); + + it('does not set aria-current="date" on any cell if today is not ' + 'the scope', () => { + testComponent.todayValue = 100000; + fixture.detectChanges(); + + const todayCell = calendarBodyNativeElement.querySelector( + '[aria-current="date"] .mat-calendar-body-today', + )!; + expect(todayCell).toBeNull(); }); it('highlights selected', () => { From 4be9a6e4a44d88bd91bebef31490b0adada0775f Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 15 Nov 2021 20:27:16 +0100 Subject: [PATCH 003/561] refactor(multiple): clean up more ViewEngine-specific code (#23960) Cleans up a bunch of workarounds and docs that were specific to ViewEngine. There are still more places to clean up, but I'm trying to make it easier to track down any potential failures. --- src/cdk-experimental/dialog/dialog-module.ts | 1 - src/cdk/drag-drop/directives/drag.spec.ts | 11 ---- src/cdk/portal/portal.md | 4 +- src/dev-app/main-module.ts | 2 - .../column-resize/column-resize-module.ts | 1 - src/material-experimental/mdc-button/fab.ts | 32 ++--------- .../mdc-dialog/module.ts | 1 - .../mdc-snack-bar/module.ts | 1 - .../mdc-tooltip/module.ts | 1 - .../selection/row-selection.ts | 14 +---- .../selection/selection-toggle.ts | 14 +---- .../bottom-sheet/bottom-sheet-module.ts | 1 - src/material/bottom-sheet/bottom-sheet.md | 33 ----------- .../datepicker/date-range-input-parts.ts | 56 +------------------ src/material/datepicker/datepicker-module.ts | 1 - src/material/dialog/dialog-module.ts | 1 - src/material/dialog/dialog.md | 34 +---------- src/material/snack-bar/snack-bar-module.ts | 1 - src/material/tooltip/tooltip-module.ts | 1 - src/material/tree/toggle.ts | 17 +----- .../kitchen-sink-mdc/kitchen-sink-mdc.ts | 1 - .../kitchen-sink/kitchen-sink.ts | 1 - tools/public_api_guard/material/datepicker.md | 12 +--- tools/public_api_guard/material/tree.md | 3 - 24 files changed, 15 insertions(+), 229 deletions(-) diff --git a/src/cdk-experimental/dialog/dialog-module.ts b/src/cdk-experimental/dialog/dialog-module.ts index e6be6d315a14..22bccdc6c8c0 100644 --- a/src/cdk-experimental/dialog/dialog-module.ts +++ b/src/cdk-experimental/dialog/dialog-module.ts @@ -37,6 +37,5 @@ import { {provide: DIALOG_CONTAINER, useValue: CdkDialogContainer}, {provide: DIALOG_CONFIG, useValue: DialogConfig}, ], - entryComponents: [CdkDialogContainer], }) export class DialogModule {} diff --git a/src/cdk/drag-drop/directives/drag.spec.ts b/src/cdk/drag-drop/directives/drag.spec.ts index 71b67e7f0451..651ac3d0ee42 100644 --- a/src/cdk/drag-drop/directives/drag.spec.ts +++ b/src/cdk/drag-drop/directives/drag.spec.ts @@ -4698,17 +4698,6 @@ describe('CdkDrag', () => { })); it('should be able to start dragging again if the dragged item is destroyed', fakeAsync(() => { - // We have some behavior where we move the dragged element out to the bottom of the `body`, - // in order to work around a browser issue. We restore the element when dragging stops, but - // the problem is that if it's destroyed before we've had a chance to return it, ViewEngine - // will throw an error since the element isn't in its original parent. Skip this test if the - // component hasn't been compiled with Ivy since the assertions depend on the element being - // removed while dragging. - // TODO(crisbeto): remove this check once ViewEngine has been dropped. - if (!DraggableInDropZone.hasOwnProperty('ɵcmp')) { - return; - } - const fixture = createComponent(DraggableInDropZone); fixture.detectChanges(); diff --git a/src/cdk/portal/portal.md b/src/cdk/portal/portal.md index a5919ae7afe4..6c75a6bb6105 100644 --- a/src/cdk/portal/portal.md +++ b/src/cdk/portal/portal.md @@ -50,9 +50,7 @@ A component can use `@ViewChild` or `@ViewChildren` to get a reference to a `CdkPortal`. ##### `ComponentPortal` -Used to create a portal from a component type. When a component is dynamically created using -portals, it must be included in the `entryComponents` of its `NgModule`if your project uses ViewEngine. Projects -using Angular Ivy don't need `entryComponents`. +Used to create a portal from a component type. Usage: ```ts diff --git a/src/dev-app/main-module.ts b/src/dev-app/main-module.ts index c14ad3506f8b..8e2b6c951cb0 100644 --- a/src/dev-app/main-module.ts +++ b/src/dev-app/main-module.ts @@ -24,8 +24,6 @@ import {ANIMATIONS_STORAGE_KEY} from './dev-app/dev-app-layout'; @NgModule({ imports: [ BrowserAnimationsModule.withConfig({ - // Note that this doesn't seem to work on ViewEngine, but it's - // not a compilation error either so we can live with it. disableAnimations: localStorage.getItem(ANIMATIONS_STORAGE_KEY) === 'true', }), BrowserModule, diff --git a/src/material-experimental/column-resize/column-resize-module.ts b/src/material-experimental/column-resize/column-resize-module.ts index 5b7a78717115..55add11dcc99 100644 --- a/src/material-experimental/column-resize/column-resize-module.ts +++ b/src/material-experimental/column-resize/column-resize-module.ts @@ -23,7 +23,6 @@ const ENTRY_COMMON_COMPONENTS = [MatColumnResizeOverlayHandle]; @NgModule({ declarations: ENTRY_COMMON_COMPONENTS, exports: ENTRY_COMMON_COMPONENTS, - entryComponents: ENTRY_COMMON_COMPONENTS, }) export class MatColumnResizeCommonModule {} diff --git a/src/material-experimental/mdc-button/fab.ts b/src/material-experimental/mdc-button/fab.ts index bfd96b75232c..d666aecbaa5e 100644 --- a/src/material-experimental/mdc-button/fab.ts +++ b/src/material-experimental/mdc-button/fab.ts @@ -66,20 +66,11 @@ const defaults = MAT_FAB_DEFAULT_OPTIONS_FACTORY(); selector: `button[mat-fab]`, templateUrl: 'button.html', styleUrls: ['fab.css'], - // TODO: change to MAT_BUTTON_INPUTS/MAT_BUTTON_HOST with spread after ViewEngine is deprecated - inputs: ['disabled', 'disableRipple', 'color', 'extended'], + inputs: [...MAT_BUTTON_INPUTS, 'extended'], host: { + ...MAT_BUTTON_HOST, '[class.mdc-fab--extended]': 'extended', '[class.mat-mdc-extended-fab]': 'extended', - '[attr.disabled]': 'disabled || null', - '[class._mat-animation-noopable]': '_animationMode === "NoopAnimations"', - // MDC automatically applies the primary theme color to the button, but we want to support - // an unthemed version. If color is undefined, apply a CSS class that makes it easy to - // select and style this "theme". - '[class.mat-unthemed]': '!color', - // Add a class that applies to all buttons. This makes it easier to target if somebody - // wants to target all Material buttons. - '[class.mat-mdc-button-base]': 'true', }, exportAs: 'matButton', encapsulation: ViewEncapsulation.None, @@ -153,26 +144,11 @@ export class MatMiniFabButton extends MatButtonBase { selector: `a[mat-fab]`, templateUrl: 'button.html', styleUrls: ['fab.css'], - // TODO: change to MAT_ANCHOR_INPUTS/MAT_ANCHOR_HOST with spread after ViewEngine is deprecated - inputs: ['disabled', 'disableRipple', 'color', 'tabIndex', 'extended'], + inputs: [...MAT_BUTTON_INPUTS, 'extended'], host: { + ...MAT_ANCHOR_HOST, '[class.mdc-fab--extended]': 'extended', '[class.mat-mdc-extended-fab]': 'extended', - '[attr.disabled]': 'disabled || null', - '[class._mat-animation-noopable]': '_animationMode === "NoopAnimations"', - - // Note that we ignore the user-specified tabindex when it's disabled for - // consistency with the `mat-button` applied on native buttons where even - // though they have an index, they're not tabbable. - '[attr.tabindex]': 'disabled ? -1 : (tabIndex || 0)', - '[attr.aria-disabled]': 'disabled.toString()', - // MDC automatically applies the primary theme color to the button, but we want to support - // an unthemed version. If color is undefined, apply a CSS class that makes it easy to - // select and style this "theme". - '[class.mat-unthemed]': '!color', - // Add a class that applies to all buttons. This makes it easier to target if somebody - // wants to target all Material buttons. - '[class.mat-mdc-button-base]': 'true', }, exportAs: 'matButton, matAnchor', encapsulation: ViewEncapsulation.None, diff --git a/src/material-experimental/mdc-dialog/module.ts b/src/material-experimental/mdc-dialog/module.ts index e4b26d298239..955ff295e8c4 100644 --- a/src/material-experimental/mdc-dialog/module.ts +++ b/src/material-experimental/mdc-dialog/module.ts @@ -37,6 +37,5 @@ import { MatDialogContent, ], providers: [MatDialog, MAT_DIALOG_SCROLL_STRATEGY_PROVIDER], - entryComponents: [MatDialogContainer], }) export class MatDialogModule {} diff --git a/src/material-experimental/mdc-snack-bar/module.ts b/src/material-experimental/mdc-snack-bar/module.ts index d48f54fd5293..8c655c815b3d 100644 --- a/src/material-experimental/mdc-snack-bar/module.ts +++ b/src/material-experimental/mdc-snack-bar/module.ts @@ -33,6 +33,5 @@ import {MatSnackBarAction, MatSnackBarActions, MatSnackBarLabel} from './snack-b MatSnackBarActions, MatSnackBarAction, ], - entryComponents: [MatSimpleSnackBar, MatSnackBarContainer], }) export class MatSnackBarModule {} diff --git a/src/material-experimental/mdc-tooltip/module.ts b/src/material-experimental/mdc-tooltip/module.ts index 641c3c1ecf03..33aca8410dd2 100644 --- a/src/material-experimental/mdc-tooltip/module.ts +++ b/src/material-experimental/mdc-tooltip/module.ts @@ -19,7 +19,6 @@ import {MatTooltip, TooltipComponent} from './tooltip'; imports: [A11yModule, CommonModule, OverlayModule, MatCommonModule], exports: [MatTooltip, TooltipComponent, MatCommonModule, CdkScrollableModule], declarations: [MatTooltip, TooltipComponent], - entryComponents: [TooltipComponent], providers: [MAT_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER], }) export class MatTooltipModule {} diff --git a/src/material-experimental/selection/row-selection.ts b/src/material-experimental/selection/row-selection.ts index 8996c3b0c4e1..66138133e10c 100644 --- a/src/material-experimental/selection/row-selection.ts +++ b/src/material-experimental/selection/row-selection.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {coerceNumberProperty} from '@angular/cdk/coercion'; import {CdkRowSelection} from '@angular/cdk-experimental/selection'; import {Input, Directive} from '@angular/core'; @@ -24,20 +23,9 @@ import {Input, Directive} from '@angular/core'; '[attr.aria-selected]': '_selection.isSelected(this.value, this.index)', }, providers: [{provide: CdkRowSelection, useExisting: MatRowSelection}], + inputs: ['index: matRowSelectionIndex'], }) -// tslint:disable-next-line: coercion-types export class MatRowSelection extends CdkRowSelection { /** The value that is associated with the row */ @Input('matRowSelectionValue') override value: T; - - /** The index of the value in the list. Required when used with `trackBy` */ - @Input('matRowSelectionIndex') - override get index(): number | undefined { - return this._index; - } - override set index(index: number | undefined) { - // TODO: when we remove support for ViewEngine, change this setter to an input - // alias in the decorator metadata. - this._index = coerceNumberProperty(index); - } } diff --git a/src/material-experimental/selection/selection-toggle.ts b/src/material-experimental/selection/selection-toggle.ts index f9e1706a8f13..671277726c72 100644 --- a/src/material-experimental/selection/selection-toggle.ts +++ b/src/material-experimental/selection/selection-toggle.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {coerceNumberProperty} from '@angular/cdk/coercion'; import {CdkSelectionToggle} from '@angular/cdk-experimental/selection'; import {Directive, Input} from '@angular/core'; @@ -23,21 +22,10 @@ import {Directive, Input} from '@angular/core'; @Directive({ selector: '[matSelectionToggle]', exportAs: 'matSelectionToggle', + inputs: ['index: matSelectionToggleIndex'], providers: [{provide: CdkSelectionToggle, useExisting: MatSelectionToggle}], }) -// tslint:disable-next-line: coercion-types export class MatSelectionToggle extends CdkSelectionToggle { /** The value that is associated with the toggle */ @Input('matSelectionToggleValue') override value: T; - - /** The index of the value in the list. Required when used with `trackBy` */ - @Input('matSelectionToggleIndex') - override get index(): number | undefined { - return this._index; - } - override set index(index: number | undefined) { - // TODO: when we remove support for ViewEngine, change this setter to an input - // alias in the decorator metadata. - this._index = coerceNumberProperty(index); - } } diff --git a/src/material/bottom-sheet/bottom-sheet-module.ts b/src/material/bottom-sheet/bottom-sheet-module.ts index e4826af000d9..38a5c3957291 100644 --- a/src/material/bottom-sheet/bottom-sheet-module.ts +++ b/src/material/bottom-sheet/bottom-sheet-module.ts @@ -16,6 +16,5 @@ import {MatBottomSheetContainer} from './bottom-sheet-container'; imports: [OverlayModule, MatCommonModule, PortalModule], exports: [MatBottomSheetContainer, MatCommonModule], declarations: [MatBottomSheetContainer], - entryComponents: [MatBottomSheetContainer], }) export class MatBottomSheetModule {} diff --git a/src/material/bottom-sheet/bottom-sheet.md b/src/material/bottom-sheet/bottom-sheet.md index 89b659320dd5..3dc4a822fdee 100644 --- a/src/material/bottom-sheet/bottom-sheet.md +++ b/src/material/bottom-sheet/bottom-sheet.md @@ -49,39 +49,6 @@ export class HobbitSheet { } ``` -### Configuring bottom sheet content via `entryComponents` -**You only need to specify `entryComponents` if your project uses ViewEngine. Projects -using Angular Ivy don't need `entryComponents`.** - -Similarly to `MatDialog`, `MatBottomSheet` instantiates components at run-time. In order for it to -work, the Angular compiler needs extra information to create the necessary `ComponentFactory` for -your bottom sheet content component. - -Any components that are included inside of a bottom sheet have to be added to the `entryComponents` -inside your `NgModule`. - - -```ts -@NgModule({ - imports: [ - // ... - MatBottomSheetModule - ], - - declarations: [ - AppComponent, - ExampleBottomSheetComponent - ], - - entryComponents: [ - ExampleBottomSheetComponent - ], - - bootstrap: [AppComponent] -}) -export class AppModule {} -``` - ### Specifying global configuration defaults Default bottom sheet options can be specified by providing an instance of `MatBottomSheetConfig` for `MAT_BOTTOM_SHEET_DEFAULT_OPTIONS` in your application's root module. diff --git a/src/material/datepicker/date-range-input-parts.ts b/src/material/datepicker/date-range-input-parts.ts index f161e45d33d1..19e5641ac8bd 100644 --- a/src/material/datepicker/date-range-input-parts.ts +++ b/src/material/datepicker/date-range-input-parts.ts @@ -208,10 +208,7 @@ const _MatDateRangeInputBase = mixinErrorState(MatDateRangeInputPartBase); outputs: ['dateChange', 'dateInput'], inputs: ['errorStateMatcher'], }) -export class MatStartDate - extends _MatDateRangeInputBase - implements CanUpdateErrorState, DoCheck, OnInit -{ +export class MatStartDate extends _MatDateRangeInputBase implements CanUpdateErrorState { /** Validator that checks that the start date isn't after the end date. */ private _startValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { const start = this._dateAdapter.getValidDateOrNull( @@ -233,9 +230,6 @@ export class MatStartDate @Optional() dateAdapter: DateAdapter, @Optional() @Inject(MAT_DATE_FORMATS) dateFormats: MatDateFormats, ) { - // TODO(crisbeto): this constructor shouldn't be necessary, but ViewEngine doesn't seem to - // handle DI correctly when it is inherited from `MatDateRangeInputPartBase`. We can drop this - // constructor once ViewEngine is removed. super( rangeInput, elementRef, @@ -248,26 +242,6 @@ export class MatStartDate ); } - override ngOnInit() { - // Normally this happens automatically, but it seems to break if not added explicitly when all - // of the criteria below are met: - // 1) The class extends a TS mixin. - // 2) The application is running in ViewEngine. - // 3) The application is being transpiled through tsickle. - // This can be removed once google3 is completely migrated to Ivy. - super.ngOnInit(); - } - - override ngDoCheck() { - // Normally this happens automatically, but it seems to break if not added explicitly when all - // of the criteria below are met: - // 1) The class extends a TS mixin. - // 2) The application is running in ViewEngine. - // 3) The application is being transpiled through tsickle. - // This can be removed once google3 is completely migrated to Ivy. - super.ngDoCheck(); - } - protected _validator = Validators.compose([...super._getValidators(), this._startValidator]); protected _getValueFromModel(modelValue: DateRange) { @@ -334,10 +308,7 @@ export class MatStartDate outputs: ['dateChange', 'dateInput'], inputs: ['errorStateMatcher'], }) -export class MatEndDate - extends _MatDateRangeInputBase - implements CanUpdateErrorState, DoCheck, OnInit -{ +export class MatEndDate extends _MatDateRangeInputBase implements CanUpdateErrorState { /** Validator that checks that the end date isn't before the start date. */ private _endValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { const end = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(control.value)); @@ -357,9 +328,6 @@ export class MatEndDate @Optional() dateAdapter: DateAdapter, @Optional() @Inject(MAT_DATE_FORMATS) dateFormats: MatDateFormats, ) { - // TODO(crisbeto): this constructor shouldn't be necessary, but ViewEngine doesn't seem to - // handle DI correctly when it is inherited from `MatDateRangeInputPartBase`. We can drop this - // constructor once ViewEngine is removed. super( rangeInput, elementRef, @@ -372,26 +340,6 @@ export class MatEndDate ); } - override ngOnInit() { - // Normally this happens automatically, but it seems to break if not added explicitly when all - // of the criteria below are met: - // 1) The class extends a TS mixin. - // 2) The application is running in ViewEngine. - // 3) The application is being transpiled through tsickle. - // This can be removed once google3 is completely migrated to Ivy. - super.ngOnInit(); - } - - override ngDoCheck() { - // Normally this happens automatically, but it seems to break if not added explicitly when all - // of the criteria below are met: - // 1) The class extends a TS mixin. - // 2) The application is running in ViewEngine. - // 3) The application is being transpiled through tsickle. - // This can be removed once google3 is completely migrated to Ivy. - super.ngDoCheck(); - } - protected _validator = Validators.compose([...super._getValidators(), this._endValidator]); protected _getValueFromModel(modelValue: DateRange) { diff --git a/src/material/datepicker/datepicker-module.ts b/src/material/datepicker/datepicker-module.ts index 001fbf0db1e1..4732992723eb 100644 --- a/src/material/datepicker/datepicker-module.ts +++ b/src/material/datepicker/datepicker-module.ts @@ -83,6 +83,5 @@ import {MatDatepickerActions, MatDatepickerApply, MatDatepickerCancel} from './d MatDatepickerApply, ], providers: [MatDatepickerIntl, MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER], - entryComponents: [MatDatepickerContent, MatCalendarHeader], }) export class MatDatepickerModule {} diff --git a/src/material/dialog/dialog-module.ts b/src/material/dialog/dialog-module.ts index 608a1912fb48..01442e29c80b 100644 --- a/src/material/dialog/dialog-module.ts +++ b/src/material/dialog/dialog-module.ts @@ -37,6 +37,5 @@ import { MatDialogContent, ], providers: [MatDialog, MAT_DIALOG_SCROLL_STRATEGY_PROVIDER], - entryComponents: [MatDialogContainer], }) export class MatDialogModule {} diff --git a/src/material/dialog/dialog.md b/src/material/dialog/dialog.md index ad29d63fc096..6aefedfce50b 100644 --- a/src/material/dialog/dialog.md +++ b/src/material/dialog/dialog.md @@ -39,38 +39,6 @@ export class YourDialog { } ``` -### Configuring dialog content via `entryComponents` -**You only need to specify `entryComponents` if your project uses ViewEngine. Projects -using Angular Ivy don't need `entryComponents`.** - -Because `MatDialog` instantiates components at run-time, the Angular compiler needs extra -information to create the necessary `ComponentFactory` for your dialog content component. - -For any component loaded into a dialog, you must include your component class in the list of -`entryComponents` in your NgModule definition so that the Angular compiler knows to create -the `ComponentFactory` for it. - -```ts -@NgModule({ - imports: [ - // ... - MatDialogModule - ], - - declarations: [ - AppComponent, - ExampleDialogComponent - ], - - entryComponents: [ - ExampleDialogComponent - ], - - bootstrap: [AppComponent] -}) -export class AppModule {} -``` - ### Specifying global configuration defaults Default dialog options can be specified by providing an instance of `MatDialogConfig` for MAT_DIALOG_DEFAULT_OPTIONS in your application's root module. @@ -194,5 +162,5 @@ sheet attempts to restore focus. You can add handling for this situation with the `afterClosed()` observable from `MatDialogRef`. diff --git a/src/material/snack-bar/snack-bar-module.ts b/src/material/snack-bar/snack-bar-module.ts index a24d6f505e83..881c55389909 100644 --- a/src/material/snack-bar/snack-bar-module.ts +++ b/src/material/snack-bar/snack-bar-module.ts @@ -19,6 +19,5 @@ import {MatSnackBarContainer} from './snack-bar-container'; imports: [OverlayModule, PortalModule, CommonModule, MatButtonModule, MatCommonModule], exports: [MatSnackBarContainer, MatCommonModule], declarations: [MatSnackBarContainer, SimpleSnackBar], - entryComponents: [MatSnackBarContainer, SimpleSnackBar], }) export class MatSnackBarModule {} diff --git a/src/material/tooltip/tooltip-module.ts b/src/material/tooltip/tooltip-module.ts index 080a69d27ab8..da0a2bb3cfa6 100644 --- a/src/material/tooltip/tooltip-module.ts +++ b/src/material/tooltip/tooltip-module.ts @@ -22,7 +22,6 @@ import { imports: [A11yModule, CommonModule, OverlayModule, MatCommonModule], exports: [MatTooltip, TooltipComponent, MatCommonModule, CdkScrollableModule], declarations: [MatTooltip, TooltipComponent], - entryComponents: [TooltipComponent], providers: [MAT_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER], }) export class MatTooltipModule {} diff --git a/src/material/tree/toggle.ts b/src/material/tree/toggle.ts index a61102eebf35..370abd8423d1 100644 --- a/src/material/tree/toggle.ts +++ b/src/material/tree/toggle.ts @@ -6,9 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {coerceBooleanProperty} from '@angular/cdk/coercion'; import {CdkTreeNodeToggle} from '@angular/cdk/tree'; -import {Directive, Input} from '@angular/core'; +import {Directive} from '@angular/core'; /** * Wrapper for the CdkTree's toggle with Material design styles. @@ -16,16 +15,6 @@ import {Directive, Input} from '@angular/core'; @Directive({ selector: '[matTreeNodeToggle]', providers: [{provide: CdkTreeNodeToggle, useExisting: MatTreeNodeToggle}], + inputs: ['recursive: matTreeNodeToggleRecursive'], }) -// tslint:disable-next-line: coercion-types -export class MatTreeNodeToggle extends CdkTreeNodeToggle { - @Input('matTreeNodeToggleRecursive') - override get recursive(): boolean { - return this._recursive; - } - override set recursive(value: boolean) { - // TODO: when we remove support for ViewEngine, change this setter to an input - // alias in the decorator metadata. - this._recursive = coerceBooleanProperty(value); - } -} +export class MatTreeNodeToggle extends CdkTreeNodeToggle {} diff --git a/src/universal-app/kitchen-sink-mdc/kitchen-sink-mdc.ts b/src/universal-app/kitchen-sink-mdc/kitchen-sink-mdc.ts index 9afeb3f473a3..0b4b248ec134 100644 --- a/src/universal-app/kitchen-sink-mdc/kitchen-sink-mdc.ts +++ b/src/universal-app/kitchen-sink-mdc/kitchen-sink-mdc.ts @@ -62,7 +62,6 @@ export class KitchenSinkMdc { ], declarations: [KitchenSinkMdc, TestEntryComponent], exports: [KitchenSinkMdc, TestEntryComponent], - entryComponents: [TestEntryComponent], providers: [ { // If an error is thrown asynchronously during server-side rendering it'll get logged to stderr, diff --git a/src/universal-app/kitchen-sink/kitchen-sink.ts b/src/universal-app/kitchen-sink/kitchen-sink.ts index f3dbc2239e92..72b7f3cb65a9 100644 --- a/src/universal-app/kitchen-sink/kitchen-sink.ts +++ b/src/universal-app/kitchen-sink/kitchen-sink.ts @@ -145,7 +145,6 @@ export class KitchenSink { ], declarations: [KitchenSink, TestEntryComponent], exports: [KitchenSink, TestEntryComponent], - entryComponents: [TestEntryComponent], providers: [ { // If an error is thrown asynchronously during server-side rendering it'll get logged to stderr, diff --git a/tools/public_api_guard/material/datepicker.md b/tools/public_api_guard/material/datepicker.md index 510ea0ec7719..bc0afe8e697e 100644 --- a/tools/public_api_guard/material/datepicker.md +++ b/tools/public_api_guard/material/datepicker.md @@ -741,17 +741,13 @@ export abstract class MatDateSelectionModel extends _MatDateRangeInputBase implements CanUpdateErrorState, DoCheck, OnInit { +export class MatEndDate extends _MatDateRangeInputBase implements CanUpdateErrorState { constructor(rangeInput: MatDateRangeInputParent, elementRef: ElementRef, defaultErrorStateMatcher: ErrorStateMatcher, injector: Injector, parentForm: NgForm, parentFormGroup: FormGroupDirective, dateAdapter: DateAdapter, dateFormats: MatDateFormats); // (undocumented) protected _assignValueToModel(value: D | null): void; // (undocumented) protected _getValueFromModel(modelValue: DateRange): D | null; // (undocumented) - ngDoCheck(): void; - // (undocumented) - ngOnInit(): void; - // (undocumented) _onKeydown(event: KeyboardEvent): void; // (undocumented) protected _shouldHandleChangeEvent(change: DateSelectionModelChange>): boolean; @@ -885,7 +881,7 @@ export class MatSingleDateSelectionModel extends MatDateSelectionModel extends _MatDateRangeInputBase implements CanUpdateErrorState, DoCheck, OnInit { +export class MatStartDate extends _MatDateRangeInputBase implements CanUpdateErrorState { constructor(rangeInput: MatDateRangeInputParent, elementRef: ElementRef, defaultErrorStateMatcher: ErrorStateMatcher, injector: Injector, parentForm: NgForm, parentFormGroup: FormGroupDirective, dateAdapter: DateAdapter, dateFormats: MatDateFormats); // (undocumented) protected _assignValueToModel(value: D | null): void; @@ -895,10 +891,6 @@ export class MatStartDate extends _MatDateRangeInputBase implements CanUpd // (undocumented) protected _getValueFromModel(modelValue: DateRange): D | null; // (undocumented) - ngDoCheck(): void; - // (undocumented) - ngOnInit(): void; - // (undocumented) protected _shouldHandleChangeEvent(change: DateSelectionModelChange>): boolean; // (undocumented) protected _validator: ValidatorFn | null; diff --git a/tools/public_api_guard/material/tree.md b/tools/public_api_guard/material/tree.md index 10b51b166c38..b8f9ddc2a93f 100644 --- a/tools/public_api_guard/material/tree.md +++ b/tools/public_api_guard/material/tree.md @@ -170,9 +170,6 @@ export class MatTreeNodePadding extends CdkTreeNodePadding { // @public export class MatTreeNodeToggle extends CdkTreeNodeToggle { - // (undocumented) - get recursive(): boolean; - set recursive(value: boolean); // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration, "[matTreeNodeToggle]", never, { "recursive": "matTreeNodeToggleRecursive"; }, {}, never>; // (undocumented) From 4350552f5c145182c8fc17586d8a826c40481b59 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 15 Nov 2021 20:27:31 +0100 Subject: [PATCH 004/561] perf(multiple): don't emit fallbacks for CSS variables in experimental components (#23912) Reworks the theming for the MDC-based components so that it doesn't emit fallback values for CSS variables. This trims ~27kb from the dev app theme. Also wraps the structural styles of the MDC-based radio button and slide toggle who have been converted to the new MDC theming API. --- .../mdc-helpers/_mdc-helpers.scss | 17 ++++++++++++-- .../mdc-radio/radio.scss | 22 +++++++++++++------ .../mdc-slide-toggle/slide-toggle.scss | 10 ++++++--- .../mdc-tabs/_tabs-common.scss | 6 +++-- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/material-experimental/mdc-helpers/_mdc-helpers.scss b/src/material-experimental/mdc-helpers/_mdc-helpers.scss index 828f5027ff26..58abe05ac36c 100644 --- a/src/material-experimental/mdc-helpers/_mdc-helpers.scss +++ b/src/material-experimental/mdc-helpers/_mdc-helpers.scss @@ -5,6 +5,7 @@ @use '@material/feature-targeting' as mdc-feature-targeting; @use '@material/typography' as mdc-typography; @use '@material/theme/theme-color' as mdc-theme-color; +@use '@material/theme/css' as mdc-theme-css; @use 'sass:map'; @use '../../material/core/theming/theming'; @use '../../material/core/typography/typography'; @@ -169,7 +170,9 @@ $mat-typography-mdc-level-mappings: ( ); // Apply given rules. - @content; + @include disable-fallback-declarations { + @content; + } // Reset the original values. mdc-theme-color.$primary: $orig-primary; @@ -196,8 +199,18 @@ $mat-typography-mdc-level-mappings: ( } // Apply given rules. - @content; + @include disable-fallback-declarations { + @content; + } // Reset the original values. mdc-typography.$styles: $orig-mdc-typography-styles; } + +// Disables MDC's CSS custom property fallbacks for the specified mixin content. +@mixin disable-fallback-declarations { + $previous-value: mdc-theme-css.$enable-fallback-declarations; + mdc-theme-css.$enable-fallback-declarations: false; + @content; + mdc-theme-css.$enable-fallback-declarations: $previous-value; +} diff --git a/src/material-experimental/mdc-radio/radio.scss b/src/material-experimental/mdc-radio/radio.scss index 3be586f6050e..fff2351c07cd 100644 --- a/src/material-experimental/mdc-radio/radio.scss +++ b/src/material-experimental/mdc-radio/radio.scss @@ -6,18 +6,24 @@ @use '../../cdk/a11y'; @use '../../material/core/style/layout-common'; -@include mdc-radio.without-ripple($query: mdc-helpers.$mat-base-styles-without-animation-query); -@include mdc-form-field.core-styles($query: mdc-helpers.$mat-base-styles-query); +@include mdc-helpers.disable-fallback-declarations { + @include mdc-radio.without-ripple($query: mdc-helpers.$mat-base-styles-without-animation-query); + @include mdc-form-field.core-styles($query: mdc-helpers.$mat-base-styles-query); +} .mat-mdc-radio-button { &:not(._mat-animation-noopable) { - @include mdc-radio.without-ripple($query: animation); + @include mdc-helpers.disable-fallback-declarations { + @include mdc-radio.without-ripple($query: animation); + } } .mdc-radio { // MDC theme styles also include structural styles so we have to include the theme at least // once here. The values will be overwritten by our own theme file afterwards. - @include mdc-radio-theme.theme-styles(mdc-radio-theme.$light-theme); + @include mdc-helpers.disable-fallback-declarations { + @include mdc-radio-theme.theme-styles(mdc-radio-theme.$light-theme); + } } // This is necessary because we do not depend on MDC's ripple, but have our own that should be @@ -35,9 +41,11 @@ // Element used to provide a larger tap target for users on touch devices. .mat-mdc-radio-touch-target { - @include mdc-touch-target.touch-target( - $set-width: true, - $query: mdc-helpers.$mat-base-styles-query); + @include mdc-helpers.disable-fallback-declarations { + @include mdc-touch-target.touch-target( + $set-width: true, + $query: mdc-helpers.$mat-base-styles-query); + } [dir='rtl'] & { left: 0; diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.scss b/src/material-experimental/mdc-slide-toggle/slide-toggle.scss index b2340c42604b..5b815d842fb8 100644 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle.scss +++ b/src/material-experimental/mdc-slide-toggle/slide-toggle.scss @@ -8,8 +8,10 @@ @use '../../material/core/style/layout-common'; @use '../../cdk/a11y'; -@include mdc-form-field.core-styles($query: mdc-helpers.$mat-base-styles-query); -@include mdc-switch.static-styles-without-ripple; +@include mdc-helpers.disable-fallback-declarations { + @include mdc-form-field.core-styles($query: mdc-helpers.$mat-base-styles-query); + @include mdc-switch.static-styles-without-ripple; +} .mat-mdc-slide-toggle { display: inline-block; @@ -20,7 +22,9 @@ .mdc-switch { // MDC theme styles also include structural styles so we have to include the theme at least // once here. The values will be overwritten by our own theme file afterwards. - @include mdc-switch-theme.theme-styles(mdc-switch-theme.$light-theme); + @include mdc-helpers.disable-fallback-declarations { + @include mdc-switch-theme.theme-styles(mdc-switch-theme.$light-theme); + } } // The ripple needs extra specificity so the base ripple styling doesn't override its `position`. diff --git a/src/material-experimental/mdc-tabs/_tabs-common.scss b/src/material-experimental/mdc-tabs/_tabs-common.scss index 432aa2964b69..95412c1c69be 100644 --- a/src/material-experimental/mdc-tabs/_tabs-common.scss +++ b/src/material-experimental/mdc-tabs/_tabs-common.scss @@ -10,8 +10,10 @@ $mat-tab-animation-duration: 500ms !default; // Combines the various structural styles we need for the tab group and tab nav bar. @mixin structural-styles { - @include mdc-tab.without-ripple($query: mdc-helpers.$mat-base-styles-query); - @include mdc-tab-indicator.core-styles($query: mdc-helpers.$mat-base-styles-query); + @include mdc-helpers.disable-fallback-declarations { + @include mdc-tab.without-ripple($query: mdc-helpers.$mat-base-styles-query); + @include mdc-tab-indicator.core-styles($query: mdc-helpers.$mat-base-styles-query); + } .mat-mdc-tab-ripple { position: absolute; From 48df9d6788c2bc4e7622755284b4901447a568bb Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 6 Oct 2021 11:17:29 +0200 Subject: [PATCH 005/561] fix(cdk/testing): fake events not propagating through shadow DOM Adds the `composed` flag to the fake event objects so that they can propagate through a `mode: 'open'` shadow root. --- .../testbed/fake-events/event-objects.spec.ts | 72 +++++++++++++++++++ .../testbed/fake-events/event-objects.ts | 7 +- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/cdk/testing/testbed/fake-events/event-objects.spec.ts b/src/cdk/testing/testbed/fake-events/event-objects.spec.ts index 643341c823b8..0fe80cca0f8e 100644 --- a/src/cdk/testing/testbed/fake-events/event-objects.spec.ts +++ b/src/cdk/testing/testbed/fake-events/event-objects.spec.ts @@ -1,3 +1,9 @@ +import { + dispatchMouseEvent, + dispatchKeyboardEvent, + dispatchPointerEvent, + dispatchFakeEvent, +} from './dispatch-events'; import {createKeyboardEvent, createMouseEvent} from './event-objects'; describe('event objects', () => { @@ -40,4 +46,70 @@ describe('event objects', () => { expect(preventDefaultSpy).toHaveBeenCalledTimes(2); }); }); + + describe('shadow DOM', () => { + it('should allow dispatched mouse events to propagate through the shadow root', () => { + if (!testElement.attachShadow) { + return; + } + + const spy = jasmine.createSpy('listener'); + const shadowRoot = testElement.attachShadow({mode: 'open'}); + const child = document.createElement('div'); + shadowRoot.appendChild(child); + + testElement.addEventListener('mousedown', spy); + dispatchMouseEvent(child, 'mousedown'); + + expect(spy).toHaveBeenCalled(); + }); + + it('should allow dispatched keyboard events to propagate through the shadow root', () => { + if (!testElement.attachShadow) { + return; + } + + const spy = jasmine.createSpy('listener'); + const shadowRoot = testElement.attachShadow({mode: 'open'}); + const child = document.createElement('div'); + shadowRoot.appendChild(child); + + testElement.addEventListener('keydown', spy); + dispatchKeyboardEvent(child, 'keydown'); + + expect(spy).toHaveBeenCalled(); + }); + + it('should allow dispatched pointer events to propagate through the shadow root', () => { + if (!testElement.attachShadow) { + return; + } + + const spy = jasmine.createSpy('listener'); + const shadowRoot = testElement.attachShadow({mode: 'open'}); + const child = document.createElement('div'); + shadowRoot.appendChild(child); + + testElement.addEventListener('pointerdown', spy); + dispatchPointerEvent(child, 'pointerdown'); + + expect(spy).toHaveBeenCalled(); + }); + + it('should allow dispatched fake events to propagate through the shadow root', () => { + if (!testElement.attachShadow) { + return; + } + + const spy = jasmine.createSpy('listener'); + const shadowRoot = testElement.attachShadow({mode: 'open'}); + const child = document.createElement('div'); + shadowRoot.appendChild(child); + + testElement.addEventListener('fake', spy); + dispatchFakeEvent(child, 'fake'); + + expect(spy).toHaveBeenCalled(); + }); + }); }); diff --git a/src/cdk/testing/testbed/fake-events/event-objects.ts b/src/cdk/testing/testbed/fake-events/event-objects.ts index 547252ba7dd9..d68892693ce5 100644 --- a/src/cdk/testing/testbed/fake-events/event-objects.ts +++ b/src/cdk/testing/testbed/fake-events/event-objects.ts @@ -32,6 +32,7 @@ export function createMouseEvent( const event = new MouseEvent(type, { bubbles: true, cancelable: true, + composed: true, // Required for shadow DOM events. view: window, detail: 0, relatedTarget: null, @@ -74,6 +75,7 @@ export function createPointerEvent( return new PointerEvent(type, { bubbles: true, cancelable: true, + composed: true, // Required for shadow DOM events. view: window, clientX, clientY, @@ -116,6 +118,7 @@ export function createKeyboardEvent( return new KeyboardEvent(type, { bubbles: true, cancelable: true, + composed: true, // Required for shadow DOM events. view: window, keyCode: keyCode, key: key, @@ -130,8 +133,8 @@ export function createKeyboardEvent( * Creates a fake event object with any desired event type. * @docs-private */ -export function createFakeEvent(type: string, bubbles = false, cancelable = true) { - return new Event(type, {bubbles, cancelable}); +export function createFakeEvent(type: string, bubbles = false, cancelable = true, composed = true) { + return new Event(type, {bubbles, cancelable, composed}); } /** From b9cda574c9f9b127b7b018212556678e4fd573c2 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 6 Oct 2021 11:31:03 +0200 Subject: [PATCH 006/561] fix(cdk/drag-drop): handle not working when it has a child inside shadow DOM Fixes that the children of the drag handle inside a shadow root weren't being detected. The problem was that we were using `_getEventTarget` to resolve the actual event target and using `contains` to verify that it's inside the handle. Since `contains` doesn't descend into shadow root, the call failed. These changes remove the `_getEventTarget` call since we can use the event `target` directly. Fixes #23680. --- src/cdk/drag-drop/directives/drag.spec.ts | 43 +++++++++++++++++++++++ src/cdk/drag-drop/drag-ref.ts | 3 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/cdk/drag-drop/directives/drag.spec.ts b/src/cdk/drag-drop/directives/drag.spec.ts index 651ac3d0ee42..4e5b687d9a75 100644 --- a/src/cdk/drag-drop/directives/drag.spec.ts +++ b/src/cdk/drag-drop/directives/drag.spec.ts @@ -1581,6 +1581,26 @@ describe('CdkDrag', () => { flush(); }).toThrowError(/^cdkDragHandle must be attached to an element node/); })); + + it('should be able to drag an element using a handle with a shadow DOM child', fakeAsync(() => { + if (!_supportsShadowDom()) { + return; + } + + const fixture = createComponent( + StandaloneDraggableWithShadowInsideHandle, + undefined, + undefined, + [ShadowWrapper], + ); + fixture.detectChanges(); + const dragElement = fixture.componentInstance.dragElement.nativeElement; + const handleChild = fixture.componentInstance.handleChild.nativeElement; + + expect(dragElement.style.transform).toBeFalsy(); + dragElementViaMouse(fixture, handleChild, 50, 100); + expect(dragElement.style.transform).toBe('translate3d(50px, 100px, 0px)'); + })); }); describe('in a drop container', () => { @@ -6461,6 +6481,29 @@ class StandaloneDraggableWithIndirectHandle { @ViewChild('handleElement') handleElement: ElementRef; } +@Component({ + selector: 'shadow-wrapper', + template: '', + encapsulation: ViewEncapsulation.ShadowDom, +}) +class ShadowWrapper {} + +@Component({ + template: ` +
+
+ +
+
+
+
+ `, +}) +class StandaloneDraggableWithShadowInsideHandle { + @ViewChild('dragElement') dragElement: ElementRef; + @ViewChild('handleChild') handleChild: ElementRef; +} + @Component({ encapsulation: ViewEncapsulation.None, styles: [ diff --git a/src/cdk/drag-drop/drag-ref.ts b/src/cdk/drag-drop/drag-ref.ts index c1dedf537add..ac23a0ad4838 100644 --- a/src/cdk/drag-drop/drag-ref.ts +++ b/src/cdk/drag-drop/drag-ref.ts @@ -629,8 +629,7 @@ export class DragRef { // Delegate the event based on whether it started from a handle or the element itself. if (this._handles.length) { const targetHandle = this._handles.find(handle => { - const target = _getEventTarget(event); - return !!target && (target === handle || handle.contains(target as HTMLElement)); + return event.target && (event.target === handle || handle.contains(event.target as Node)); }); if (targetHandle && !this._disabledHandles.has(targetHandle) && !this.disabled) { From 8cdd3d7c763113ed03cf6b6889fb41f40f57dda9 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 15 Nov 2021 20:28:39 +0100 Subject: [PATCH 007/561] fix(material/tabs): pagination sometimes incorrectly shown after zoom (#23759) Currently if one of the end tabs is selected and the user zooms in, we may end up showing the pagination unnecessarily. The issue comes from the fact that there's a transition on the ink bar which can cause the parent overflow while it is being measured. These changes resolve the issue by measuring a different element. Fixes #23724. --- src/material-experimental/mdc-tabs/tab-header.html | 2 +- src/material-experimental/mdc-tabs/tab-header.ts | 1 + .../mdc-tabs/tab-nav-bar/tab-nav-bar.html | 2 +- .../mdc-tabs/tab-nav-bar/tab-nav-bar.ts | 1 + src/material/tabs/paginated-tab-header.ts | 7 ++++--- src/material/tabs/tab-header.html | 2 +- src/material/tabs/tab-header.ts | 1 + src/material/tabs/tab-nav-bar/tab-nav-bar.html | 2 +- src/material/tabs/tab-nav-bar/tab-nav-bar.ts | 1 + tools/public_api_guard/material/tabs.md | 4 ++++ 10 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/material-experimental/mdc-tabs/tab-header.html b/src/material-experimental/mdc-tabs/tab-header.html index bb0d58bf46f0..90931f7cd96d 100644 --- a/src/material-experimental/mdc-tabs/tab-header.html +++ b/src/material-experimental/mdc-tabs/tab-header.html @@ -21,7 +21,7 @@ class="mat-mdc-tab-list" role="tablist" (cdkObserveContent)="_onContentChanges()"> -
+
diff --git a/src/material-experimental/mdc-tabs/tab-header.ts b/src/material-experimental/mdc-tabs/tab-header.ts index dd3721875d28..9d080ac7a599 100644 --- a/src/material-experimental/mdc-tabs/tab-header.ts +++ b/src/material-experimental/mdc-tabs/tab-header.ts @@ -54,6 +54,7 @@ export class MatTabHeader extends _MatTabHeaderBase implements AfterContentInit @ContentChildren(MatTabLabelWrapper, {descendants: false}) _items: QueryList; @ViewChild('tabListContainer', {static: true}) _tabListContainer: ElementRef; @ViewChild('tabList', {static: true}) _tabList: ElementRef; + @ViewChild('tabListInner', {static: true}) _tabListInner: ElementRef; @ViewChild('nextPaginator') _nextPaginator: ElementRef; @ViewChild('previousPaginator') _previousPaginator: ElementRef; _inkBar: MatInkBar; diff --git a/src/material-experimental/mdc-tabs/tab-nav-bar/tab-nav-bar.html b/src/material-experimental/mdc-tabs/tab-nav-bar/tab-nav-bar.html index 84e155b3bde9..10e25a46578c 100644 --- a/src/material-experimental/mdc-tabs/tab-nav-bar/tab-nav-bar.html +++ b/src/material-experimental/mdc-tabs/tab-nav-bar/tab-nav-bar.html @@ -12,7 +12,7 @@