Skip to content

Commit 85d024c

Browse files
authored
Merge pull request #7761 from IgniteUI/add-calendar-events-10x
Add calendar events when presented month/year in view is changed
2 parents 686ac35 + f78864d commit 85d024c

16 files changed

+210
-226
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ All notable changes for each version of this project will be documented in this
55
## 10.1.0
66

77
### New Features
8+
- `IgxCalendar` and `IgxMonthPicker`
9+
- `onViewDateChanged` emitted after the month/year presented in the view is changed after user interaction.
10+
- `onActiveViewChanged` event emitted after the active view (DEFAULT, YEAR, DECADE) is changed after user interaction.
11+
- `viewDate` day value is always 1.
12+
- `activeView` setter is now available as an input property.
13+
- `IgxGridState` directive
14+
- Added support for expansion states, column selection and row pinning.
15+
- Added support for `IgxTreeGrid` and `IgxHierarchicalGrid` (including child grids)
816
- `IgxColumn`
917
- Added `byHeader` parameter to the `autosize` method which specifies if the autosizing should be based only on the header content width.
1018
- `IgxToast`

projects/igniteui-angular/src/lib/calendar/README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ The calendar header will not be rendered when the selection is either `multi` or
133133

134134
- `viewDate: Date`
135135

136-
Controls the year/month that will be presented in the default view when the calendar renders. By default it is the current year/month.
136+
Controls the year/month that will be presented in the default view when the calendar renders. By default it is the first day of the current year/month.
137137

138138
- `value: Date | Date[]`
139139

@@ -173,7 +173,17 @@ Controls the visibility of the dates that do not belong to the current month.
173173
- `onSelection(): Date | Date[]`
174174

175175
Event fired when a value is selected through UI interaction.
176-
Returns the selected value (depending on the type of selection).
176+
Emits the selected value (depending on the type of selection).
177+
178+
- `onViewDateChanged(): IViewDateChangeEventArgs`
179+
180+
Event fired after the the month/year presented in the view is changed.
181+
Emits an object containing the previous and current value of the `viewDate` property.
182+
183+
- `onActiveViewChanged(): CalendarView`
184+
185+
Event fired after the active view is changed.
186+
Emits an CalendarView enum, indicating the `activeView` property value.
177187

178188

179189
### Methods

projects/igniteui-angular/src/lib/calendar/calendar-base.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ControlValueAccessor } from '@angular/forms';
44
import { DateRangeDescriptor } from '../core/dates';
55
import { Subject } from 'rxjs';
66
import { isDate } from '../core/utils';
7+
import { CalendarView } from './month-picker-base';
78

89
/**
910
* Sets the selction type - single, multi or range.
@@ -20,6 +21,11 @@ export enum ScrollMonth {
2021
NONE = 'none'
2122
}
2223

24+
export interface IViewDateChangeEventArgs {
25+
previousValue: Date;
26+
currentValue: Date;
27+
}
28+
2329
/** @hidden @internal */
2430
@Directive({
2531
selector: '[igxCalendarBase]',
@@ -168,7 +174,8 @@ export class IgxCalendarBaseDirective implements ControlValueAccessor {
168174
* Sets the date that will be presented in the default view when the component renders.
169175
*/
170176
public set viewDate(value: Date) {
171-
this._viewDate = this.getDateOnly(value);
177+
const date = this.getDateOnly(value).setDate(1);
178+
this._viewDate = new Date(date);
172179
}
173180

174181
/**
@@ -240,6 +247,34 @@ export class IgxCalendarBaseDirective implements ControlValueAccessor {
240247
@Output()
241248
public onSelection = new EventEmitter<Date | Date[]>();
242249

250+
/**
251+
* Emits an event when the month in view is changed.
252+
* ```html
253+
* <igx-calendar (onViewDateChanged)="viewDateChanged($event)"></igx-calendar>
254+
* ```
255+
* ```typescript
256+
* public viewDateChanged(event: IViewDateChangeEventArgs) {
257+
* let newDate = event.newViewDate;
258+
* }
259+
* ```
260+
*/
261+
@Output()
262+
public onViewDateChanged = new EventEmitter<IViewDateChangeEventArgs>();
263+
264+
/**
265+
* Emits an event when the active view is changed.
266+
* ```html
267+
* <igx-calendar (onActiveViewChanged)="activeViewChanged($event)"></igx-calendar>
268+
* ```
269+
* ```typescript
270+
* public activeViewChanged(event: CalendarView) {
271+
* let activeView = event;
272+
* }
273+
* ```
274+
*/
275+
@Output()
276+
public onActiveViewChanged = new EventEmitter<CalendarView>();
277+
243278
/**
244279
* @hidden
245280
*/

projects/igniteui-angular/src/lib/calendar/calendar.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ <h2 class="igx-calendar__header-date">
2222
</h2>
2323
</div>
2424

25-
<div *ngIf="isDefaultView" class="igx-calendar__body" [@animateView]="activeView" (swiperight)="previousMonth()"
25+
<div *ngIf="isDefaultView" class="igx-calendar__body" [@animateView]="activeView" (@animateView.done)="viewRendered($event)" (swiperight)="previousMonth()"
2626
(swipeleft)="nextMonth()">
2727
<div class="igx-calendar-picker">
2828
<div tabindex="0" class="igx-calendar-picker__prev" #prevMonthBtn
@@ -63,15 +63,15 @@ <h2 class="igx-calendar__header-date">
6363
</div>
6464
</div>
6565

66-
<igx-months-view *ngIf="isYearView" [@animateView]="activeView" #months
66+
<igx-months-view *ngIf="isYearView" [@animateView]="activeView" #months (@animateView.done)="viewRendered($event)"
6767
[date]="viewDate"
6868
[locale]="locale"
6969
[formatView]="formatViews.month"
7070
[monthFormat]="formatOptions.month"
7171
(onSelection)="changeMonth($event)">
7272
</igx-months-view>
7373

74-
<igx-years-view *ngIf="isDecadeView" [@animateView]="activeView" #decade
74+
<igx-years-view *ngIf="isDecadeView" [@animateView]="activeView" #decade (@animateView.done)="viewRendered($event)"
7575
[date]="viewDate"
7676
[locale]="locale"
7777
[formatView]="formatViews.year"

projects/igniteui-angular/src/lib/calendar/calendar.component.spec.ts

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import {
77
Calendar, IgxCalendarComponent, IgxCalendarModule, isLeap,
88
monthRange, weekDay, WEEKDAYS
99
} from './public_api';
10-
import { UIInteractions } from '../test-utils/ui-interactions.spec';
10+
import { UIInteractions, wait } from '../test-utils/ui-interactions.spec';
1111
import { DateRangeDescriptor, DateRangeType } from '../core/dates/dateRange';
1212

1313
import { configureTestSuite } from '../test-utils/configure-suite';
1414
import { IgxDayItemComponent } from './days-view/day-item.component';
1515
import { HelperTestFunctions } from './calendar-helper-utils';
16+
import { CalendarView } from './month-picker-base';
17+
import { IViewDateChangeEventArgs } from './calendar-base';
1618

1719
describe('IgxCalendar - ', () => {
1820

@@ -252,8 +254,8 @@ describe('IgxCalendar - ', () => {
252254
expect(calendar.formatOptions).toEqual(jasmine.objectContaining(defaultOptions));
253255
expect(calendar.formatViews).toEqual(jasmine.objectContaining(defaultViews));
254256
expect(headerYear.nativeElement.textContent.trim()).toMatch('2018');
255-
expect(headerWeekday.nativeElement.textContent.trim()).toMatch('Mon');
256-
expect(headerDate.nativeElement.textContent.trim()).toMatch('Sep 17');
257+
expect(headerWeekday.nativeElement.textContent.trim()).toMatch('Sat');
258+
expect(headerDate.nativeElement.textContent.trim()).toMatch('Sep 1');
257259
expect(bodyYear.nativeElement.textContent.trim()).toMatch('2018');
258260
expect(bodyMonth.nativeElement.textContent.trim()).toMatch('Sep');
259261

@@ -267,8 +269,8 @@ describe('IgxCalendar - ', () => {
267269
expect(calendar.formatOptions).toEqual(jasmine.objectContaining(Object.assign(defaultOptions, formatOptions)));
268270
expect(calendar.formatViews).toEqual(jasmine.objectContaining(Object.assign(defaultViews, formatViews)));
269271
expect(headerYear.nativeElement.textContent.trim()).toMatch('18');
270-
expect(headerWeekday.nativeElement.textContent.trim()).toMatch('Mon');
271-
expect(headerDate.nativeElement.textContent.trim()).toMatch('September 17');
272+
expect(headerWeekday.nativeElement.textContent.trim()).toMatch('Sat');
273+
expect(headerDate.nativeElement.textContent.trim()).toMatch('September 1');
272274
expect(bodyYear.nativeElement.textContent.trim()).toMatch('18');
273275
expect(bodyMonth.nativeElement.textContent.trim()).toMatch('September');
274276

@@ -283,8 +285,8 @@ describe('IgxCalendar - ', () => {
283285
expect(calendar.formatOptions).toEqual(jasmine.objectContaining(Object.assign(defaultOptions, formatOptions)));
284286
expect(calendar.formatViews).toEqual(jasmine.objectContaining(Object.assign(defaultViews, formatViews)));
285287
expect(headerYear.nativeElement.textContent.trim()).toMatch('2018');
286-
expect(headerWeekday.nativeElement.textContent.trim()).toMatch('Mon');
287-
expect(headerDate.nativeElement.textContent.trim()).toMatch('September 17');
288+
expect(headerWeekday.nativeElement.textContent.trim()).toMatch('Sat');
289+
expect(headerDate.nativeElement.textContent.trim()).toMatch('September 1');
288290
expect(bodyYear.nativeElement.textContent.trim()).toMatch('2018');
289291
expect(bodyMonth.nativeElement.textContent.trim()).toMatch('8');
290292
});
@@ -305,8 +307,8 @@ describe('IgxCalendar - ', () => {
305307
fixture.detectChanges();
306308

307309
expect(headerYear.nativeElement.textContent.trim()).toMatch('2018');
308-
expect(headerWeekday.nativeElement.textContent.trim()).toMatch('Mon');
309-
expect(headerDate.nativeElement.textContent.trim()).toMatch('Sep 17');
310+
expect(headerWeekday.nativeElement.textContent.trim()).toMatch('Sat');
311+
expect(headerDate.nativeElement.textContent.trim()).toMatch('Sep 1');
310312
expect(bodyYear.nativeElement.textContent.trim()).toMatch('2018');
311313
expect(bodyMonth.nativeElement.textContent.trim()).toMatch('Sep');
312314
expect(bodyWeekday.nativeElement.textContent.trim()).toMatch('Sun');
@@ -319,8 +321,8 @@ describe('IgxCalendar - ', () => {
319321
bodyWeekday = dom.query(By.css(HelperTestFunctions.WEEKSTART_LABEL_CSSCLASS));
320322
expect(calendar.locale).toEqual(locale);
321323
expect(headerYear.nativeElement.textContent.trim()).toMatch('18');
322-
expect(headerWeekday.nativeElement.textContent.trim()).toMatch('lun.,');
323-
expect(headerDate.nativeElement.textContent.trim()).toMatch('17 sept.');
324+
expect(headerWeekday.nativeElement.textContent.trim()).toMatch('sam.,');
325+
expect(headerDate.nativeElement.textContent.trim()).toMatch('1 sept.');
324326
expect(bodyYear.nativeElement.textContent.trim()).toMatch('18');
325327
expect(bodyMonth.nativeElement.textContent.trim()).toMatch('sept.');
326328
expect(bodyWeekday.nativeElement.textContent.trim()).toMatch('Dim.');
@@ -1559,36 +1561,41 @@ describe('IgxCalendar - ', () => {
15591561
prev.nativeElement.focus();
15601562

15611563
expect(prev.nativeElement).toBe(document.activeElement);
1562-
15631564
UIInteractions.triggerKeyDownEvtUponElem('Enter', prev.nativeElement);
15641565
tick(100);
15651566
fixture.detectChanges();
15661567

15671568
expect(calendar.viewDate.getMonth()).toEqual(4);
1568-
15691569
const next = dom.queryAll(By.css(HelperTestFunctions.CALENDAR_NEXT_BUTTON_CSSCLASS))[0];
15701570
next.nativeElement.focus();
15711571
expect(next.nativeElement).toBe(document.activeElement);
15721572

15731573
UIInteractions.triggerKeyDownEvtUponElem('Enter', next.nativeElement);
1574-
tick(100);
1575-
fixture.detectChanges();
15761574

1575+
fixture.detectChanges();
1576+
tick(100);
15771577
UIInteractions.triggerKeyDownEvtUponElem('Enter', next.nativeElement);
15781578
tick(100);
15791579
fixture.detectChanges();
15801580

1581+
15811582
expect(calendar.viewDate.getMonth()).toEqual(6);
15821583
}));
15831584

1584-
it('Should open years view, navigate through and select an year via KB.', () => {
1585+
it('Should open years view, navigate through and select an year via KB.', fakeAsync(() => {
15851586
const year = dom.queryAll(By.css(HelperTestFunctions.CALENDAR_DATE_CSSCLASS))[1];
15861587
year.nativeElement.focus();
15871588

15881589
expect(year.nativeElement).toBe(document.activeElement);
15891590

1591+
spyOn(calendar.onActiveViewChanged, 'emit').and.callThrough();
1592+
15901593
UIInteractions.triggerKeyDownEvtUponElem('Enter', document.activeElement);
15911594
fixture.detectChanges();
1595+
tick();
1596+
1597+
expect(calendar.onActiveViewChanged.emit).toHaveBeenCalledTimes(1);
1598+
expect(calendar.onActiveViewChanged.emit).toHaveBeenCalledWith(CalendarView.DECADE);
15921599

15931600
const years = dom.queryAll(By.css(HelperTestFunctions.YEAR_CSSCLASS));
15941601
let currentYear = dom.query(By.css(HelperTestFunctions.CURRENT_YEAR_CSSCLASS));
@@ -1609,20 +1616,33 @@ describe('IgxCalendar - ', () => {
16091616
currentYear = dom.query(By.css(HelperTestFunctions.CURRENT_YEAR_CSSCLASS));
16101617
expect(currentYear.nativeElement.textContent.trim()).toMatch('2016');
16111618

1619+
const previousValue = fixture.componentInstance.calendar.viewDate;
1620+
spyOn(calendar.onViewDateChanged, 'emit').and.callThrough();
1621+
16121622
UIInteractions.triggerKeyDownEvtUponElem('Enter', currentYear.nativeElement);
1623+
16131624
fixture.detectChanges();
1625+
tick();
16141626

1627+
const eventArgs: IViewDateChangeEventArgs = { previousValue, currentValue: fixture.componentInstance.calendar.viewDate };
1628+
expect(calendar.onViewDateChanged.emit).toHaveBeenCalledTimes(1);
1629+
expect(calendar.onViewDateChanged.emit).toHaveBeenCalledWith(eventArgs);
16151630
expect(calendar.viewDate.getFullYear()).toEqual(2016);
1616-
});
1631+
}));
16171632

1618-
it('Should open months view, navigate through and select a month via KB.', () => {
1633+
it('Should open months view, navigate through and select a month via KB.', fakeAsync(() => {
16191634
const month = dom.queryAll(By.css(HelperTestFunctions.CALENDAR_DATE_CSSCLASS))[0];
16201635
month.nativeElement.focus();
1636+
spyOn(calendar.onActiveViewChanged, 'emit').and.callThrough();
16211637

16221638
expect(month.nativeElement).toBe(document.activeElement);
16231639

16241640
UIInteractions.triggerKeyDownEvtUponElem('Enter', document.activeElement);
16251641
fixture.detectChanges();
1642+
tick();
1643+
1644+
expect(calendar.onActiveViewChanged.emit).toHaveBeenCalledTimes(1);
1645+
expect(calendar.onActiveViewChanged.emit).toHaveBeenCalledWith(CalendarView.YEAR);
16261646

16271647
const months = dom.queryAll(By.css(HelperTestFunctions.MONTH_CSSCLASS));
16281648
const currentMonth = dom.query(By.css(HelperTestFunctions.CURRENT_MONTH_CSSCLASS));
@@ -1651,11 +1671,18 @@ describe('IgxCalendar - ', () => {
16511671

16521672
expect(document.activeElement.textContent.trim()).toMatch('Sep');
16531673

1674+
const previousValue = fixture.componentInstance.calendar.viewDate;
1675+
spyOn(calendar.onViewDateChanged, 'emit').and.callThrough();
1676+
16541677
UIInteractions.triggerKeyDownEvtUponElem('Enter', document.activeElement);
16551678
fixture.detectChanges();
1679+
tick();
16561680

1681+
const eventArgs: IViewDateChangeEventArgs = { previousValue, currentValue: fixture.componentInstance.calendar.viewDate };
1682+
expect(calendar.onViewDateChanged.emit).toHaveBeenCalledTimes(1);
1683+
expect(calendar.onViewDateChanged.emit).toHaveBeenCalledWith(eventArgs);
16571684
expect(calendar.viewDate.getMonth()).toEqual(8);
1658-
});
1685+
}));
16591686

16601687
it('Should navigate to the first enabled date from the previous month when using "arrow up" key.', fakeAsync(() => {
16611688
const dateRangeDescriptors: DateRangeDescriptor[] = [];

projects/igniteui-angular/src/lib/calendar/calendar.component.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
466466
* @internal
467467
*/
468468
public previousMonth(isKeydownTrigger = false) {
469+
this.previousViewDate = this.viewDate;
469470
this.viewDate = this.calendarModel.getPrevMonth(this.viewDate);
470471
this.animationAction = ScrollMonth.PREV;
471472
this.isKeydownTrigger = isKeydownTrigger;
@@ -478,6 +479,7 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
478479
* @internal
479480
*/
480481
public nextMonth(isKeydownTrigger = false) {
482+
this.previousViewDate = this.viewDate;
481483
this.viewDate = this.calendarModel.getNextMonth(this.viewDate);
482484
this.animationAction = ScrollMonth.NEXT;
483485
this.isKeydownTrigger = isKeydownTrigger;
@@ -610,6 +612,7 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
610612
this.daysView.daysNavService.focusNextDate(day.nativeElement, args.key, true);
611613
}
612614
};
615+
this.previousViewDate = this.viewDate;
613616
this.viewDate = this.nextDate;
614617
}
615618

@@ -618,6 +621,7 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
618621
* @intenal
619622
*/
620623
public changeMonth(event: Date) {
624+
this.previousViewDate = this.viewDate;
621625
this.viewDate = this.calendarModel.getFirstViewDate(event, 'month', this.activeViewIdx);
622626
this.activeView = CalendarView.DEFAULT;
623627

@@ -711,6 +715,11 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
711715
* @internal
712716
*/
713717
public animationDone(event) {
718+
if ((event.fromState === ScrollMonth.NONE && (event.toState === ScrollMonth.PREV || event.toState === ScrollMonth.NEXT)) ||
719+
(event.fromState === 'void' && event.toState === ScrollMonth.NONE)) {
720+
this.onViewDateChanged.emit({ previousValue: this.previousViewDate, currentValue: this.viewDate });
721+
}
722+
714723
if (this.monthScrollDirection !== ScrollMonth.NONE) {
715724
this.scrollMonth$.next();
716725
}
@@ -736,6 +745,16 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
736745
this.animationAction = ScrollMonth.NONE;
737746
}
738747

748+
/**
749+
* @hidden
750+
* @internal
751+
*/
752+
public viewRendered(event) {
753+
if (event.fromState !== 'void') {
754+
this.onActiveViewChanged.emit(this.activeView);
755+
}
756+
}
757+
739758
/**
740759
* Keyboard navigation of the calendar
741760
* @hidden
@@ -813,6 +832,7 @@ export class IgxCalendarComponent extends IgxMonthPickerBaseDirective implements
813832

814833
const isPageDown = event.key === 'PageDown';
815834
const step = isPageDown ? 1 : -1;
835+
this.previousViewDate = this.viewDate;
816836
this.viewDate = this.calendarModel.timedelta(this.viewDate, 'year', step);
817837

818838
this.animationAction = isPageDown ? ScrollMonth.NEXT : ScrollMonth.PREV;

0 commit comments

Comments
 (0)