Skip to content

Commit dfdf5cf

Browse files
authored
feat(datepicker): add viewChanged output to calendar (#13759) (#20468)
- replace deprecated async with waitForAsync
1 parent a0b44d4 commit dfdf5cf

File tree

5 files changed

+68
-3
lines changed

5 files changed

+68
-3
lines changed

src/material/datepicker/calendar-header.spec.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import {Directionality} from '@angular/cdk/bidi';
22
import {Component} from '@angular/core';
3-
import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing';
3+
import {
4+
ComponentFixture,
5+
TestBed,
6+
waitForAsync
7+
} from '@angular/core/testing';
48
import {MatNativeDateModule, DateAdapter} from '@angular/material/core';
59
import {DEC, FEB, JAN} from '@angular/material/testing';
610
import {By} from '@angular/platform-browser';
@@ -72,6 +76,44 @@ describe('MatCalendarHeader', () => {
7276
expect(calendarInstance.currentView).toBe('month');
7377
});
7478

79+
it('should emit viewChanged when view changed from \'month\' to \'multi-year\'',
80+
() => {
81+
expect(calendarInstance.currentView).toBe('month');
82+
spyOn(calendarInstance.viewChanged, 'emit');
83+
84+
periodButton.click();
85+
fixture.detectChanges();
86+
87+
expect(calendarInstance.viewChanged.emit).toHaveBeenCalledWith('multi-year');
88+
});
89+
90+
it('should emit viewChanged when view changed from \'multi-year\' to \'month\'',
91+
() => {
92+
periodButton.click();
93+
fixture.detectChanges();
94+
expect(calendarInstance.currentView).toBe('multi-year');
95+
spyOn(calendarInstance.viewChanged, 'emit');
96+
97+
periodButton.click();
98+
fixture.detectChanges();
99+
100+
expect(calendarInstance.viewChanged.emit).toHaveBeenCalledWith('month');
101+
});
102+
103+
it('should emit viewChanged when view changed from \'multi-year\' to \'year\'',
104+
() => {
105+
periodButton.click();
106+
fixture.detectChanges();
107+
expect(calendarInstance.currentView).toBe('multi-year');
108+
spyOn(calendarInstance.viewChanged, 'emit');
109+
110+
(calendarElement.querySelector('.mat-calendar-body-active') as HTMLElement).click();
111+
fixture.detectChanges();
112+
113+
expect(calendarInstance.viewChanged.emit).toHaveBeenCalledWith('year');
114+
});
115+
116+
75117
it('should go to next and previous month', () => {
76118
expect(calendarInstance.activeDate).toEqual(new Date(2017, JAN, 31));
77119

src/material/datepicker/calendar.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,12 @@ export class MatCalendar<D> implements AfterContentInit, AfterViewChecked, OnDes
268268
*/
269269
@Output() readonly monthSelected: EventEmitter<D> = new EventEmitter<D>();
270270

271+
/**
272+
* Emits when the current view changes.
273+
*/
274+
@Output() readonly viewChanged: EventEmitter<MatCalendarView> =
275+
new EventEmitter<MatCalendarView>(true);
276+
271277
/** Emits when any date is selected. */
272278
@Output() readonly _userSelection: EventEmitter<MatCalendarUserEvent<D | null>> =
273279
new EventEmitter<MatCalendarUserEvent<D | null>>();
@@ -296,9 +302,13 @@ export class MatCalendar<D> implements AfterContentInit, AfterViewChecked, OnDes
296302
/** Whether the calendar is in month view. */
297303
get currentView(): MatCalendarView { return this._currentView; }
298304
set currentView(value: MatCalendarView) {
305+
const viewChangedResult = this._currentView !== value ? value : null;
299306
this._currentView = value;
300307
this._moveFocusOnNextTick = true;
301308
this._changeDetectorRef.markForCheck();
309+
if (viewChangedResult) {
310+
this.viewChanged.emit(viewChangedResult);
311+
}
302312
}
303313
private _currentView: MatCalendarView;
304314

src/material/datepicker/datepicker-base.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ import {
5050
import {MatDialog, MatDialogRef} from '@angular/material/dialog';
5151
import {merge, Subject, Observable, Subscription} from 'rxjs';
5252
import {filter, take} from 'rxjs/operators';
53-
import {MatCalendar} from './calendar';
53+
import {MatCalendar, MatCalendarView} from './calendar';
5454
import {matDatepickerAnimations} from './datepicker-animations';
5555
import {createMissingDateImplError} from './datepicker-errors';
5656
import {MatCalendarUserEvent, MatCalendarCellClassFunction} from './calendar-body';
@@ -301,6 +301,12 @@ export abstract class MatDatepickerBase<C extends MatDatepickerControl<D>, S,
301301
*/
302302
@Output() readonly monthSelected: EventEmitter<D> = new EventEmitter<D>();
303303

304+
/**
305+
* Emits when the current view changes.
306+
*/
307+
@Output() readonly viewChanged: EventEmitter<MatCalendarView> =
308+
new EventEmitter<MatCalendarView>(true);
309+
304310
/** Classes to be passed to the date picker panel. Supports the same syntax as `ngClass`. */
305311
@Input() panelClass: string | string[];
306312

@@ -413,6 +419,11 @@ export abstract class MatDatepickerBase<C extends MatDatepickerControl<D>, S,
413419
this.monthSelected.emit(normalizedMonth);
414420
}
415421

422+
/** Emits changed view */
423+
_viewChanged(view: MatCalendarView): void {
424+
this.viewChanged.emit(view);
425+
}
426+
416427
/**
417428
* Register an input with this datepicker.
418429
* @param input The datepicker input to register with this datepicker.

src/material/datepicker/datepicker-content.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414
[@fadeInCalendar]="'enter'"
1515
(yearSelected)="datepicker._selectYear($event)"
1616
(monthSelected)="datepicker._selectMonth($event)"
17+
(viewChanged)="datepicker._viewChanged($event)"
1718
(_userSelection)="_handleUserSelection($event)">
1819
</mat-calendar>

tools/public_api_guard/material/datepicker.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export declare class MatCalendar<D> implements AfterContentInit, AfterViewChecke
7777
set startAt(value: D | null);
7878
startView: MatCalendarView;
7979
stateChanges: Subject<void>;
80+
readonly viewChanged: EventEmitter<MatCalendarView>;
8081
readonly yearSelected: EventEmitter<D>;
8182
yearView: MatYearView<D>;
8283
constructor(_intl: MatDatepickerIntl, _dateAdapter: DateAdapter<D>, _dateFormats: MatDateFormats, _changeDetectorRef: ChangeDetectorRef);
@@ -90,7 +91,7 @@ export declare class MatCalendar<D> implements AfterContentInit, AfterViewChecke
9091
ngOnChanges(changes: SimpleChanges): void;
9192
ngOnDestroy(): void;
9293
updateTodaysDate(): void;
93-
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatCalendar<any>, "mat-calendar", ["matCalendar"], { "headerComponent": "headerComponent"; "startAt": "startAt"; "startView": "startView"; "selected": "selected"; "minDate": "minDate"; "maxDate": "maxDate"; "dateFilter": "dateFilter"; "dateClass": "dateClass"; "comparisonStart": "comparisonStart"; "comparisonEnd": "comparisonEnd"; }, { "selectedChange": "selectedChange"; "yearSelected": "yearSelected"; "monthSelected": "monthSelected"; "_userSelection": "_userSelection"; }, never, never>;
94+
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatCalendar<any>, "mat-calendar", ["matCalendar"], { "headerComponent": "headerComponent"; "startAt": "startAt"; "startView": "startView"; "selected": "selected"; "minDate": "minDate"; "maxDate": "maxDate"; "dateFilter": "dateFilter"; "dateClass": "dateClass"; "comparisonStart": "comparisonStart"; "comparisonEnd": "comparisonEnd"; }, { "selectedChange": "selectedChange"; "yearSelected": "yearSelected"; "monthSelected": "monthSelected"; "viewChanged": "viewChanged"; "_userSelection": "_userSelection"; }, never, never>;
9495
static ɵfac: i0.ɵɵFactoryDef<MatCalendar<any>, [null, { optional: true; }, { optional: true; }, null]>;
9596
}
9697

0 commit comments

Comments
 (0)