diff --git a/projects/igniteui-angular/src/lib/calendar/calendar-multi-view.component.spec.ts b/projects/igniteui-angular/src/lib/calendar/calendar-multi-view.component.spec.ts index ab8993ed522..52853b64d36 100644 --- a/projects/igniteui-angular/src/lib/calendar/calendar-multi-view.component.spec.ts +++ b/projects/igniteui-angular/src/lib/calendar/calendar-multi-view.component.spec.ts @@ -5,7 +5,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { configureTestSuite } from '../test-utils/configure-suite'; import { UIInteractions, wait } from '../test-utils/ui-interactions.spec'; import { IgxCalendarComponent, IgxCalendarModule } from './public_api'; -import { IgxDatePickerComponent, IgxDatePickerModule } from '../date-picker/date-picker.component'; +import { IgxDatePickerComponent, IgxDatePickerModule } from '../date-picker/public_api'; import { DateRangeType } from '../core/dates'; import { HelperTestFunctions } from './calendar-helper-utils'; diff --git a/projects/igniteui-angular/src/lib/core/enums.ts b/projects/igniteui-angular/src/lib/core/enums.ts deleted file mode 100644 index 9ffc0e61896..00000000000 --- a/projects/igniteui-angular/src/lib/core/enums.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { mkenum } from './utils'; -/** - * This enumeration is used to configure whether the date/time picker has an editable input with drop down - * or is readonly - the date/time is selected only through a dialog. - */ -export const InteractionMode = mkenum({ - DropDown: 'dropdown', - Dialog: 'dialog' -}); -export type InteractionMode = (typeof InteractionMode)[keyof typeof InteractionMode]; diff --git a/projects/igniteui-angular/src/lib/date-common/calendar-container/calendar-container.component.ts b/projects/igniteui-angular/src/lib/date-common/calendar-container/calendar-container.component.ts index e1122ccaf8d..313b318f647 100644 --- a/projects/igniteui-angular/src/lib/date-common/calendar-container/calendar-container.component.ts +++ b/projects/igniteui-angular/src/lib/date-common/calendar-container/calendar-container.component.ts @@ -1,8 +1,7 @@ - import { CommonModule } from '@angular/common'; import { NgModule, Component, ViewChild, Output, EventEmitter, HostListener, HostBinding } from '@angular/core'; import { IgxCalendarComponent, IgxCalendarModule } from '../../calendar/public_api'; -import { InteractionMode } from '../../core/enums'; +import { PickerInteractionMode } from '../../date-common/types'; import { IgxButtonModule } from '../../directives/button/button.directive'; import { IgxRippleModule } from '../../directives/ripple/ripple.directive'; import { IgxPickerActionsDirective } from '../picker-icons.common'; @@ -28,18 +27,18 @@ export class IgxCalendarContainerComponent { @HostBinding('class.igx-date-picker--dropdown') public get dropdownCSS(): boolean { - return this.mode === InteractionMode.DropDown; + return this.mode === PickerInteractionMode.DropDown; } @HostBinding('class.igx-date-picker--vertical') public get verticalCSS(): boolean { - return this.vertical && this.mode === InteractionMode.Dialog; + return this.vertical && this.mode === PickerInteractionMode.Dialog; } public vertical = false; public closeButtonLabel: string; public todayButtonLabel: string; - public mode = InteractionMode.DropDown; + public mode = PickerInteractionMode.DropDown; public pickerActions: IgxPickerActionsDirective; @HostListener('keydown.alt.arrowup', ['$event']) @@ -49,7 +48,7 @@ export class IgxCalendarContainerComponent { } public get isReadonly() { - return this.mode === InteractionMode.Dialog; + return this.mode === PickerInteractionMode.Dialog; } } diff --git a/projects/igniteui-angular/src/lib/date-common/pickers-base.directive.ts b/projects/igniteui-angular/src/lib/date-common/picker-base.directive.ts similarity index 90% rename from projects/igniteui-angular/src/lib/date-common/pickers-base.directive.ts rename to projects/igniteui-angular/src/lib/date-common/picker-base.directive.ts index daf2d570917..f479a34f6bc 100644 --- a/projects/igniteui-angular/src/lib/date-common/pickers-base.directive.ts +++ b/projects/igniteui-angular/src/lib/date-common/picker-base.directive.ts @@ -1,7 +1,7 @@ import { ElementRef, EventEmitter, Inject, LOCALE_ID, Optional, Input, Directive, Output } from '@angular/core'; import { DisplayDensityBase, DisplayDensityToken, IDisplayDensityOptions } from '../core/density'; import { EditorProvider } from '../core/edit-provider'; -import { InteractionMode } from '../core/enums'; +import { PickerInteractionMode } from './types'; import { IToggleView } from '../core/navigation'; import { IBaseCancelableBrowserEventArgs, IBaseEventArgs } from '../core/utils'; import { DateRange } from '../date-range-picker/public_api'; @@ -10,7 +10,7 @@ import { IgxInputGroupType, IGX_INPUT_GROUP_TYPE } from '../input-group/public_a import { OverlaySettings } from '../services/overlay/utilities'; @Directive() -export abstract class PickersBaseDirective extends DisplayDensityBase implements IToggleView, EditorProvider { +export abstract class PickerBaseDirective extends DisplayDensityBase implements IToggleView, EditorProvider { /** * The editor's input mask. * @@ -64,7 +64,7 @@ export abstract class PickersBaseDirective extends DisplayDensityBase implements * ``` */ @Input() - public mode = InteractionMode.DropDown; + public mode: PickerInteractionMode = PickerInteractionMode.DropDown; /** * Overlay settings used to display the pop-up element. @@ -138,6 +138,17 @@ export abstract class PickersBaseDirective extends DisplayDensityBase implements return this._type || this._inputGroupType || 'box'; } + /** + * Gets/Sets the default template editor's tabindex. + * + * @example + * ```html + * + * ``` + */ + @Input() + public tabIndex: number | string; + /** * Emitted when the calendar has started opening, cancelable. * @@ -199,6 +210,11 @@ export abstract class PickersBaseDirective extends DisplayDensityBase implements return this._collapsed; } + /** @hidden @internal */ + public get isDropdown(): boolean { + return this.mode === PickerInteractionMode.DropDown; + } + public abstract valueChange: EventEmitter; constructor(public element: ElementRef, @@ -213,5 +229,5 @@ export abstract class PickersBaseDirective extends DisplayDensityBase implements public abstract open(settings?: OverlaySettings): void; public abstract toggle(settings?: OverlaySettings): void; public abstract close(): void; - public abstract getEditElement(); + public abstract getEditElement(): HTMLInputElement; } diff --git a/projects/igniteui-angular/src/lib/date-common/types.ts b/projects/igniteui-angular/src/lib/date-common/types.ts index b90fe7acd95..05047d5a1e7 100644 --- a/projects/igniteui-angular/src/lib/date-common/types.ts +++ b/projects/igniteui-angular/src/lib/date-common/types.ts @@ -1,7 +1,17 @@ import { mkenum } from '../core/utils'; /** Header orientation in `dialog` mode. */ -export const HeaderOrientation = mkenum({ +export const PickerHeaderOrientation = mkenum({ Horizontal: 'horizontal', Vertical: 'vertical' }); -export type HeaderOrientation = (typeof HeaderOrientation)[keyof typeof HeaderOrientation]; +export type PickerHeaderOrientation = (typeof PickerHeaderOrientation)[keyof typeof PickerHeaderOrientation]; + +/** + * This enumeration is used to configure whether the date/time picker has an editable input with drop down + * or is readonly - the date/time is selected only through a dialog. + */ +export const PickerInteractionMode = mkenum({ + DropDown: 'dropdown', + Dialog: 'dialog' +}); +export type PickerInteractionMode = (typeof PickerInteractionMode)[keyof typeof PickerInteractionMode]; diff --git a/projects/igniteui-angular/src/lib/date-common/util/date-time.util.ts b/projects/igniteui-angular/src/lib/date-common/util/date-time.util.ts index 924372d3846..9003a157c10 100644 --- a/projects/igniteui-angular/src/lib/date-common/util/date-time.util.ts +++ b/projects/igniteui-angular/src/lib/date-common/util/date-time.util.ts @@ -164,20 +164,20 @@ export abstract class DateTimeUtil { } /** Spins the date portion in a date-time editor. */ - public static spinDate(delta: number, newDate: Date, isSpinLoop: boolean): void { + public static spinDate(delta: number, newDate: Date, spinLoop: boolean): void { const maxDate = DateTimeUtil.daysInMonth(newDate.getFullYear(), newDate.getMonth()); let date = newDate.getDate() + delta; if (date > maxDate) { - date = isSpinLoop ? date % maxDate : maxDate; + date = spinLoop ? date % maxDate : maxDate; } else if (date < 1) { - date = isSpinLoop ? maxDate + (date % maxDate) : 1; + date = spinLoop ? maxDate + (date % maxDate) : 1; } newDate.setDate(date); } /** Spins the month portion in a date-time editor. */ - public static spinMonth(delta: number, newDate: Date, isSpinLoop: boolean): void { + public static spinMonth(delta: number, newDate: Date, spinLoop: boolean): void { const maxDate = DateTimeUtil.daysInMonth(newDate.getFullYear(), newDate.getMonth() + delta); if (newDate.getDate() > maxDate) { newDate.setDate(maxDate); @@ -187,9 +187,9 @@ export abstract class DateTimeUtil { const minMonth = 0; let month = newDate.getMonth() + delta; if (month > maxMonth) { - month = isSpinLoop ? (month % maxMonth) - 1 : maxMonth; + month = spinLoop ? (month % maxMonth) - 1 : maxMonth; } else if (month < minMonth) { - month = isSpinLoop ? maxMonth + (month % maxMonth) + 1 : minMonth; + month = spinLoop ? maxMonth + (month % maxMonth) + 1 : minMonth; } newDate.setMonth(month); @@ -206,42 +206,42 @@ export abstract class DateTimeUtil { } /** Spins the hours portion in a date-time editor. */ - public static spinHours(delta: number, newDate: Date, isSpinLoop: boolean): void { + public static spinHours(delta: number, newDate: Date, spinLoop: boolean): void { const maxHour = 23; const minHour = 0; let hours = newDate.getHours() + delta; if (hours > maxHour) { - hours = isSpinLoop ? hours % maxHour - 1 : maxHour; + hours = spinLoop ? hours % maxHour - 1 : maxHour; } else if (hours < minHour) { - hours = isSpinLoop ? maxHour + (hours % maxHour) + 1 : minHour; + hours = spinLoop ? maxHour + (hours % maxHour) + 1 : minHour; } newDate.setHours(hours); } /** Spins the minutes portion in a date-time editor. */ - public static spinMinutes(delta: number, newDate: Date, isSpinLoop: boolean): void { + public static spinMinutes(delta: number, newDate: Date, spinLoop: boolean): void { const maxMinutes = 59; const minMinutes = 0; let minutes = newDate.getMinutes() + delta; if (minutes > maxMinutes) { - minutes = isSpinLoop ? minutes % maxMinutes - 1 : maxMinutes; + minutes = spinLoop ? minutes % maxMinutes - 1 : maxMinutes; } else if (minutes < minMinutes) { - minutes = isSpinLoop ? maxMinutes + (minutes % maxMinutes) + 1 : minMinutes; + minutes = spinLoop ? maxMinutes + (minutes % maxMinutes) + 1 : minMinutes; } newDate.setMinutes(minutes); } /** Spins the seconds portion in a date-time editor. */ - public static spinSeconds(delta: number, newDate: Date, isSpinLoop: boolean): void { + public static spinSeconds(delta: number, newDate: Date, spinLoop: boolean): void { const maxSeconds = 59; const minSeconds = 0; let seconds = newDate.getSeconds() + delta; if (seconds > maxSeconds) { - seconds = isSpinLoop ? seconds % maxSeconds - 1 : maxSeconds; + seconds = spinLoop ? seconds % maxSeconds - 1 : maxSeconds; } else if (seconds < minSeconds) { - seconds = isSpinLoop ? maxSeconds + (seconds % maxSeconds) + 1 : minSeconds; + seconds = spinLoop ? maxSeconds + (seconds % maxSeconds) + 1 : minSeconds; } newDate.setSeconds(seconds); diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.common.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.common.ts index c14a64bcf29..7014d67dfd2 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.common.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.common.ts @@ -1,12 +1,19 @@ -/** @hidden */ -export const IGX_DATE_PICKER_COMPONENT = 'IgxDatePickerComponentToken'; +import { IBaseEventArgs } from '../core/utils'; +import { IgxDatePickerComponent } from './date-picker.component'; -/** @hidden */ -export interface IDatePicker { - value: Date; - mask: string; - inputMask: string; - rawDateString: string; - dateFormatParts: any[]; - invalidDate: string; +/** + * Provides information about date picker reference and its current value + * when onDisabledDate event is fired. + */ +export interface IDatePickerDisabledDateEventArgs extends IBaseEventArgs { + datePicker: IgxDatePickerComponent; + currentValue: Date; +} + +/** + * Provides information about date picker reference and its previously valid value + * when onValidationFailed event is fired. + */ +export interface IDatePickerValidationFailedEventArgs extends IBaseEventArgs { + prevValue: Date; } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.html b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.html index 11c1819a8d9..fc806cbb8c1 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.html +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.html @@ -1,54 +1,29 @@ - - - + + + today + - - - - - today - - - - - + - - - - - today - - - - - clear - - - + + clear + - + + + + + + + + + + + + + diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.spec.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.spec.ts index b3856bb1767..2442ea1a5a8 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.spec.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.spec.ts @@ -1,1792 +1,309 @@ -import { Component, ViewChild, ElementRef, EventEmitter, QueryList, Renderer2, DebugElement } from '@angular/core'; -import { fakeAsync, TestBed, tick, flush, ComponentFixture, waitForAsync } from '@angular/core/testing'; -import { FormsModule, FormGroup, FormBuilder, ReactiveFormsModule, Validators, NgControl } from '@angular/forms'; -import { By } from '@angular/platform-browser'; +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { IgxDatePickerComponent, IgxDatePickerModule } from './date-picker.component'; -import { IgxLabelDirective } from '../directives/label/label.directive'; -import { IgxInputDirective, IgxInputState } from '../directives/input/input.directive'; -import { UIInteractions, wait } from '../test-utils/ui-interactions.spec'; -import { IgxInputGroupModule, IgxInputGroupComponent } from '../input-group/public_api'; +import { UIInteractions } from '../test-utils/ui-interactions.spec'; +import { IgxInputGroupComponent, IgxInputGroupModule } from '../input-group/public_api'; import { IgxTextSelectionModule } from '../directives/text-selection/text-selection.directive'; import { configureTestSuite } from '../test-utils/configure-suite'; import { IgxButtonModule } from '../directives/button/button.directive'; import { IgxCalendarModule } from '../calendar/public_api'; -import { InteractionMode } from '../core/enums'; -import { DateRangeType } from '../core/dates/dateRange'; import { IgxIconModule } from '../icon/public_api'; -import { - OverlayCancelableEventArgs, - OverlayClosingEventArgs, - OverlayEventArgs -} from '../services/public_api'; import { IgxCalendarContainerModule } from '../date-common/calendar-container/calendar-container.component'; +import { IgxDatePickerComponent, IgxDatePickerModule } from './public_api'; +import { IgxOverlayService, OverlayCancelableEventArgs, OverlayClosingEventArgs, OverlayEventArgs } from '../services/public_api'; +import { AnimationMetadata, AnimationOptions } from '@angular/animations'; +import { EventEmitter, QueryList, Renderer2 } from '@angular/core'; -xdescribe('IgxDatePicker', () => { +fdescribe('IgxDatePicker', () => { configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [ - IgxDatePickerTestComponent, - IgxDatePickerProjectedLabelTestComponent, - IgxDatePickerWithWeekStartComponent, - IgxDatePickerWithCustomFormatterComponent, - IgxDatePickerWithPassedDateComponent, - IgxDatePickerWIthLocaleComponent, - IgxDatePickerNgModelComponent, - IgxDatePickerRetemplatedComponent, - IgxDatePickerEditableComponent, - IgxDatePickerCustomizedComponent, - IgxDropDownDatePickerRetemplatedComponent, - IgxDatePickerOpeningComponent, - IgxDatePickerReactiveFormComponent, - IgxDatePickerDropdownButtonsComponent ], - imports: [IgxDatePickerModule, FormsModule, ReactiveFormsModule, NoopAnimationsModule, IgxInputGroupModule, IgxCalendarModule, - IgxButtonModule, IgxTextSelectionModule, IgxIconModule, IgxCalendarContainerModule] + imports: [IgxDatePickerModule, FormsModule, ReactiveFormsModule, + NoopAnimationsModule, IgxInputGroupModule, IgxCalendarModule, + IgxButtonModule, IgxTextSelectionModule, IgxIconModule, + IgxCalendarContainerModule] }) .compileComponents(); })); - afterEach(() => { - UIInteractions.clearOverlay(); - }); - - describe('Base Tests', () => { - // configureTestSuite(); - let fixture: ComponentFixture; - let datePicker: IgxDatePickerComponent; - - beforeEach(() => { - fixture = TestBed.createComponent(IgxDatePickerTestComponent); - datePicker = fixture.componentInstance.datePicker; - fixture.detectChanges(); - }); - - it('Initialize a datepicker component', () => { - expect(fixture.componentInstance).toBeDefined(); - expect(datePicker.displayData).toBe(''); - }); - - it('Initialize a datepicker component with id', () => { - const domDatePicker = fixture.debugElement.query(By.css('igx-date-picker')).nativeElement; - - expect(datePicker.id).toContain('igx-date-picker-'); - expect(domDatePicker.id).toContain('igx-date-picker-'); - - datePicker.id = 'customDatePicker'; - fixture.detectChanges(); - - expect(datePicker.id).toBe('customDatePicker'); - expect(domDatePicker.id).toBe('customDatePicker'); - }); - - it('Datepicker open/close event', async () => { - const dom = fixture.debugElement; - const target = dom.query(By.css('.igx-date-picker__input-date')); - - spyOn(datePicker.onOpened, 'emit'); - spyOn(datePicker.onClosed, 'emit'); - - UIInteractions.simulateClickAndSelectEvent(target); - fixture.detectChanges(); - await wait(); - - expect(datePicker.onOpened.emit).toHaveBeenCalled(); - expect(datePicker.onOpened.emit).toHaveBeenCalledWith(datePicker); - - const overlayDiv = document.getElementsByClassName('igx-overlay__wrapper--modal')[0]; - expect(overlayDiv).toBeDefined(); - expect(overlayDiv.classList.contains('igx-overlay__wrapper--modal')).toBeTruthy(); - overlayDiv.dispatchEvent(new Event('click')); - - fixture.detectChanges(); - await wait(); - - expect(datePicker.onClosed.emit).toHaveBeenCalled(); - expect(datePicker.onClosed.emit).toHaveBeenCalledWith(datePicker); - }); - - it('Datepicker onSelection and valueChange events and selectDate method propagation', () => { - spyOn(datePicker.onSelection, 'emit'); - spyOn(datePicker.valueChange, 'emit'); - const newDate: Date = new Date(2016, 4, 6); - datePicker.selectDate(newDate); - fixture.detectChanges(); - - expect(datePicker.onSelection.emit).toHaveBeenCalled(); - expect(datePicker.valueChange.emit).toHaveBeenCalled(); - expect(datePicker.valueChange.emit).toHaveBeenCalledWith(newDate); - expect(datePicker.value).toBe(newDate); - }); - - it('check disabled state', () => { - const dom = fixture.debugElement; - const inputGroup = dom.query(By.css('.igx-input-group')); - const disabled = dom.query(By.css('.igx-input-group--disabled')); - expect(disabled).toBeNull(); - expect(inputGroup.nativeElement.classList.contains('igx-input-group--disabled')).toBeFalsy(); - - datePicker.disabled = true; - fixture.detectChanges(); - - const disabledGroup = dom.query(By.css('.igx-input-group--disabled')); - expect(disabledGroup).toBeDefined(); - expect(inputGroup.nativeElement.classList.contains('igx-input-group--disabled')).toBeTruthy(); - }); - - it('should not be able to toggle & clear when disabled', () => { - const date = new Date(); - datePicker.value = date; - datePicker.disabled = true; - fixture.detectChanges(); - expect(datePicker.collapsed).toBeTruthy(); - - datePicker.openDialog(); - fixture.detectChanges(); - expect(datePicker.collapsed).toBeTruthy(); - - datePicker.clear(); - fixture.detectChanges(); - expect(datePicker.value).toEqual(date); - }); - - it('When labelVisibility is set to false the label should not be visible', () => { - let label = fixture.debugElement.query(By.directive(IgxLabelDirective)); - - expect(label.nativeElement.innerText).toBe(datePicker.label); - - fixture.componentInstance.labelVisibility = false; - fixture.detectChanges(); - - label = fixture.debugElement.query(By.directive(IgxLabelDirective)); - expect(label).toBeNull(); - }); - - it('When update label property it should reflect on the label text of the datepicker', () => { - let label = fixture.debugElement.query(By.directive(IgxLabelDirective)); - expect(label.nativeElement.innerText).toEqual(datePicker.label); - - const expectedResult = 'new label'; - datePicker.label = expectedResult; - fixture.detectChanges(); - - label = fixture.debugElement.query(By.directive(IgxLabelDirective)); - expect(label.nativeElement.innerText).toEqual(expectedResult); - }); - - it('Visualize the label of the datepicker when initially is hidden', () => { - fixture.componentInstance.labelVisibility = false; - fixture.detectChanges(); - - let label = fixture.debugElement.query(By.directive(IgxLabelDirective)); - expect(label).toBeNull(); - - fixture.componentInstance.labelVisibility = true; - fixture.detectChanges(); - - label = fixture.debugElement.query(By.directive(IgxLabelDirective)); - expect(label).not.toBeNull(); - }); - - it('should display default and custom label', () => { - const fixtureProjectedLabel = TestBed.createComponent(IgxDatePickerProjectedLabelTestComponent); - const dom = fixtureProjectedLabel.debugElement; - const testComponent = fixtureProjectedLabel.componentInstance; - fixtureProjectedLabel.detectChanges(); - - let label = dom.query(By.directive(IgxLabelDirective)).nativeElement.innerText; - expect(label).toEqual(testComponent.customLabel); - - testComponent.customLabelVisibility = false; - fixtureProjectedLabel.detectChanges(); - - label = dom.query(By.directive(IgxLabelDirective)).nativeElement.innerText; - expect(label).toEqual('Date'); - - testComponent.labelVisibility = false; - fixtureProjectedLabel.detectChanges(); - testComponent.customLabelVisibility = false; - fixtureProjectedLabel.detectChanges(); - label = dom.query(By.directive(IgxLabelDirective)); - fixtureProjectedLabel.detectChanges(); - expect(label).toBeNull(); - - testComponent.customLabelVisibility = true; - fixtureProjectedLabel.detectChanges(); - label = dom.query(By.directive(IgxLabelDirective)).nativeElement.innerText; - expect(label).toEqual(testComponent.customLabel); - }); - - it('Handling keyboard navigation with `space`(open) and `esc`(close) buttons', fakeAsync(() => { - const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker')); - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - fixture.detectChanges(); - - const overlayDiv = document.getElementsByClassName('igx-overlay__wrapper--modal')[0]; - expect(overlayDiv).toBeDefined(); - expect(overlayDiv.classList.contains('igx-overlay__wrapper--modal')).toBeTruthy(); - - UIInteractions.triggerKeyDownEvtUponElem('Escape', overlayDiv, true); - flush(); - fixture.detectChanges(); - - const overlays = document.getElementsByClassName('igx-overlay__wrapper--modal'); - expect(overlays.length).toEqual(0); - })); - - it('When modal datepicker is closed via `Escape` Key and the dialog disappear, the focus should remain on the input', - fakeAsync(() => { - const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker')); - let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); - expect(overlayToggle.length).toEqual(0); - - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - flush(); - fixture.detectChanges(); - - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); - expect(overlayToggle[0]).not.toBeNull(); - expect(overlayToggle[0]).toBeDefined(); - - UIInteractions.triggerKeyDownEvtUponElem('Escape', overlayToggle[0], true); - flush(); - fixture.detectChanges(); - - const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; - expect(input).toEqual(document.activeElement); - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); - expect(overlayToggle[0]).toBeUndefined(); - })); - - it('When a modal datepicker is closed via outside click, the focus should remain on the input', - fakeAsync(() => { - const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker')); - let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); - expect(overlayToggle.length).toEqual(0); - - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - flush(); - fixture.detectChanges(); - - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); - expect(overlayToggle[0]).not.toBeNull(); - expect(overlayToggle[0]).toBeDefined(); - - UIInteractions.simulateClickAndSelectEvent(overlayToggle[0]); - flush(); - fixture.detectChanges(); - - const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; - expect(input).toEqual(document.activeElement); - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); - expect(overlayToggle[0]).toBeUndefined(); - })); - - it('When datepicker is closed upon selecting a date, the focus should remain on the input', - fakeAsync(() => { - const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker')); - let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); - expect(overlayToggle.length).toEqual(0); - - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - flush(); - fixture.detectChanges(); - - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); - expect(overlayToggle[0]).not.toBeNull(); - expect(overlayToggle[0]).toBeDefined(); - - // select a date - const dateElemToSelect = document.getElementsByClassName('igx-calendar__date-content')[10]; - UIInteractions.simulateClickAndSelectEvent(dateElemToSelect); - flush(); - fixture.detectChanges(); - - const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; - expect(input).toEqual(document.activeElement); - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); - expect(overlayToggle[0]).toBeUndefined(); - })); - - it('should allow setting editorTabIndex', () => { - fixture.componentInstance.datePicker.editorTabIndex = 3; - fixture.detectChanges(); - const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; - expect(input.tabIndex).toBe(3); - }); - }); - - describe('ARIA Tests', () => { - let labelID: string; - let inputLabelledBy: string; - let dom: DebugElement; - - it('ARIA Test for a picker with an input group template', () => { - const fixture = TestBed.createComponent(IgxDatePickerRetemplatedComponent); - fixture.detectChanges(); - dom = fixture.debugElement; - - labelID = dom.query(By.directive(IgxLabelDirective)).nativeElement.id; - inputLabelledBy = dom.query(By.directive(IgxInputDirective)).nativeElement.getAttribute('aria-labelledby'); - expect(inputLabelledBy).toEqual(labelID); - }); - - it('ARIA Test for picker with a dialog mode', () => { - const fixture = TestBed.createComponent(IgxDatePickerTestComponent); - fixture.detectChanges(); - dom = fixture.debugElement; - - labelID = dom.query(By.directive(IgxLabelDirective)).nativeElement.id; - inputLabelledBy = dom.query(By.directive(IgxInputDirective)).nativeElement.getAttribute('aria-labelledby'); - expect(inputLabelledBy).toEqual(labelID); - }); - - it('ARIA Test for picker with a dropdown mode', () => { - const fixture = TestBed.createComponent(IgxDatePickerOpeningComponent); - fixture.detectChanges(); - dom = fixture.debugElement; - - labelID = dom.query(By.directive(IgxLabelDirective)).nativeElement.id; - inputLabelledBy = dom.query(By.directive(IgxInputDirective)).nativeElement.getAttribute('aria-labelledby'); - expect(inputLabelledBy).toEqual(labelID); - }); - }); - - describe('DatePicker with passed date', () => { - // configureTestSuite(); - let fixture: ComponentFixture; - let datePicker: IgxDatePickerComponent; - let inputTarget; - - beforeEach(() => { - fixture = TestBed.createComponent(IgxDatePickerWithPassedDateComponent); - datePicker = fixture.componentInstance.datePicker; - fixture.detectChanges(); - inputTarget = fixture.debugElement.query(By.css('.igx-date-picker__input-date')).nativeElement; - - }); - - it('@Input properties', () => { - expect(datePicker.value).toEqual(new Date(2017, 7, 7)); - }); - - it('Datepicker DOM input value', () => { - const today = new Date(2017, 7, 7); - const formattedDate = `${today.getMonth() + 1}/${today.getDate()}/${today.getFullYear()}`; - - expect(inputTarget.value).toEqual(formattedDate); - }); - - it('Datepicker custom locale(EN) date format', () => { - const todayToEnLocale = new Date(2017, 7, 7).toLocaleDateString('en'); - expect(inputTarget.value).toEqual(todayToEnLocale); - }); - - it('Set formatOptions for month to be numeric', () => { - const getMonthFromPickerDate = fixture.componentInstance.date.getMonth() + 1; - inputTarget.dispatchEvent(new Event('click', { bubbles: true })); - fixture.detectChanges(); - - const headerDate = document.getElementsByClassName('igx-calendar__header-date')[0]; - const getMonthFromCalendarHeader = (headerDate.children[1] as HTMLElement).innerText.substring(0, 1); - - expect(parseInt(getMonthFromCalendarHeader, 10)).toBe(getMonthFromPickerDate); - }); - }); - - it('When datepicker in "dropdown" mode is closed via outside click, the input should not receive focus', - fakeAsync(() => { - const fixture = TestBed.createComponent(IgxDatePickerDropdownButtonsComponent); - fixture.detectChanges(); - - const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker')); - const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; - let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper'); - - expect(overlayToggle.length).toEqual(0); - - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - flush(); - fixture.detectChanges(); - - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper'); - expect(overlayToggle).not.toBeNull(); - expect(overlayToggle).toBeDefined(); - - const dummyInput = fixture.componentInstance.dummyInput.nativeElement; - dummyInput.focus(); - dummyInput.click(); - tick(); - fixture.detectChanges(); - - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper'); - expect(overlayToggle[0]).toBeUndefined(); - expect(input).not.toEqual(document.activeElement); - expect(dummyInput).toEqual(document.activeElement); - })); - - it('When datepicker in "dropdown" mode, should focus input on user interaction with Today btn, Cancel btn, Enter Key, Escape key', - fakeAsync(() => { - const fixture = TestBed.createComponent(IgxDatePickerDropdownButtonsComponent); - fixture.detectChanges(); - const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker')); - const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; - let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper'); - expect(overlayToggle.length).toEqual(0); - - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - flush(); - fixture.detectChanges(); - const buttons = document.getElementsByClassName('igx-button--flat'); - expect(buttons.length).toEqual(2); - - // Today btn - const todayBtn = buttons[1] as HTMLElement; - expect(todayBtn.innerText).toBe('Today'); - todayBtn.click(); - tick(); - fixture.detectChanges(); - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper'); - expect(overlayToggle[0]).toBeUndefined(); - expect(input).toEqual(document.activeElement); - - // Cancel btn - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - flush(); - fixture.detectChanges(); - expect(buttons.length).toEqual(2); - const cancelBtn = buttons[0] as HTMLElement; - expect(cancelBtn.innerText).toBe('Cancel'); - cancelBtn.click(); - tick(); - fixture.detectChanges(); - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper'); - expect(overlayToggle[0]).toBeUndefined(); - expect(input).toEqual(document.activeElement); - - // Enter key - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - flush(); - fixture.detectChanges(); - expect(buttons.length).toEqual(2); - document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); - tick(); - fixture.detectChanges(); - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper'); - expect(overlayToggle[0]).toBeUndefined(); - expect(input).toEqual(document.activeElement); - - // Esc key - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - flush(); - fixture.detectChanges(); - expect(buttons.length).toEqual(2); - document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); - tick(); - fixture.detectChanges(); - - overlayToggle = document.getElementsByClassName('igx-overlay__wrapper'); - expect(overlayToggle[0]).toBeUndefined(); - expect(input).toEqual(document.activeElement); - })); - - it('Datepicker week start day (Monday)', () => { - const fixture = TestBed.createComponent(IgxDatePickerWithWeekStartComponent); - fixture.detectChanges(); - - const dom = fixture.debugElement; - const datePickerTarget = dom.query(By.css('.igx-date-picker__input-date')); - - UIInteractions.simulateClickAndSelectEvent(datePickerTarget); - fixture.detectChanges(); - - const firstDayValue = (document.getElementsByClassName('igx-calendar__label')[0] as HTMLElement).innerText.trim(); - const expectedResult = 'Mon'; - - expect(firstDayValue).toBe(expectedResult); - }); - - it('Retemplated calendar in date picker', () => { - const fixture = TestBed.createComponent(IgxDatePickerCustomizedComponent); - fixture.detectChanges(); - - const dom = fixture.debugElement; - const datePickerTarget = dom.query(By.css('.igx-date-picker__input-date')); - - UIInteractions.simulateClickAndSelectEvent(datePickerTarget); - fixture.detectChanges(); - - const formattedHeaderDate = document.getElementsByClassName('igx-calendar__header-date')[0]; - const formattedHeaderText = (formattedHeaderDate as HTMLElement).innerText; - expect(formattedHeaderText).toBe('10/20/19'); - - const picker = document.getElementsByClassName('igx-calendar-picker'); - const formattedSubHeaderText = picker[0].querySelector('.igx-calendar-picker__dates').textContent.trim(); - - expect(formattedSubHeaderText).toBe('2019/Oct'); - - const buttons = document.getElementsByClassName('igx-button--flat'); - expect(buttons.length).toEqual(1); - expect((buttons[0] as HTMLElement).innerText).toBe('TEST'); - }); - - it('Retemplated calendar in date picker - dropdown mode', () => { - const fixture = TestBed.createComponent(IgxDatePickerCustomizedComponent); - const datePicker = fixture.componentInstance.customizedDatePicker; - datePicker.mode = InteractionMode.DropDown; - fixture.detectChanges(); - - const dom = fixture.debugElement; - const iconDate = dom.query(By.css('.igx-icon')); - expect(iconDate).toBeDefined(); - - UIInteractions.simulateClickAndSelectEvent(iconDate); - fixture.detectChanges(); - - const picker = document.getElementsByClassName('igx-calendar-picker'); - const formattedSubHeaderText = picker[0].querySelector('.igx-calendar-picker__dates').textContent.trim(); - expect(formattedSubHeaderText).toBe('2019/Oct'); - - const buttons = document.getElementsByClassName('igx-button--flat'); - expect(buttons.length).toEqual(1); - expect((buttons[0] as HTMLElement).innerText).toBe('TEST'); - }); - - it('locale propagate calendar value (de-DE)', () => { - const fixture = TestBed.createComponent(IgxDatePickerWIthLocaleComponent); - fixture.detectChanges(); - - const datePicker = fixture.componentInstance.datePicker; - const dateConvertedToDeLocale = fixture.componentInstance.date.toLocaleDateString('de-DE'); - - expect(datePicker.displayData).toBe(dateConvertedToDeLocale); - }); - - it('Datepicker custom formatter', () => { - const fixture = TestBed.createComponent(IgxDatePickerWithCustomFormatterComponent); - fixture.detectChanges(); - - const compInstance = fixture.componentInstance; - const datePicker = compInstance.datePicker; - const dom = fixture.debugElement; - const inputTarget = dom.query(By.css('.igx-date-picker__input-date')).nativeElement; - const date = new Date(2017, 7, 7); - const formattedDate = compInstance.customFormatter(date); - - expect(inputTarget.value).toEqual(formattedDate); - }); - - it('Value should respond when is bound through ngModel and selection through selectDate method is made.', fakeAsync(() => { - const fix = TestBed.createComponent(IgxDatePickerNgModelComponent); - const datePicker = fix.componentInstance.datePicker; - let expectedRes = new Date(2011, 11, 11); - fix.detectChanges(); - flush(); - - expect(datePicker.value).toEqual(expectedRes); - expectedRes = new Date(Date.now()); - datePicker.selectDate(expectedRes); - - tick(); - expect(datePicker.value).toEqual(expectedRes); - - const boundValue = fix.componentInstance.val; - expect(boundValue).toEqual(expectedRes); - })); - - it('Retemplate a DatePicker input group', fakeAsync(() => { - const fix = TestBed.createComponent(IgxDatePickerRetemplatedComponent); - tick(); - fix.detectChanges(); - - const dom = fix.debugElement; - const inputGroup = dom.query(By.css('.igx-input-group')); - expect(inputGroup).not.toBeNull(); - expect(dom.query(By.css('.igx-icon'))).toBeNull(); - expect(inputGroup.nativeElement.classList.contains('igx-input-group--invalid')).toBe(false); - })); - - it('Should be able to deselect using the API.', () => { - const fix = TestBed.createComponent(IgxDatePickerTestComponent); - const datePicker = fix.componentInstance.datePicker; - fix.detectChanges(); - - const date = new Date(Date.now()); - datePicker.selectDate(date); - fix.detectChanges(); - - expect(datePicker.value).toBe(date); - - datePicker.deselectDate(); - fix.detectChanges(); - - expect(datePicker.value).toBe(null); - }); - - it('Should not alter hours, minutes, seconds and milliseconds when changing date.', () => { - const fixture = TestBed.createComponent(IgxDatePickerTestComponent); - const debugElement = fixture.debugElement; - const datePicker = fixture.componentInstance.datePicker; - const date = new Date(2030, 1, 1, 15, 16, 17, 18); - datePicker.value = date; - fixture.detectChanges(); - - const datePickerTarget = debugElement.query(By.css('.igx-date-picker__input-date')); - datePickerTarget.nativeElement.dispatchEvent(new Event('click', { bubbles: true })); - fixture.detectChanges(); - - const targetDate = 15; - const fromDate = datePicker.calendar.daysView.dates.filter( - d => d.date.date.getDate() === targetDate)[0]; - fromDate.nativeElement.click(); - fixture.detectChanges(); - - expect(datePicker.value.getFullYear()).toBe(date.getFullYear()); - expect(datePicker.value.getMonth()).toBe(date.getMonth()); - expect(datePicker.value.getDate()).toBe(targetDate); - expect(datePicker.value.getHours()).toBe(date.getHours()); - expect(datePicker.value.getMinutes()).toBe(date.getMinutes()); - expect(datePicker.value.getSeconds()).toBe(date.getSeconds()); - expect(datePicker.value.getMilliseconds()).toBe(date.getMilliseconds()); - }); - - it('Should focus the today date', fakeAsync(() => { - const fixture = TestBed.createComponent(IgxDatePickerTestComponent); - const datePicker = fixture.componentInstance.datePicker; - fixture.detectChanges(); - const dom = fixture.debugElement; - - const target = dom.query(By.css('.igx-date-picker__input-date')); - UIInteractions.simulateClickAndSelectEvent(target); - fixture.detectChanges(); - tick(200); - - const todayDate = datePicker.calendar.daysView.dates.find(d => d.isToday); - - expect(document.activeElement).toEqual(todayDate.nativeElement); - })); - - it('#3595 - Should be able to change year', fakeAsync(() => { - const fixture = TestBed.createComponent(IgxDatePickerTestComponent); - fixture.detectChanges(); - - const dom = fixture.debugElement; - const target = dom.query(By.css('.igx-date-picker__input-date')); - - UIInteractions.simulateClickAndSelectEvent(target); - fixture.detectChanges(); - - let year = document.getElementsByClassName('igx-calendar-picker__date')[1]; - year.dispatchEvent(new Event('click')); - tick(); - fixture.detectChanges(); - - const firstYear = document.getElementsByClassName('igx-calendar__year')[1]; - const expectedResult = (firstYear as HTMLElement).innerText.trim(); - firstYear.dispatchEvent(new Event('click')); - tick(); - fixture.detectChanges(); - - year = document.getElementsByClassName('igx-calendar-picker__date')[1]; - expect((year as HTMLElement).innerText).toBe(expectedResult); - })); - - it('#3595 - Should be able to change month', fakeAsync(() => { - const fixture = TestBed.createComponent(IgxDatePickerTestComponent); - fixture.componentInstance.datePicker.value = new Date(2019, 2, 10); - tick(300); - fixture.detectChanges(); - - const dom = fixture.debugElement; - const target = dom.query(By.css('.igx-date-picker__input-date')); - UIInteractions.simulateClickAndSelectEvent(target); - tick(200); - fixture.detectChanges(); - - let month = document.getElementsByClassName('igx-calendar-picker__date')[0]; - month.dispatchEvent(new Event('click')); - tick(200); - fixture.detectChanges(); - - const firstMonth = document.getElementsByClassName('igx-calendar__month')[0]; - const expectedResult = (firstMonth as HTMLElement).innerText; - - firstMonth.dispatchEvent(new Event('click')); - tick(200); - fixture.detectChanges(); - - month = document.getElementsByClassName('igx-calendar-picker__date')[0]; - expect((month as HTMLElement).innerText.trim()).toBe(expectedResult.trim()); - })); - - describe('Drop-down opening', () => { - // configureTestSuite(); - let fixture: ComponentFixture; - let datePicker: IgxDatePickerComponent; - - beforeEach(() => { - fixture = TestBed.createComponent(IgxDatePickerOpeningComponent); - datePicker = fixture.componentInstance.datePicker; - fixture.detectChanges(); - }); - - it('Drop-down should open below the input by default if there is enough space - dropdown mode', fakeAsync(() => { - const dom = fixture.debugElement; - - const inputGroup = document.getElementsByTagName('igx-input-group'); - const inputGroupRect = inputGroup[0].getBoundingClientRect() as DOMRect; - const inputGroupTop = inputGroupRect.top; - - const iconDate = dom.query(By.css('.igx-icon')); - UIInteractions.simulateClickAndSelectEvent(iconDate); - fixture.detectChanges(); - tick(); - - const calendar = document.getElementsByTagName('igx-calendar-container'); - const calendarRect = calendar[0].getBoundingClientRect() as DOMRect; - const calendarTop = calendarRect.top; - - expect(inputGroupTop).toBeLessThan(calendarTop); - })); - - it('Drop-down should open above the input when there is no enough space below - dropdown mode', fakeAsync(() => { - const dom = fixture.debugElement; - - // check if drop down is opened above the input if there is no space below - datePicker.element.nativeElement.style = 'position: fixed; bottom: 150px'; - fixture.detectChanges(); - tick(); - - const inputGroup = document.getElementsByTagName('igx-input-group'); - const inputGroupRect = inputGroup[0].getBoundingClientRect() as DOMRect; - const inputGroupTop = inputGroupRect.top; - - const iconDate = dom.query(By.css('.igx-icon')); - UIInteractions.simulateClickAndSelectEvent(iconDate); - fixture.detectChanges(); - tick(); - - const calendar = document.getElementsByTagName('igx-calendar-container'); - const calendarRect = calendar[0].getBoundingClientRect() as DOMRect; - const calendarTop = calendarRect.top; - - expect(inputGroupTop).toBeGreaterThan(calendarTop); - })); - }); - - describe('Drop-down Retemplated Date Picker', () => { - // configureTestSuite(); - let fixture: ComponentFixture; - let datePicker: IgxDatePickerComponent; - - beforeEach(() => { - fixture = TestBed.createComponent(IgxDropDownDatePickerRetemplatedComponent); - datePicker = fixture.componentInstance.datePicker; - fixture.detectChanges(); - }); - - it('Retemplate dropdown date picker - dropdown mode', () => { - const dom = fixture.debugElement; - - const input = dom.query(By.css('.igx-input-group__input')); - expect(input).not.toBeNull(); - expect(input.nativeElement.value).toBe('10/20/2020'); - - const button = dom.query(By.css('.igx-button--flat')); - UIInteractions.simulateClickAndSelectEvent(button); - fixture.detectChanges(); - - const dropdown = document.getElementsByClassName('igx-date-picker--dropdown'); - expect(dropdown.length).toBe(1); - expect(dropdown[0]).not.toBeNull(); - }); - - it('Drop-down should open below the input by default if there is enough space - retemplated dropdown mode', () => { - const dom = fixture.debugElement; - - const input = dom.query(By.css('.igx-input-group__input')); - const inputRect = input.nativeElement.getBoundingClientRect() as DOMRect; - const inputTop = inputRect.top; - - const button = dom.query(By.css('.igx-button--flat')); - UIInteractions.simulateClickAndSelectEvent(button); - fixture.detectChanges(); - - const calendar = document.getElementsByTagName('igx-calendar-container'); - const calendarRect = calendar[0].getBoundingClientRect() as DOMRect; - const calendarTop = calendarRect.top; - - expect(inputTop).toBeLessThan(calendarTop); - }); - - it('Drop-down should open above the input when there is no enough space below - retemplated dropdown mode', () => { - const dom = fixture.debugElement; - - // check if drop down is opened above the input if there is no space below - datePicker.element.nativeElement.style = 'position: fixed; bottom: 150px'; - fixture.detectChanges(); - - const input = dom.query(By.css('.igx-input-group__input')); - const inputRect = input.nativeElement.getBoundingClientRect() as DOMRect; - const inputTop = inputRect.top; - - const button = dom.query(By.css('.igx-button--flat')); - UIInteractions.simulateClickAndSelectEvent(button); - fixture.detectChanges(); - - const calendar = document.getElementsByTagName('igx-calendar-container'); - const calendarRect = calendar[0].getBoundingClientRect() as DOMRect; - const calendarTop = calendarRect.top; - - expect(inputTop).toBeGreaterThan(calendarTop); - }); + let mockElement: any; + let mockElementRef: any; + let mockFactoryResolver: any; + let mockApplicationRef: any; + let mockAnimationBuilder: any; + let mockDocument: any; + let mockNgZone: any; + let mockPlatformUtil: any; + let overlay: IgxOverlayService | any; + let mockInjector; + let ngModuleRef: any; + const elementRef = { nativeElement: null }; + const mockNgControl = jasmine.createSpyObj('NgControl', + ['registerOnChangeCb', + 'registerOnTouchedCb', + 'registerOnValidatorChangeCb']); + beforeEach(() => { + mockFactoryResolver = { + resolveComponentFactory: (c: any) => ({ // eslint-disable-line + create: (i: any) => ({ // eslint-disable-line + hostView: '', + location: mockElementRef, + changeDetectorRef: { detectChanges: () => { } }, + destroy: () => { } + }) + }) + }; + ngModuleRef = ({ // eslint-disable-line + injector: (...args: any[]) => { }, // eslint-disable-line + componentFactoryResolver: mockFactoryResolver, + instance: () => { }, + destroy: () => { }, + onDestroy: (fn: any) => { } // eslint-disable-line + }); + mockElement = { + style: { visibility: '', cursor: '', transitionDuration: '' }, + classList: { add: () => { }, remove: () => { } }, + appendChild: () => { }, + removeChild: () => { }, + addEventListener: (type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) => { }, // eslint-disable-line + removeEventListener: (type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) => { }, // eslint-disable-line + getBoundingClientRect: () => ({ width: 10, height: 10 }), + insertBefore: (newChild: HTMLDivElement, refChild: Node) => { }, // eslint-disable-line + contains: () => { } + }; + mockElement.parent = mockElement; + mockElement.parentElement = mockElement; + mockElementRef = { nativeElement: mockElement }; + mockApplicationRef = { attachView: (h: any) => { }, detachView: (h: any) => { } }; // eslint-disable-line + mockInjector = jasmine.createSpyObj('Injector', { + get: mockNgControl + }); + mockAnimationBuilder = { + build: (a: AnimationMetadata | AnimationMetadata[]) => ({ // eslint-disable-line + create: (e: any, opt?: AnimationOptions) => ({ // eslint-disable-line + onDone: (fn: any) => { }, // eslint-disable-line + onStart: (fn: any) => { }, // eslint-disable-line + onDestroy: (fn: any) => { }, // eslint-disable-line + init: () => { }, + hasStarted: () => true, + play: () => { }, + pause: () => { }, + restart: () => { }, + finish: () => { }, + destroy: () => { }, + rest: () => { }, + setPosition: (p: any) => { }, // eslint-disable-line + getPosition: () => 0, + parentPlayer: {}, + totalTime: 0, + beforeDestroy: () => { } + }) + }) + }; + mockDocument = { + body: mockElement, + defaultView: mockElement, + createElement: () => mockElement, + appendChild: () => { }, + addEventListener: (type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) => { }, // eslint-disable-line + removeEventListener: (type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) => { } // eslint-disable-line + }; + mockNgZone = {}; + mockPlatformUtil = { isIOS: false }; + + overlay = new IgxOverlayService( + mockFactoryResolver, mockApplicationRef, mockInjector, mockAnimationBuilder, mockDocument, mockNgZone, mockPlatformUtil); }); - describe('Drop-down mode', () => { - // configureTestSuite(); - let fixture: ComponentFixture; - let datePicker: IgxDatePickerComponent; - - beforeEach(() => { - fixture = TestBed.createComponent(IgxDatePickerEditableComponent); - datePicker = fixture.componentInstance.datePicker; - fixture.detectChanges(); - }); - - it('Editable Datepicker open/close event - dropdown mode', async () => { - const dom = fixture.debugElement; - const iconDate = dom.query(By.css('.igx-icon')); - expect(iconDate).not.toBeNull(); - - spyOn(datePicker.onOpened, 'emit'); - spyOn(datePicker.onClosed, 'emit'); - - UIInteractions.simulateClickAndSelectEvent(iconDate); - fixture.detectChanges(); - await wait(); - - expect(datePicker.onOpened.emit).toHaveBeenCalled(); - expect(datePicker.onOpened.emit).toHaveBeenCalledWith(datePicker); - - const dropDown = document.getElementsByClassName('igx-date-picker--dropdown'); - expect(dropDown.length).toBe(1); - expect(dropDown[0]).not.toBeNull(); - - dom.nativeElement.dispatchEvent(new Event('click')); - - fixture.detectChanges(); - await wait(); - - expect(datePicker.onClosed.emit).toHaveBeenCalled(); - expect(datePicker.onClosed.emit).toHaveBeenCalledWith(datePicker); - }); - - it('Handling keyboard navigation with `space`(open) and `esc`(close) buttons - dropdown mode', fakeAsync(() => { - const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker')); - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - fixture.detectChanges(); - - const overlayDiv = document.getElementsByClassName('igx-overlay__content')[0]; - expect(overlayDiv).toBeDefined(); - expect(overlayDiv.classList.contains('igx-overlay__content')).toBeTruthy(); - - const dropDown = document.getElementsByClassName('igx-date-picker--dropdown'); - expect(dropDown.length).toBe(1); - expect(dropDown[0]).not.toBeNull(); - - UIInteractions.triggerKeyDownEvtUponElem('Escape', dropDown[0], false); - flush(); - fixture.detectChanges(); - - const overlays = document.getElementsByClassName('igx-overlay__content'); - expect(overlays.length).toEqual(0); - })); - - it('Open/close drop-down with `alt + down`(open) and `alt + up`(close) - dropdown mode', fakeAsync(() => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - input.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', altKey: true })); - tick(); - fixture.detectChanges(); - - const overlayDiv = document.getElementsByClassName('igx-overlay__content')[0]; - expect(overlayDiv).toBeDefined(); - expect(overlayDiv.classList.contains('igx-overlay__content')).toBeTruthy(); - - const dropDown = document.getElementsByClassName('igx-date-picker--dropdown'); - expect(dropDown.length).toBe(1); - expect(dropDown[0]).not.toBeNull(); - - dropDown[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', altKey: true })); - flush(); - fixture.detectChanges(); - - const overlays = document.getElementsByClassName('igx-overlay__content'); - expect(overlays.length).toEqual(0); - })); - - it('Datepicker onSelection and valueChange events and selectDate method propagation - dropdown mode', () => { - spyOn(datePicker.onSelection, 'emit'); - spyOn(datePicker.valueChange, 'emit'); - const newDate: Date = new Date(2016, 4, 6); - datePicker.selectDate(newDate); - fixture.detectChanges(); - - expect(datePicker.onSelection.emit).toHaveBeenCalled(); - expect(datePicker.valueChange.emit).toHaveBeenCalled(); - expect(datePicker.valueChange.emit).toHaveBeenCalledWith(newDate); - expect(datePicker.value).toBe(newDate); - - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - expect(input).toBeDefined(); - expect(input.nativeElement.value).toBe('06.05.2016'); - }); - - it('should open the dropdown when click on the date icon - dropdown mode', (() => { - const dom = fixture.debugElement; - fixture.detectChanges(); - - const iconDate = dom.query(By.css('.igx-icon')); - expect(iconDate).toBeDefined(); - - UIInteractions.simulateClickAndSelectEvent(iconDate); - fixture.detectChanges(); - - const dropDown = dom.query(By.css('.igx-date-picker--dropdown')); - expect(dropDown).toBeDefined(); - })); - - it('should have correctly selected date - dropdown mode', (() => { - const dom = fixture.debugElement; - fixture.detectChanges(); - - const iconDate = dom.query(By.css('.igx-icon')); - expect(iconDate).toBeDefined(); - - UIInteractions.simulateClickAndSelectEvent(iconDate); - fixture.detectChanges(); - - const dropDown = dom.query(By.css('.igx-date-picker--dropdown')); - expect(dropDown).toBeDefined(); - - const selectedSpans = document.getElementsByClassName('igx-calendar__date--selected'); - expect(selectedSpans.length).toBe(1); - expect((selectedSpans[0] as HTMLElement).innerText.trim()).toBe('20'); - - const dateHeader = document.getElementsByClassName('igx-calendar-picker__date'); - expect(dateHeader.length).toBe(2); - const month = dateHeader[0].innerHTML.trim(); - const year = dateHeader[1].innerHTML.trim(); - expect(month).toBe('Oct'); - expect(year).toBe('2011'); - })); - - it('should be able to apply display format - dropdown mode', async () => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - expect(input).toBeDefined(); - - input.nativeElement.dispatchEvent(new Event('focus')); - fixture.detectChanges(); - - input.nativeElement.dispatchEvent(new Event('blur')); - fixture.detectChanges(); - await wait(); - - expect(input.nativeElement.value).toBe('20.10.2011'); - }); - - it('should be able to apply editor mask - dropdown mode', (() => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - input.nativeElement.dispatchEvent(new Event('focus')); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('20-10-11'); - - // Check for formatted empty value on blur - placeholder is displayed - datePicker.deselectDate(); - fixture.detectChanges(); - - input.nativeElement.dispatchEvent(new Event('focus')); - fixture.detectChanges(); - - input.nativeElement.dispatchEvent(new Event('blur')); - fixture.detectChanges(); - - expect(input.nativeNode.placeholder).toBe('dd-MM-yy'); - })); - - it('Should be able to deselect using the API - dropdown mode', () => { - const date = new Date(Date.now()); - datePicker.selectDate(date); - fixture.detectChanges(); - - expect(datePicker.value).toBe(date); - - datePicker.deselectDate(); - fixture.detectChanges(); - - expect(datePicker.value).toBe(null); - }); - - it('should increase date parts using arrows - dropdown mode', fakeAsync(() => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - expect(input).toBeDefined(); - input.nativeElement.dispatchEvent(new Event('focus')); - fixture.detectChanges(); - expect(input.nativeElement.value).toBe('20-10-11'); - - // initial input value is 20-10-11 / dd-MM-yy - // focus the day part, position the caret at the beginning - input.nativeElement.setSelectionRange(0, 0); - - // press arrow up - UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', input.nativeElement, false); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('21-10-11', 'ArrowUp on day failed'); - - // test month part - // position caret at the month part - input.nativeElement.setSelectionRange(3, 3); - UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', input.nativeElement, false); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('21-11-11', 'ArrowUp on month failed'); - - // test year part - // position caret at the year part - input.nativeElement.setSelectionRange(7, 7); - UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', input.nativeElement, false); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('21-11-12', 'ArrowUp on year failed'); - - input.nativeElement.dispatchEvent(new Event('blur')); - fixture.detectChanges(); - tick(100); - - // format dd.MM.y - expect(input.nativeElement.value).toBe('21.11.2012'); - })); - - it('should decrease date parts using arrows - dropdown mode', fakeAsync(() => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - expect(input).toBeDefined(); - input.nativeElement.dispatchEvent(new Event('focus')); - fixture.detectChanges(); - expect(input.nativeElement.value).toBe('20-10-11'); - - // initial input value is 20-10-11 / dd-MM-yy - // focus the day part, position the caret at the beginning - input.nativeElement.setSelectionRange(0, 0); - - // press arrow down - UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', input.nativeElement, false); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('19-10-11', 'ArrowDown on day failed'); - - // press arrow down - UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', input.nativeElement, false); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('18-10-11', 'ArrowDown on day failed on the second try'); - - // test month part - // position caret at the month part - input.nativeElement.setSelectionRange(3, 3); - UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', input.nativeElement, false); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('18-09-11', 'ArrowDown on month failed'); - - // test year part - // position caret at the year part - input.nativeElement.setSelectionRange(7, 7); - UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', input.nativeElement, false); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('18-09-10', 'ArrowDown on year failed'); - - input.nativeElement.dispatchEvent(new Event('blur')); - fixture.detectChanges(); - tick(100); - - // format dd.MM.y - expect(input.nativeElement.value).toBe('18.09.2010'); - })); - - it('should increase/decrease date parts using mouse wheel - dropdown mode', fakeAsync(() => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - expect(input).toBeDefined(); - input.nativeElement.dispatchEvent(new Event('focus')); - fixture.detectChanges(); - expect(input.nativeElement.value).toBe('20-10-11'); - - // initial input value is 20-10-11 / dd-MM-yy - // focus the day part, position the caret at the beginning - input.nativeElement.setSelectionRange(0, 0); - - // up - UIInteractions.simulateWheelEvent(input.nativeElement, 0, -100); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('21-10-11', 'MouseWheel Up on day failed.'); - - input.nativeElement.setSelectionRange(3, 3); - // down - UIInteractions.simulateWheelEvent(input.nativeElement, 0, 100); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('21-09-11', 'MouseWheel down on month part failed.'); - - // test year part - // position caret at the year part - input.nativeElement.setSelectionRange(7, 7); - UIInteractions.simulateWheelEvent(input.nativeElement, 0, -100); - tick(100); - fixture.detectChanges(); - - UIInteractions.simulateWheelEvent(input.nativeElement, 0, -100); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('21-09-13', 'MouseWheel Up on year failed'); - - input.nativeElement.dispatchEvent(new Event('blur')); - fixture.detectChanges(); - tick(100); - - // format dd.MM.y - expect(input.nativeElement.value).toBe('21.09.2013'); - })); - - it('should reset value on clear button click - dropdown mode', () => { - spyOn(datePicker.valueChange, 'emit'); - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - const dom = fixture.debugElement; - const clear = dom.queryAll(By.css('.igx-icon'))[1]; - UIInteractions.simulateClickAndSelectEvent(clear); - fixture.detectChanges(); - - expect(datePicker.valueChange.emit).toHaveBeenCalled(); - expect(datePicker.valueChange.emit).toHaveBeenCalledWith(null); - expect(datePicker.value).toEqual(null); - expect(input.nativeElement.innerText).toEqual(''); - - input.nativeElement.dispatchEvent(new Event('blur')); - fixture.detectChanges(); - - expect(input.nativeElement.placeholder).toBe('dd-MM-yy'); - }); - - it('should emit onValidationFailed event when entered invalid date - dropdown mode', () => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - spyOn(datePicker.onValidationFailed, 'emit'); - - input.nativeElement.dispatchEvent(new Event('focus')); - fixture.detectChanges(); - expect(input.nativeElement.value).toBe('20-10-11'); - - UIInteractions.clickAndSendInputElementValue(input, '28-02-19'); - fixture.detectChanges(); - - UIInteractions.clickAndSendInputElementValue(input, '29-02-19'); - fixture.detectChanges(); - - // invalid date - expect(input.nativeElement.value).toBe('29-02-19'); - expect(datePicker.onValidationFailed.emit).toHaveBeenCalledTimes(1); - }); - - it('should emit onDisabledDate event when entered disabled date - dropdown mode', fakeAsync(() => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - spyOn(datePicker.onDisabledDate, 'emit'); - - datePicker.disabledDates = [{ - type: DateRangeType.Between, dateRange: [ - new Date(2018, 8, 2), - new Date(2018, 8, 8) - ] - }]; - - input.nativeElement.dispatchEvent(new Event('focus')); - fixture.detectChanges(); - expect(input.nativeElement.value).toBe('20-10-11'); - - UIInteractions.clickAndSendInputElementValue(input, '03-05-19'); - fixture.detectChanges(); - - // disabled date - UIInteractions.clickAndSendInputElementValue(input, '03-09-18'); - fixture.detectChanges(); - expect(input.nativeElement.value).toBe('03-09-18'); - - UIInteractions.clickAndSendInputElementValue(input, '07-09-18'); - fixture.detectChanges(); - expect(input.nativeElement.value).toBe('07-09-18'); - - expect(datePicker.onDisabledDate.emit).toHaveBeenCalledTimes(2); - })); - - it('should stop spinning on max/min when isSpinLoop is set to false - dropdown mode', fakeAsync(() => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - expect(input).toBeDefined(); - datePicker.isSpinLoop = false; - - input.triggerEventHandler('focus', {}); - fixture.detectChanges(); // bound transformedDate assign - UIInteractions.clickAndSendInputElementValue(input, '31-03-19'); - expect(input.nativeElement.value).toBe('31-03-19'); - - input.nativeElement.focus(); - input.nativeElement.setSelectionRange(0, 0); - - // check max day - UIInteractions.simulateWheelEvent(input.nativeElement, 0, -100); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('31-03-19'); - - input.nativeElement.focus(); - UIInteractions.clickAndSendInputElementValue(input, '01-03-19'); - expect(input.nativeElement.value).toBe('01-03-19'); - - input.nativeElement.focus(); - input.nativeElement.setSelectionRange(0, 0); - - // check min day - UIInteractions.simulateWheelEvent(input.nativeElement, 0, 100); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('01-03-19'); - - // check min month - input.nativeElement.focus(); - UIInteractions.clickAndSendInputElementValue(input, '15-01-19'); - expect(input.nativeElement.value).toBe('15-01-19'); - - input.nativeElement.setSelectionRange(3, 3); - UIInteractions.simulateWheelEvent(input.nativeElement, 0, 100); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('15-01-19'); - - // check max month - input.nativeElement.focus(); - UIInteractions.clickAndSendInputElementValue(input, '31-12-19'); - expect(input.nativeElement.value).toBe('31-12-19'); - - input.nativeElement.setSelectionRange(3, 3); - UIInteractions.simulateWheelEvent(input.nativeElement, 0, -100); - tick(100); - fixture.detectChanges(); - - expect(input.nativeElement.value).toBe('31-12-19'); - - input.nativeElement.dispatchEvent(new Event('blur')); - fixture.detectChanges(); - tick(100); - - // format dd.MM.y - expect(input.nativeElement.value).toBe('31.12.2019'); - })); - - it('check disabled state - dropdown mode', () => { - const dom = fixture.debugElement; - const inputGroup = dom.query(By.css('.igx-input-group')); - const disabled = dom.query(By.css('.igx-input-group--disabled')); - expect(disabled).toBeNull(); - expect(inputGroup.nativeElement.classList.contains('igx-input-group--disabled')).toBeFalsy(); - - datePicker.disabled = true; - fixture.detectChanges(); - - const disabledGroup = dom.query(By.css('.igx-input-group--disabled')); - expect(disabledGroup).toBeDefined(); - expect(inputGroup.nativeElement.classList.contains('igx-input-group--disabled')).toBeTruthy(); - }); - }); - - describe('EditorProvider', () => { - it('Should return correct edit element', () => { - const fixture = TestBed.createComponent(IgxDatePickerTestComponent); - fixture.detectChanges(); - - const instance = fixture.componentInstance.datePicker; - const editElement = fixture.debugElement.query(By.css('.igx-date-picker__input-date')).nativeElement; - - expect(instance.getEditElement()).toBe(editElement); - }); - }); - - describe('Drop-down mode select all text on focus', () => { - let fixture: ComponentFixture; - let datePicker: IgxDatePickerComponent; - - beforeEach(() => { - fixture = TestBed.createComponent(IgxDatePickerEditableComponent); - datePicker = fixture.componentInstance.datePicker; - fixture.detectChanges(); - }); - - it('Should select all input text on input focus', fakeAsync(() => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; - input.focus(); - fixture.detectChanges(); - tick(100); - - expect(input).toEqual(document.activeElement); - expect(input.selectionEnd).toEqual(input.value.length); - expect(input.selectionStart).toEqual(0); - expect(input.value.substring(input.selectionStart, input.selectionEnd)).toEqual(input.value); - })); - }); - - describe('Reactive form', () => { - let fixture: ComponentFixture; - let datePickerOnChangeComponent: IgxDatePickerComponent; - let datePickerOnBlurComponent: IgxDatePickerComponent; - let datePickerTemplateIGComponent: IgxDatePickerComponent; - - beforeEach(() => { - fixture = TestBed.createComponent(IgxDatePickerReactiveFormComponent); - datePickerOnChangeComponent = fixture.componentInstance.datePickerOnChangeComponent; - datePickerOnBlurComponent = fixture.componentInstance.datePickerOnBlurComponent; - datePickerTemplateIGComponent = fixture.componentInstance.datePickerTemplateIGComponent; - fixture.detectChanges(); - }); - - it('Should set date picker status to invalid when it is required and has no value', fakeAsync(() => { - const inputGroupsElements = fixture.debugElement.queryAll(By.directive(IgxInputDirective)); - const inputGroupElement = inputGroupsElements.find(d => d.componentInstance === datePickerOnChangeComponent); - const inputDirective = inputGroupElement.injector.get(IgxInputDirective) as IgxInputDirective; - - expect(inputDirective.valid).toEqual(IgxInputState.INITIAL); - - datePickerOnChangeComponent.value = null; - fixture.detectChanges(); - - expect(inputDirective.valid).toEqual(IgxInputState.INVALID); - })); - - it('Should set date picker status to invalid when it is required and has no value onBlur', fakeAsync(() => { - datePickerOnBlurComponent.mode = InteractionMode.DropDown; - datePickerOnBlurComponent.mask = 'dd/mm/yyyy'; - datePickerOnBlurComponent.inputMask = 'dd/mm/yyyy'; - fixture.detectChanges(); - - const inputDirectiveElements = fixture.debugElement.queryAll(By.directive(IgxInputDirective)); - const inputDirectiveElement = inputDirectiveElements.find(d => d.componentInstance === datePickerOnBlurComponent); - const inputDirective = inputDirectiveElement.injector.get(IgxInputDirective) as IgxInputDirective; - - expect(inputDirective.valid).toEqual(IgxInputState.INITIAL); - - inputDirectiveElement.triggerEventHandler('focus', {}); - fixture.detectChanges(); - - expect(inputDirective.valid).toEqual(IgxInputState.INITIAL); - - datePickerOnBlurComponent.value = null; - fixture.detectChanges(); - expect(inputDirective.valid).toEqual(IgxInputState.INITIAL); - - inputDirectiveElement.triggerEventHandler('blur', { target: { value: '' } }); - fixture.detectChanges(); - - expect(inputDirective.valid).toEqual(IgxInputState.INVALID); - })); - - it('Should set date picker status to invalid when date is disabled', fakeAsync(() => { - datePickerOnChangeComponent.disabledDates = [{ type: DateRangeType.Before, dateRange: [new Date()] }]; - const inputGroupsElements = fixture.debugElement.queryAll(By.directive(IgxInputDirective)); - const inputGroupElement = inputGroupsElements.find(d => d.componentInstance === datePickerOnChangeComponent); - const inputDirective = inputGroupElement.injector.get(IgxInputDirective) as IgxInputDirective; - - const today = new Date(); - datePickerOnChangeComponent.value = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1); - fixture.detectChanges(); - expect(inputDirective.valid).toEqual(IgxInputState.INVALID); - - datePickerOnChangeComponent.value = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1); - fixture.detectChanges(); - expect(inputDirective.valid).toEqual(IgxInputState.INITIAL); - - datePickerOnChangeComponent.disabledDates = [{ - type: DateRangeType.Before, - dateRange: [new Date(today.getFullYear(), today.getMonth(), today.getDate() + 2)] - }]; - fixture.detectChanges(); - expect(inputDirective.valid).toEqual(IgxInputState.INVALID); - })); - - it('Should set date picker status to invalid if date is included in disabledDates range and user pass a template', fakeAsync(() => { - datePickerTemplateIGComponent.disabledDates = [{ type: DateRangeType.Before, dateRange: [new Date()] }]; - const inputDirective = datePickerTemplateIGComponent.inputDirective; - - const today = new Date(); - datePickerTemplateIGComponent.value = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1); - fixture.detectChanges(); - expect(inputDirective.valid).toEqual(IgxInputState.INVALID); - - datePickerTemplateIGComponent.value = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1); - fixture.detectChanges(); - expect(inputDirective.valid).toEqual(IgxInputState.INITIAL); - - datePickerTemplateIGComponent.disabledDates = [{ - type: DateRangeType.Before, - dateRange: [new Date(today.getFullYear(), today.getMonth(), today.getDate() + 2)] - }]; - fixture.detectChanges(); - expect(inputDirective.valid).toEqual(IgxInputState.INVALID); - })); - - it('Should set date picker status to invalid on blur when pass or change a template', fakeAsync(() => { - datePickerTemplateIGComponent.disabledDates = [{ type: DateRangeType.Before, dateRange: [new Date()] }]; - const templateInputDirective = datePickerTemplateIGComponent.inputDirective; - const templateInput = templateInputDirective.nativeElement; - templateInput.dispatchEvent(new Event('blur')); - fixture.detectChanges(); - expect(templateInputDirective.valid).toEqual(IgxInputState.INVALID); - - fixture.componentInstance.useCustomTemplate = false; - fixture.detectChanges(); - // obtain the default template input directive & input - const inputDirective = datePickerTemplateIGComponent.inputDirective; - const input = inputDirective.nativeElement; - expect(inputDirective.valid).toEqual(IgxInputState.INITIAL); - - input.dispatchEvent(new Event('blur')); - fixture.detectChanges(); - expect(inputDirective.valid).toEqual(IgxInputState.INVALID); - })); - - // Bug #6025 Date picker does not disable in reactive form - it('Should disable when form is disabled', () => { - const formGroup: FormGroup = fixture.componentInstance.reactiveForm; - const inputGroupsElements = fixture.debugElement.queryAll(By.directive(IgxInputDirective)); - const inputGroupElement = inputGroupsElements.find(d => d.componentInstance === datePickerOnBlurComponent); - const inputDirective = inputGroupElement.injector.get(IgxInputDirective) as IgxInputDirective; - expect(inputDirective.disabled).toBeFalsy(); - - formGroup.disable(); - fixture.detectChanges(); - expect(inputDirective.disabled).toBeTruthy(); - }); + afterEach(() => { + UIInteractions.clearOverlay(); }); - describe('Control value accessor unit tests', () => { + describe('Unit Tests', () => { + pending('WIP'); let ngModel; - let overlay; let element; let cdr; let moduleRef; let injector; let inputGroup: IgxInputGroupComponent; let renderer2: Renderer2; + describe('API tests', () => { + //#region API Methods + it('should properly update the collapsed state with open/close/toggle methods', () => { + const datePicker = new IgxDatePickerComponent(elementRef, null, overlay, null, mockInjector, null, null); - beforeEach(() => { - ngModel = { - control: { touched: false, dirty: false, validator: null }, - valid: false, - statusChanges: new EventEmitter(), - }; - overlay = { - onOpening: new EventEmitter(), - onOpened: new EventEmitter(), - onClosed: new EventEmitter(), - onClosing: new EventEmitter() - }; - element = {}; - cdr = { - markForCheck: () => { }, - detectChanges: () => { }, - detach: () => { }, - reattach: () => { } - }; - moduleRef = {}; - injector = { get: () => ngModel }; - inputGroup = new IgxInputGroupComponent(null, null, null, document, renderer2); - renderer2 = jasmine.createSpyObj('Renderer2', ['setAttribute'], [{}, 'aria-labelledby', 'test-label-id-1']); - spyOn(renderer2, 'setAttribute').and.callFake(() => { - }); - }); + datePicker.open(); + expect(datePicker.collapsed).toBeFalse(); - it('should initialize date picker with required correctly', () => { - const datePicker = new IgxDatePickerComponent(overlay, element, cdr, moduleRef, injector, renderer2); - datePicker['_inputGroup'] = inputGroup; - datePicker['_inputDirectiveUserTemplates'] = new QueryList(); - spyOnProperty(datePicker, 'inputGroupElement').and.returnValue(null); - ngModel.control.validator = () => ({ required: true }); - datePicker.ngOnInit(); - datePicker.ngAfterViewInit(); - datePicker.ngAfterViewChecked(); + datePicker.close(); + expect(datePicker.collapsed).toBeTrue(); - expect(datePicker).toBeDefined(); - expect(inputGroup.isRequired).toBeTruthy(); - }); + datePicker.toggle(); + expect(datePicker.collapsed).toBeFalse(); - it('should initialize date picker with required correctly with user template input-group', () => { - const datePicker = new IgxDatePickerComponent(overlay, element, cdr, moduleRef, injector, renderer2); - datePicker['_inputGroupUserTemplate'] = inputGroup; - datePicker['_inputDirectiveUserTemplates'] = new QueryList(); - spyOnProperty(datePicker, 'inputGroupElement').and.returnValue(null); - ngModel.control.validator = () => ({ required: true }); - datePicker.ngOnInit(); - datePicker.ngAfterViewInit(); - datePicker.ngAfterViewChecked(); + datePicker.toggle(); + expect(datePicker.collapsed).toBeTrue(); + }); - expect(datePicker).toBeDefined(); - expect(inputGroup.isRequired).toBeTruthy(); - }); + it('should update the picker\'s value with the select method', () => { + const datePicker = new IgxDatePickerComponent(elementRef, null, overlay, null, mockInjector, null, null); + spyOn(datePicker.valueChange, 'emit'); - it('should update inputGroup isRequired correctly', () => { - const datePicker = new IgxDatePickerComponent(overlay, element, cdr, moduleRef, injector, renderer2); - datePicker['_inputGroup'] = inputGroup; - datePicker['_inputDirectiveUserTemplates'] = new QueryList(); - spyOnProperty(datePicker, 'inputGroupElement').and.returnValue(null); - datePicker.ngOnInit(); - datePicker.ngAfterViewInit(); - datePicker.ngAfterViewChecked(); + datePicker.value = new Date(); - expect(datePicker).toBeDefined(); - expect(inputGroup.isRequired).toBeFalsy(); + const newDate = new Date(2012, 10, 5); + datePicker.select(newDate); + expect(datePicker.valueChange).toHaveBeenCalledOnceWith(newDate); + expect(datePicker.value).toEqual(newDate); + }); - ngModel.control.validator = () => ({ required: true }); - ngModel.statusChanges.emit(); - expect(inputGroup.isRequired).toBeTruthy(); + it('should clear the picker\'s value with the clear method', () => { + const datePicker = new IgxDatePickerComponent(elementRef, null, overlay, null, mockInjector, null, null); + spyOn(datePicker.valueChange, 'emit'); - ngModel.control.validator = () => ({ required: false }); - ngModel.statusChanges.emit(); - expect(inputGroup.isRequired).toBeFalsy(); - }); - }); -}); - -@Component({ - template: ` - - ` -}) -export class IgxDatePickerWithCustomFormatterComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public datePicker: IgxDatePickerComponent; + datePicker.value = new Date(); - public date = new Date(2017, 7, 7); - public customFormatter = (_: Date) => ( - `${_.getFullYear()}/${_.getMonth()}/${_.getDate()}` - ); -} + datePicker.clear(); + expect(datePicker.valueChange).toHaveBeenCalledOnceWith(null); + expect(datePicker.value).toEqual(null); + }); -@Component({ - template: ` - - ` -}) -export class IgxDatePickerWithWeekStartComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public datePicker: IgxDatePickerComponent; - public date: Date = new Date(2017, 6, 8); -} + //#endregion -@Component({ - template: ` - - - - ` -}) -export class IgxDatePickerProjectedLabelTestComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public datePicker: IgxDatePickerComponent; + //#region Events + it('should properly emit open/close events', () => { + const datePicker = new IgxDatePickerComponent(elementRef, null, overlay, null, mockInjector, null, null); + spyOn(datePicker.opened, 'emit'); + spyOn(datePicker.closed, 'emit'); - public customLabelVisibility = true; - public customLabel = 'Custom label'; - public labelVisibility = true; -} + datePicker.open(); + expect(datePicker.opened).toHaveBeenCalledOnceWith({ owner: datePicker }); -@Component({ - template: ` - - ` -}) -export class IgxDatePickerTestComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public datePicker: IgxDatePickerComponent; + datePicker.close(); + expect(datePicker.closed).toHaveBeenCalledOnceWith({ owner: datePicker }); + }); - public labelVisibility = true; -} -@Component({ - template: ` - - ` -}) -export class IgxDatePickerWithPassedDateComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public datePicker: IgxDatePickerComponent; - public date: Date = new Date(2017, 7, 7); - public formatOptions = { - day: 'numeric', - month: 'numeric', - weekday: 'short', - year: 'numeric' - }; -} + it('should properly emit opening/closing events', () => { + const datePicker = new IgxDatePickerComponent(elementRef, null, overlay, null, mockInjector, null, null); + spyOn(datePicker.opening, 'emit'); + spyOn(datePicker.closing, 'emit'); -@Component({ - template: ` - - ` -}) -export class IgxDatePickerWIthLocaleComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public datePicker: IgxDatePickerComponent; - public date: Date = new Date(2017, 7, 7); -} + datePicker.open(); + expect(datePicker.opening).toHaveBeenCalledOnceWith({ owner: datePicker, event: { cancel: false } }); -@Component({ - template: ` - - ` -}) -export class IgxDatePickerNgModelComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public datePicker: IgxDatePickerComponent; - public val: Date = new Date(2011, 11, 11); -} + datePicker.close(); + expect(datePicker.closing).toHaveBeenCalledOnceWith({ owner: datePicker, event: { cancel: false } }); + }); -@Component({ - template: ` - - - - - - - - - ` -}) -export class IgxDatePickerRetemplatedComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public datePicker: IgxDatePickerComponent; -} + it('should emit valueChange when the value changes', () => { + const datePicker = new IgxDatePickerComponent(elementRef, null, overlay, null, mockInjector, null, null); + spyOn(datePicker.valueChange, 'emit'); -@Component({ - template: ` - - - - - - - - - ` -}) -export class IgxDropDownDatePickerRetemplatedComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public datePicker: IgxDatePickerComponent; - public date: Date = new Date(2020, 9, 20); -} + const newDate = new Date(); + datePicker.value = newDate; + expect(datePicker.valueChange).toHaveBeenCalledOnceWith(newDate); + }); -@Component({ - template: ` - - ` -}) -export class IgxDatePickerEditableComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public datePicker: IgxDatePickerComponent; - public date: Date = new Date(2011, 9, 20); -} + it('should emit validationFailed if a value outside of a given range was provided', () => { + const datePicker = new IgxDatePickerComponent(elementRef, null, overlay, null, mockInjector, null, null); + spyOn(datePicker.validationFailed, 'emit'); -@Component({ - template: ` - - - {{ format.date | date:'shortDate'}} - - - {{ format.year.combined }}/ - {{ format.month.combined | titlecase }} - - - - - - ` -}) -export class IgxDatePickerCustomizedComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public customizedDatePicker: IgxDatePickerComponent; - public date: Date = new Date(2019, 9, 20); -} + const validDate = new Date(2012, 5, 7); + datePicker.minValue = new Date(2012, 5, 4); + datePicker.maxValue = new Date(2012, 5, 10); -@Component({ - template: - ` - - ` -}) -export class IgxDatePickerOpeningComponent { - @ViewChild(IgxDatePickerComponent, { static: true }) public datePicker: IgxDatePickerComponent; -} + datePicker.value = validDate; + datePicker.value = new Date(2012, 6, 1); + expect(datePicker.validationFailed).toHaveBeenCalledOnceWith({ owner: datePicker, prevValue: validDate }); + }); + //#endregion + }); + + describe('Control Value Accessor', () => { + beforeEach(() => { + ngModel = { + control: { touched: false, dirty: false, validator: null }, + valid: false, + statusChanges: new EventEmitter(), + }; + overlay = { + onOpening: new EventEmitter(), + onOpened: new EventEmitter(), + onClosed: new EventEmitter(), + onClosing: new EventEmitter() + }; + element = {}; // eslint-disable-line + cdr = { // eslint-disable-line + markForCheck: () => { }, + detectChanges: () => { }, + detach: () => { }, + reattach: () => { } + }; + moduleRef = {}; // eslint-disable-line + injector = { get: () => ngModel }; // eslint-disable-line + inputGroup = new IgxInputGroupComponent(null, null, null, document, renderer2); // eslint-disable-line + renderer2 = jasmine.createSpyObj('Renderer2', ['setAttribute'], [{}, 'aria-labelledby', 'test-label-id-1']); + spyOn(renderer2, 'setAttribute').and.callFake(() => { + }); + }); -@Component({ - template: ` -
- - - - - - - - - today - - - - -
-` -}) -class IgxDatePickerReactiveFormComponent { - @ViewChild('datePickerOnChangeComponent', { read: IgxDatePickerComponent, static: true }) - public datePickerOnChangeComponent: IgxDatePickerComponent; + it('should initialize date picker with required correctly', () => { + const datePicker = new IgxDatePickerComponent(overlay, element, cdr, moduleRef, injector, renderer2); + datePicker['_inputGroup'] = inputGroup; + datePicker['_inputDirectiveUserTemplates'] = new QueryList(); + spyOnProperty(datePicker as any, 'inputGroup').and.returnValue(null); + ngModel.control.validator = () => ({ required: true }); + datePicker.ngOnInit(); + datePicker.ngAfterViewInit(); + datePicker.ngAfterViewChecked(); + + expect(datePicker).toBeDefined(); + expect(inputGroup.isRequired).toBeTruthy(); + }); - @ViewChild('datePickerOnBlurComponent', { read: IgxDatePickerComponent, static: true }) - public datePickerOnBlurComponent: IgxDatePickerComponent; + it('should update inputGroup isRequired correctly', () => { + const datePicker = new IgxDatePickerComponent(overlay, element, cdr, moduleRef, injector, renderer2); + datePicker['_inputGroup'] = inputGroup; + datePicker['_inputDirectiveUserTemplates'] = new QueryList(); + spyOnProperty(datePicker as any, 'inputGroup').and.returnValue(null); + datePicker.ngOnInit(); + datePicker.ngAfterViewInit(); + datePicker.ngAfterViewChecked(); + + expect(datePicker).toBeDefined(); + expect(inputGroup.isRequired).toBeFalsy(); + + ngModel.control.validator = () => ({ required: true }); + ngModel.statusChanges.emit(); + expect(inputGroup.isRequired).toBeTruthy(); + + ngModel.control.validator = () => ({ required: false }); + ngModel.statusChanges.emit(); + expect(inputGroup.isRequired).toBeFalsy(); + }); - @ViewChild('datePickerTemplateIGComponent', { read: IgxDatePickerComponent, static: true }) - public datePickerTemplateIGComponent: IgxDatePickerComponent; + describe('Validator', () => { + pending('TODO'); + }); + }); + }); - reactiveForm: FormGroup; - public useCustomTemplate = true; - constructor(fb: FormBuilder) { - const date = new Date(2000, 10, 15); - this.reactiveForm = fb.group({ - datePickerOnChange: [date, Validators.required], - datePickerOnBlur: [date, { updateOn: 'blur', validators: Validators.required }], - datePickerIGTemplate: [date, Validators.required] + describe('Integration tests', () => { + describe('Events', () => { + it('should be able to cancel opening/closing', () => { + pending('TODO'); + }); }); - } -} -@Component({ - template: ` - - - -` -}) -class IgxDatePickerDropdownButtonsComponent { - @ViewChild('dropdownButtonsDatePicker', { read: IgxDatePickerComponent, static: true }) - public datePicker: IgxDatePickerComponent; + describe('Keyboard navigation', () => { + pending('TODO'); + }); - @ViewChild('dummyInput') public dummyInput: ElementRef; -} + describe('Projections', () => { + pending('TODO'); + }); + }); +}); diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts index 21e321960e1..91336b7a753 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts @@ -1,345 +1,226 @@ -import { CommonModule, formatDate } from '@angular/common'; import { - Component, - ContentChild, - EventEmitter, - HostBinding, - Input, - NgModule, - OnDestroy, - Output, - ViewChild, - ElementRef, - TemplateRef, - Inject, - ChangeDetectorRef, - HostListener, - NgModuleRef, - OnInit, - AfterViewInit, - Injector, - AfterViewChecked, - ContentChildren, - QueryList, - Renderer2 + Component, ContentChild, EventEmitter, HostBinding, Input, + OnDestroy, Output, ViewChild, ElementRef, Inject, HostListener, + NgModuleRef, OnInit, AfterViewInit, Injector, AfterViewChecked, ContentChildren, + QueryList, LOCALE_ID, Renderer2, Optional, PipeTransform } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl, AbstractControl, NG_VALIDATORS, ValidationErrors } from '@angular/forms'; import { - IgxCalendarComponent, - IgxCalendarHeaderTemplateDirective, - IgxCalendarModule, - IgxCalendarSubheaderTemplateDirective, - WEEKDAYS, - isDateInRanges + ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl, AbstractControl, + NG_VALIDATORS, ValidationErrors, Validator +} from '@angular/forms'; +import { + IgxCalendarComponent, IgxCalendarHeaderTemplateDirective, IgxCalendarSubheaderTemplateDirective, + WEEKDAYS, isDateInRanges, IFormattingViews, IFormattingOptions } from '../calendar/public_api'; -import { IgxIconModule } from '../icon/public_api'; import { - IgxInputGroupModule, - IgxInputDirective, - IgxInputGroupComponent, - IgxInputState, - IgxLabelDirective + IgxInputDirective, IgxInputGroupComponent, + IgxLabelDirective, IGX_INPUT_GROUP_TYPE, IgxInputGroupType, IgxInputState } from '../input-group/public_api'; -import { Subject, fromEvent, animationFrameScheduler, interval, Subscription, noop } from 'rxjs'; -import { filter, takeUntil, throttle } from 'rxjs/operators'; +import { Subject, fromEvent, Subscription, noop, MonoTypeOperatorFunction } from 'rxjs'; +import { filter, takeUntil } from 'rxjs/operators'; import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive'; -import { IgxTextSelectionModule } from '../directives/text-selection/text-selection.directive'; import { - OverlaySettings, - IgxOverlayService, - PositionSettings, - AbsoluteScrollStrategy, + OverlaySettings, IgxOverlayService, AbsoluteScrollStrategy, AutoPositionStrategy, - OverlayCancelableEventArgs + OverlayCancelableEventArgs, + OverlayEventArgs } from '../services/public_api'; -import { DateRangeDescriptor } from '../core/dates/dateRange'; -import { EditorProvider } from '../core/edit-provider'; -import { IgxButtonModule } from '../directives/button/button.directive'; -import { IgxRippleModule } from '../directives/ripple/ripple.directive'; -import { IgxMaskModule } from '../directives/mask/mask.directive'; -import { - DatePickerUtil, - DateState -} from './date-picker.utils'; -import { DatePickerDisplayValuePipe, DatePickerInputValuePipe } from './date-picker.pipes'; -import { IDatePicker } from './date-picker.common'; -import { KEYS, isIE, isEqual, IBaseEventArgs, mkenum, IBaseCancelableBrowserEventArgs } from '../core/utils'; -import { IgxDatePickerTemplateDirective } from './date-picker.directives'; -import { InteractionMode } from '../core/enums'; +import { DateRangeDescriptor, DateRangeType } from '../core/dates/dateRange'; +import { KEYS, isEqual, IBaseCancelableBrowserEventArgs, IBaseEventArgs, parseDate } from '../core/utils'; +import { IgxCalendarContainerComponent } from '../date-common/calendar-container/calendar-container.component'; import { fadeIn, fadeOut } from '../animations/fade'; +import { PickerBaseDirective } from '../date-common/picker-base.directive'; +import { DisplayDensityToken, IDisplayDensityOptions } from '../core/density'; +import { DatePart, DatePartDeltas, IgxDateTimeEditorDirective } from '../directives/date-time-editor/public_api'; import { DeprecateProperty } from '../core/deprecateDecorators'; -import { IgxCalendarContainerComponent, IgxCalendarContainerModule } from '../date-common/calendar-container/calendar-container.component'; -import { IgxPickerActionsDirective } from '../date-common/picker-icons.common'; +import { DateTimeUtil } from '../date-common/util/date-time.util'; +import { PickerHeaderOrientation as PickerHeaderOrientation } from '../date-common/types'; +import { + IDatePickerDisabledDateEventArgs, IDatePickerValidationFailedEventArgs +} from './date-picker.common'; +import { IgxPickerToggleComponent, IgxPickerClearComponent, IgxPickerActionsDirective } from '../date-common/public_api'; let NEXT_ID = 0; -/** - * This interface is used to provide information about date picker reference and its current value - * when onDisabledDate event is fired. - */ -export interface IDatePickerDisabledDateEventArgs extends IBaseEventArgs { - datePicker: IgxDatePickerComponent; - currentValue: Date; -} - -/** - * This interface is used to provide information about date picker reference and its previously valid value - * when onValidationFailed event is fired. - */ -export interface IDatePickerValidationFailedEventArgs extends IBaseEventArgs { - datePicker: IgxDatePickerComponent; - prevValue: Date; -} - -/** - * This interface is used to configure calendar format view options. - */ -export interface IFormatViews { - day?: boolean; - month?: boolean; - year?: boolean; -} - -/** - * This interface is used to configure calendar format options. - */ -export interface IFormatOptions { - day?: string; - month?: string; - weekday?: string; - year?: string; -} - -/** - * This enumeration is used to configure the date picker to operate with pre-defined format option used in Angular DatePipe. - * 'https://angular.io/api/common/DatePipe' - * 'shortDate': equivalent to 'M/d/yy' (6/15/15). - * 'mediumDate': equivalent to 'MMM d, y' (Jun 15, 2015). - * 'longDate': equivalent to 'MMMM d, y' (June 15, 2015). - * 'fullDate': equivalent to 'EEEE, MMMM d, y' (Monday, June 15, 2015). - */ -export const PredefinedFormatOptions = mkenum({ - ShortDate: 'shortDate', - MediumDate: 'mediumDate', - LongDate: 'longDate', - FullDate: 'fullDate' -}); -export type PredefinedFormatOptions = (typeof PredefinedFormatOptions)[keyof typeof PredefinedFormatOptions]; - /** * Date Picker displays a popup calendar that lets users select a single date. * * @igxModule IgxDatePickerModule * @igxTheme igx-calendar-theme, igx-icon-theme * @igxGroup Scheduling - * @igxKeywords datepicker, calendar, schedule, date + * @igxKeywords datepicker, calendar, schedule, date * @example * ```html * * ``` */ @Component({ - providers: - [{ - provide: NG_VALUE_ACCESSOR, - useExisting: IgxDatePickerComponent, - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: IgxDatePickerComponent, - multi: true - }], - // eslint-disable-next-line @angular-eslint/component-selector + providers: [ + { provide: NG_VALUE_ACCESSOR, useExisting: IgxDatePickerComponent, multi: true }, + { provide: NG_VALIDATORS, useExisting: IgxDatePickerComponent, multi: true } + ], selector: 'igx-date-picker', templateUrl: 'date-picker.component.html', - styles: [` - :host { - display: block; - } - `] + styles: [':host { display: block; }'] }) -export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor, - EditorProvider, OnInit, AfterViewInit, OnDestroy, AfterViewChecked { +export class IgxDatePickerComponent extends PickerBaseDirective implements ControlValueAccessor, Validator, + OnInit, AfterViewInit, OnDestroy, AfterViewChecked { /** - * Gets/Sets the `IgxDatePickerComponent` label. + * Gets/Sets on which day the week starts. * - * @remarks - * The default label is 'Date'. * @example * ```html - * + * * ``` - * @deprecated Use igxLabel inside the date picker to change the label: - * ````html - * - * - * - * ```` - * to set a custom label. */ - @DeprecateProperty(`Use igxLabel inside the date picker to change the label: - - - `) @Input() - public get label(): string { - return this._label; - } - - public set label(v: string) { - this._label = v; - } - - public get labelInternal() { - return this._label; - } - - /** @hidden @internal */ - public get labelTemplate(): IgxLabelDirective { - return this._labelDirectiveUserTemplate; - } + public weekStart: WEEKDAYS | number = WEEKDAYS.SUNDAY; /** - * Gets/Sets the `IgxDatePickerComponent` label visibility. + * Gets/Sets whether the inactive dates will be hidden. * * @remarks - * By default the visibility is set to true. + * Applies to dates that are out of the current month. + * Default value is `false`. + * @example + * ```html + * + * ``` * @example - * + * ```typescript + * let hideOutsideDays = this.datePicker.hideOutsideDays; + * ``` */ @Input() - public labelVisibility = true; + public hideOutsideDays: boolean; /** - * Gets/Sets the locales. + * Gets/Sets the number of month views displayed. + * + * @remarks + * Default value is `1`. * - * @remarks Default locale is en. * @example * ```html - * + * + * ``` + * @example + * ```typescript + * let monthViewsDisplayed = this.datePicker.displayMonthsCount; * ``` */ - @Input() public locale = 'en'; + @Input() + public displayMonthsCount = 1; /** - * Gets/Sets the default template editor's tabindex. + * Show/hide week numbers * * @example * ```html - * - * ``` + * + * `` */ - @Input() public editorTabIndex: number; + @Input() + public showWeekNumbers: boolean; /** - * Gets/Sets on which day the week starts. + * Gets/Sets a custom formatter function on the selected or passed date. * * @example * ```html - * + * * ``` */ - @Input() public weekStart: WEEKDAYS | number = WEEKDAYS.SUNDAY; + @DeprecateProperty('formatter has been deprecated, use displayFormat instead.') + @Input() + public formatter: (val: Date) => string; /** - * Gets the format options of the `IgxDatePickerComponent`. + * Gets/Sets the orientation of the `IgxDatePickerComponent` header. * - * @example - * ```typescript - * let formatOptions = this.datePicker.formatOptions; + * @example + * ```html + * * ``` */ @Input() - public get formatOptions(): IFormatOptions { - return this._formatOptions; - } + public headerOrientation: PickerHeaderOrientation = PickerHeaderOrientation.Horizontal; /** - * Sets the format options of the `IgxDatePickerComponent`. + * Gets/Sets the today button's label. * - * @example - * ```typescript - * this.datePicker.formatOptions = { day: "numeric", month: "long", weekday: "long", year: "numeric"}; + * @example + * ```html + * * ``` */ - public set formatOptions(formatOptions: IFormatOptions) { - this._formatOptions = Object.assign(this._formatOptions, formatOptions); - } + @Input() + public todayButtonLabel: string; /** - * Gets/Sets whether the inactive dates will be hidden. + * Gets/Sets the cancel button's label. * - * @remarks - * Apllies to dates that are out of the current month. - * Default value is `false`. * @example * ```html - * - * ``` - * @example - * ```typescript - * let hideOutsideDays = this.datePicker.hideOutsideDays; + * * ``` */ @Input() - public hideOutsideDays: boolean; + public cancelButtonLabel: string; /** - * Gets/Sets the number of month views displayed. + * Specify if the currently spun date segment should loop over. * - * @remarks - * Default value is `1`. - * @example + * @example * ```html - * - * ``` - * @example - * ```typescript - * let monthViewsDisplayed = this.datePicker.monthsViewNumber; + * * ``` */ @Input() - public monthsViewNumber = 1; + public spinLoop = true; /** - * Show/hide week numbers + * Delta values used to increment or decrement each editor date part on spin actions. + * All values default to `1`. * - * @exmpale + * @example * ```html - * - * `` + * + * ``` */ @Input() - public showWeekNumbers: boolean; + public spinDelta: Pick; /** - * Gets/Sets the date display format of the `IgxDatePickerComponent` in dropdown mode. + * Gets/Sets the container used for the popup element. * + * @remarks + * `outlet` is an instance of `IgxOverlayOutletDirective` or an `ElementRef`. * @example - * ```typescript - * let format = this.datePicker.format; - * this.datePicker.format = 'yyyy-M-d'; + * ```html + *
+ * //.. + * + * //.. * ``` */ @Input() - public get format(): string { - return (this._format === undefined) ? PredefinedFormatOptions.ShortDate : this._format; - } - public set format(format: string) { - this._format = format; - } + public outlet: IgxOverlayOutletDirective | ElementRef; /** - * Gets/Sets the date mask of the `IgxDatePickerComponent` when in editable dropdown mode. + * Gets/Sets the value of `id` attribute. * - * @example - * ```typescript - * let mask = this.datePicker.mask; + * @remarks If not provided it will be automatically generated. + * @example + * ```html + * * ``` */ @Input() - public mask: string; + @HostBinding('attr.id') + public id = `igx-date-picker-${NEXT_ID++}`; + + //#region calendar members /** * Gets/Sets the format views of the `IgxDatePickerComponent`. @@ -351,11 +232,11 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor * ``` */ @Input() - public get formatViews(): IFormatViews { + public get formatViews(): IFormattingViews { return this._formatViews; } - public set formatViews(formatViews: IFormatViews) { + public set formatViews(formatViews: IFormattingViews) { this._formatViews = Object.assign(this._formatViews, formatViews); } @@ -395,106 +276,31 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor } /** - * Gets/Sets the modal overlay settings. - */ - @Input() - public get modalOverlaySettings(): OverlaySettings { - return this._modalOverlay; - } - - public set modalOverlaySettings(value: OverlaySettings) { - this._modalOverlay = value; - } - - /** - * Gets/Sets the drop-down overlay settings. - */ - @Input() - public get dropDownOverlaySettings(): OverlaySettings { - return this._dropDownOverlaySettings || this._defaultDropDownOverlaySettings; - } - - public set dropDownOverlaySettings(value: OverlaySettings) { - this._dropDownOverlaySettings = value; - } - - /** - * Gets the formatted date when `IgxDatePickerComponent` is in dialog mode. + * Gets the format options of the `IgxDatePickerComponent`. * - * @example + * @example * ```typescript - * let selectedDate = this.datePicker.displayData; + * let formatOptions = this.datePicker.formatOptions; * ``` */ - public get displayData(): string { - if (this.value) { - return this._customFormatChecker(this.formatter, this.value); - } - return ''; - } - - /** @hidden @internal */ - public get transformedDate(): string { - if (this._value) { - this._transformedDate = (this._isInEditMode) ? this._getEditorDate(this._value) : this._getDisplayDate(this._value); - this.isEmpty = false; - } else { - this._transformedDate = (this._isInEditMode) ? DatePickerUtil.maskToPromptChars(this.inputMask) : ''; - } - return this._transformedDate; - } - - /** @hidden @internal */ - public set transformedDate(value) { - this._transformedDate = value; + @Input() + public get calendarFormat(): IFormattingOptions { + return this._calendarFormat; } - /** - * Gets the input group template. + * Sets the format options of the `IgxDatePickerComponent`. * * @example * ```typescript - * let template = this.template(); + * this.datePicker.formatOptions = {day: "numeric", month: "long", weekday: "long", year: "numeric"}; * ``` */ - get template(): TemplateRef { - if (this.datePickerTemplateDirective) { - return this.datePickerTemplateDirective.template; - } - return (this.mode === InteractionMode.Dialog) ? this.readOnlyDatePickerTemplate : this.editableDatePickerTemplate; - } - - /** - * Gets the context passed to the input group template. - */ - get context() { - return { - disabled: this.disabled, - disabledDates: this.disabledDates, - displayData: this.displayData, - format: this.format, - isSpinLoop: this.isSpinLoop, - labelVisibility: this.labelVisibility, - locale: this.locale, - mask: this.mask, - mode: this.mode, - specialDates: this.specialDates, - value: this.value, - openDialog: () => this.openDialog() - }; - } - - private get required(): boolean { - if (this._ngControl && this._ngControl.control && this._ngControl.control.validator) { - // Run the validation with empty object to check if required is enabled. - const error = this._ngControl.control.validator({} as AbstractControl); - return error && error.required; - } - - return false; + public set calendarFormat(options: IFormattingOptions) { + this._calendarFormat = Object.assign(this._calendarFormat, options); } + //#endregion /** * Gets/Sets the selected date. @@ -508,993 +314,648 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor public get value(): Date { return this._value; } - public set value(date: Date) { + const oldValue = this._value; this._value = date; + if (this.dateTimeEditor.value !== date) { + this.dateTimeEditor.value = date; + } + this.emitValueChange(oldValue, this.value); this._onChangeCallback(date); } /** - * Gets/Sets the value of `id` attribute. + * The minimum value the picker will accept. * - * @remarks If not provided it will be automatically generated. * @example - * ```html - * - * ``` + * */ - @HostBinding('attr.id') @Input() - public id = `igx-date-picker-${NEXT_ID++}`; + public set minValue(value: Date | string) { + this._minValue = value; + this._onValidatorChange(); + } - /** - * Gets/Sets a custom formatter function on the selected or passed date. - * - * @example - * ```html - * - * ``` - */ - @Input() - public formatter: (val: Date) => string; + public get minValue(): Date | string { + return this._minValue; + } /** - * Enables/Disables the `IgxDatePickerComponent`. + * The maximum value the picker will accept. * - * @example - * ```html - * - * ``` + * @example + * */ @Input() - public disabled: boolean; + public set maxValue(value: Date | string) { + this._maxValue = value; + this._onValidatorChange(); + } - /** - * Gets/Sets the orientation of the `IgxDatePickerComponent` header. - * - * @example - * ```html - * - * ``` - */ - @Input() - public vertical = false; + public get maxValue(): Date | string { + return this._maxValue; + } /** - * Gets/Sets the today button's label. + * Emitted when the picker's value changes. * - * @example - * ```html - * - * ``` - */ - @Input() - public todayButtonLabel: string; - - /** - * *Gets/Sets the cancel button's label. + * @remarks + * Used for `two-way` bindings. * * @example * ```html - * + * * ``` */ - @Input() - public cancelButtonLabel: string; + @Output() + public valueChange = new EventEmitter(); /** - * Gets/Sets the interaction mode - dialog or drop down. + * Emitted when the user types/spins to a disabled date in the date-picker editor. * * @example * ```html - * + * * ``` */ - @Input() - public mode: InteractionMode = InteractionMode.Dialog; + // eslint-disable-next-line @angular-eslint/no-output-on-prefix + @Output() + @DeprecateProperty('onDisabledDate has been deprecated.') + public onDisabledDate = new EventEmitter(); // TODO: remove event args as well /** - * Gets/Sets whether date should spin continuously or stop when min/max is reached. + * Emitted when the user types/spins invalid date in the date-picker editor. * * @example * ```html - * - * ``` - */ - @Input() - public isSpinLoop = true; - - /** - * Gets/Sets the container used for the popup element. - * - * @remarks - * `outlet` is an instance of `IgxOverlayOutletDirective` or an `ElementRef`. - * @example - * ```html - *
- * //.. - * - * //.. + * * ``` */ - @Input() - public outlet: IgxOverlayOutletDirective | ElementRef; - - /** - * Emitted when the `IgxDatePickerComponent` calendar is opened. - */ @Output() - public onOpened = new EventEmitter(); + public validationFailed = new EventEmitter(); - /** - * Emitted after the `IgxDatePickerComponent` is closed. - */ - @Output() - public onClosed = new EventEmitter(); + /** @hidden @internal */ + @ContentChildren(IgxPickerToggleComponent, { descendants: true }) + public toggleComponents: QueryList; - /** - * Emitted when the `IgxDatePickerComponent` is being closed. - */ - @Output() - public onClosing = new EventEmitter(); - - /** - * Emitted when selection is made in the calendar. - * - * @example - * ```html - * - * ``` - */ - @Output() - public onSelection = new EventEmitter(); - - /** - * Emitted when date picker value is changed. - * - * @example - * ```html - * - * ``` - */ - @Output() - public valueChange = new EventEmitter(); - - /** - * Emitted when the user types/spins to a disabled date in the date-picker editor. - * - * @example - * ```html - * - * ``` - */ - @Output() - public onDisabledDate = new EventEmitter(); - - /** - * Emitted when the user types/spins invalid date in the date-picker editor. - * - * @example - * ```html - * - * ``` - */ - @Output() - public onValidationFailed = new EventEmitter(); + /** @hidden @internal */ + @ContentChildren(IgxPickerClearComponent) + public clearComponents: QueryList; /** @hidden @internal */ @ContentChild(IgxLabelDirective) - public _labelDirectiveUserTemplate: IgxLabelDirective; + public label: IgxLabelDirective; - /** - * @hidden - */ - @ContentChild(IgxCalendarHeaderTemplateDirective, { read: IgxCalendarHeaderTemplateDirective }) - public headerTemplate: IgxCalendarHeaderTemplateDirective; - - /** - * @hidden - */ - @ContentChild(IgxCalendarSubheaderTemplateDirective, { read: IgxCalendarSubheaderTemplateDirective }) - public subheaderTemplate: IgxCalendarSubheaderTemplateDirective; + @ContentChild(IgxCalendarHeaderTemplateDirective) + private headerTemplate: IgxCalendarHeaderTemplateDirective; - /** - * @hidden - */ - @ContentChild(IgxPickerActionsDirective, { read: IgxPickerActionsDirective }) - public datePickerActionsDirective: IgxPickerActionsDirective; - - /* - * @hidden - */ - @ViewChild('readOnlyDatePickerTemplate', { read: TemplateRef, static: true }) - protected readOnlyDatePickerTemplate: TemplateRef; - - /* - * @hidden - */ - @ViewChild('editableDatePickerTemplate', { read: TemplateRef, static: true }) - protected editableDatePickerTemplate: TemplateRef; + @ViewChild(IgxDateTimeEditorDirective, { static: true }) + private dateTimeEditor: IgxDateTimeEditorDirective; - /* - * @hidden @internal - */ @ViewChild(IgxInputGroupComponent) - protected _inputGroup: IgxInputGroupComponent; + private inputGroup: IgxInputGroupComponent; - @ContentChild(IgxInputGroupComponent) - protected _inputGroupUserTemplate: IgxInputGroupComponent; + @ViewChild(IgxLabelDirective) + private labelDirective: IgxLabelDirective; - @ContentChild(IgxInputDirective, { read: ElementRef }) - protected _inputUserTemplateElementRef: ElementRef; + @ViewChild(IgxInputDirective) + private inputDirective: IgxInputDirective; - @ViewChild(IgxLabelDirective) - protected _labelDirective: IgxLabelDirective; + @ContentChild(IgxCalendarSubheaderTemplateDirective) + private subheaderTemplate: IgxCalendarSubheaderTemplateDirective; - /** - * @hidden - */ - @ContentChild(IgxDatePickerTemplateDirective, { read: IgxDatePickerTemplateDirective }) - protected datePickerTemplateDirective: IgxDatePickerTemplateDirective; + @ContentChild(IgxPickerActionsDirective) + private pickerActions: IgxPickerActionsDirective; - @ViewChild(IgxInputDirective, { read: ElementRef }) - private _inputElementRef: ElementRef; + private get dialogOverlaySettings(): OverlaySettings { + return Object.assign({}, this._dialogOverlaySettings, this.overlaySettings); + } - @ViewChild(IgxInputDirective) - private _inputDirective: IgxInputDirective; + private get dropDownOverlaySettings(): OverlaySettings { + return Object.assign({}, this._dropDownOverlaySettings, this.overlaySettings); + } - @ContentChildren(IgxInputDirective, { descendants: true }) - private _inputDirectiveUserTemplates: QueryList; + private get inputGroupElement(): HTMLElement { + return this.inputGroup?.element.nativeElement; + } /** @hidden @internal */ - public calendar: IgxCalendarComponent; - /** @hidden @internal */ - public hasHeader = true; - /** @hidden @internal */ - public collapsed = true; - /** @hidden @internal */ - public displayValuePipe = new DatePickerDisplayValuePipe(this); - /** @hidden @internal */ - public inputValuePipe = new DatePickerInputValuePipe(this); - /** @hidden @internal */ - public dateFormatParts = []; - /** @hidden @internal */ - public rawDateString: string; - /** @hidden @internal */ - public inputMask: string; - /** @hidden @internal */ - public isEmpty = true; - /** @hidden @internal */ - public invalidDate = ''; - - private readonly spinDelta = 1; - private readonly defaultLocale = 'en'; + public displayValue: PipeTransform = { transform: (date: Date) => this.formatter(date) }; - private _formatOptions = { + private _value: Date; + private _overlayId: string; + private _targetViewDate: Date; + private _destroy$ = new Subject(); + private _ngControl: NgControl = null; + private _statusChanges$: Subscription; + private _calendar: IgxCalendarComponent; + private _specialDates: DateRangeDescriptor[] = null; + private _disabledDates: DateRangeDescriptor[] = null; + private _overlaySubFilter: + [MonoTypeOperatorFunction, + MonoTypeOperatorFunction] = [ + filter(x => x.id === this._overlayId), + takeUntil(this._destroy$) + ]; + private _dropDownOverlaySettings: OverlaySettings = { + target: this.inputGroupElement, + closeOnOutsideClick: true, + modal: false, + scrollStrategy: new AbsoluteScrollStrategy(), + positionStrategy: new AutoPositionStrategy({ + openAnimation: fadeIn, + closeAnimation: fadeOut + }) + }; + private _dialogOverlaySettings: OverlaySettings = { + closeOnOutsideClick: true, + modal: true, + closeOnEscape: true + }; + private _calendarFormat: IFormattingOptions = { day: 'numeric', month: 'short', weekday: 'short', year: 'numeric' }; - private _formatViews = { + private _formatViews: IFormattingViews = { day: false, month: true, year: false }; - private _destroy$ = new Subject(); - private _statusChanges$: Subscription; - private _templateInputBlur$: Subscription; - private _componentID: string; - private _format: string; - private _value: Date; - private _isInEditMode: boolean; - private _disabledDates: DateRangeDescriptor[] = null; - private _specialDates: DateRangeDescriptor[] = null; - private _modalOverlay: OverlaySettings; - private _dropDownOverlaySettings: OverlaySettings; - private _positionSettings: PositionSettings; - private _defaultDropDownOverlaySettings: OverlaySettings; - private _modalOverlaySettings: OverlaySettings; - private _transformedDate; - private _onOpen = new EventEmitter(); - private _onClose = new EventEmitter(); - private _ngControl: NgControl = null; - private _label = 'Date'; - - //#region ControlValueAccessor - private _onChangeCallback: (_: Date) => void = noop; private _onTouchedCallback: () => void = noop; private _onValidatorChange: () => void = noop; - constructor(@Inject( - IgxOverlayService) private _overlayService: IgxOverlayService, - public element: ElementRef, - private _cdr: ChangeDetectorRef, + constructor(public element: ElementRef, + @Inject(LOCALE_ID) protected _localeId: string, + @Inject(IgxOverlayService) private _overlayService: IgxOverlayService, private _moduleRef: NgModuleRef, private _injector: Injector, - private _renderer: Renderer2) { - } - - /** - * @hidden - */ - @HostListener('keydown.spacebar', ['$event']) - @HostListener('keydown.space', ['$event']) - public onSpaceClick(event: KeyboardEvent) { - this.openDialog(); - event.preventDefault(); - } - - /** @hidden @internal */ - public writeValue(value: Date) { - this._value = value; - // TODO: do we need next call - this._cdr.markForCheck(); - } - - /** @hidden @internal */ - public registerOnChange(fn: (_: Date) => void) { - this._onChangeCallback = fn; - } - - /** @hidden @internal */ - public registerOnTouched(fn: () => void) { - this._onTouchedCallback = fn; + private _renderer: Renderer2, + @Optional() @Inject(DisplayDensityToken) protected _displayDensityOptions?: IDisplayDensityOptions, + @Optional() @Inject(IGX_INPUT_GROUP_TYPE) protected _inputGroupType?: IgxInputGroupType,) { + super(element, _localeId, _displayDensityOptions, _inputGroupType); } /** @hidden @internal */ - public setDisabledState(isDisabled: boolean): void { - this.disabled = isDisabled; - } - - /** @hidden @internal */ - public registerOnValidatorChange(fn: any) { - this._onValidatorChange = fn; - } - - /** @hidden @internal */ - public validate(): ValidationErrors | null { - if (!!this.value && this.disabledDates && isDateInRanges(this.value, this.disabledDates)) { - return { dateIsDisabled: true }; + public get required(): boolean { + if (this._ngControl && this._ngControl.control && this._ngControl.control.validator) { + // Run the validation with empty object to check if required is enabled. + const error = this._ngControl.control.validator({} as AbstractControl); + return error && error.required; } - return null; - } - //#endregion - - /** @hidden */ - public getEditElement() { - const inputDirectiveElementRef = this._inputElementRef || this._inputUserTemplateElementRef; - return (inputDirectiveElementRef) ? inputDirectiveElementRef.nativeElement : null; - } - - /** @hidden @internal */ - public get inputGroupElement(): HTMLElement { - return this.inputGroup?.element.nativeElement; - } - - /** @hidden @internal */ - public get inputGroup(): IgxInputGroupComponent { - return this._inputGroup || this._inputGroupUserTemplate || null; - } - /** @hidden @internal */ - public get inputDirective(): IgxInputDirective { - return this._inputDirective || this._inputDirectiveUserTemplates.first || null; + return false; } /** @hidden @internal */ - public get labelDirective(): IgxLabelDirective { - return this._labelDirective || this._labelDirectiveUserTemplate || null; + @HostListener('keydown.spacebar', ['$event']) + @HostListener('keydown.space', ['$event']) + public onSpaceClick(event: KeyboardEvent) { + event.preventDefault(); + this.open(); } /** @hidden @internal */ - public ngOnInit(): void { - this._positionSettings = { - openAnimation: fadeIn, - closeAnimation: fadeOut - }; - - this._defaultDropDownOverlaySettings = { - target: this.inputGroupElement, - closeOnOutsideClick: true, - modal: false, - scrollStrategy: new AbsoluteScrollStrategy(), - positionStrategy: new AutoPositionStrategy(this._positionSettings), - outlet: this.outlet - }; - - this._modalOverlaySettings = { - closeOnOutsideClick: true, - modal: true, - closeOnEscape: true, - outlet: this.outlet - }; - - this._overlayService.onOpening.pipe( - filter((overlay) => overlay.id === this._componentID), - takeUntil(this._destroy$)).subscribe((eventArgs) => { - this._onOpening(eventArgs); - }); - - this._overlayService.onOpened.pipe( - filter((overlay) => overlay.id === this._componentID), - takeUntil(this._destroy$)).subscribe(() => { - this._onOpened(); - }); - - this._overlayService.onClosed.pipe( - filter(overlay => overlay.id === this._componentID), - takeUntil(this._destroy$)).subscribe(() => { - this._onClosed(); - }); - - this._overlayService.onClosing.pipe( - filter(overlay => overlay.id === this._componentID), - takeUntil(this._destroy$)).subscribe((event) => { - this.onClosing.emit(event); - // If canceled in a user onClosing handler - if (event.cancel) { - return; + @HostListener('keydown', ['$event']) + public onKeyDown(event: KeyboardEvent) { + switch (event.key) { + case KEYS.UP_ARROW: + case KEYS.UP_ARROW_IE: + if (event.altKey) { + this.close(); } - // Do not focus the input if clicking outside in dropdown mode - const input = this.getEditElement(); - if (input && !(event.event && this.mode === InteractionMode.DropDown)) { - input.focus(); - } else { - // outside click - this._updateValidityOnBlur(); + break; + case KEYS.DOWN_ARROW: + case KEYS.DOWN_ARROW_IE: + if (event.altKey) { + this.open(); } - }); - - if (this.mode === InteractionMode.DropDown) { - this.dateFormatParts = DatePickerUtil.parseDateFormat(this.mask, this.locale); - if (this.mask === undefined) { - this.mask = DatePickerUtil.getMask(this.dateFormatParts); - } - this.inputMask = DatePickerUtil.getInputMask(this.dateFormatParts); + break; } - - this._ngControl = this._injector.get(NgControl, null); } - /** @hidden @internal */ - public ngAfterViewInit() { - if (this.mode === InteractionMode.DropDown && this._inputElementRef) { - fromEvent(this._inputElementRef.nativeElement, 'keydown').pipe( - throttle(() => interval(0, animationFrameScheduler)), - takeUntil(this._destroy$) - ).subscribe((res) => this.onKeyDown(res)); - } - - if (this._ngControl) { - this._statusChanges$ = this._ngControl.statusChanges.subscribe(this.onStatusChanged.bind(this)); + /** + * Opens the picker's dropdown or dialog. + * + * @example + * ```html + * + * + * + * ``` + */ + public open(settings?: OverlaySettings): void { + if (!this.collapsed || this.disabled) { + return; } - this._inputDirectiveUserTemplates.changes.subscribe(() => { - this.attachTemplateBlur(); - }); - this.attachTemplateBlur(); - } + const overlaySettings = Object.assign({}, this.isDropdown + ? this.dropDownOverlaySettings + : this.dialogOverlaySettings + , settings); - public ngAfterViewChecked() { - // If one sets mode at run time this forces initialization of new igxInputGroup - // As a result a new igxInputDirective is initialized too. In ngAfterViewInit of - // the new directive isRequired of the igxInputGroup is set again. However - // ngAfterViewInit of date picker is not called again and we may finish with wrong - // isRequired in igxInputGroup. This is why we should set it her, only when needed - if (this.inputGroup && this.inputGroup.isRequired !== this.required) { - this.inputGroup.isRequired = this.required; - this._cdr.detectChanges(); + if (this.isDropdown && this.inputGroupElement) { + overlaySettings.target = this.inputGroupElement; } - // TODO: persist validation state when dynamically changing 'dropdown' to 'dialog' ot vice versa. - // For reference -> it is currently persisted if a user template is passed (as template is not recreated) - - if (this.labelDirective) { - this._renderer.setAttribute(this.inputDirective.nativeElement, 'aria-labelledby', this.labelDirective.id); + if (this.outlet) { + overlaySettings.outlet = this.outlet; } + + this._overlayId = this._overlayService + .attach(IgxCalendarContainerComponent, overlaySettings, this._moduleRef); + this._overlayService.show(this._overlayId); } - /** @hidden @internal */ - public ngOnDestroy(): void { - if (this._componentID) { - this._overlayService.detach(this._componentID); - } - if (this._statusChanges$) { - this._statusChanges$.unsubscribe(); + /** + * Toggles the picker's dropdown or dialog + * + * @example + * ```html + * + * + * + * ``` + */ + public toggle(settings?: OverlaySettings): void { + if (this.collapsed) { + this.open(settings); + } else { + this.close(); } - this._destroy$.next(true); - this._destroy$.complete(); } /** - * Selects today's date from calendar. + * Closes the picker's dropdown or dialog. * - * @remarks - * Changes the input field value, @calendar.viewDate and @calendar.value. - * @example - * ```typescript - * this.datePicker.triggerTodaySelection(); + * @example + * ```html + * + * + * * ``` */ - public triggerTodaySelection(): void { - const today = new Date(Date.now()); - this.handleSelection(today); + public close(): void { + if (!this.collapsed) { + this._overlayService.hide(this._overlayId); + } } /** - * Change the calendar selection. + * Selects a date. + * + * @remarks Updates the value in the input field. * - * @remarks - * Calling this method will emit the @calendar.onSelection event, - * which will fire @handleSelection method. * @example * ```typescript - * this.datePicker.selectDate(this.date); + * this.datePicker.select(date); * ``` * @param date passed date that has to be set to the calendar. */ - public selectDate(date: Date): void { + public select(value: Date): void { const oldValue = this.value; - this.value = date; - - this.emitValueChangeEvent(oldValue, this.value); - this.onSelection.emit(date); + this.value = value; + if (DateTimeUtil.validateMinMax(value, this.minValue, this.maxValue)) { + this.validationFailed.emit({ + owner: this, + prevValue: oldValue + }); + } } /** - * Deselects the calendar date. + * Selects today's date and closes the picker. * * @example - * ```typescript - * this.datePicker.deselectDate(); + * ```html + * + * + * * ``` - */ - public deselectDate(): void { - const oldValue = this.value; - this.value = null; - this.emitValueChangeEvent(oldValue, this.value); - if (this.calendar) { - this.calendar.deselectDate(); - } + * */ + public selectToday(): void { + const today = new Date(); + today.setHours(0); + today.setMinutes(0); + today.setSeconds(0); + today.setMilliseconds(0); + this.select(today); + this.close(); } /** - * Opens the date picker drop down or dialog. + * Clears the input field and the picker's value. * - * @param target HTMLElement - the target element to use for positioning the drop down container according to * @example * ```typescript - * this.datePicker.openDialog(target); + * this.datePicker.clear(); * ``` */ - public openDialog(): void { - if (!this.collapsed || this.disabled) { - return; - } - if (this._componentID) { - this._overlayService.detach(this._componentID); - } - switch (this.mode) { - case InteractionMode.Dialog: { - this.hasHeader = true; - const modalOverlay = (this.modalOverlaySettings !== undefined) ? this._modalOverlay : this._modalOverlaySettings; - this._componentID = this._overlayService.attach(IgxCalendarContainerComponent, modalOverlay, this._moduleRef); - this._overlayService.show(this._componentID); - break; - } - case InteractionMode.DropDown: { - this.hasHeader = false; - const target = this.inputGroupElement; - if (target) { - this.dropDownOverlaySettings.target = target; - } - this._componentID = this._overlayService.attach(IgxCalendarContainerComponent, - this.dropDownOverlaySettings, this._moduleRef); - this._overlayService.show(this._componentID); - break; - } + public clear(): void { + if (!this.disabled) { + this._calendar?.deselectDate(); + this.dateTimeEditor.clear(); } } /** - * Close the calendar. + * Increment a specified `DatePart`. * - * @hidden @internal + * @param datePart The optional DatePart to increment. Defaults to Date. + * @param delta The optional delta to increment by. Overrides `spinDelta`. + * @example + * ```typescript + * this.datePicker.increment(DatePart.Date); + * ``` */ - public closeCalendar(): void { - this._overlayService.hide(this._componentID); + public increment(datePart?: DatePart, delta?: number): void { + this.dateTimeEditor.increment(datePart, delta); } /** - * Clear the input field, date picker value and calendar selection. + * Decrement a specified `DatePart` * - * @hidden @internal + * @param datePart The optional DatePart to decrement. Defaults to Date. + * @param delta The optional delta to decrement by. Overrides `spinDelta`. + * @example + * ```typescript + * this.datePicker.decrement(DatePart.Date); + * ``` */ - public clear(): void { - if (!this.disabled) { - this.isEmpty = true; - this.invalidDate = ''; - this.deselectDate(); - this._setCursorPosition(0); - } + public decrement(datePart?: DatePart, delta?: number): void { + this.dateTimeEditor.decrement(datePart, delta); } - /** - * Evaluates when @calendar.onSelection event was fired - * and update the input value. - * - * @param event selected value from calendar. - * - * @hidden @internal - */ - public handleSelection(date: Date): void { - if (this.value) { - date.setHours(this.value.getHours()); - date.setMinutes(this.value.getMinutes()); - date.setSeconds(this.value.getSeconds()); - date.setMilliseconds(this.value.getMilliseconds()); + //#region Control Value Accessor + /** @hidden @internal */ + public writeValue(value: Date) { + this._value = value; + if (this.dateTimeEditor.value !== value) { + this.dateTimeEditor.value = this.value; } - const oldValue = this.value; - this.value = date; + } - this.emitValueChangeEvent(oldValue, this.value); - this.calendar.viewDate = date; - this.closeCalendar(); - this.onSelection.emit(date); + /** @hidden @internal */ + public registerOnChange(fn: any) { + this._onChangeCallback = fn; } /** @hidden @internal */ - public onOpenClick(event: MouseEvent) { - event.stopPropagation(); - this.openDialog(); + public registerOnTouched(fn: any) { + this._onTouchedCallback = fn; } /** @hidden @internal */ - public onBlur(event, calcDate = true): void { - this._isInEditMode = false; - if (this.mode === InteractionMode.DropDown && calcDate) { - this.calculateDate(event.target.value, event.type); - } + public setDisabledState?(isDisabled: boolean): void { + this.disabled = isDisabled; + } + //#endregion - if (this.collapsed) { - this._updateValidityOnBlur(); - } + //#region Validator + /** @hidden @internal */ + public registerOnValidatorChange(fn: any) { + this._onValidatorChange = fn; } /** @hidden @internal */ - public onFocus(): void { - this._isInEditMode = true; - if (this.value && this.invalidDate === '') { - this._transformedDate = this._getEditorDate(this.value); + public validate(control: AbstractControl): ValidationErrors | null { + const value = control.value; + const errors = {}; + if (!value) { + Object.assign(errors, { value: true }); + } + if (value && this.disabledDates && isDateInRanges(value, this.disabledDates)) { + Object.assign(errors, { dateIsDisabled: true }); } + Object.assign(errors, DateTimeUtil.validateMinMax(value, this.minValue, this.maxValue)); + + return Object.keys(errors).length > 0 ? errors : null; } + //#endregion /** @hidden @internal */ - public onKeyDown(event) { - switch (event.key) { - case KEYS.UP_ARROW: - case KEYS.UP_ARROW_IE: - event.preventDefault(); - event.stopPropagation(); - this.spinValue(event.target.value, 1, event.type); - break; - case KEYS.DOWN_ARROW: - case KEYS.DOWN_ARROW_IE: - if (event.altKey) { - this.openDialog(); - } else { - event.preventDefault(); - event.stopPropagation(); - this.spinValue(event.target.value, -1, event.type); + public ngOnInit(): void { + this._ngControl = this._injector.get(NgControl, null); + } + + /** @hidden @internal */ + public ngAfterViewInit() { + this.subscribeToClick(); + this.subscribeToOverlayEvents(); + this.subscribeToDateEditorEvents(); + + fromEvent(this.inputDirective.nativeElement, 'blur') + .pipe(takeUntil(this._destroy$)) + .subscribe(() => { + if (this.collapsed) { + this._onTouchedCallback(); + this.updateValidity(); } - break; - default: - break; + }); + + if (this._ngControl) { + this._statusChanges$ = + this._ngControl.statusChanges.subscribe(this.onStatusChanged.bind(this)); } } /** @hidden @internal */ - public onWheel(event) { - if (this._isInEditMode) { - event.preventDefault(); - event.stopPropagation(); - const sign = (event.deltaY > 0) ? -1 : 1; - this.spinValue(event.target.value, sign, event.type); + public ngAfterViewChecked() { + if (this.labelDirective) { + this._renderer.setAttribute(this.inputDirective.nativeElement, 'aria-labelledby', this.labelDirective.id); } } /** @hidden @internal */ - public onInput(event) { - /** - * Fix for #8165 until refactoring (#6483). - * The IgxDateTimeEditor will be used to handle all inputs, i.e. this handler will be removed. - * It extends the IgxMaskDirective which contains logic that handles this issue. - */ - if (isIE() && !this._isInEditMode && !this.inputGroup.isFocused) { - return; + public ngOnDestroy(): void { + if (this._overlayId) { + this._overlayService.hide(this._overlayId); } - const targetValue = event.target.value; - const cursorPosition = this._getCursorPosition(); - const checkInput = DatePickerUtil.checkForCompleteDateInput(this.dateFormatParts, targetValue); - this._isInEditMode = true; - - if (targetValue !== DatePickerUtil.maskToPromptChars(this.inputMask)) { - this.isEmpty = false; + if (this._statusChanges$) { + this._statusChanges$.unsubscribe(); } + this._destroy$.next(); + this._destroy$.complete(); + this._overlayService.detach(this._overlayId); + } - // If all date parts are completed, change the date-picker value, stay in edit mode - if (checkInput === 'complete' && event.inputType !== 'deleteContentBackward') { - this._transformedDate = targetValue; - this.calculateDate(targetValue, event.type); - this._setCursorPosition(cursorPosition); - } else if (checkInput === 'partial') { - // While editing, if one date part is deleted, date-picker value is set to null, the remaining input stays intact. - this.deselectDate(); - requestAnimationFrame(() => { - this.getEditElement().value = targetValue; - this._setCursorPosition(cursorPosition); - }); - } else if (checkInput === 'empty') { - // Total clean-up as input is deleted. - this.isEmpty = true; - this.deselectDate(); - } + /** @hidden @internal */ + public getEditElement(): HTMLInputElement { + return this.inputDirective.nativeElement; } - public _updateValidityOnBlur() { - this._onTouchedCallback(); - const input = this.inputDirective; - if (input && this._ngControl && !this._ngControl.valid) { - input.valid = IgxInputState.INVALID; - } else { - input.valid = IgxInputState.INITIAL; - } + /** @hidden @internal */ + public subscribeToClick() { + fromEvent(this.getEditElement(), 'click') + .pipe(takeUntil(this._destroy$)) + .subscribe(() => { + if (!this.isDropdown) { + this.open(); + } + }); } - protected onStatusChanged() { - if ((this._ngControl.control.touched || this._ngControl.control.dirty) && - (this.inputDirective && this._ngControl.control.validator || this._ngControl.control.asyncValidator)) { + private updateValidity() { + if (this._ngControl) { if (this.inputGroup.isFocused) { - this.inputDirective.valid = this._ngControl.valid ? IgxInputState.VALID : IgxInputState.INVALID; + this.inputDirective.valid = this._ngControl.valid + ? IgxInputState.VALID + : IgxInputState.INVALID; } else { - this.inputDirective.valid = this._ngControl.valid ? IgxInputState.INITIAL : IgxInputState.INVALID; + this.inputDirective.valid = this._ngControl.valid + ? IgxInputState.INITIAL + : IgxInputState.INVALID; } } - - if (this.inputGroup && this.inputGroup.isRequired !== this.required) { - this.inputGroup.isRequired = this.required; - } } - private attachTemplateBlur() { - if (this._templateInputBlur$) { - this._templateInputBlur$.unsubscribe(); - } - - if (this._inputDirectiveUserTemplates.first) { - const directive = this._inputDirectiveUserTemplates.first; - this._templateInputBlur$ = fromEvent(directive.nativeElement, 'blur').pipe( - takeUntil(this._destroy$)).subscribe((res) => { - this.rawDateString = (res.target as HTMLInputElement).value; - this.onBlur(res, false); - }); - // TODO: Refactor custom template handling. - // Revise blur handling when custom template is passed + private onStatusChanged = () => { + if ((this._ngControl.control.touched || this._ngControl.control.dirty) && + (this._ngControl.control.validator || this._ngControl.control.asyncValidator)) { + this.updateValidity(); } - } + this.inputGroup.isRequired = this.required; + }; - private emitValueChangeEvent(oldValue: Date, newValue: Date) { - if (!isEqual(oldValue, newValue)) { - this.valueChange.emit(newValue); + private handleSelection(date: Date): void { + if (this.value) { + date.setHours(this.value.getHours()); + date.setMinutes(this.value.getMinutes()); + date.setSeconds(this.value.getSeconds()); + date.setMilliseconds(this.value.getMilliseconds()); } + this.value = date; + this._calendar.viewDate = date; + this.close(); } - private calculateDate(dateString: string, invokedByEvent: string): void { - if (dateString !== '') { - const prevDateValue = this.value; - const inputValue = (invokedByEvent === 'blur') ? this.rawDateString : dateString; - const newDateArray = DatePickerUtil.parseDateArray(this.dateFormatParts, prevDateValue, inputValue); - - if (newDateArray.state === DateState.Valid) { - const newValue = newDateArray.date; - // Restore the time part if any - if (prevDateValue) { - newValue.setHours(prevDateValue.getHours()); - newValue.setMinutes(prevDateValue.getMinutes()); - newValue.setSeconds(prevDateValue.getSeconds()); - newValue.setMilliseconds(prevDateValue.getMilliseconds()); - } - - if (this.disabledDates === null - || (this.disabledDates !== null && !isDateInRanges(newValue, this.disabledDates))) { - const oldValue = this.value; - this.value = newValue; - - this.emitValueChangeEvent(oldValue, this.value); - this.invalidDate = ''; - } else { - const args: IDatePickerDisabledDateEventArgs = { - datePicker: this, - currentValue: newValue, - }; - this.onDisabledDate.emit(args); + private subscribeToDateEditorEvents(): void { + this.dateTimeEditor.valueChange.pipe( + takeUntil(this._destroy$)).subscribe(newDate => { + this.value = newDate; + if (newDate && this.disabledDates && !isDateInRanges(newDate, this.disabledDates)) { + this.onDisabledDate.emit({ currentValue: this.value, datePicker: this }); } - } else { - const args: IDatePickerValidationFailedEventArgs = { - datePicker: this, - prevValue: prevDateValue - }; - this.invalidDate = dateString; - this.onValidationFailed.emit(args); - } - } + }); + this.dateTimeEditor.validationFailed.pipe( + takeUntil(this._destroy$)).subscribe((event) => { + this.validationFailed.emit({ + owner: this, + prevValue: event.oldValue + }); + }); } - private spinValue(inputValue: string, sign: number, eventType: string): void { - this._isInEditMode = true; - this.isEmpty = false; - const cursorPosition = this._getCursorPosition(); + private subscribeToOverlayEvents() { + this._overlayService.onOpening.pipe(...this._overlaySubFilter).subscribe((eventArgs) => { + const args = eventArgs as IBaseCancelableBrowserEventArgs; + this.opening.emit(args); + if (args.cancel) { + this._overlayService.detach(this._overlayId); + return; + } - const modifiedInputValue = - DatePickerUtil.getModifiedDateInput(this.dateFormatParts, inputValue, cursorPosition, this.spinDelta * sign, this.isSpinLoop); + this._initializeCalendarContainer(eventArgs.componentRef.instance); + this._collapsed = false; + }); - this.getEditElement().value = modifiedInputValue; - this._setCursorPosition(cursorPosition); + this._overlayService.onOpened.pipe(...this._overlaySubFilter).subscribe((eventArgs) => { + this.opened.emit(eventArgs as IBaseEventArgs); + if (this._calendar?.daysView?.selectedDates) { + this._calendar?.daysView?.focusActiveDate(); + } else { + this._targetViewDate.setHours(0, 0, 0, 0); + this._calendar?.daysView?.dates + .find(d => d.date.date.getTime() === this._targetViewDate.getTime())?.nativeElement.focus(); + } + }); - const checkInput = DatePickerUtil.checkForCompleteDateInput(this.dateFormatParts, modifiedInputValue); - if (checkInput === 'complete') { - this._isInEditMode = true; - this.calculateDate(modifiedInputValue, eventType); - this._setCursorPosition(cursorPosition); - } - } + this._overlayService.onClosing.pipe(...this._overlaySubFilter).subscribe((eventArgs) => { + const args = eventArgs as IBaseCancelableBrowserEventArgs; + this.closing.emit(args); + if (args.cancel) { + return; + } + // do not focus the input if clicking outside in dropdown mode + if (this.getEditElement() && !(args.event && this.isDropdown)) { + this.inputDirective.focus(); + } + }); - private _onOpening(event: OverlayCancelableEventArgs) { - this._initializeCalendarContainer(event.componentRef.instance as IgxCalendarContainerComponent); - this.collapsed = false; + this._overlayService.onClosed.pipe(...this._overlaySubFilter).subscribe((event) => { + this.closed.emit(event as IBaseEventArgs); + this._overlayService.detach(this._overlayId); + this._collapsed = true; + this._overlayId = null; + }); } - private _onOpened(): void { - this.onOpened.emit(this); - - if (this.calendar) { - this._focusCalendarDate(); + private emitValueChange(oldValue: Date, newValue: Date) { + if (!isEqual(oldValue, newValue)) { + this.valueChange.emit(newValue); } } - private _onClosed(): void { - this._overlayService.detach(this._componentID); - this.collapsed = true; - this._componentID = null; - this.onClosed.emit(this); + private setDisabledDates(): void { + this._calendar.disabledDates = this.disabledDates ? [...this.disabledDates] : []; + // TODO: ISO support + const minValue = parseDate(this.minValue); + if (minValue) { + this._calendar.disabledDates.push({ type: DateRangeType.Before, dateRange: [minValue] }); + } + const maxValue = parseDate(this.maxValue); + if (maxValue) { + this._calendar.disabledDates.push({ type: DateRangeType.After, dateRange: [maxValue] }); + } } private _initializeCalendarContainer(componentInstance: IgxCalendarContainerComponent) { - this.calendar = componentInstance.calendar; - const isVertical = (this.vertical && this.mode === InteractionMode.Dialog); - this.calendar.hasHeader = this.hasHeader; - this.calendar.formatOptions = this.formatOptions; - this.calendar.formatViews = this.formatViews; - this.calendar.locale = this.locale; - this.calendar.vertical = isVertical; - this.calendar.weekStart = this.weekStart; - this.calendar.specialDates = this.specialDates; - this.calendar.disabledDates = this.disabledDates; - this.calendar.headerTemplate = this.headerTemplate; - this.calendar.subheaderTemplate = this.subheaderTemplate; - this.calendar.hideOutsideDays = this.hideOutsideDays; - this.calendar.monthsViewNumber = this.monthsViewNumber; - this.calendar.showWeekNumbers = this.showWeekNumbers; - this.calendar.selected.pipe(takeUntil(this._destroy$)).subscribe((ev: Date) => this.handleSelection(ev)); - - if (this.value) { - this.calendar.value = this.value; - this.calendar.viewDate = this.value; - } + this._calendar = componentInstance.calendar; + const isVertical = this.headerOrientation === PickerHeaderOrientation.Vertical; + this._calendar.hasHeader = !this.isDropdown; + this._calendar.formatOptions = this.calendarFormat; + this._calendar.formatViews = this.formatViews; + this._calendar.locale = this.locale; + this._calendar.vertical = isVertical; + this._calendar.weekStart = this.weekStart; + this._calendar.specialDates = this.specialDates; + this._calendar.headerTemplate = this.headerTemplate; + this._calendar.subheaderTemplate = this.subheaderTemplate; + this._calendar.hideOutsideDays = this.hideOutsideDays; + this._calendar.monthsViewNumber = this.displayMonthsCount; + this._calendar.showWeekNumbers = this.showWeekNumbers; + this._calendar.selected.pipe(takeUntil(this._destroy$)).subscribe((ev: Date) => this.handleSelection(ev)); + this.setDisabledDates(); + + if (DateTimeUtil.isValidDate(this.value)) { + // calendar will throw if this.value is InvalidDate #9208 + this._calendar.value = this.value; + } + this.setCalendarViewDate(); componentInstance.mode = this.mode; componentInstance.vertical = isVertical; componentInstance.closeButtonLabel = this.cancelButtonLabel; componentInstance.todayButtonLabel = this.todayButtonLabel; - componentInstance.pickerActions = this.datePickerActionsDirective; + componentInstance.pickerActions = this.pickerActions; - componentInstance.calendarClose.pipe(takeUntil(this._destroy$)).subscribe(() => this.closeCalendar()); - componentInstance.todaySelection.pipe(takeUntil(this._destroy$)).subscribe(() => this.triggerTodaySelection()); + componentInstance.calendarClose.pipe(takeUntil(this._destroy$)).subscribe(() => this.close()); + componentInstance.todaySelection.pipe(takeUntil(this._destroy$)).subscribe(() => this.selectToday()); } - // Focus a date, after the calendar appearance into DOM. - private _focusCalendarDate(): void { - requestAnimationFrame(() => { - this.calendar.daysView.focusActiveDate(); - }); - } - - private _setLocaleToDate(value: Date): string { - if (isIE()) { - // this is a workaround fixing the following IE11 issue: - // IE11 has added character code 8206 (mark for RTL) to the output of toLocaleDateString() that - // precedes each portion that comprises the total date... For more information read this article: - // eslint-disable-next-line max-len - // https://www.csgpro.com/blog/2016/08/a-bad-date-with-internet-explorer-11-trouble-with-new-unicode-characters-in-javascript-date-strings/ - const localeDateStrIE = new Date(value.getFullYear(), value.getMonth(), value.getDate(), - value.getHours(), value.getMinutes(), value.getSeconds(), value.getMilliseconds()); - return localeDateStrIE.toLocaleDateString(this.locale); + private setCalendarViewDate() { + // TODO: use ISO date parser from DateUtil + const minValueAsDate = parseDate(this.minValue); + const maxValueAsDate = parseDate(this.maxValue); + if (DateTimeUtil.lessThanMinValue(this.value, minValueAsDate)) { + this._calendar.viewDate = this._targetViewDate = minValueAsDate; + return; } - - return value.toLocaleDateString(this.locale); - } - - private _getCursorPosition(): number { - return this.getEditElement().selectionStart; - } - - private _setCursorPosition(start: number, end: number = start): void { - requestAnimationFrame(() => { - this.getEditElement().setSelectionRange(start, end); - }); - } - - /** - * Apply custom user formatter upon date. - * - * @param formatter custom formatter function. - * @param date passed date - */ - private _customFormatChecker(formatter: (_: Date) => string, date: Date) { - return this.formatter ? this.formatter(date) : this._setLocaleToDate(date); - } - - /* - * Transforms the date according to the specified format when `IgxDatePickerComponent` is in edit mode - * using @angular/common formatDate method: https://angular.io/api/common/formatDate - * @param value: string | number | Date - * @returns formatted string - */ - private _getDisplayDate(value: any): string { - if (this.format && !this.formatter) { - const locale = this.locale || this.defaultLocale; - return formatDate(value, this.format, locale); - } else { - return this._customFormatChecker(this.formatter, value); + if (DateTimeUtil.greaterThanMaxValue(this.value, maxValueAsDate)) { + this._calendar.viewDate = this._targetViewDate = maxValueAsDate; + return; } - } - - private _getEditorDate(value: any) { - const locale = this.locale || this.defaultLocale; - const changedValue = (value) ? formatDate(value, this.mask, locale) : ''; - return DatePickerUtil.addPromptCharsEditMode(this.dateFormatParts, this.value, changedValue); + this._calendar.viewDate = this.value; } } - -/** - * @hidden - */ -@NgModule({ - declarations: [ - IgxDatePickerComponent, - IgxDatePickerTemplateDirective, - DatePickerDisplayValuePipe, - DatePickerInputValuePipe - ], - entryComponents: [IgxCalendarContainerComponent], - exports: [ - IgxDatePickerComponent, - IgxDatePickerTemplateDirective, - DatePickerDisplayValuePipe, - DatePickerInputValuePipe, - IgxInputGroupModule, - IgxCalendarContainerModule - ], - imports: [ - CommonModule, - IgxIconModule, - IgxInputGroupModule, - IgxCalendarModule, - IgxButtonModule, - IgxRippleModule, - IgxMaskModule, - IgxTextSelectionModule - ] -}) -export class IgxDatePickerModule { } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.module.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.module.ts new file mode 100644 index 00000000000..397b8774dcd --- /dev/null +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.module.ts @@ -0,0 +1,36 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { IgxCalendarModule } from '../calendar/public_api'; +import { IgxCalendarContainerModule } from '../date-common/calendar-container/calendar-container.component'; +import { IgxPickersCommonModule } from '../date-common/picker-icons.common'; +import { IgxDateTimeEditorModule } from '../directives/date-time-editor/public_api'; +import { IgxMaskModule } from '../directives/mask/mask.directive'; +import { IgxTextSelectionModule } from '../directives/text-selection/text-selection.directive'; +import { IgxIconModule } from '../icon/public_api'; +import { IgxInputGroupModule } from '../input-group/public_api'; +import { IgxDatePickerComponent } from './date-picker.component'; + +/** @hidden */ +@NgModule({ + declarations: [ + IgxDatePickerComponent + ], + exports: [ + IgxDatePickerComponent, + IgxPickersCommonModule + ], + imports: [ + FormsModule, + CommonModule, + IgxIconModule, + IgxMaskModule, + IgxCalendarModule, + IgxInputGroupModule, + IgxPickersCommonModule, + IgxTextSelectionModule, + IgxDateTimeEditorModule, + IgxCalendarContainerModule, + ] +}) +export class IgxDatePickerModule { } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts deleted file mode 100644 index 049a8175f66..00000000000 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { PipeTransform, Pipe, Inject } from '@angular/core'; -import { IGX_DATE_PICKER_COMPONENT, IDatePicker } from './date-picker.common'; -import { DatePickerUtil } from './date-picker.utils'; - -/** - * @hidden - */ -@Pipe({ - name: 'displayValue' -}) -export class DatePickerDisplayValuePipe implements PipeTransform { - constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IDatePicker) { } - transform(value: any, args?: any): any { - if (value !== '') { - if (value === DatePickerUtil.maskToPromptChars(this._datePicker.inputMask)) { - return ''; - } - this._datePicker.rawDateString = value; - return DatePickerUtil.trimEmptyPlaceholders(value); - } - return ''; - } -} - -/** - * @hidden - */ -@Pipe({ - name: 'inputValue' -}) -export class DatePickerInputValuePipe implements PipeTransform { - constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IDatePicker) { } - transform(value: any, args?: any): any { - /** - * TODO(D.P.): This plugs into the mask, but constantly received display strings it can't handle at all - * Those are almost immediately overridden by the pickers onFocus handling anyway; Refactor ASAP - */ - if (this._datePicker.invalidDate !== '') { - return this._datePicker.invalidDate; - } else { - if (this._datePicker.value === null || this._datePicker.value === undefined) { - return DatePickerUtil.maskToPromptChars(this._datePicker.inputMask); - } else { - return (this._datePicker as any)._getEditorDate(this._datePicker.value); - // return DatePickerUtil.addPromptCharsEditMode(this._datePicker.dateFormatParts, this._datePicker.value, value); - } - } - } -} diff --git a/projects/igniteui-angular/src/lib/date-picker/public_api.ts b/projects/igniteui-angular/src/lib/date-picker/public_api.ts new file mode 100644 index 00000000000..96e209535d1 --- /dev/null +++ b/projects/igniteui-angular/src/lib/date-picker/public_api.ts @@ -0,0 +1,2 @@ +export * from './date-picker.component'; +export * from './date-picker.module'; diff --git a/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.spec.ts b/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.spec.ts index f5268bc00da..180d01de509 100644 --- a/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.spec.ts +++ b/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync, flush } from '@angular/core/testing'; import { Component, OnInit, ViewChild, DebugElement } from '@angular/core'; import { IgxInputGroupModule } from '../input-group/public_api'; -import { InteractionMode } from '../core/enums'; +import { PickerInteractionMode } from '../date-common/types'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { FormsModule, FormControl } from '@angular/forms'; import { IgxDateRangePickerModule } from './date-range-picker.module'; @@ -344,7 +344,7 @@ describe('IgxDateRangePicker', () => { describe('Selection tests', () => { it('should assign range dates to the input when selecting a range from the calendar', () => { - fixture.componentInstance.mode = InteractionMode.DropDown; + fixture.componentInstance.mode = PickerInteractionMode.DropDown; fixture.componentInstance.dateRange.displayFormat = 'M/d/yyyy'; fixture.detectChanges(); @@ -358,7 +358,7 @@ describe('IgxDateRangePicker', () => { }); it('should assign range values correctly when selecting dates in reversed order', () => { - fixture.componentInstance.mode = InteractionMode.DropDown; + fixture.componentInstance.mode = PickerInteractionMode.DropDown; fixture.componentInstance.dateRange.displayFormat = 'M/d/yyyy'; fixture.detectChanges(); @@ -371,7 +371,7 @@ describe('IgxDateRangePicker', () => { }); it('should set start and end dates on single date selection', () => { - fixture.componentInstance.mode = InteractionMode.DropDown; + fixture.componentInstance.mode = PickerInteractionMode.DropDown; fixture.componentInstance.dateRange.displayFormat = 'M/d/yyyy'; fixture.detectChanges(); @@ -486,7 +486,7 @@ describe('IgxDateRangePicker', () => { }); it('should close the calendar with the "Done" button', fakeAsync(() => { - fixture.componentInstance.mode = InteractionMode.Dialog; + fixture.componentInstance.mode = PickerInteractionMode.Dialog; fixture.componentInstance.dateRange.displayFormat = 'M/d/yyyy'; fixture.detectChanges(); spyOn(dateRange.closing, 'emit').and.callThrough(); @@ -522,7 +522,7 @@ describe('IgxDateRangePicker', () => { })); it('should show the "Done" button only in dialog mode', fakeAsync(() => { - fixture.componentInstance.mode = InteractionMode.Dialog; + fixture.componentInstance.mode = PickerInteractionMode.Dialog; fixture.detectChanges(); dateRange.open(); @@ -533,7 +533,7 @@ describe('IgxDateRangePicker', () => { tick(); fixture.detectChanges(); - fixture.componentInstance.mode = InteractionMode.DropDown; + fixture.componentInstance.mode = PickerInteractionMode.DropDown; fixture.detectChanges(); dateRange.open(); @@ -544,7 +544,7 @@ describe('IgxDateRangePicker', () => { })); it('should be able to change the "Done" button text', fakeAsync(() => { - fixture.componentInstance.mode = InteractionMode.Dialog; + fixture.componentInstance.mode = PickerInteractionMode.Dialog; fixture.detectChanges(); dateRange.toggle(); @@ -694,7 +694,7 @@ describe('IgxDateRangePicker', () => { })); it('should not open calendar with ALT + DOWN ARROW key if disabled is set to true', fakeAsync(() => { - fixture.componentInstance.mode = InteractionMode.DropDown; + fixture.componentInstance.mode = PickerInteractionMode.DropDown; fixture.componentInstance.disabled = true; fixture.detectChanges(); @@ -774,7 +774,7 @@ describe('IgxDateRangePicker', () => { describe('Selection tests', () => { it('should assign range values correctly when selecting dates from the calendar', () => { - fixture.componentInstance.mode = InteractionMode.DropDown; + fixture.componentInstance.mode = PickerInteractionMode.DropDown; fixture.detectChanges(); let dayRange = 15; @@ -794,7 +794,7 @@ describe('IgxDateRangePicker', () => { }); it('should assign range values correctly when selecting dates in reversed order', () => { - fixture.componentInstance.mode = InteractionMode.DropDown; + fixture.componentInstance.mode = PickerInteractionMode.DropDown; fixture.detectChanges(); const dayRange = -10; @@ -806,7 +806,7 @@ describe('IgxDateRangePicker', () => { }); it('should apply selection to start and end dates when single date is selected', () => { - fixture.componentInstance.mode = InteractionMode.DropDown; + fixture.componentInstance.mode = PickerInteractionMode.DropDown; fixture.detectChanges(); const today = new Date(); @@ -994,7 +994,7 @@ describe('IgxDateRangePicker', () => { })); it('should toggle the calendar with ALT + DOWN/UP ARROW key - dialog mode', fakeAsync(() => { - fixture.componentInstance.mode = InteractionMode.Dialog; + fixture.componentInstance.mode = PickerInteractionMode.Dialog; fixture.detectChanges(); expect(dateRange.collapsed).toBeTruthy(); @@ -1075,7 +1075,7 @@ describe('IgxDateRangePicker', () => { })); it('should focus the last focused input after the calendar closes - dialog', fakeAsync(() => { - fixture.componentInstance.mode = InteractionMode.Dialog; + fixture.componentInstance.mode = PickerInteractionMode.Dialog; fixture.detectChanges(); endInput = fixture.debugElement.queryAll(By.css('.igx-input-group'))[1]; UIInteractions.simulateClickAndSelectEvent(endInput.nativeElement); @@ -1275,7 +1275,7 @@ export class DateRangeTestComponent implements OnInit { public dateRange: IgxDateRangePickerComponent; public doneButtonText: string; - public mode = InteractionMode.DropDown; + public mode = PickerInteractionMode.DropDown; public disabled = false; public minValue: Date | string; public maxValue: Date | string; diff --git a/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts b/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts index 20de3c7ce95..921bcb12174 100644 --- a/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts +++ b/projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts @@ -15,13 +15,12 @@ import { fadeIn, fadeOut } from '../animations/fade'; import { CalendarSelection, IgxCalendarComponent, WEEKDAYS } from '../calendar/public_api'; import { DateRangeType } from '../core/dates'; import { DisplayDensityToken, IDisplayDensityOptions } from '../core/density'; -import { InteractionMode } from '../core/enums'; import { CurrentResourceStrings } from '../core/i18n/resources'; import { DateTimeUtil } from '../date-common/util/date-time.util'; import { IBaseCancelableBrowserEventArgs, KEYS, parseDate } from '../core/utils'; import { IgxCalendarContainerComponent } from '../date-common/calendar-container/calendar-container.component'; import { IgxPickerActionsDirective, IgxPickerToggleComponent } from '../date-common/picker-icons.common'; -import { PickersBaseDirective } from '../date-common/pickers-base.directive'; +import { PickerBaseDirective } from '../date-common/picker-base.directive'; import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive'; import { IgxInputDirective, IgxInputGroupComponent, IgxInputGroupType, IgxInputState, @@ -68,22 +67,8 @@ const SingleInputDatesConcatenationString = ' - '; { provide: NG_VALIDATORS, useExisting: IgxDateRangePickerComponent, multi: true } ] }) -export class IgxDateRangePickerComponent extends PickersBaseDirective +export class IgxDateRangePickerComponent extends PickerBaseDirective implements OnChanges, OnInit, AfterViewInit, OnDestroy, ControlValueAccessor, Validator { - /** - * Display calendar in either `dialog` or `dropdown` mode. - * - * @remarks - * Default mode is `dialog` - * - * @example - * ```html - * - * ``` - */ - @Input() - public mode = InteractionMode.DropDown; - /** * The number of displayed month views. * @@ -395,10 +380,6 @@ export class IgxDateRangePickerComponent extends PickersBaseDirective return this._calendar; } - private get isDropdown() { - return this.mode === InteractionMode.DropDown; - } - private get dropdownOverlaySettings(): OverlaySettings { return Object.assign({}, this._dropDownOverlaySettings, this.overlaySettings); } @@ -651,9 +632,6 @@ export class IgxDateRangePickerComponent extends PickersBaseDirective /** @hidden @internal */ public ngOnChanges(changes: SimpleChanges): void { - if (changes['locale'] && !this.inputFormat) { - this.inputFormat = DateTimeUtil.getDefaultInputFormat(this.locale); - } if (changes['displayFormat'] && this.hasProjectedInputs) { this.updateDisplayFormat(); } diff --git a/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.common.ts b/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.common.ts index e2290351278..7106365e031 100644 --- a/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.common.ts +++ b/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.common.ts @@ -28,10 +28,10 @@ export interface DatePartInfo { /** Delta values used for spin actions. */ export interface DatePartDeltas { - date: number; - month: number; - year: number; - hour: number; - minute: number; - second: number; + date?: number; + month?: number; + year?: number; + hour?: number; + minute?: number; + second?: number; } diff --git a/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.directive.spec.ts b/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.directive.spec.ts index 8336fe4224c..da44f2311a6 100644 --- a/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.directive.spec.ts +++ b/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.directive.spec.ts @@ -194,7 +194,7 @@ describe('IgxDateTimeEditor', () => { inputDate = '31/03/2020'; elementRef = { nativeElement: { value: inputDate } }; initializeDateTimeEditor(); - dateTimeEditor.isSpinLoop = false; + dateTimeEditor.spinLoop = false; dateTimeEditor.value = new Date(2020, 2, 31); spyOnProperty((dateTimeEditor as any), 'inputValue', 'get').and.returnValue(inputDate); @@ -310,7 +310,7 @@ describe('IgxDateTimeEditor', () => { inputDate = '20/01/2019 23:13:12'; elementRef = { nativeElement: { value: inputDate } }; initializeDateTimeEditor(); - dateTimeEditor.isSpinLoop = false; + dateTimeEditor.spinLoop = false; dateTimeEditor.value = new Date(2019, 1, 20, 23, 0, 12); spyOnProperty((dateTimeEditor as any), 'inputValue', 'get').and.returnValue(inputDate); diff --git a/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.directive.ts b/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.directive.ts index 610249980cc..5afd8c6d339 100644 --- a/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.directive.ts @@ -118,11 +118,11 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnCh * * @example * ```html - * + * * ``` */ @Input() - public isSpinLoop = true; + public spinLoop = true; /** * Set both pre-defined format options such as `shortDate` and `longDate`, @@ -155,7 +155,7 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnCh } public get inputFormat(): string { - return this._format; + return this._format || this._inputFormat; } /** @@ -183,7 +183,7 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnCh * * @example * ```html - * + * * ``` */ @Input() @@ -295,7 +295,10 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnCh /** @hidden @internal */ public ngOnChanges(changes: SimpleChanges) { - if (changes['inputFormat'] || changes['locale']) { + if (changes['locale'] && !this._format) { + this._inputFormat = DateTimeUtil.getDefaultInputFormat(this.locale); + } + if (changes['inputFormat']) { this.updateInputFormat(); } } @@ -435,6 +438,9 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnCh /** @hidden @internal */ public onFocus(): void { + if (this.nativeElement.readOnly) { + return; + } this._isFocused = true; this.onTouchCallback(); this.updateMask(); @@ -450,6 +456,11 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnCh this.updateMask(); } + // TODO: think of a better way to set displayValuePipe in mask directive + if (this.displayValuePipe) { + return; + } + super.onBlur(value); } @@ -465,11 +476,15 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnCh this.inputValue = ''; return; } + if (this.displayValuePipe) { + // TODO: remove when formatter func has been deleted + this.inputValue = this.displayValuePipe.transform(this.value); + return; + } const format = this.displayFormat || this.inputFormat; if (format) { this.inputValue = DateTimeUtil.formatDate(this.value, format.replace('tt', 'aa'), this.locale); } else { - // TODO: formatter function? this.inputValue = this.value.toLocaleString(); } } @@ -484,7 +499,7 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnCh private getMaskedValue(): string { let mask = this.emptyMask; - if (this.value) { + if (DateTimeUtil.isValidDate(this.value)) { for (const part of this._inputDateParts) { if (part.type === DatePart.Literal) { continue; @@ -539,22 +554,22 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnCh const newDate = new Date(this.value.getTime()); switch (datePart) { case DatePart.Date: - DateTimeUtil.spinDate(delta, newDate, this.isSpinLoop); + DateTimeUtil.spinDate(delta, newDate, this.spinLoop); break; case DatePart.Month: - DateTimeUtil.spinMonth(delta, newDate, this.isSpinLoop); + DateTimeUtil.spinMonth(delta, newDate, this.spinLoop); break; case DatePart.Year: DateTimeUtil.spinYear(delta, newDate); break; case DatePart.Hours: - DateTimeUtil.spinHours(delta, newDate, this.isSpinLoop); + DateTimeUtil.spinHours(delta, newDate, this.spinLoop); break; case DatePart.Minutes: - DateTimeUtil.spinMinutes(delta, newDate, this.isSpinLoop); + DateTimeUtil.spinMinutes(delta, newDate, this.spinLoop); break; case DatePart.Seconds: - DateTimeUtil.spinSeconds(delta, newDate, this.isSpinLoop); + DateTimeUtil.spinSeconds(delta, newDate, this.spinLoop); break; case DatePart.AmPm: const formatPart = this._inputDateParts.find(dp => dp.type === DatePart.AmPm); diff --git a/projects/igniteui-angular/src/lib/directives/focus/focus.directive.spec.ts b/projects/igniteui-angular/src/lib/directives/focus/focus.directive.spec.ts index f0dc8f60f7a..61dbeee9dd1 100644 --- a/projects/igniteui-angular/src/lib/directives/focus/focus.directive.spec.ts +++ b/projects/igniteui-angular/src/lib/directives/focus/focus.directive.spec.ts @@ -6,7 +6,7 @@ import { IgxFocusDirective, IgxFocusModule } from './focus.directive'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { EditorProvider } from '../../core/edit-provider'; import { IgxCheckboxModule, IgxCheckboxComponent } from '../../checkbox/checkbox.component'; -import { IgxDatePickerModule, IgxDatePickerComponent } from '../../date-picker/date-picker.component'; +import { IgxDatePickerModule, IgxDatePickerComponent } from '../../date-picker/public_api'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; describe('igxFocus', () => { diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index 489c84e5b2d..fefe29e314a 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -108,7 +108,6 @@ [locale]="grid.locale" [(value)]="editValue" [igxFocus]="true" - [labelVisibility]="false" > diff --git a/projects/igniteui-angular/src/lib/grids/common/shared.module.ts b/projects/igniteui-angular/src/lib/grids/common/shared.module.ts index 3d894630e3a..7df7d500abc 100644 --- a/projects/igniteui-angular/src/lib/grids/common/shared.module.ts +++ b/projects/igniteui-angular/src/lib/grids/common/shared.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { IgxButtonModule } from '../../directives/button/button.directive'; -import { IgxDatePickerModule } from '../../date-picker/date-picker.component'; import { IgxIconModule } from '../../icon/public_api'; import { IgxRippleModule } from '../../directives/ripple/ripple.directive'; import { IgxInputGroupModule } from '../../input-group/public_api'; @@ -22,6 +21,7 @@ import { IgxSelectModule } from '../../select/select.module'; import { IgxDropDownModule } from '../../drop-down/public_api'; import { IgxGridStateModule } from '../state.directive'; import { IgxSnackbarModule } from '../../snackbar/snackbar.component'; +import { IgxDatePickerModule } from '../../date-picker/date-picker.module'; @NgModule({ diff --git a/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.html b/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.html index 2fea568a3b6..8e52e444f11 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.html @@ -48,7 +48,7 @@ [value]="value" [outlet]="filteringService.grid.outlet" [locale]="filteringService.grid.locale" - (onSelection)="onDateSelected($event)" + (valueChange)="onDateSelected($event)" (onClosed)="datePickerClose()"> diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-default-expression.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-default-expression.component.ts index 65e9bc4af09..7b2f5558515 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-default-expression.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-default-expression.component.ts @@ -110,7 +110,7 @@ export class IgxExcelStyleDefaultExpressionComponent implements AfterViewInit { } protected get inputValuesElement() { - return this.inputValuesDirective; + return this.inputValuesDirective.nativeElement; } public ngAfterViewInit(): void { diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts index d540c2bd565..e9da3af089e 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts @@ -17,7 +17,6 @@ import { FormsModule } from '@angular/forms'; import { IgxGridPipesModule } from '../../common/grid-pipes.module'; import { IgxButtonModule } from '../../../directives/button/button.directive'; import { IgxButtonGroupModule } from '../../../buttonGroup/buttonGroup.component'; -import { IgxDatePickerModule } from '../../../date-picker/date-picker.component'; import { IgxIconModule } from '../../../icon/public_api'; import { IgxRippleModule } from '../../../directives/ripple/ripple.directive'; import { IgxInputGroupModule } from '../../../input-group/input-group.component'; @@ -35,6 +34,7 @@ import { IgxExcelStyleHidingComponent } from './excel-style-hiding.component'; import { IgxExcelStyleSelectingComponent } from './excel-style-selecting.component'; import { IgxExcelStyleClearFiltersComponent } from './excel-style-clear-filters.component'; import { IgxExcelStyleConditionalFilterComponent } from './excel-style-conditional-filter.component'; +import { IgxDatePickerModule } from '../../../date-picker/date-picker.module'; /** * @hidden diff --git a/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html b/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html index 4d39120953b..aca333cee0b 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/grid/expandable-cell.component.html @@ -56,7 +56,7 @@ + [locale]="grid.locale" [(value)]="editValue" [igxFocus]="true"> diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html index 1c516670e61..9d4e5aee46a 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-cell.component.html @@ -99,7 +99,6 @@ [locale]="grid.locale" [(value)]="editValue" [igxFocus]="true" - [labelVisibility]="false" > diff --git a/projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts b/projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts index d934a9529cf..14fbc8d296c 100644 --- a/projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts +++ b/projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts @@ -16,7 +16,7 @@ import { scaleInVerTop, scaleOutVerTop } from '../../animations/main'; import { IgxAvatarComponent, IgxAvatarModule } from '../../avatar/avatar.component'; import { IgxCalendarComponent, IgxCalendarModule } from '../../calendar/public_api'; import { IgxCalendarContainerComponent } from '../../date-common/calendar-container/calendar-container.component'; -import { IgxDatePickerComponent, IgxDatePickerModule } from '../../date-picker/date-picker.component'; +import { IgxDatePickerComponent, IgxDatePickerModule } from '../../date-picker/public_api'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { UIInteractions } from '../../test-utils/ui-interactions.spec'; import { IgxOverlayOutletDirective, IgxToggleDirective, IgxToggleModule } from './../../directives/toggle/toggle.directive'; diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.common.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.common.ts index adece008406..fb30b250479 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.common.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.common.ts @@ -1,5 +1,5 @@ import { ElementRef } from '@angular/core'; -import { InteractionMode } from '../core/enums'; +import { PickerInteractionMode } from '../date-common/types'; /** @hidden */ export const IGX_TIME_PICKER_COMPONENT = 'IgxTimePickerComponentToken'; @@ -25,7 +25,7 @@ export interface IgxTimePickerBase { format: string; promptChar: string; cleared: boolean; - mode: InteractionMode; + mode: PickerInteractionMode; showHoursList: boolean; showMinutesList: boolean; showSecondsList: boolean; diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts index 1739c57f531..5b7ac64c376 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts @@ -9,7 +9,7 @@ import { IgxTimePickerComponent, IgxTimePickerModule } from './time-picker.compo import { UIInteractions } from '../test-utils/ui-interactions.spec'; import { IgxInputGroupModule, IgxInputGroupComponent } from '../input-group/public_api'; import { configureTestSuite } from '../test-utils/configure-suite'; -import { InteractionMode } from '../core/enums'; +import { PickerInteractionMode } from '../date-common/types'; import { IgxIconModule } from '../icon/public_api'; import { IgxToggleModule } from '../directives/toggle/toggle.directive'; import { IBaseCancelableBrowserEventArgs } from '../core/utils'; @@ -49,7 +49,7 @@ describe('IgxTimePicker', () => { it('Should display default and custom label', (() => { const fixture = TestBed.createComponent(IgxTimePickerCustomLabelComponent); const testComponent = fixture.componentInstance; - testComponent.mode = InteractionMode.DropDown; + testComponent.mode = PickerInteractionMode.DropDown; fixture.detectChanges(); const dom = fixture.debugElement; @@ -61,7 +61,7 @@ describe('IgxTimePicker', () => { label = dom.query(By.directive(IgxLabelDirective)).nativeElement.innerText; expect(label).toEqual('Time'); - testComponent.mode = InteractionMode.Dialog; + testComponent.mode = PickerInteractionMode.Dialog; fixture.detectChanges(); label = dom.query(By.directive(IgxLabelDirective)).nativeElement.innerText; expect(label).toEqual('Time'); @@ -1523,7 +1523,7 @@ describe('IgxTimePicker', () => { expect(dom.query(By.css('.igx-time-picker--dropdown'))).toBeDefined(); - fixture.componentInstance.timePicker.mode = InteractionMode.Dialog; + fixture.componentInstance.timePicker.mode = PickerInteractionMode.Dialog; fixture.detectChanges(); UIInteractions.simulateClickAndSelectEvent(iconTime); @@ -2049,7 +2049,7 @@ describe('IgxTimePicker', () => { it('Should render dialog and input group correctly when format contains only minutes.', fakeAsync(() => { fixture.componentInstance.format = 'mm'; - fixture.componentInstance.mode = InteractionMode.Dialog; + fixture.componentInstance.mode = PickerInteractionMode.Dialog; fixture.detectChanges(); input = dom.query(By.directive(IgxInputDirective)); @@ -2077,7 +2077,7 @@ describe('IgxTimePicker', () => { it('Should render dialog and input group correctly when format contains only hours.', fakeAsync(() => { fixture.componentInstance.format = 'hh tt'; - fixture.componentInstance.mode = InteractionMode.Dialog; + fixture.componentInstance.mode = PickerInteractionMode.Dialog; fixture.detectChanges(); input = dom.query(By.directive(IgxInputDirective)); @@ -2136,7 +2136,7 @@ describe('IgxTimePicker', () => { })); it('Should set time picker status to invalid when it is required and has no value onBlur', fakeAsync(() => { - timePickerOnBlurComponent.mode = InteractionMode.DropDown; + timePickerOnBlurComponent.mode = PickerInteractionMode.DropDown; timePickerOnBlurComponent.mask = 'dd/mm/yyyy'; fixture.detectChanges(); @@ -2365,7 +2365,7 @@ export class IgxTimePickerRetemplatedComponent { } }) export class IgxTimePickerCustomLabelComponent { public customLabel = true; - public mode = InteractionMode.DropDown; + public mode = PickerInteractionMode.DropDown; } @@ -2401,7 +2401,7 @@ export class IgxTimePickerDropDownComponent { export class IgxTimePickerDropDownSingleHourComponent { @ViewChild(IgxTimePickerComponent, { static: true }) public timePicker: IgxTimePickerComponent; public customDate = new Date(2018, 10, 27, 4, 5); - public mode = InteractionMode.DropDown; + public mode = PickerInteractionMode.DropDown; public format = 'H:m'; } @Component({ @@ -2414,7 +2414,7 @@ export class IgxTimePickerDropDownSingleHourComponent { }) export class IgxTimePickerDropDownNoValueComponent { @ViewChild(IgxTimePickerComponent, { static: true }) public timePicker: IgxTimePickerComponent; - public mode = InteractionMode.DropDown; + public mode = PickerInteractionMode.DropDown; } diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts index 60e188eff89..73b3648ae36 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts @@ -49,7 +49,7 @@ import { TimeDisplayFormatPipe, TimeInputFormatPipe } from './time-picker.pipes' import { ITimePickerResourceStrings } from '../core/i18n/time-picker-resources'; import { CurrentResourceStrings } from '../core/i18n/resources'; import { KEYS, IBaseEventArgs, IBaseCancelableBrowserEventArgs } from '../core/utils'; -import { InteractionMode } from '../core/enums'; +import { PickerInteractionMode } from '../date-common/types'; import { IgxTextSelectionModule } from '../directives/text-selection/text-selection.directive'; import { IgxLabelDirective } from '../directives/label/label.directive'; @@ -185,7 +185,7 @@ export class IgxTimePickerComponent implements * a dialog picker or dropdown with editable masked input. * Deafult is dialog picker. * ```html - * public mode = InteractionMode.DROPDOWN; + * public mode = PickerInteractionMode.DROPDOWN; * //.. * * ``` @@ -193,7 +193,7 @@ export class IgxTimePickerComponent implements * @memberof IgxTimePickerComponent */ @Input() - public mode: InteractionMode = InteractionMode.Dialog; + public mode: PickerInteractionMode = PickerInteractionMode.Dialog; /** * Determines the container the popup element should be attached to. @@ -551,7 +551,7 @@ export class IgxTimePickerComponent implements if (this.timePickerTemplateDirective) { return this.timePickerTemplateDirective.template; } - return this.mode === InteractionMode.Dialog ? this.defaultTimePickerTemplate : this.dropdownInputTemplate; + return this.mode === PickerInteractionMode.Dialog ? this.defaultTimePickerTemplate : this.dropdownInputTemplate; } /** @@ -638,7 +638,7 @@ export class IgxTimePickerComponent implements this._onChangeCallback(value); const dispVal = this._formatTime(this.value, this.format); - if (this.mode === InteractionMode.DropDown && this._displayValue !== dispVal) { + if (this.mode === PickerInteractionMode.DropDown && this._displayValue !== dispVal) { this.displayValue = dispVal; } @@ -810,7 +810,7 @@ export class IgxTimePickerComponent implements public get overlaySettings(): OverlaySettings { return this._overlaySettings ? this._overlaySettings : - (this.mode === InteractionMode.Dialog ? this._dialogOverlaySettings : this._dropDownOverlaySettings); + (this.mode === PickerInteractionMode.Dialog ? this._dialogOverlaySettings : this._dropDownOverlaySettings); } constructor( @@ -845,7 +845,7 @@ export class IgxTimePickerComponent implements this._value = value; - if (this.mode === InteractionMode.DropDown) { + if (this.mode === PickerInteractionMode.DropDown) { this.displayValue = this._formatTime(this.value, this.format); } } @@ -932,7 +932,7 @@ export class IgxTimePickerComponent implements * @hidden */ public ngAfterViewInit(): void { - if (this.mode === InteractionMode.DropDown && this._inputElementRef) { + if (this.mode === PickerInteractionMode.DropDown && this._inputElementRef) { fromEvent(this._inputElementRef.nativeElement, 'keydown').pipe( throttle(() => interval(0, animationFrameScheduler)), takeUntil(this._destroy$) @@ -950,7 +950,7 @@ export class IgxTimePickerComponent implements if (this.toggleRef) { this.toggleRef.onClosed.pipe(takeUntil(this._destroy$)).subscribe(() => { - if (this.mode === InteractionMode.DropDown) { + if (this.mode === PickerInteractionMode.DropDown) { this._onDropDownClosed(); } @@ -969,7 +969,7 @@ export class IgxTimePickerComponent implements } // Do not focus the input if clicking outside in dropdown mode const input = this.getEditElement(); - if (input && !(event.event && this.mode === InteractionMode.DropDown)) { + if (input && !(event.event && this.mode === PickerInteractionMode.DropDown)) { input.focus(); } else { this._updateValidityOnBlur(); @@ -1328,7 +1328,7 @@ export class IgxTimePickerComponent implements * ``` */ public cancelButtonClick(): void { - if (this.mode === InteractionMode.DropDown) { + if (this.mode === PickerInteractionMode.DropDown) { this.displayValue = this.value ? this._formatTime(this.value, this.format) : this.parseMask(false); } @@ -1501,7 +1501,7 @@ export class IgxTimePickerComponent implements * @hidden */ public onBlur(event): void { - if (this.mode === InteractionMode.DropDown) { + if (this.mode === PickerInteractionMode.DropDown) { const value = event.target.value; this.isNotEmpty = value !== ''; @@ -1995,7 +1995,7 @@ export class IgxTimePickerComponent implements } private _updateEditableInput(): void { - if (this.mode === InteractionMode.DropDown) { + if (this.mode === PickerInteractionMode.DropDown) { this.displayValue = this._formatTime(this._getSelectedTime(), this.format); } } diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.directives.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.directives.ts index c6deda9266f..160c429ec73 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.directives.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.directives.ts @@ -14,7 +14,7 @@ import { TemplateRef } from '@angular/core'; import { IGX_TIME_PICKER_COMPONENT, IgxTimePickerBase } from './time-picker.common'; -import { InteractionMode } from '../core/enums'; +import { PickerInteractionMode } from '../date-common/types'; /** @hidden */ @Directive({ @@ -136,7 +136,7 @@ export class IgxItemListDirective { public onKeydownEnter(event: KeyboardEvent) { event.preventDefault(); - if (this.timePicker.mode === InteractionMode.DropDown) { + if (this.timePicker.mode === PickerInteractionMode.DropDown) { this.timePicker.close(); return; } diff --git a/projects/igniteui-angular/src/public_api.ts b/projects/igniteui-angular/src/public_api.ts index b2a6551db94..3def0755fed 100644 --- a/projects/igniteui-angular/src/public_api.ts +++ b/projects/igniteui-angular/src/public_api.ts @@ -63,7 +63,7 @@ export * from './lib/carousel/carousel.component'; export * from './lib/checkbox/checkbox.component'; export * from './lib/chips/public_api'; export * from './lib/combo/public_api'; -export * from './lib/date-picker/date-picker.component'; +export * from './lib/date-picker/public_api'; export * from './lib/dialog/dialog.component'; export * from './lib/drop-down/public_api'; export * from './lib/grids/common/enums'; @@ -119,5 +119,5 @@ export { ICalendarResourceStrings } from './lib/core/i18n/calendar-resources'; export { ITimePickerResourceStrings } from './lib/core/i18n/time-picker-resources'; export { IDateRangePickerResourceStrings } from './lib/core/i18n/date-range-picker-resources'; export { IListResourceStrings } from './lib/core/i18n/list-resources'; -export { InteractionMode } from './lib/core/enums'; +export { PickerInteractionMode } from './lib/date-common/types'; export { SplitterType } from './lib/splitter/splitter.component'; diff --git a/src/app/date-picker/date-picker.sample.html b/src/app/date-picker/date-picker.sample.html index 3e138ecfb5c..029cb35cc77 100644 --- a/src/app/date-picker/date-picker.sample.html +++ b/src/app/date-picker/date-picker.sample.html @@ -1,97 +1,82 @@
-

Default Date Picker - dialog mode

-

Added buttons - today and cancel

+

Default Date Picker

- - + + - - - -
+
-

Default Date Picker

-

With Months View Number

+

Custom View Count And Validation

- + + + This field is required. + Value is not in range. +
- -
+
-

Default Date Picker

-

Custom formatter applied

+

Custom Formats

- + + +
+
-

Default Date Picker

-

Retemplated input group

+

Templated Icons And Special Dates

- - - - - - - + + + + calendar_view_day + + + delete +
-

DropDown Date Picker

-

Retemplated Date Picker using #dropDownTarget

+

Additional Icons And Custom Buttons

- - - - - - alarm - - - - + + + + alarm + +
- +
-
-

DropDown Date Picker

-

Format: dd.MM.y / mask: M/d/y

+

Date Picker With Disabled Dates

- + + -
-

DropDown Date Picker bind with ngModel

-

isSpinLoop set to false

+

Date Picker Bound With ngModel And With Default Buttons

- + -
diff --git a/src/app/date-picker/date-picker.sample.ts b/src/app/date-picker/date-picker.sample.ts index 3bc481e6b74..e68f84e499e 100644 --- a/src/app/date-picker/date-picker.sample.ts +++ b/src/app/date-picker/date-picker.sample.ts @@ -1,6 +1,6 @@ import { Component, ViewChild } from '@angular/core'; import { IgxDatePickerComponent } from 'igniteui-angular'; -import { formatDate } from '@angular/common'; +import { DateRangeDescriptor, DateRangeType } from 'projects/igniteui-angular/src/lib/core/dates/dateRange'; @Component({ selector: 'app-date-picker-sample', @@ -9,88 +9,17 @@ import { formatDate } from '@angular/common'; }) export class DatePickerSampleComponent { - @ViewChild('datepicker1', { read: IgxDatePickerComponent, static: true }) - private datepicker1: IgxDatePickerComponent; - - @ViewChild('retemplated', { static: true }) - private retemplatedDP; - - @ViewChild('datePicker', { static: true }) - private dp: IgxDatePickerComponent; - - public date = new Date('10/3/2018'); - public formatOptions = { - day: 'numeric', - month: 'long', - weekday: 'short', - year: 'numeric' - }; - public date1; - public date2; - public date3; - public date4; - public testDate = new Date(2000, 3, 5); - - public date5 = new Date('10/5/2020'); - - public range = [ - new Date(new Date().getFullYear(), new Date().getMonth(), 3), - new Date(new Date().getFullYear(), new Date().getMonth(), 8) - ]; - - constructor() { - const date1 = new Date(); - date1.setDate(8); - date1.setMonth(5); - date1.setFullYear(1978); - - const date2 = new Date(); - date2.setDate(6); - date2.setMonth(4); - date2.setFullYear(2020); - - const date3 = new Date(); - date3.setDate(14); - date3.setMonth(10); - date3.setFullYear(2021); - - this.date1 = date1; - this.date2 = date2; - this.date3 = date3; - this.date4 = date3; - } - - public formatter = (_: Date) => _.toLocaleString('en'); - - public deselect(datePicker) { - datePicker.deselectDate(); - } - - public setMonthsViewNumber(args: HTMLInputElement) { - this.datepicker1.monthsViewNumber = parseInt(args.value, 10); - } - - public changeDate(event) { - const input = event.target.value; - const dateParts = input.split(new RegExp('[^0-9]', 'g')).filter((part) => part !== ''); - - let date = ''; - for (let i = 0; i < dateParts.length; i++) { - date += dateParts[i]; - if (i !== dateParts.length - 1) { - date += '/'; - } - } - - const parsedDate = (date !== '') ? new Date(formatDate(date, this.retemplatedDP.format, this.retemplatedDP.locale)) : ''; - this.retemplatedDP.value = parsedDate; - } - - public selectToday(picker: IgxDatePickerComponent) { - picker.calendar.value = picker.calendar.viewDate = new Date(Date.now()); - } - - public d() { - this.dp.triggerTodaySelection(); - } + @ViewChild('dp5') + public datePicker: IgxDatePickerComponent; + + public date1 = new Date(); + public date2 = new Date(new Date(this.date1.getFullYear(), this.date1.getMonth(), this.date1.getDate() + 1)); + public date3 = new Date(new Date(this.date2.getFullYear(), this.date2.getMonth(), this.date2.getDate() + 1)); + public date4 = new Date(new Date(this.date3.getFullYear(), this.date3.getMonth(), this.date3.getDate() + 1)); + public date5 = new Date(new Date(this.date4.getFullYear(), this.date4.getMonth(), this.date4.getDate() + 1)); + public date6 = new Date(new Date(this.date5.getFullYear(), this.date5.getMonth(), this.date5.getDate() + 1)); + public date7 = new Date(new Date(this.date6.getFullYear(), this.date6.getMonth(), this.date6.getDate() + 1)); + + public disabledDates: DateRangeDescriptor[] = [{ type: DateRangeType.Specific, dateRange: [this.date1, this.date2, this.date3] }]; + public specialDates: DateRangeDescriptor[] = [{ type: DateRangeType.Specific, dateRange: [this.date5, this.date6, this.date7] }]; } diff --git a/src/app/time-picker/time-picker.sample.html b/src/app/time-picker/time-picker.sample.html index 460da44d86d..07142a91bc9 100644 --- a/src/app/time-picker/time-picker.sample.html +++ b/src/app/time-picker/time-picker.sample.html @@ -4,7 +4,7 @@

Time Picker with Dropdown

{{showDate(date)}}
diff --git a/src/app/time-picker/time-picker.sample.ts b/src/app/time-picker/time-picker.sample.ts index 6eef0e3cf05..cde77b99e4e 100644 --- a/src/app/time-picker/time-picker.sample.ts +++ b/src/app/time-picker/time-picker.sample.ts @@ -1,5 +1,5 @@ import { Component, ViewChild, AfterViewInit } from '@angular/core'; -import { IgxTimePickerComponent, InteractionMode, IgxInputDirective, AutoPositionStrategy, OverlaySettings } from 'igniteui-angular'; +import { IgxTimePickerComponent, PickerInteractionMode, IgxInputDirective, AutoPositionStrategy, OverlaySettings } from 'igniteui-angular'; @Component({ selector: 'app-time-picker-sample', @@ -20,7 +20,7 @@ export class TimePickerSampleComponent implements AfterViewInit { public format = 'hh:mm:ss tt'; public isSpinLoop = true; public isVertical = true; - public mode: InteractionMode = InteractionMode.DropDown; + public mode = PickerInteractionMode.DropDown; public date1 = new Date(2018, 10, 27, 17, 45, 0, 0); public date = new Date(2018, 10, 27, 9, 45, 0, 0);