From 73118b8ff794778680bd09e66c1f5fb20c0b4742 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Fri, 30 Nov 2018 11:11:09 +0200 Subject: [PATCH 01/57] feat(date-picker): Initial changes to support edit field #3034 --- .../src/lib/calendar/calendar.component.html | 11 +- .../src/lib/calendar/calendar.component.ts | 3 + .../date-picker/date-picker.component.html | 60 +- .../lib/date-picker/date-picker.component.ts | 550 +++++++++++------- src/app/date-picker/date-picker.sample.html | 16 +- 5 files changed, 419 insertions(+), 221 deletions(-) diff --git a/projects/igniteui-angular/src/lib/calendar/calendar.component.html b/projects/igniteui-angular/src/lib/calendar/calendar.component.html index a9ecf93d246..b70553e0022 100644 --- a/projects/igniteui-angular/src/lib/calendar/calendar.component.html +++ b/projects/igniteui-angular/src/lib/calendar/calendar.component.html @@ -12,7 +12,7 @@ -
+
{{ formattedYear(headerDate) }}

@@ -20,7 +20,8 @@

-
+
keyboard_arrow_left @@ -40,8 +41,8 @@

-
+
{{ formattedDate(day.date) }} @@ -62,4 +63,4 @@

{{ formattedYear(year) }}

-
+
\ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/calendar/calendar.component.ts b/projects/igniteui-angular/src/lib/calendar/calendar.component.ts index 231345176a7..3c4c2b96f93 100644 --- a/projects/igniteui-angular/src/lib/calendar/calendar.component.ts +++ b/projects/igniteui-angular/src/lib/calendar/calendar.component.ts @@ -281,6 +281,9 @@ export class IgxCalendarComponent implements OnInit, ControlValueAccessor { this.initFormatters(); } + @Input() + public hasHeader = true; + /** * Gets whether the `day`, `month` and `year` should be rendered * according to the locale and formatOptions, if any. 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 8137960bdc0..0bcd997bb6a 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,16 +1,46 @@ -
- - - - today - - - - - - - - - + +
+ + + + today + + + + + + +
+
+ + + + + + + clear + + + today + + + + +
+ + +
+ + +
+ + \ No newline at end of file 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 f1ac34d5ace..0e050f2600a 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,7 +1,6 @@ import { CommonModule } from '@angular/common'; import { Component, - ComponentFactoryResolver, ComponentRef, ContentChild, EventEmitter, @@ -16,7 +15,8 @@ import { HostListener, ElementRef, TemplateRef, - Directive + Directive, + Inject } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { @@ -26,25 +26,33 @@ import { IgxCalendarSubheaderTemplateDirective, WEEKDAYS } from '../calendar/index'; -import { IgxDialogComponent, IgxDialogModule } from '../dialog/dialog.component'; import { IgxIconModule } from '../icon/index'; -import { IgxInputGroupModule, IgxInputDirective } from '../input-group/index'; +import { IgxInputGroupModule } from '../input-group/index'; import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { filter, takeUntil } from 'rxjs/operators'; import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive'; -import { OverlaySettings } from '../services'; +import { + OverlaySettings, + IgxOverlayService, + VerticalAlignment, + HorizontalAlignment, + PositionSettings, + ConnectedPositioningStrategy +} from '../services'; import { DeprecateClass } from '../core/deprecateDecorators'; 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 { IgxFocusModule } from '../directives/focus/focus.directive'; @Directive({ selector: '[igxDatePickerTemplate]' }) export class IgxDatePickerTemplateDirective { - constructor(public template: TemplateRef) {} + constructor(public template: TemplateRef) { } } - export interface IFormatViews { day?: boolean; month?: boolean; @@ -57,7 +65,14 @@ export interface IFormatOptions { weekday?: string; year?: string; } + let NEXT_ID = 0; + +export enum InteractionMode { + EDITABLE = 'editable', + READONLY = 'readonly' +} + /** * **Ignite UI for Angular Date Picker** - * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/date_picker.html) @@ -79,90 +94,6 @@ let NEXT_ID = 0; }) @DeprecateClass('\'igx-datePicker\' selector is deprecated. Use \'igx-date-picker\' selector instead.') export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvider, OnInit, OnDestroy { - /** - *An @Input property that sets the value of `id` attribute. If not provided it will be automatically generated. - *```html - * - *``` - */ - @HostBinding('attr.id') - @Input() - public id = `igx-date-picker-${NEXT_ID++}`; - - /** - *An @Input property that applies custom formatter on the selected or passed date. - *```typescript - *public date: Date = new Date(); - *private dayFormatter = new Intl.DateTimeFormat("en", { weekday: "long" }); - *private monthFormatter = new Intl.DateTimeFormat("en", { month: "long" }); - *public formatter = (date: Date) => { return `You selected - * ${this.dayFormatter.format(date)}, - * ${date.getDate()} ${this.monthFormatter.format(date)}, - * ${date.getFullYear()}`; - *} - *``` - *```html - * - *``` - */ - @Input() - public formatter: (val: Date) => string; - - /** - *An @Input property that disables the `IgxDatePickerComponent`. - *```html - * - * ``` - */ - @Input() - public disabled: boolean; - - /** - *An @Input property that sets the selected date. - *```typescript - *public date: Date = new Date(); - *``` - *```html - * - *``` - */ - @Input() - public value: Date; - - /** - * An @Input property that sets the `IgxDatePickerComponent` label. - * The default label is 'Date'. - * ```html - * - * ``` - */ - @Input() - public label = 'Date'; - - /** - * An @Input property that sets the `IgxDatePickerComponent` label visibility. - * By default the visibility is set to true. - * - */ - @Input() - public labelVisibility = true; - - /** - *An @Input property that sets locales. Default locale is en. - *```html - * - *``` - */ - @Input() public locale: string = Constants.DEFAULT_LOCALE_DATE; - - /** - *An @Input property that sets on which day the week starts. - *```html - * - *``` - */ - @Input() public weekStart: WEEKDAYS | number = WEEKDAYS.SUNDAY; - /** *Returns the format options of the `IgxDatePickerComponent`. *```typescript @@ -177,6 +108,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi public get formatOptions(): IFormatOptions { return this._formatOptions; } + /** *Sets the format options of the `IgxDatePickerComponent`. *```typescript @@ -281,6 +213,150 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi this._specialDates = value; } + /** + *Returns the formatted date. + *```typescript + *@ViewChild("MyDatePicker") + *public datePicker: IgxDatePickerComponent; + *public selection(event){ + * let selectedDate = this.datePicker.displayData; + * alert(selectedDate); + *} + *``` + *```html + * + *``` + */ + public get displayData() { + if (this.value) { + return this._customFormatChecker(this.formatter, this.value); + } + + return ''; + } + + constructor(@Inject(IgxOverlayService) private overlayService: IgxOverlayService) { } + + /** + * Gets the input group template. + * ```typescript + * let template = this.template(); + * ``` + * @memberof IgxTimePickerComponent + */ + get template(): TemplateRef { + if (this.datePickerTemplateDirective) { + return this.datePickerTemplateDirective.template; + } + return this.defaultDatePickerTemplate; + } + + /** + * Gets the input group template. + * ```typescript + * let template = this.template(); + * ``` + * @memberof IgxTimePickerComponent + */ + get datePickerTemplate(): TemplateRef { + return (this.mode === InteractionMode.READONLY) ? this.readOnlyDatePickerTemplate : this.editableDatePickerTemplate; + } + + /** + * Gets the context passed to the input group template. + * @memberof IgxTimePickerComponent + */ + get context() { + return { + value: this.value, + displayData: this.displayData, + openCalendar: () => { this.openCalendar(); } + }; + } + /** + *An @Input property that sets the value of `id` attribute. If not provided it will be automatically generated. + *```html + * + *``` + */ + @HostBinding('attr.id') + @Input() + public id = `igx-date-picker-${NEXT_ID++}`; + + /** + *An @Input property that applies custom formatter on the selected or passed date. + *```typescript + *public date: Date = new Date(); + *private dayFormatter = new Intl.DateTimeFormat("en", { weekday: "long" }); + *private monthFormatter = new Intl.DateTimeFormat("en", { month: "long" }); + *public formatter = (date: Date) => { return `You selected + * ${this.dayFormatter.format(date)}, + * ${date.getDate()} ${this.monthFormatter.format(date)}, + * ${date.getFullYear()}`; + *} + *``` + *```html + * + *``` + */ + @Input() + public formatter: (val: Date) => string; + + /** + *An @Input property that disables the `IgxDatePickerComponent`. + *```html + * + * ``` + */ + @Input() + public disabled: boolean; + + /** + *An @Input property that sets the selected date. + *```typescript + *public date: Date = new Date(); + *``` + *```html + * + *``` + */ + @Input() + public value: Date; + + /** + * An @Input property that sets the `IgxDatePickerComponent` label. + * The default label is 'Date'. + * ```html + * + * ``` + */ + @Input() + public label = 'Date'; + + /** + * An @Input property that sets the `IgxDatePickerComponent` label visibility. + * By default the visibility is set to true. + * + */ + @Input() + public labelVisibility = true; + + /** + *An @Input property that sets locales. Default locale is en. + *```html + * + *``` + */ + @Input() public locale: string = Constants.DEFAULT_LOCALE_DATE; + + /** + *An @Input property that sets on which day the week starts. + *```html + * + *``` + */ + @Input() public weekStart: WEEKDAYS | number = WEEKDAYS.SUNDAY; + /** *An @Input proeprty that sets the orientation of the `IgxDatePickerComponent` header. *```html @@ -308,11 +384,14 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi @Input() public cancelButtonLabel: string; + @Input() + public mode = InteractionMode.READONLY; + /** - *An event that is emitted when the `IgxDatePickerComponent` is opened. + *An event that is emitted when the `IgxDatePickerComponent` calendar is opened. *```typescript *public open(event){ - * alert("The date-picker has been opened!"); + * alert("The date-picker calendar has been opened!"); *} *``` *```html @@ -355,33 +434,32 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi @ViewChild('defaultDatePickerTemplate', { read: TemplateRef }) protected defaultDatePickerTemplate: TemplateRef; + /* + * @hidden + */ + @ViewChild('readOnlyDatePickerTemplate', { read: TemplateRef }) + protected readOnlyDatePickerTemplate: TemplateRef; + + /* + * @hidden + */ + @ViewChild('editableDatePickerTemplate', { read: TemplateRef }) + protected editableDatePickerTemplate: TemplateRef; + /** *@hidden */ @ContentChild(IgxDatePickerTemplateDirective, { read: IgxDatePickerTemplateDirective }) protected datePickerTemplateDirective: IgxDatePickerTemplateDirective; - /** - *Returns the formatted date. - *```typescript - *@ViewChild("MyDatePicker") - *public datePicker: IgxDatePickerComponent; - *public selection(event){ - * let selectedDate = this.datePicker.displayData; - * alert(selectedDate); - *} - *``` - *```html - * - *``` - */ - public get displayData() { - if (this.value) { - return this._customFormatChecker(this.formatter, this.value); - } + @ViewChild('editableInput', { read: ElementRef }) + protected editableInput: ElementRef; - return ''; - } + @ViewChild('readonlyInput', { read: ElementRef }) + protected readonlyInput: ElementRef; + + @ViewChild('calendar') + protected calendar: IgxCalendarComponent; /** *@hidden @@ -404,8 +482,8 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi /** *@hidden */ - @ViewChild(IgxDialogComponent) - public alert: IgxDialogComponent; + @ViewChild('calendarContainer') + public calendarContainer: ElementRef; /** *@hidden @@ -421,11 +499,12 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi /** *@hidden */ - public get calendar() { - return this.calendarRef.instance; - } + @Input() + public calendarOutlet: IgxOverlayOutletDirective | ElementRef; + + private _destroy$ = new Subject(); - protected destroy$ = new Subject(); + private _componentID; private _formatOptions = { day: 'numeric', @@ -444,9 +523,14 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi private _specialDates: DateRangeDescriptor[] = null; - @ViewChild(IgxInputDirective) protected input: IgxInputDirective; + private _positionSettings: PositionSettings; + private _dropDownOverlaySettings: OverlaySettings; + private _modalOverlaySettings: OverlaySettings; + private _collapsed = true; - constructor(private resolver: ComponentFactoryResolver) { } + public inputDate = ''; + public isCalendarVisible = false; + public hasHeader = true; /** *Method that sets the selected date. @@ -477,23 +561,54 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi /** @hidden */ getEditElement() { - return this.input.nativeElement; + return ((this.mode === InteractionMode.READONLY) ? this.readonlyInput : this.editableInput).nativeElement; } /** *@hidden */ public ngOnInit(): void { - this.alert.onOpen.pipe(takeUntil(this.destroy$)).subscribe((ev) => this._focusCalendarDate()); - this.alert.toggleRef.onClosed.pipe(takeUntil(this.destroy$)).subscribe((ev) => this.handleDialogCloseAction()); + // this.alert.onOpen.pipe(takeUntil(this.destroy$)).subscribe((ev) => this._focusCalendarDate()); + // this.alert.toggleRef.onClosed.pipe(takeUntil(this.destroy$)).subscribe((ev) => this.handleDialogCloseAction()); + + this._positionSettings = { + horizontalDirection: HorizontalAlignment.Right, + verticalDirection: VerticalAlignment.Bottom, + }; + + this._dropDownOverlaySettings = { + closeOnOutsideClick: true, + modal: false, + positionStrategy: new ConnectedPositioningStrategy(this._positionSettings), + // outlet: this.outlet + }; + + this._modalOverlaySettings = { + closeOnOutsideClick: true, + modal: true, + // outlet: this.outlet + }; + + 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(); + }); } /** *@hidden */ public ngOnDestroy(): void { - this.destroy$.next(true); - this.destroy$.complete(); + this.overlayService.hide(this._componentID); + this._destroy$.next(true); + this._destroy$.complete(); } /** @@ -552,29 +667,54 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi * * @hidden */ - public openDialog(): void { - this.createCalendarRef(); - if (this.outlet) { - const overlaySettings: OverlaySettings = { - outlet: this.outlet - }; - this.alert.open(overlaySettings); - } else { - this.alert.open(); + public openCalendar(): void { + this.isCalendarVisible = true; + switch (this.mode) { + case InteractionMode.READONLY: { + this.hasHeader = true; + requestAnimationFrame(() => { + this._componentID = this.overlayService.show(this.calendarContainer, this._modalOverlaySettings); + }); + + break; + } + case InteractionMode.EDITABLE: { + if (this._collapsed) { + this._dropDownOverlaySettings.positionStrategy.settings.target = this.editableInput.nativeElement; + this.hasHeader = false; + requestAnimationFrame(() => { + this._componentID = this.overlayService.show(this.calendarContainer, this._dropDownOverlaySettings); + }); + } + + break; + } } - this._onTouchedCallback(); - this.onOpen.emit(this); } - private createCalendarRef(): void { - const factory = this.resolver.resolveComponentFactory(IgxCalendarComponent); + public closeCalendar() { + this.overlayService.hide(this._componentID); + } - this.calendarRef = this.container.createComponent(factory); + public clear() { + // TODO - clear selected date? + this.value = undefined; + } - this.calendarRef.changeDetectorRef.detach(); - this.updateCalendarInstance(); - this.calendarRef.location.nativeElement.classList.add('igx-date-picker__date--opened'); - this.calendarRef.changeDetectorRef.reattach(); + public calculateDate(data: string) { + // debugger; + const isValid = this.isDateValid(data); + if (isValid) { + this.value = new Date(data); + } else { + this.value = undefined; + } + } + + private isDateValid(data) { + const isValid = (new Date(data).toLocaleString(this.locale) !== 'Invalid Date'); + // debugger; + return isValid; } /** @@ -582,13 +722,13 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi * * @hidden */ - public handleDialogCloseAction() { - this.onClose.emit(this); - this.calendarRef.destroy(); - if (this.input) { - this.input.nativeElement.focus(); - } - } + // public handleDialogCloseAction() { + // this.onClose.emit(this); + // this.calendarRef.destroy(); + // if (this.input) { + // this.input.nativeElement.focus(); + // } + // } /** * Evaluates when @calendar.onSelection event was fired @@ -609,68 +749,85 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi this.value = date; this.calendar.viewDate = date; this._onChangeCallback(date); - this.alert.close(); + + this.closeCalendar(); + this.onSelection.emit(date); } - @HostListener('keydown.spacebar', ['$event']) - @HostListener('keydown.space', ['$event']) - public onSpaceClick(event) { - this.openDialog(); + public handleInput(eventArgs) { + // debugger; + this.calculateDate(eventArgs.target.value); + } + + @HostListener('keydown.alt.arrowdown', ['$event']) + public onAltArrowDownKeydown(event: KeyboardEvent) { + // debugger; event.preventDefault(); + event.stopPropagation(); + this.calculateDate(this.editableInput.nativeElement.value); + this.openCalendar(); } - /** - * Gets the input group template. - * ```typescript - * let template = this.template(); - * ``` - * @memberof IgxTimePickerComponent - */ - get template(): TemplateRef { - if (this.datePickerTemplateDirective) { - return this.datePickerTemplateDirective.template; - } - return this.defaultDatePickerTemplate; + @HostListener('keydown.alt.arrowup', ['$event']) + public onAltArrowUpKeydown(event: KeyboardEvent) { + console.log('onAltArrowUpKeydown'); + event.preventDefault(); + event.stopPropagation(); + this.closeCalendar(); } - /** - * Gets the context passed to the input group template. - * @memberof IgxTimePickerComponent - */ - get context() { - return { - value: this.value, - displayData: this.displayData, - openDialog: () => { this.openDialog(); } - }; + @HostListener('keydown.esc', ['$event']) + public onEscKeydown(event) { + console.log('onEscKeydown'); + event.preventDefault(); + event.stopPropagation(); + this.closeCalendar(); } - private updateCalendarInstance() { - this.calendar.formatOptions = this._formatOptions; - this.calendar.formatViews = this._formatViews; - this.calendar.locale = this.locale; - this.calendar.vertical = this.vertical; - this.calendar.disabledDates = this.disabledDates; - this.calendar.specialDates = this.specialDates; - if (this.headerTemplate) { - this.calendar.headerTemplate = this.headerTemplate; - } - if (this.subheaderTemplate) { - this.calendar.subheaderTemplate = this.subheaderTemplate; - } + + public onKeyDown() { + debugger; + } + + @HostListener('keydown.spacebar', ['$event']) + @HostListener('keydown.space', ['$event']) + public onSpaceClick(event) { + this.openCalendar(); + event.preventDefault(); + } + + private onOpened() { + // debugger; + this._collapsed = false; if (this.value) { this.calendar.value = this.value; this.calendar.viewDate = this.value; } - this.calendar.weekStart = this.weekStart; - this.calendar.onSelection.pipe(takeUntil(this.destroy$)).subscribe((ev: Date) => this.handleSelection(ev)); + + this._onTouchedCallback(); + this.onOpen.emit(this); + + if (this.calendar && this.value) { + this._focusCalendarDate(); + } + } + + private onClosed() { + debugger; + this.isCalendarVisible = false; + this._collapsed = true; + this.onClose.emit(this); + + if (this.editableInput) { + this.editableInput.nativeElement.focus(); + } } - // Focus a date, after the celendar appearence into DOM. + // Focus a date, after the calendar appearence into DOM. private _focusCalendarDate() { requestAnimationFrame(() => { this.calendar.focusActiveDate(); @@ -704,8 +861,7 @@ class Constants { */ @NgModule({ declarations: [IgxDatePickerComponent, IgxDatePickerTemplateDirective], - entryComponents: [IgxCalendarComponent], exports: [IgxDatePickerComponent, IgxDatePickerTemplateDirective], - imports: [CommonModule, IgxIconModule, IgxInputGroupModule, IgxDialogModule, IgxCalendarModule] + imports: [CommonModule, IgxIconModule, IgxInputGroupModule, IgxCalendarModule, IgxButtonModule, IgxRippleModule, IgxFocusModule] }) export class IgxDatePickerModule { } diff --git a/src/app/date-picker/date-picker.sample.html b/src/app/date-picker/date-picker.sample.html index da562e90135..d2659056a90 100644 --- a/src/app/date-picker/date-picker.sample.html +++ b/src/app/date-picker/date-picker.sample.html @@ -9,6 +9,14 @@

Default Date Picker.

+ +
+

Editable Date Picker

+
+ +
+
+

Date Picker with passed date.

Detailed description to be added.

@@ -28,15 +36,15 @@

Date Picker with retemplated input group.

Detailed description to be added.

- - + + - +
-
+
\ No newline at end of file From 59caea3c5cd1339852a1d9dba6ba9fdabe3f80a9 Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Wed, 5 Dec 2018 15:02:58 +0200 Subject: [PATCH 02/57] refactor(themes): update date-picker theme after refactor --- .../date-picker/_date-picker-component.scss | 14 +- .../date-picker/_date-picker-theme.scss | 131 +++++++++++++++--- .../src/lib/core/styles/themes/_index.scss | 2 +- .../themes/schemas/dark/_date-picker.scss | 14 -- .../styles/themes/schemas/dark/_index.scss | 3 - .../themes/schemas/light/_date-picker.scss | 10 -- .../styles/themes/schemas/light/_index.scss | 3 - .../date-picker/date-picker.component.html | 42 +++--- 8 files changed, 140 insertions(+), 79 deletions(-) delete mode 100644 projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_date-picker.scss delete mode 100644 projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_date-picker.scss diff --git a/projects/igniteui-angular/src/lib/core/styles/components/date-picker/_date-picker-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/date-picker/_date-picker-component.scss index 91beb7f050e..ef55163463c 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/date-picker/_date-picker-component.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/date-picker/_date-picker-component.scss @@ -9,13 +9,17 @@ $this: bem--selector-to-string(&); @include register-component(str-slice($this, 2, -1)); - .igx-dialog__window { - @extend %date-picker-display !optional; + @extend %date-picker !optional; + + @include e(buttons) { + @extend %date-picker__buttons !optional; } @include m(vertical) { - .igx-dialog__window { - @extend %date-picker-display--vertical !optional; - } + @extend %date-picker--vertical !optional; + } + + @include m(dropdown) { + @extend %date-picker--dropdown !optional; } } diff --git a/projects/igniteui-angular/src/lib/core/styles/components/date-picker/_date-picker-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/date-picker/_date-picker-theme.scss index 2b407f6949c..e149e0da6a0 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/date-picker/_date-picker-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/date-picker/_date-picker-theme.scss @@ -5,31 +5,116 @@ /// @author Marin Popov //// -/// Date Picker -/// @param {Map} $palette [$default-palette] - The palette used as basis for styling the component. -/// @param {Map} $schema [$light-schema] - The schema used as basis for styling the component. -/// -/// @requires $default-palette -/// @requires $light-schema -/// @requires apply-palette -/// @requires extend -@function igx-date-picker-theme( - $palette: $default-palette, - $schema: $light-schema -) { - $name: 'igx-date-picker'; - $theme: apply-palette(map-get($schema, $name), $palette); - @return extend($theme, (name: $name, palette: $palette)); -} - -/// @param {Map} $theme - The theme used to style the component. +/// @param {Map} $theme - The calendar theme used to style the component. +/// @requires $elevations +/// @requires igx-elevation +/// @requires rem @mixin igx-date-picker($theme) { - %date-picker-display { - width: 340px; + %date-picker { + min-width: 200px; + max-width: 340px; + box-shadow: igx-elevation($elevations, 24); + border-radius: rem(4px); + background: --var($theme, 'content-background'); + overflow: hidden; + + %cal-display { + background: --var($theme, 'content-background'); + } + + %cal-header-display { + background-color: --var($theme, 'header-background'); + color: --var($theme, 'header-text-color'); + } + + %cal-picker-arrow { + color: --var($theme, 'picker-arrow-color'); + + &:hover { + color: --var($theme, 'picker-arrow-hover-color'); + } + } + + %cal-picker-date { + color: --var($theme, 'picker-text-color'); + + &:hover, + &:focus { + color: --var($theme, 'picker-text-hover-color'); + } + } + + %cal-value { + color: --var($theme, 'content-text-color'); + } + + %cal-value--label { + color: --var($theme, 'inactive-text-color'); + } + + %cal-value--weekend { + color: --var($theme, 'weekend-text-color'); + } + + %cal-value--special { + color: --var($theme, 'date-special-text-color'); + background: --var($theme, 'date-special-background'); + } + + %cal-value--disabled { + color: --var($theme, 'date-disabled-text-color'); + } + + %cal-value--year-current { + color: --var($theme, 'year-current-text-color'); + } + + %cal-value--year-hover { + color: --var($theme, 'year-hover-text-color'); + } + + %cal-value--month-hover { + color: --var($theme, 'month-hover-text-color'); + } + + %cal-value--month-current { + color: --var($theme, 'month-current-text-color'); + } + + %cal-value--inactive { + color: --var($theme, 'inactive-text-color'); + } + + %cal-value--selected { + color: --var($theme, 'date-selected-text-color'); + background-color: --var($theme, 'date-selected-background') !important; + } + + %cal-value--current { + color: --var($theme, 'date-current-text-color'); + } + + %cal-value--hover { + background-color: --var($theme, 'date-hover-background'); + } } - %date-picker-display--vertical { - width: 540px; + %date-picker--vertical { + min-width: 368px; /* 168px for header + 200px for the content */ + max-width: 540px; + } + + %date-picker--dropdown { + display: flex; + flex: 1 0 0; + border-top-left-radius: 0; + border-top-right-radius: 0; + box-shadow: igx-elevation($elevations, 3); } -} + %date-picker__buttons { + display: flex; + justify-content: flex-end; + padding: rem(8px); + } +} diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/_index.scss b/projects/igniteui-angular/src/lib/core/styles/themes/_index.scss index 13268b17f62..90d2d353b18 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/_index.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/_index.scss @@ -164,7 +164,7 @@ } @if not(index($exclude, 'igx-date-picker')) { - @include igx-date-picker(igx-date-picker-theme($palette, $schema)); + @include igx-date-picker(igx-calendar-theme($palette, $schema)); } @if not(index($exclude, 'igx-dialog')) { diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_date-picker.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_date-picker.scss deleted file mode 100644 index a97d3065710..00000000000 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_date-picker.scss +++ /dev/null @@ -1,14 +0,0 @@ -@import '../light/date-picker'; - -//// -/// @group schemas -/// @access private -/// @author Simeon Simeonoff -//// - -/// Generates a dark date picker schema. -/// @type {Map} -/// @requires extend -/// @requires $_light-date-picker -/// @see $default-palette -$_dark-date-picker: extend($_light-date-picker, ()); diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_index.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_index.scss index 2a96244a1df..8b61bf67140 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_index.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_index.scss @@ -11,7 +11,6 @@ @import './chip'; @import './column-hiding'; @import './combo'; -@import './date-picker'; @import './dialog'; @import './drop-down'; @import './expansion-panel'; @@ -52,7 +51,6 @@ /// @property {Map} igx-chip [$_dark-chip] /// @property {Map} igx-column-hiding [$_dark-column-hiding] /// @property {Map} igx-combo [$_dark-combo] -/// @property {Map} igx-date-picker [$_dark-date-picker] /// @property {Map} igx-dialog [$_dark-dialog] /// @property {Map} igx-drop-down [$_dark-drop-down] /// @property {Map} igx-expansion-panel [$_dark-expansion-panel] @@ -93,7 +91,6 @@ $dark-schema: ( igx-chip: $_dark-chip, igx-column-hiding: $_dark-column-hiding, igx-combo: $_dark-combo, - igx-date-picker: extend($_dark-calendar, $_dark-date-picker), igx-dialog: $_dark-dialog, igx-drop-down: $_dark-drop-down, igx-expansion-panel: $_dark-expansion-panel, diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_date-picker.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_date-picker.scss deleted file mode 100644 index 1a2fe5b578c..00000000000 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_date-picker.scss +++ /dev/null @@ -1,10 +0,0 @@ -//// -/// @group schemas -/// @access private -/// @author Simeon Simeonoff -//// - -/// Generates a light date picker schema. -/// @type {Map} -/// @see $default-palette -$_light-date-picker: (); diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_index.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_index.scss index 3bd6057c9db..c64343fdc64 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_index.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_index.scss @@ -17,7 +17,6 @@ @import './chip'; @import './column-hiding'; @import './combo'; -@import './date-picker'; @import './dialog'; @import './drop-down'; @import './expansion-panel'; @@ -58,7 +57,6 @@ /// @property {Map} igx-chip [$_light-chip] /// @property {Map} igx-column-hiding [$_light-column-hiding] /// @property {Map} igx-combo [$_light-combo] -/// @property {Map} igx-date-picker [$_light-date-picker] /// @property {Map} igx-dialog [$_light-dialog] /// @property {Map} igx-drop-down [$_light-drop-down] /// @property {Map} igx-expansion-panel [$_light-expansion-panel] @@ -99,7 +97,6 @@ $light-schema: ( igx-chip: $_light-chip, igx-column-hiding: $_light-column-hiding, igx-combo: $_light-combo, - igx-date-picker: extend($_light-calendar, $_light-date-picker), igx-dialog: $_light-dialog, igx-drop-down: $_light-drop-down, igx-expansion-panel: $_light-expansion-panel, 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 0bcd997bb6a..da0aeed72a6 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,17 +1,15 @@ -
- - - - today - - - - - - -
+ + + + today + + + + + +
@@ -19,21 +17,25 @@ - + clear - + today -
+
+ [formatViews]="formatViews" [locale]="locale" [vertical]="vertical && mode !== 'editable'" [weekStart]="weekStart" (onSelection)="handleSelection($event)"> -
+
@@ -43,4 +45,4 @@
- \ No newline at end of file + From 2859891b730b3b7abaec2f9ad3bcd5de41ef2828 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Wed, 5 Dec 2018 18:56:59 +0200 Subject: [PATCH 03/57] feat(date-picker): Fixes in editable date-picker #3034 --- .../date-picker/date-picker.component.html | 44 ++++--- .../lib/date-picker/date-picker.component.ts | 113 ++++++------------ src/app/date-picker/date-picker.sample.html | 4 +- 3 files changed, 60 insertions(+), 101 deletions(-) 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 da0aeed72a6..56954396122 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,40 +1,38 @@ - - - - today - - - - - - + + + today + + + + - + + + today + + (onclick)="closeCalendar()" /> clear - - today - -
+ +
- + -
-
- - +
\ No newline at end of file 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 0e050f2600a..9daa3490f29 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 @@ -44,7 +44,6 @@ 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 { IgxFocusModule } from '../directives/focus/focus.directive'; @Directive({ selector: '[igxDatePickerTemplate]' @@ -248,17 +247,6 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi if (this.datePickerTemplateDirective) { return this.datePickerTemplateDirective.template; } - return this.defaultDatePickerTemplate; - } - - /** - * Gets the input group template. - * ```typescript - * let template = this.template(); - * ``` - * @memberof IgxTimePickerComponent - */ - get datePickerTemplate(): TemplateRef { return (this.mode === InteractionMode.READONLY) ? this.readOnlyDatePickerTemplate : this.editableDatePickerTemplate; } @@ -428,12 +416,6 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi @Output() public onSelection = new EventEmitter(); - /** - * @hidden - */ - @ViewChild('defaultDatePickerTemplate', { read: TemplateRef }) - protected defaultDatePickerTemplate: TemplateRef; - /* * @hidden */ @@ -452,6 +434,9 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi @ContentChild(IgxDatePickerTemplateDirective, { read: IgxDatePickerTemplateDirective }) protected datePickerTemplateDirective: IgxDatePickerTemplateDirective; + @ViewChild('editableInputGroup', { read: ElementRef }) + protected editableInputGroup: ElementRef; + @ViewChild('editableInput', { read: ElementRef }) protected editableInput: ElementRef; @@ -520,17 +505,15 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi }; private _disabledDates: DateRangeDescriptor[] = null; - private _specialDates: DateRangeDescriptor[] = null; private _positionSettings: PositionSettings; private _dropDownOverlaySettings: OverlaySettings; private _modalOverlaySettings: OverlaySettings; - private _collapsed = true; public inputDate = ''; - public isCalendarVisible = false; public hasHeader = true; + public collapsed = true; /** *Method that sets the selected date. @@ -568,9 +551,6 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi *@hidden */ public ngOnInit(): void { - // this.alert.onOpen.pipe(takeUntil(this.destroy$)).subscribe((ev) => this._focusCalendarDate()); - // this.alert.toggleRef.onClosed.pipe(takeUntil(this.destroy$)).subscribe((ev) => this.handleDialogCloseAction()); - this._positionSettings = { horizontalDirection: HorizontalAlignment.Right, verticalDirection: VerticalAlignment.Bottom, @@ -600,13 +580,16 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi takeUntil(this._destroy$)).subscribe(() => { this.onClosed(); }); + + if (this.calendarContainer) { + this.calendarContainer.nativeElement.style.display = 'none'; + } } /** *@hidden */ public ngOnDestroy(): void { - this.overlayService.hide(this._componentID); this._destroy$.next(true); this._destroy$.complete(); } @@ -668,7 +651,6 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi * @hidden */ public openCalendar(): void { - this.isCalendarVisible = true; switch (this.mode) { case InteractionMode.READONLY: { this.hasHeader = true; @@ -679,8 +661,8 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi break; } case InteractionMode.EDITABLE: { - if (this._collapsed) { - this._dropDownOverlaySettings.positionStrategy.settings.target = this.editableInput.nativeElement; + if (this.collapsed) { + this._dropDownOverlaySettings.positionStrategy.settings.target = this.editableInputGroup.nativeElement; this.hasHeader = false; requestAnimationFrame(() => { this._componentID = this.overlayService.show(this.calendarContainer, this._dropDownOverlaySettings); @@ -702,7 +684,6 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } public calculateDate(data: string) { - // debugger; const isValid = this.isDateValid(data); if (isValid) { this.value = new Date(data); @@ -712,24 +693,9 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } private isDateValid(data) { - const isValid = (new Date(data).toLocaleString(this.locale) !== 'Invalid Date'); - // debugger; - return isValid; + return (new Date(data).toLocaleString(this.locale) !== 'Invalid Date'); } - /** - * Closes the dialog, after was clearing all calendar items from dom. - * - * @hidden - */ - // public handleDialogCloseAction() { - // this.onClose.emit(this); - // this.calendarRef.destroy(); - // if (this.input) { - // this.input.nativeElement.focus(); - // } - // } - /** * Evaluates when @calendar.onSelection event was fired * and update the input value. @@ -756,52 +722,45 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } public handleInput(eventArgs) { - // debugger; this.calculateDate(eventArgs.target.value); } @HostListener('keydown.alt.arrowdown', ['$event']) public onAltArrowDownKeydown(event: KeyboardEvent) { - // debugger; - event.preventDefault(); - event.stopPropagation(); this.calculateDate(this.editableInput.nativeElement.value); this.openCalendar(); } - @HostListener('keydown.alt.arrowup', ['$event']) - public onAltArrowUpKeydown(event: KeyboardEvent) { - console.log('onAltArrowUpKeydown'); - event.preventDefault(); - event.stopPropagation(); - this.closeCalendar(); - } + // @HostListener('keydown.esc', ['$event']) + // public onEscKeydown(event) { + // this.closeCalendar(); + // event.preventDefault(); + // event.stopPropagation(); + // } - @HostListener('keydown.esc', ['$event']) - public onEscKeydown(event) { - console.log('onEscKeydown'); + @HostListener('keydown.spacebar', ['$event']) + @HostListener('keydown.space', ['$event']) + public onSpaceClick(event) { + this.openCalendar(); event.preventDefault(); - event.stopPropagation(); - this.closeCalendar(); } - - - - public onKeyDown() { - debugger; + @HostListener('keydown.arrowdown', ['$event']) + public onArrowDownKeydown(event) { + event.preventDefault(); + const cursor = this._getCursorPosition(); } - @HostListener('keydown.spacebar', ['$event']) - @HostListener('keydown.space', ['$event']) - public onSpaceClick(event) { - this.openCalendar(); + @HostListener('keydown.arrowup', ['$event']) + public onArrowUpKeydown(event) { event.preventDefault(); + const cursor = this._getCursorPosition(); } private onOpened() { // debugger; - this._collapsed = false; + this.collapsed = false; + this.calendarContainer.nativeElement.style.display = 'block'; if (this.value) { this.calendar.value = this.value; @@ -817,9 +776,9 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } private onClosed() { - debugger; - this.isCalendarVisible = false; - this._collapsed = true; + // debugger; + this.collapsed = true; + this.calendarContainer.nativeElement.style.display = 'none'; this.onClose.emit(this); if (this.editableInput) { @@ -838,6 +797,10 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi return value.toLocaleDateString(locale); } + private _getCursorPosition(): number { + return this.editableInput.nativeElement.selectionStart; + } + /** * Apply custom user formatter upon date. * @param formatter custom formatter function. @@ -862,6 +825,6 @@ class Constants { @NgModule({ declarations: [IgxDatePickerComponent, IgxDatePickerTemplateDirective], exports: [IgxDatePickerComponent, IgxDatePickerTemplateDirective], - imports: [CommonModule, IgxIconModule, IgxInputGroupModule, IgxCalendarModule, IgxButtonModule, IgxRippleModule, IgxFocusModule] + imports: [CommonModule, IgxIconModule, IgxInputGroupModule, IgxCalendarModule, IgxButtonModule, IgxRippleModule] }) export class IgxDatePickerModule { } diff --git a/src/app/date-picker/date-picker.sample.html b/src/app/date-picker/date-picker.sample.html index d2659056a90..104f898fd2c 100644 --- a/src/app/date-picker/date-picker.sample.html +++ b/src/app/date-picker/date-picker.sample.html @@ -36,12 +36,12 @@

Date Picker with retemplated input group.

Detailed description to be added.

- + -
From 512bcf9bcefccf283da165a9c7f41da426231999 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Thu, 20 Dec 2018 11:35:32 +0200 Subject: [PATCH 04/57] feat(date-picker): Implementing edit mode #3034 --- .../date-picker/date-picker.component.html | 15 +- .../lib/date-picker/date-picker.component.ts | 393 +++++++++++++++--- .../src/lib/date-picker/date-picker.utils.ts | 342 +++++++++++++++ src/app/date-picker/date-picker.sample.html | 14 +- src/app/date-picker/date-picker.sample.ts | 5 +- 5 files changed, 691 insertions(+), 78 deletions(-) create mode 100644 projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts 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 56954396122..dab167b1195 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,5 +1,5 @@ - + today @@ -11,13 +11,14 @@ - + today - - + + clear @@ -41,4 +42,6 @@ {{ todayButtonLabel }}
-
\ No newline at end of file +
+ +
\ No newline at end of file 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 9daa3490f29..47dcd3b4160 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,7 +1,6 @@ -import { CommonModule } from '@angular/common'; +import { CommonModule, DatePipe } from '@angular/common'; import { Component, - ComponentRef, ContentChild, EventEmitter, HostBinding, @@ -16,7 +15,9 @@ import { ElementRef, TemplateRef, Directive, - Inject + Inject, + Pipe, + PipeTransform } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { @@ -44,6 +45,8 @@ 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 { PREDEFINED_FORMAT_OPTIONS, PREDEFINED_FORMATS, PREDEFINED_MASKS, DateUtil, FORMAT_DESC, DATE_CHARS } from './date-picker.utils'; @Directive({ selector: '[igxDatePickerTemplate]' @@ -51,7 +54,6 @@ import { IgxRippleModule } from '../directives/ripple/ripple.directive'; export class IgxDatePickerTemplateDirective { constructor(public template: TemplateRef) { } } - export interface IFormatViews { day?: boolean; month?: boolean; @@ -67,7 +69,7 @@ export interface IFormatOptions { let NEXT_ID = 0; -export enum InteractionMode { +export enum DatePickerInteractionMode { EDITABLE = 'editable', READONLY = 'readonly' } @@ -91,6 +93,7 @@ export enum InteractionMode { styles: [':host {display: block;}'], templateUrl: 'date-picker.component.html' }) + @DeprecateClass('\'igx-datePicker\' selector is deprecated. Use \'igx-date-picker\' selector instead.') export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvider, OnInit, OnDestroy { /** @@ -129,6 +132,34 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi this._formatOptions = Object.assign(this._formatOptions, formatOptions); } + /** + *Returns the edit mode date format of the `IgxDatePickerComponent`. + *```typescript + *@ViewChild("MyDatePicker") + *public datePicker: IgxDatePickerComponent; + *ngAfterViewInit(){ + * let format = this.datePicker.format; + *} + *``` + */ + @Input() + public get format(): string { + return (this._format === undefined) ? this.DEFAULT_DATE_FORMAT : this._format; + } + + /** + *Sets the edit mode format of the `IgxDatePickerComponent`. + *```typescript + *@ViewChild("MyDatePicker") + *public datePicker: IgxDatePickerComponent; + *this.datePicker.format = 'yyyy-M-d'; + *} + *``` + */ + public set format(format: string) { + this._format = format; + } + /** *Returns the format views of the `IgxDatePickerComponent`. *```typescript @@ -234,6 +265,31 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi return ''; } + /** + *Returns the transformed date. + *```typescript + *@ViewChild("MyDatePicker") + *public datePicker: IgxDatePickerComponent; + *public selection(event){ + * let transformedDate = this.datePicker.transformedDate; + * alert(transformedDate); + *} + *``` + *```html + * + *``` + */ + public get transformedDate() { + if (this._value) { + return this.transformDate(this._value); + } + return ''; + } + + public set transformedDate(value: string) { + this._transformedDate = value; + } + constructor(@Inject(IgxOverlayService) private overlayService: IgxOverlayService) { } /** @@ -247,7 +303,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi if (this.datePickerTemplateDirective) { return this.datePickerTemplateDirective.template; } - return (this.mode === InteractionMode.READONLY) ? this.readOnlyDatePickerTemplate : this.editableDatePickerTemplate; + return (this.mode === DatePickerInteractionMode.READONLY) ? this.readOnlyDatePickerTemplate : this.editableDatePickerTemplate; } /** @@ -258,7 +314,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi return { value: this.value, displayData: this.displayData, - openCalendar: () => { this.openCalendar(); } + openCalendar: (eventArgs) => { this.openCalendar(eventArgs); } }; } /** @@ -309,7 +365,31 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi *``` */ @Input() - public value: Date; + public get value(): Date { + console.log('get value ' + this._value); + return this._value; + } + + public set value(date: Date) { + switch (this.mode) { + case DatePickerInteractionMode.EDITABLE: { + if (date !== null) { + this._value = date; + console.log('set value ' + this._value); + this._transformedDate = this.transformDate(date); + console.log('set _transformedDate ' + this.transformDate(date)); + } else { + this._value = null; + this._transformedDate = ''; + } + break; + } + case DatePickerInteractionMode.READONLY: { + this._value = date; + break; + } + } + } /** * An @Input property that sets the `IgxDatePickerComponent` label. @@ -373,7 +453,13 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi public cancelButtonLabel: string; @Input() - public mode = InteractionMode.READONLY; + public mode = DatePickerInteractionMode.READONLY; + + /** + *@hidden + */ + @Input() + public outlet: IgxOverlayOutletDirective | ElementRef; /** *An event that is emitted when the `IgxDatePickerComponent` calendar is opened. @@ -444,7 +530,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi protected readonlyInput: ElementRef; @ViewChild('calendar') - protected calendar: IgxCalendarComponent; + public calendar: IgxCalendarComponent; /** *@hidden @@ -470,25 +556,14 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi @ViewChild('calendarContainer') public calendarContainer: ElementRef; - /** - *@hidden - */ - public calendarRef: ComponentRef; - - /** - *@hidden - */ - @Input() - public outlet: IgxOverlayOutletDirective | ElementRef; - - /** - *@hidden - */ - @Input() - public calendarOutlet: IgxOverlayOutletDirective | ElementRef; + public hasHeader = true; + public collapsed = true; + public mask; + public displayValue = new DisplayValuePipe(this); + public inputValue = new InputValuePipe(this); + public dateStruct = []; private _destroy$ = new Subject(); - private _componentID; private _formatOptions = { @@ -511,9 +586,10 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi private _dropDownOverlaySettings: OverlaySettings; private _modalOverlaySettings: OverlaySettings; - public inputDate = ''; - public hasHeader = true; - public collapsed = true; + private _format; + private _value; + private _transformedDate; + private DEFAULT_DATE_FORMAT = PREDEFINED_FORMAT_OPTIONS.SHORT_DATE; /** *Method that sets the selected date. @@ -544,7 +620,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi /** @hidden */ getEditElement() { - return ((this.mode === InteractionMode.READONLY) ? this.readonlyInput : this.editableInput).nativeElement; + return ((this.mode === DatePickerInteractionMode.READONLY) ? this.readonlyInput : this.editableInput).nativeElement; } /** @@ -560,13 +636,13 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi closeOnOutsideClick: true, modal: false, positionStrategy: new ConnectedPositioningStrategy(this._positionSettings), - // outlet: this.outlet + outlet: this.outlet }; this._modalOverlaySettings = { closeOnOutsideClick: true, modal: true, - // outlet: this.outlet + outlet: this.outlet }; this.overlayService.onOpened.pipe( @@ -584,12 +660,18 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi if (this.calendarContainer) { this.calendarContainer.nativeElement.style.display = 'none'; } + + if (this.mode === DatePickerInteractionMode.EDITABLE) { + this.getFormatOptions(this.format); + this.dateStruct = DateUtil.parseDateFormat(this.format); + } } /** *@hidden */ public ngOnDestroy(): void { + this.overlayService.hideAll(); this._destroy$.next(true); this._destroy$.complete(); } @@ -642,6 +724,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi */ public deselectDate() { this.value = null; + this.calendar.deselectDate(); this._onChangeCallback(null); } @@ -650,25 +733,20 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi * * @hidden */ - public openCalendar(): void { + public openCalendar(eventArgs): void { + eventArgs.stopPropagation(); switch (this.mode) { - case InteractionMode.READONLY: { + case DatePickerInteractionMode.READONLY: { this.hasHeader = true; - requestAnimationFrame(() => { - this._componentID = this.overlayService.show(this.calendarContainer, this._modalOverlaySettings); - }); - + this._componentID = this.overlayService.show(this.calendarContainer, this._modalOverlaySettings); break; } - case InteractionMode.EDITABLE: { + case DatePickerInteractionMode.EDITABLE: { if (this.collapsed) { this._dropDownOverlaySettings.positionStrategy.settings.target = this.editableInputGroup.nativeElement; this.hasHeader = false; - requestAnimationFrame(() => { - this._componentID = this.overlayService.show(this.calendarContainer, this._dropDownOverlaySettings); - }); + this._componentID = this.overlayService.show(this.calendarContainer, this._dropDownOverlaySettings); } - break; } } @@ -679,21 +757,53 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } public clear() { - // TODO - clear selected date? - this.value = undefined; + this.deselectDate(); } public calculateDate(data: string) { - const isValid = this.isDateValid(data); - if (isValid) { - this.value = new Date(data); - } else { - this.value = undefined; - } + // Remove underscore mask prompt char + const trimmedData = data.replace(/_/g, ''); + // let day, month; + // let dayPart; + // let dayFormat; + // let monthPart; + // let dayPartPosition; + // let monthFormat; + // let monthPartPosition; + + // dayPart = this.dateStruct.filter(part => part.type === DATE_PARTS.DAY); + // dayFormat = dayPart[0].formatType; + // dayPartPosition = dayPart[0].position; + + // monthPart = this.dateStruct.filter(part => part.type === DATE_PARTS.MONTH); + // monthFormat = monthPart[0].formatType; + // monthPartPosition = monthPart[0].position; + + // if (dayFormat === FORMAT_DESC.NUMERIC) { + // day = this.trimUnderlines(data.substring(dayPartPosition[0], dayPartPosition[1] + 1)); + // } + + // if (monthFormat === FORMAT_DESC.NUMERIC) { + // month = this.trimUnderlines(data.substring(monthPartPosition[0], monthPartPosition[1] + 1)); + // } + + // const dateData = new Date(trimmedData); + // const year = dateData.getFullYear(); + // const modifiedDate = new Date(); + + // modifiedDate.setDate(day); + // modifiedDate.setMonth(month - 1); + // modifiedDate.setFullYear(year); + + this.value = new Date(trimmedData); + } + + private trimUnderlines(value: string) { + return value.replace(/_/g, ''); } - private isDateValid(data) { - return (new Date(data).toLocaleString(this.locale) !== 'Invalid Date'); + public isDateValid(data) { + return new Date(data.replace(/_/g, '')).toString() !== 'Invalid Date'; } /** @@ -714,6 +824,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi this.value = date; this.calendar.viewDate = date; + this._onChangeCallback(date); this.closeCalendar(); @@ -721,14 +832,19 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi this.onSelection.emit(date); } - public handleInput(eventArgs) { + public onBlur(eventArgs) { this.calculateDate(eventArgs.target.value); } + public isOneDigit(char, index) { + const temp = (this.format.match(new RegExp(char, 'g')).length === 1 && index < 10); + return temp; + } + @HostListener('keydown.alt.arrowdown', ['$event']) public onAltArrowDownKeydown(event: KeyboardEvent) { this.calculateDate(this.editableInput.nativeElement.value); - this.openCalendar(); + this.openCalendar(event); } // @HostListener('keydown.esc', ['$event']) @@ -738,12 +854,12 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi // event.stopPropagation(); // } - @HostListener('keydown.spacebar', ['$event']) - @HostListener('keydown.space', ['$event']) - public onSpaceClick(event) { - this.openCalendar(); - event.preventDefault(); - } + // @HostListener('keydown.spacebar', ['$event']) + // @HostListener('keydown.space', ['$event']) + // public onSpaceClick(event) { + // this.openCalendar(); + // event.preventDefault(); + // } @HostListener('keydown.arrowdown', ['$event']) public onArrowDownKeydown(event) { @@ -758,7 +874,6 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } private onOpened() { - // debugger; this.collapsed = false; this.calendarContainer.nativeElement.style.display = 'block'; @@ -776,7 +891,6 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } private onClosed() { - // debugger; this.collapsed = true; this.calendarContainer.nativeElement.style.display = 'none'; this.onClose.emit(this); @@ -801,6 +915,36 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi return this.editableInput.nativeElement.selectionStart; } + private getFormatOptions(format) { + switch (format) { + case PREDEFINED_FORMAT_OPTIONS.SHORT_DATE: { + this.mask = PREDEFINED_MASKS.SHORT_DATE_MASK; + this.format = PREDEFINED_FORMATS.SHORT_DATE_FORMAT; + break; + } + case PREDEFINED_FORMAT_OPTIONS.MEDIUM_DATE: { + this.mask = PREDEFINED_MASKS.MEDIUM_DATE_MASK; + this.format = PREDEFINED_FORMATS.MEDIUM_DATE_FORMAT; + break; + } + case PREDEFINED_FORMAT_OPTIONS.LONG_DATE: { + this.mask = PREDEFINED_MASKS.LONG_DATE_MASK; + this.format = PREDEFINED_FORMATS.LONG_DATE_FORMAT; + break; + } + case PREDEFINED_FORMAT_OPTIONS.FULL_DATE: { + this.mask = PREDEFINED_MASKS.FULL_DATE_MASK; + this.format = PREDEFINED_FORMATS.FULL_DATE_FORMAT; + break; + } + default: { + this.mask = DateUtil.getFormatMask(format); + this.format = format; + break; + } + } + } + /** * Apply custom user formatter upon date. * @param formatter custom formatter function. @@ -813,18 +957,137 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi private _onTouchedCallback: () => void = () => { }; private _onChangeCallback: (_: Date) => void = () => { }; + + public transformDate(date: Date) { + const formattedDate = new DateFormatPipe(Constants.DEFAULT_LOCALE_DATE).transform(date, this.format); + console.log('formattedDate ' + formattedDate); + return formattedDate; + } } class Constants { public static readonly DEFAULT_LOCALE_DATE = 'en'; } +@Pipe({ + name: 'format' +}) + +export class DateFormatPipe extends DatePipe implements PipeTransform { + transform(value: any, args?: any): any { + return super.transform(value, args); + } +} + +@Pipe({ + name: 'displayValue' +}) +export class DisplayValuePipe implements PipeTransform { + constructor(public datePicker: IgxDatePickerComponent) { } + // on blur + transform(value: any, args?: any): any { + if (value !== '') { + if (value === this.trimMask(this.datePicker.mask)) { + return ''; + } + + return value.replace(/_/g, ''); + } + + return ''; + } + + trimMask(mask) { + return mask.replace(/0|L/g, '_'); + } +} + +@Pipe({ + name: 'inputValue' +}) +export class InputValuePipe implements PipeTransform { + constructor(public datePicker: IgxDatePickerComponent) { } + // on focus + transform(value: any, args?: any): any { + if (this.datePicker.value !== null && this.datePicker.value !== undefined) { + let result; + let offset = 0; + const dateArray = Array.from(value); + const monthName = this.datePicker.value.toLocaleString('en', { + month: 'long' + }); + const dayName = this.datePicker.value.toLocaleString('en', { + weekday: 'long' + }); + + const dateStruct = this.datePicker.dateStruct; + + for (let i = 0; i < dateStruct.length; i++) { + if (dateStruct[i].type === 'weekday') { + if (dateStruct[i].formatType === FORMAT_DESC.LONG) { + offset += DateUtil.MAX_WEEKDAY_SYMBOLS - 4; + for (let j = dayName.length; j < DateUtil.MAX_WEEKDAY_SYMBOLS; j++) { + dateArray.splice(j, 0, '_'); + } + dateArray.join(''); + } + } + + if (dateStruct[i].type === 'month') { + if (dateStruct[i].formatType === FORMAT_DESC.LONG) { + const startPos = offset + dateStruct[i].initialPosition + monthName.length; + const endPos = startPos + DateUtil.MAX_MONTH_SYMBOLS - monthName.length; + offset += DateUtil.MAX_MONTH_SYMBOLS - 4; + for (let j = startPos; j < endPos; j++) { + dateArray.splice(j, 0, '_'); + } + dateArray.join(''); + } + if (dateStruct[i].formatType === FORMAT_DESC.NUMERIC + || dateStruct[i].formatType === FORMAT_DESC.TWO_DIGITS) { + const isOneDigit = this.datePicker.isOneDigit(DATE_CHARS.MONTH_CHAR, this.datePicker.value.getMonth() + 1); + if (isOneDigit) { + const startPos = offset + dateStruct[i].initialPosition; + dateArray.splice(startPos, 0, '_'); + } + offset += 1; + dateArray.join(''); + } + } + + if (dateStruct[i].type === 'day') { + if (dateStruct[i].formatType === FORMAT_DESC.NUMERIC + || dateStruct[i].formatType === FORMAT_DESC.TWO_DIGITS) { + const isOneDigit = this.datePicker.isOneDigit(DATE_CHARS.DAY_CHAR, this.datePicker.value.getDate()); + if (isOneDigit) { + const startPos = offset + dateStruct[i].initialPosition; + dateArray.splice(startPos, 0, '_'); + } + offset += 1; + dateArray.join(''); + } + } + } + + result = dateArray.join(''); + + return result; + } + + return this.trimMask(this.datePicker.mask); + } + + trimMask(mask) { + return mask.replace(/0|L/g, '_'); + } +} + /** * The IgxDatePickerModule provides the {@link IgxDatePickerComponent} inside your application. */ @NgModule({ declarations: [IgxDatePickerComponent, IgxDatePickerTemplateDirective], exports: [IgxDatePickerComponent, IgxDatePickerTemplateDirective], - imports: [CommonModule, IgxIconModule, IgxInputGroupModule, IgxCalendarModule, IgxButtonModule, IgxRippleModule] + imports: [CommonModule, IgxIconModule, IgxInputGroupModule, IgxCalendarModule, IgxButtonModule, IgxRippleModule, IgxMaskModule] }) export class IgxDatePickerModule { } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts new file mode 100644 index 00000000000..03c0b212a66 --- /dev/null +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -0,0 +1,342 @@ +export enum PREDEFINED_FORMAT_OPTIONS { + SHORT_DATE = 'shortDate', + MEDIUM_DATE = 'mediumDate', + LONG_DATE = 'longDate', + FULL_DATE = 'fullDate' +} + +export enum PREDEFINED_FORMATS { + SHORT_DATE_FORMAT = 'M/d/yy', + MEDIUM_DATE_FORMAT = 'MMM d, y', + LONG_DATE_FORMAT = 'MMMM d, y', + FULL_DATE_FORMAT = 'EEEE, MMMM d, y' +} + +export enum PREDEFINED_MASKS { + SHORT_DATE_MASK = '00/00/00', + MEDIUM_DATE_MASK = 'LLL 00, 0000', + LONG_DATE_MASK = 'LLLLLLLLL 00, 0000', // longest month - sep - 9 chars + FULL_DATE_MASK = 'LLLLLLLLL, LLLLLLLLL 00, 0000' // longest month - sep - 9 characters, longest week day - wed - 9 chars +} + +export enum FORMAT_DESC { + NUMERIC = 'numeric', + TWO_DIGITS = 'twoDigits', + SHORT = 'short', + LONG = 'long', + NARROW = 'narrow' +} + +export enum DATE_CHARS { + YEAR_CHAR = 'y', + MONTH_CHAR = 'M', + DAY_CHAR = 'd', + WEEKDAY_CHAR = 'E' +} + +export enum DATE_PARTS { + DAY = 'day', + MONTH = 'month', + YEAR = 'year', + WEEKDAY = 'weekday' +} + +export abstract class DateUtil { + public static MAX_MONTH_SYMBOLS = 9; + public static MAX_WEEKDAY_SYMBOLS = 9; + + public static getYearFormatType(format: string) { + let type; + const occurences = format.match(new RegExp(DATE_CHARS.YEAR_CHAR, 'g')).length; + + switch (occurences) { + case 1: { + // y + type = FORMAT_DESC.NUMERIC; + break; + } + case 4: { + // yyyy + type = FORMAT_DESC.NUMERIC; + break; + } + case 2: { + // yy + type = FORMAT_DESC.TWO_DIGITS; + break; + } + } + + return type; + } + public static getMonthFormatType(format: string) { + let type; + const occurences = format.match(new RegExp(DATE_CHARS.MONTH_CHAR, 'g')).length; + + switch (occurences) { + case 1: { + // M + type = FORMAT_DESC.NUMERIC; + break; + } + case 2: { + // MM + type = FORMAT_DESC.TWO_DIGITS; + break; + } + case 3: { + // MMM + type = FORMAT_DESC.SHORT; + break; + } + case 4: { + // MMMM + type = FORMAT_DESC.LONG; + break; + } + case 5: { + // MMMMM + type = FORMAT_DESC.NARROW; + break; + } + } + + return type; + } + public static getDayFormatType(format: string) { + let type; + const occurences = format.match(new RegExp(DATE_CHARS.DAY_CHAR, 'g')).length; + + switch (occurences) { + case 1: { + // d + type = FORMAT_DESC.NUMERIC; + break; + } + case 2: { + // dd + type = FORMAT_DESC.TWO_DIGITS; + break; + } + } + + return type; + } + public static getWeekDayFormatType(format: string) { + let type; + const occurences = format.match(new RegExp(DATE_CHARS.WEEKDAY_CHAR, 'g')).length; + + switch (occurences) { + case 3: { + // EEE (Tue) + type = FORMAT_DESC.SHORT; + break; + } + case 4: { + // EEEE (Tuesday) + type = FORMAT_DESC.LONG; + break; + } + case 5: { + // EEEEE (T) + type = FORMAT_DESC.NARROW; + break; + } + } + + return type; + } + public static parseDateFormat(format: string) { + const dateStruct = []; + const maskArray = Array.from(format); + const weekdayInitPosition = format.indexOf('E'); + const monthInitPosition = format.indexOf('M'); + const dayInitPosition = format.indexOf('d'); + const yearInitPosition = format.indexOf('y'); + + if (yearInitPosition !== -1) { + dateStruct.push({ + type: DATE_PARTS.YEAR, + initialPosition: yearInitPosition, + formatType: this.getYearFormatType(format) + }); + } + + if (weekdayInitPosition !== -1) { + dateStruct.push({ + type: DATE_PARTS.WEEKDAY, + initialPosition: weekdayInitPosition, + formatType: this.getWeekDayFormatType(format) + }); + } + + if (monthInitPosition !== -1) { + dateStruct.push({ + type: DATE_PARTS.MONTH, + initialPosition: monthInitPosition, + formatType: this.getMonthFormatType(format) + }); + } + + if (dayInitPosition !== -1) { + dateStruct.push({ + type: DATE_PARTS.DAY, + initialPosition: dayInitPosition, + formatType: this.getDayFormatType(format) + }); + } + + for (let i = 0; i < maskArray.length; i++) { + if (!DateUtil.isSpecialSymbol(maskArray[i])) { + dateStruct.push({ + type: 'literal', + initialPosition: i, + value: maskArray[i] + }); + } + } + + dateStruct.sort((a, b) => a.initialPosition - b.initialPosition); + DateUtil.fillDatePartsPositions(dateStruct); + + return dateStruct; + } + + private static fillDatePartsPositions(dateArray) { + let offset = 0; + + for (let i = 0; i < dateArray.length; i++) { + if (dateArray[i].type === DATE_PARTS.DAY) { + dateArray[i].position = DateUtil.fillValues(offset, 2); + offset += 2; + } + + if (dateArray[i].type === DATE_PARTS.MONTH) { + switch (dateArray[i].formatType) { + case FORMAT_DESC.SHORT: { + dateArray[i].position = DateUtil.fillValues(offset, 3); + offset += 3; + break; + } + case FORMAT_DESC.LONG: { + dateArray[i].position = DateUtil.fillValues(offset, 9); + offset += 9; + break; + } + case FORMAT_DESC.NARROW: { + dateArray[i].position = DateUtil.fillValues(offset, 1); + offset++; + break; + } + default: { + // FORMAT_DESC.NUMERIC || FORMAT_DESC.TWO_DIGITS + dateArray[i].position = DateUtil.fillValues(offset, 2); + offset += 2; + break; + } + } + } + + if (dateArray[i].type === 'literal') { + dateArray[i].position = DateUtil.fillValues(offset, 1); + offset++; + } + + if (dateArray[i].type === DATE_PARTS.YEAR) { + switch (dateArray[i].formatType) { + case FORMAT_DESC.NUMERIC: { + dateArray[i].position = DateUtil.fillValues(offset, 4); + offset += 4; + break; + } + case FORMAT_DESC.TWO_DIGITS: { + dateArray[i].position = DateUtil.fillValues(offset, 2); + offset += 2; + break; + } + } + } + } + } + + private static fillValues(start: number, offset: number) { + const array = []; + for (let i = start; i < start + offset; i++) { + array.push(i); + } + + return array; + } + + public static isSpecialSymbol(char: string) { + return (char !== DATE_CHARS.YEAR_CHAR + && char !== DATE_CHARS.MONTH_CHAR + && char !== DATE_CHARS.DAY_CHAR + && char !== DATE_CHARS.WEEKDAY_CHAR) ? false : true; + } + + public static getFormatMask(format: string) { + const mask = []; + const dateStruct = DateUtil.parseDateFormat(format); + + for (let i = 0; i < dateStruct.length; i++) { + if (dateStruct[i].type === DATE_PARTS.DAY) { + mask.push('00'); + } + if (dateStruct[i].type === DATE_PARTS.MONTH) { + switch (dateStruct[i].formatType) { + case FORMAT_DESC.SHORT: { + mask.push('LLL'); + break; + } + case FORMAT_DESC.LONG: { + mask.push('LLLLLLLLL'); + break; + } + case FORMAT_DESC.NARROW: { + mask.push('L'); + break; + } + default: { + // M && MM + mask.push('00'); + break; + } + } + } + if (dateStruct[i].type === DATE_PARTS.YEAR) { + switch (dateStruct[i].formatType) { + case FORMAT_DESC.NUMERIC: { + mask.push('0000'); + break; + } + case FORMAT_DESC.TWO_DIGITS: { + mask.push('00'); + break; + } + } + } + if (dateStruct[i].type === DATE_PARTS.WEEKDAY) { + switch (dateStruct[i].formatType) { + case FORMAT_DESC.SHORT: { + mask.push('LLL'); + break; + } + case FORMAT_DESC.LONG: { + mask.push('LLLLLLLLL'); + break; + } + case FORMAT_DESC.NARROW: { + mask.push('L'); + break; + } + } + } + if (dateStruct[i].type === 'literal') { + mask.push(dateStruct[i].value); + } + } + + return mask.join(''); + } +} diff --git a/src/app/date-picker/date-picker.sample.html b/src/app/date-picker/date-picker.sample.html index 104f898fd2c..5870b221fb7 100644 --- a/src/app/date-picker/date-picker.sample.html +++ b/src/app/date-picker/date-picker.sample.html @@ -3,17 +3,19 @@

Default Date Picker.

-

Detailed description to be added.

+
- +

Editable Date Picker

-
- + +
+
@@ -23,7 +25,7 @@

Date Picker with passed date.

- + -->

Date Picker with passed date and custom formatter.

Detailed description to be added.

@@ -38,7 +40,7 @@

Date Picker with retemplated input group.

- + diff --git a/src/app/date-picker/date-picker.sample.ts b/src/app/date-picker/date-picker.sample.ts index 2aa4f00a6ef..c2f75a1ae80 100644 --- a/src/app/date-picker/date-picker.sample.ts +++ b/src/app/date-picker/date-picker.sample.ts @@ -6,9 +6,12 @@ import { IgxDatePickerComponent } from 'igniteui-angular'; styleUrls: ['date-picker.sample.css'], templateUrl: 'date-picker.sample.html' }) + export class DatePickerSampleComponent { @ViewChild('datePicker') datePicker: IgxDatePickerComponent; - date = new Date(Date.now()); + date = new Date('10/3/2018'); + + date1 = new Date(); formatter = (_: Date) => { return _.toDateString(); From a856cfc31ce3c960a34b238fb86b320d2cbe0534 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Thu, 27 Dec 2018 12:03:44 +0200 Subject: [PATCH 05/57] feat(date-picker): More changes #3034 --- .../lib/date-picker/date-picker.component.ts | 150 ++++++++---------- .../src/lib/date-picker/date-picker.utils.ts | 66 +++++--- 2 files changed, 117 insertions(+), 99 deletions(-) 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 47dcd3b4160..f26e0ac432a 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 @@ -46,7 +46,15 @@ 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 { PREDEFINED_FORMAT_OPTIONS, PREDEFINED_FORMATS, PREDEFINED_MASKS, DateUtil, FORMAT_DESC, DATE_CHARS } from './date-picker.utils'; +import { + PREDEFINED_FORMAT_OPTIONS, + PREDEFINED_FORMATS, + PREDEFINED_MASKS, + DatePickerUtil, + FORMAT_DESC, + DATE_CHARS, + DATE_PARTS +} from './date-picker.utils'; @Directive({ selector: '[igxDatePickerTemplate]' @@ -366,7 +374,6 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi */ @Input() public get value(): Date { - console.log('get value ' + this._value); return this._value; } @@ -375,9 +382,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi case DatePickerInteractionMode.EDITABLE: { if (date !== null) { this._value = date; - console.log('set value ' + this._value); this._transformedDate = this.transformDate(date); - console.log('set _transformedDate ' + this.transformDate(date)); } else { this._value = null; this._transformedDate = ''; @@ -562,6 +567,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi public displayValue = new DisplayValuePipe(this); public inputValue = new InputValuePipe(this); public dateStruct = []; + public rawData; private _destroy$ = new Subject(); private _componentID; @@ -663,7 +669,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi if (this.mode === DatePickerInteractionMode.EDITABLE) { this.getFormatOptions(this.format); - this.dateStruct = DateUtil.parseDateFormat(this.format); + this.dateStruct = DatePickerUtil.parseDateFormat(this.format); } } @@ -761,49 +767,44 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } public calculateDate(data: string) { - // Remove underscore mask prompt char - const trimmedData = data.replace(/_/g, ''); - // let day, month; - // let dayPart; - // let dayFormat; - // let monthPart; - // let dayPartPosition; - // let monthFormat; - // let monthPartPosition; - - // dayPart = this.dateStruct.filter(part => part.type === DATE_PARTS.DAY); - // dayFormat = dayPart[0].formatType; - // dayPartPosition = dayPart[0].position; - - // monthPart = this.dateStruct.filter(part => part.type === DATE_PARTS.MONTH); - // monthFormat = monthPart[0].formatType; - // monthPartPosition = monthPart[0].position; - - // if (dayFormat === FORMAT_DESC.NUMERIC) { - // day = this.trimUnderlines(data.substring(dayPartPosition[0], dayPartPosition[1] + 1)); - // } - - // if (monthFormat === FORMAT_DESC.NUMERIC) { - // month = this.trimUnderlines(data.substring(monthPartPosition[0], monthPartPosition[1] + 1)); - // } - - // const dateData = new Date(trimmedData); - // const year = dateData.getFullYear(); - // const modifiedDate = new Date(); - - // modifiedDate.setDate(day); - // modifiedDate.setMonth(month - 1); - // modifiedDate.setFullYear(year); - - this.value = new Date(trimmedData); - } + if (data !== '') { + const trimmedData = DatePickerUtil.trimUnderlines(data); + const monthPart = this.dateStruct.filter(part => part.type === DATE_PARTS.MONTH); + + if (monthPart[0].formatType === FORMAT_DESC.NUMERIC + || monthPart[0].formatType === FORMAT_DESC.TWO_DIGITS) { + let fullYear; + + const dayPartPosition = this.dateStruct.filter(part => part.type === DATE_PARTS.DAY)[0].position; + const dayStartIdx = dayPartPosition[0]; + const dayEndIdx = dayStartIdx + dayPartPosition.length; + + const monthStartIdx = monthPart[0].position[0]; + const monthEndIdx = monthStartIdx + monthPart[0].position.length; + + const yearFormat = this.dateStruct.filter(part => part.type === DATE_PARTS.YEAR)[0].formatType; + const yearPartPosition = this.dateStruct.filter(part => part.type === DATE_PARTS.YEAR)[0].position; + const yearStartIdx = yearPartPosition[0]; + const yearEndIdx = yearStartIdx + yearPartPosition.length; + + const day = DatePickerUtil.trimUnderlines(this.rawData.substring(dayStartIdx, dayEndIdx)); + const month = DatePickerUtil.trimUnderlines(this.rawData.substring(monthStartIdx, monthEndIdx)); + const year = this.rawData.substring(yearStartIdx, yearEndIdx); + if (yearFormat === FORMAT_DESC.TWO_DIGITS) { + fullYear = '20'.concat(year); + } else { + fullYear = year; + } - private trimUnderlines(value: string) { - return value.replace(/_/g, ''); + this.value = DatePickerUtil.createDate(Number(day), Number(month) - 1, Number(fullYear)); + } else { + this.value = new Date(trimmedData); + } + } } public isDateValid(data) { - return new Date(data.replace(/_/g, '')).toString() !== 'Invalid Date'; + return new Date(DatePickerUtil.trimUnderlines(data)).toString() !== 'Invalid Date'; } /** @@ -837,8 +838,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } public isOneDigit(char, index) { - const temp = (this.format.match(new RegExp(char, 'g')).length === 1 && index < 10); - return temp; + return (this.format.match(new RegExp(char, 'g')).length === 1 && index < 10); } @HostListener('keydown.alt.arrowdown', ['$event']) @@ -864,13 +864,15 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi @HostListener('keydown.arrowdown', ['$event']) public onArrowDownKeydown(event) { event.preventDefault(); - const cursor = this._getCursorPosition(); + const cursor = this._getCursorPosition() - 1; + const datePart = this.dateStruct.filter(element => element.position.find(pos => pos === cursor)); } @HostListener('keydown.arrowup', ['$event']) public onArrowUpKeydown(event) { event.preventDefault(); - const cursor = this._getCursorPosition(); + const cursor = this._getCursorPosition() - 1; + const datePart = this.dateStruct.filter(element => element.position.find(pos => pos === cursor)); } private onOpened() { @@ -938,7 +940,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi break; } default: { - this.mask = DateUtil.getFormatMask(format); + this.mask = DatePickerUtil.getFormatMask(format); this.format = format; break; } @@ -958,10 +960,8 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi private _onChangeCallback: (_: Date) => void = () => { }; - public transformDate(date: Date) { - const formattedDate = new DateFormatPipe(Constants.DEFAULT_LOCALE_DATE).transform(date, this.format); - console.log('formattedDate ' + formattedDate); - return formattedDate; + public transformDate(date) { + return new DateFormatPipe(Constants.DEFAULT_LOCALE_DATE).transform(date, this.format); } } @@ -987,19 +987,15 @@ export class DisplayValuePipe implements PipeTransform { // on blur transform(value: any, args?: any): any { if (value !== '') { - if (value === this.trimMask(this.datePicker.mask)) { + if (value === DatePickerUtil.trimMaskSymbols(this.datePicker.mask)) { return ''; } - - return value.replace(/_/g, ''); + this.datePicker.rawData = value; + return DatePickerUtil.trimUnderlines(value); } return ''; } - - trimMask(mask) { - return mask.replace(/0|L/g, '_'); - } } @Pipe({ @@ -1013,31 +1009,27 @@ export class InputValuePipe implements PipeTransform { let result; let offset = 0; const dateArray = Array.from(value); - const monthName = this.datePicker.value.toLocaleString('en', { - month: 'long' - }); - const dayName = this.datePicker.value.toLocaleString('en', { - weekday: 'long' - }); + const monthName = DatePickerUtil.getLongMonthName(this.datePicker.value); + const dayName = DatePickerUtil.getLongDayName(this.datePicker.value); const dateStruct = this.datePicker.dateStruct; for (let i = 0; i < dateStruct.length; i++) { - if (dateStruct[i].type === 'weekday') { + if (dateStruct[i].type === DATE_PARTS.WEEKDAY) { if (dateStruct[i].formatType === FORMAT_DESC.LONG) { - offset += DateUtil.MAX_WEEKDAY_SYMBOLS - 4; - for (let j = dayName.length; j < DateUtil.MAX_WEEKDAY_SYMBOLS; j++) { + offset += DatePickerUtil.MAX_WEEKDAY_SYMBOLS - 4; + for (let j = dayName.length; j < DatePickerUtil.MAX_WEEKDAY_SYMBOLS; j++) { dateArray.splice(j, 0, '_'); } dateArray.join(''); } } - if (dateStruct[i].type === 'month') { + if (dateStruct[i].type === DATE_PARTS.MONTH) { if (dateStruct[i].formatType === FORMAT_DESC.LONG) { const startPos = offset + dateStruct[i].initialPosition + monthName.length; - const endPos = startPos + DateUtil.MAX_MONTH_SYMBOLS - monthName.length; - offset += DateUtil.MAX_MONTH_SYMBOLS - 4; + const endPos = startPos + DatePickerUtil.MAX_MONTH_SYMBOLS - monthName.length; + offset += DatePickerUtil.MAX_MONTH_SYMBOLS - 4; for (let j = startPos; j < endPos; j++) { dateArray.splice(j, 0, '_'); } @@ -1047,21 +1039,23 @@ export class InputValuePipe implements PipeTransform { || dateStruct[i].formatType === FORMAT_DESC.TWO_DIGITS) { const isOneDigit = this.datePicker.isOneDigit(DATE_CHARS.MONTH_CHAR, this.datePicker.value.getMonth() + 1); if (isOneDigit) { + const symbol = (dateStruct[i].formatType === FORMAT_DESC.TWO_DIGITS) ? '0' : '_'; const startPos = offset + dateStruct[i].initialPosition; - dateArray.splice(startPos, 0, '_'); + dateArray.splice(startPos, 0, symbol); } offset += 1; dateArray.join(''); } } - if (dateStruct[i].type === 'day') { + if (dateStruct[i].type === DATE_PARTS.DAY) { if (dateStruct[i].formatType === FORMAT_DESC.NUMERIC || dateStruct[i].formatType === FORMAT_DESC.TWO_DIGITS) { const isOneDigit = this.datePicker.isOneDigit(DATE_CHARS.DAY_CHAR, this.datePicker.value.getDate()); if (isOneDigit) { + const symbol = (dateStruct[i].formatType === FORMAT_DESC.TWO_DIGITS) ? '0' : '_'; const startPos = offset + dateStruct[i].initialPosition; - dateArray.splice(startPos, 0, '_'); + dateArray.splice(startPos, 0, symbol); } offset += 1; dateArray.join(''); @@ -1074,11 +1068,7 @@ export class InputValuePipe implements PipeTransform { return result; } - return this.trimMask(this.datePicker.mask); - } - - trimMask(mask) { - return mask.replace(/0|L/g, '_'); + return DatePickerUtil.trimMaskSymbols(this.datePicker.mask); } } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts index 03c0b212a66..d6f87ae6aba 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -41,7 +41,7 @@ export enum DATE_PARTS { WEEKDAY = 'weekday' } -export abstract class DateUtil { +export abstract class DatePickerUtil { public static MAX_MONTH_SYMBOLS = 9; public static MAX_WEEKDAY_SYMBOLS = 9; @@ -51,17 +51,17 @@ export abstract class DateUtil { switch (occurences) { case 1: { - // y + // y (2020) type = FORMAT_DESC.NUMERIC; break; } case 4: { - // yyyy + // yyyy (2020) type = FORMAT_DESC.NUMERIC; break; } case 2: { - // yy + // yy (20) type = FORMAT_DESC.TWO_DIGITS; break; } @@ -149,10 +149,10 @@ export abstract class DateUtil { public static parseDateFormat(format: string) { const dateStruct = []; const maskArray = Array.from(format); - const weekdayInitPosition = format.indexOf('E'); - const monthInitPosition = format.indexOf('M'); - const dayInitPosition = format.indexOf('d'); - const yearInitPosition = format.indexOf('y'); + const weekdayInitPosition = format.indexOf(DATE_CHARS.WEEKDAY_CHAR); + const monthInitPosition = format.indexOf(DATE_CHARS.MONTH_CHAR); + const dayInitPosition = format.indexOf(DATE_CHARS.DAY_CHAR); + const yearInitPosition = format.indexOf(DATE_CHARS.YEAR_CHAR); if (yearInitPosition !== -1) { dateStruct.push({ @@ -187,7 +187,7 @@ export abstract class DateUtil { } for (let i = 0; i < maskArray.length; i++) { - if (!DateUtil.isSpecialSymbol(maskArray[i])) { + if (!DatePickerUtil.isSpecialSymbol(maskArray[i])) { dateStruct.push({ type: 'literal', initialPosition: i, @@ -197,7 +197,7 @@ export abstract class DateUtil { } dateStruct.sort((a, b) => a.initialPosition - b.initialPosition); - DateUtil.fillDatePartsPositions(dateStruct); + DatePickerUtil.fillDatePartsPositions(dateStruct); return dateStruct; } @@ -207,30 +207,30 @@ export abstract class DateUtil { for (let i = 0; i < dateArray.length; i++) { if (dateArray[i].type === DATE_PARTS.DAY) { - dateArray[i].position = DateUtil.fillValues(offset, 2); + dateArray[i].position = DatePickerUtil.fillValues(offset, 2); offset += 2; } if (dateArray[i].type === DATE_PARTS.MONTH) { switch (dateArray[i].formatType) { case FORMAT_DESC.SHORT: { - dateArray[i].position = DateUtil.fillValues(offset, 3); + dateArray[i].position = DatePickerUtil.fillValues(offset, 3); offset += 3; break; } case FORMAT_DESC.LONG: { - dateArray[i].position = DateUtil.fillValues(offset, 9); + dateArray[i].position = DatePickerUtil.fillValues(offset, 9); offset += 9; break; } case FORMAT_DESC.NARROW: { - dateArray[i].position = DateUtil.fillValues(offset, 1); + dateArray[i].position = DatePickerUtil.fillValues(offset, 1); offset++; break; } default: { // FORMAT_DESC.NUMERIC || FORMAT_DESC.TWO_DIGITS - dateArray[i].position = DateUtil.fillValues(offset, 2); + dateArray[i].position = DatePickerUtil.fillValues(offset, 2); offset += 2; break; } @@ -238,19 +238,19 @@ export abstract class DateUtil { } if (dateArray[i].type === 'literal') { - dateArray[i].position = DateUtil.fillValues(offset, 1); + dateArray[i].position = DatePickerUtil.fillValues(offset, 1); offset++; } if (dateArray[i].type === DATE_PARTS.YEAR) { switch (dateArray[i].formatType) { case FORMAT_DESC.NUMERIC: { - dateArray[i].position = DateUtil.fillValues(offset, 4); + dateArray[i].position = DatePickerUtil.fillValues(offset, 4); offset += 4; break; } case FORMAT_DESC.TWO_DIGITS: { - dateArray[i].position = DateUtil.fillValues(offset, 2); + dateArray[i].position = DatePickerUtil.fillValues(offset, 2); offset += 2; break; } @@ -277,7 +277,7 @@ export abstract class DateUtil { public static getFormatMask(format: string) { const mask = []; - const dateStruct = DateUtil.parseDateFormat(format); + const dateStruct = DatePickerUtil.parseDateFormat(format); for (let i = 0; i < dateStruct.length; i++) { if (dateStruct[i].type === DATE_PARTS.DAY) { @@ -339,4 +339,32 @@ export abstract class DateUtil { return mask.join(''); } + + public static createDate(day, month, year) { + const date = new Date(); + date.setDate(day); + date.setMonth(month); + date.setFullYear(year); + return date; + } + + public static trimMaskSymbols(mask) { + return mask.replace(/0|L/g, '_'); + } + + public static trimUnderlines(value: string) { + return value.replace(/_/g, ''); + } + + public static getLongMonthName(value) { + return value.toLocaleString('en', { + month: 'long' + }); + } + + public static getLongDayName(value) { + return value.toLocaleString('en', { + weekday: 'long' + }); + } } From cd0935fd18c57930e3142264f5395da435ab7a8d Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Fri, 4 Jan 2019 09:54:59 +0200 Subject: [PATCH 06/57] feat(date-picker): Added spinning of the date parts #3034 --- .../src/lib/date-picker/date-picker.common.ts | 0 .../lib/date-picker/date-picker.component.ts | 149 ++++-------------- .../src/lib/date-picker/date-picker.pipes.ts | 105 ++++++++++++ .../src/lib/date-picker/date-picker.utils.ts | 100 ++++++++++-- 4 files changed, 220 insertions(+), 134 deletions(-) create mode 100644 projects/igniteui-angular/src/lib/date-picker/date-picker.common.ts create mode 100644 projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts 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 new file mode 100644 index 00000000000..e69de29bb2d 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 f26e0ac432a..eff01000982 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 @@ -52,9 +52,9 @@ import { PREDEFINED_MASKS, DatePickerUtil, FORMAT_DESC, - DATE_CHARS, DATE_PARTS } from './date-picker.utils'; +import { DisplayValuePipe, InputValuePipe, DateFormatPipe } from './date-picker.pipes'; @Directive({ selector: '[igxDatePickerTemplate]' @@ -566,7 +566,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi public mask; public displayValue = new DisplayValuePipe(this); public inputValue = new InputValuePipe(this); - public dateStruct = []; + public dateFormatParts = []; public rawData; private _destroy$ = new Subject(); @@ -669,7 +669,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi if (this.mode === DatePickerInteractionMode.EDITABLE) { this.getFormatOptions(this.format); - this.dateStruct = DatePickerUtil.parseDateFormat(this.format); + this.dateFormatParts = DatePickerUtil.parseDateFormat(this.format); } } @@ -769,21 +769,21 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi public calculateDate(data: string) { if (data !== '') { const trimmedData = DatePickerUtil.trimUnderlines(data); - const monthPart = this.dateStruct.filter(part => part.type === DATE_PARTS.MONTH); + const monthPart = this.dateFormatParts.filter(part => part.type === DATE_PARTS.MONTH); if (monthPart[0].formatType === FORMAT_DESC.NUMERIC || monthPart[0].formatType === FORMAT_DESC.TWO_DIGITS) { let fullYear; - const dayPartPosition = this.dateStruct.filter(part => part.type === DATE_PARTS.DAY)[0].position; + const dayPartPosition = this.dateFormatParts.filter(part => part.type === DATE_PARTS.DAY)[0].position; const dayStartIdx = dayPartPosition[0]; const dayEndIdx = dayStartIdx + dayPartPosition.length; const monthStartIdx = monthPart[0].position[0]; const monthEndIdx = monthStartIdx + monthPart[0].position.length; - const yearFormat = this.dateStruct.filter(part => part.type === DATE_PARTS.YEAR)[0].formatType; - const yearPartPosition = this.dateStruct.filter(part => part.type === DATE_PARTS.YEAR)[0].position; + const yearFormat = this.dateFormatParts.filter(part => part.type === DATE_PARTS.YEAR)[0].formatType; + const yearPartPosition = this.dateFormatParts.filter(part => part.type === DATE_PARTS.YEAR)[0].position; const yearStartIdx = yearPartPosition[0]; const yearEndIdx = yearStartIdx + yearPartPosition.length; @@ -803,9 +803,9 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } } - public isDateValid(data) { - return new Date(DatePickerUtil.trimUnderlines(data)).toString() !== 'Invalid Date'; - } + // public isDateValid(data) { + // return new Date(DatePickerUtil.trimUnderlines(data)).toString() !== 'Invalid Date'; + // } /** * Evaluates when @calendar.onSelection event was fired @@ -864,15 +864,27 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi @HostListener('keydown.arrowdown', ['$event']) public onArrowDownKeydown(event) { event.preventDefault(); - const cursor = this._getCursorPosition() - 1; - const datePart = this.dateStruct.filter(element => element.position.find(pos => pos === cursor)); + event.stopPropagation(); + + const cursorPos = this._getCursorPosition(); + const inputValue = event.target.value; + this.editableInput.nativeElement.value = DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, -1); + requestAnimationFrame(() => { + this._setCursorPosition(cursorPos); + }); } @HostListener('keydown.arrowup', ['$event']) public onArrowUpKeydown(event) { event.preventDefault(); - const cursor = this._getCursorPosition() - 1; - const datePart = this.dateStruct.filter(element => element.position.find(pos => pos === cursor)); + event.stopPropagation(); + + const cursorPos = this._getCursorPosition(); + const inputValue = event.target.value; + this.editableInput.nativeElement.value = DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, 1); + requestAnimationFrame(() => { + this._setCursorPosition(cursorPos); + }); } private onOpened() { @@ -917,6 +929,10 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi return this.editableInput.nativeElement.selectionStart; } + private _setCursorPosition(start: number, end: number = start): void { + this.editableInput.nativeElement.setSelectionRange(start, end); + } + private getFormatOptions(format) { switch (format) { case PREDEFINED_FORMAT_OPTIONS.SHORT_DATE: { @@ -960,7 +976,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi private _onChangeCallback: (_: Date) => void = () => { }; - public transformDate(date) { + private transformDate(date) { return new DateFormatPipe(Constants.DEFAULT_LOCALE_DATE).transform(date, this.format); } } @@ -969,109 +985,6 @@ class Constants { public static readonly DEFAULT_LOCALE_DATE = 'en'; } -@Pipe({ - name: 'format' -}) - -export class DateFormatPipe extends DatePipe implements PipeTransform { - transform(value: any, args?: any): any { - return super.transform(value, args); - } -} - -@Pipe({ - name: 'displayValue' -}) -export class DisplayValuePipe implements PipeTransform { - constructor(public datePicker: IgxDatePickerComponent) { } - // on blur - transform(value: any, args?: any): any { - if (value !== '') { - if (value === DatePickerUtil.trimMaskSymbols(this.datePicker.mask)) { - return ''; - } - this.datePicker.rawData = value; - return DatePickerUtil.trimUnderlines(value); - } - - return ''; - } -} - -@Pipe({ - name: 'inputValue' -}) -export class InputValuePipe implements PipeTransform { - constructor(public datePicker: IgxDatePickerComponent) { } - // on focus - transform(value: any, args?: any): any { - if (this.datePicker.value !== null && this.datePicker.value !== undefined) { - let result; - let offset = 0; - const dateArray = Array.from(value); - const monthName = DatePickerUtil.getLongMonthName(this.datePicker.value); - const dayName = DatePickerUtil.getLongDayName(this.datePicker.value); - - const dateStruct = this.datePicker.dateStruct; - - for (let i = 0; i < dateStruct.length; i++) { - if (dateStruct[i].type === DATE_PARTS.WEEKDAY) { - if (dateStruct[i].formatType === FORMAT_DESC.LONG) { - offset += DatePickerUtil.MAX_WEEKDAY_SYMBOLS - 4; - for (let j = dayName.length; j < DatePickerUtil.MAX_WEEKDAY_SYMBOLS; j++) { - dateArray.splice(j, 0, '_'); - } - dateArray.join(''); - } - } - - if (dateStruct[i].type === DATE_PARTS.MONTH) { - if (dateStruct[i].formatType === FORMAT_DESC.LONG) { - const startPos = offset + dateStruct[i].initialPosition + monthName.length; - const endPos = startPos + DatePickerUtil.MAX_MONTH_SYMBOLS - monthName.length; - offset += DatePickerUtil.MAX_MONTH_SYMBOLS - 4; - for (let j = startPos; j < endPos; j++) { - dateArray.splice(j, 0, '_'); - } - dateArray.join(''); - } - if (dateStruct[i].formatType === FORMAT_DESC.NUMERIC - || dateStruct[i].formatType === FORMAT_DESC.TWO_DIGITS) { - const isOneDigit = this.datePicker.isOneDigit(DATE_CHARS.MONTH_CHAR, this.datePicker.value.getMonth() + 1); - if (isOneDigit) { - const symbol = (dateStruct[i].formatType === FORMAT_DESC.TWO_DIGITS) ? '0' : '_'; - const startPos = offset + dateStruct[i].initialPosition; - dateArray.splice(startPos, 0, symbol); - } - offset += 1; - dateArray.join(''); - } - } - - if (dateStruct[i].type === DATE_PARTS.DAY) { - if (dateStruct[i].formatType === FORMAT_DESC.NUMERIC - || dateStruct[i].formatType === FORMAT_DESC.TWO_DIGITS) { - const isOneDigit = this.datePicker.isOneDigit(DATE_CHARS.DAY_CHAR, this.datePicker.value.getDate()); - if (isOneDigit) { - const symbol = (dateStruct[i].formatType === FORMAT_DESC.TWO_DIGITS) ? '0' : '_'; - const startPos = offset + dateStruct[i].initialPosition; - dateArray.splice(startPos, 0, symbol); - } - offset += 1; - dateArray.join(''); - } - } - } - - result = dateArray.join(''); - - return result; - } - - return DatePickerUtil.trimMaskSymbols(this.datePicker.mask); - } -} - /** * The IgxDatePickerModule provides the {@link IgxDatePickerComponent} inside your application. */ 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 new file mode 100644 index 00000000000..7d495030289 --- /dev/null +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts @@ -0,0 +1,105 @@ +import { PipeTransform, Pipe } from '@angular/core'; +import { IgxDatePickerComponent } from 'igniteui-angular'; +import { DatePickerUtil, DATE_PARTS, FORMAT_DESC, DATE_CHARS } from './date-picker.utils'; +import { DatePipe } from '@angular/common'; + +@Pipe({ + name: 'format' +}) + +export class DateFormatPipe extends DatePipe implements PipeTransform { + transform(value: any, args?: any): any { + return super.transform(value, args); + } +} + +@Pipe({ + name: 'displayValue' +}) +export class DisplayValuePipe implements PipeTransform { + constructor(public datePicker: IgxDatePickerComponent) { } + // on blur + transform(value: any, args?: any): any { + if (value !== '') { + if (value === DatePickerUtil.trimMaskSymbols(this.datePicker.mask)) { + return ''; + } + this.datePicker.rawData = value; + return DatePickerUtil.trimUnderlines(value); + } + + return ''; + } +} + +@Pipe({ + name: 'inputValue' +}) +export class InputValuePipe implements PipeTransform { + constructor(public datePicker: IgxDatePickerComponent) { } + // on focus + transform(value: any, args?: any): any { + if (this.datePicker.value !== null && this.datePicker.value !== undefined) { + let result; + let offset = 0; + const dateArray = Array.from(value); + const monthName = DatePickerUtil.getLongMonthName(this.datePicker.value); + const dayName = DatePickerUtil.getLongDayName(this.datePicker.value); + + const dateFormatParts = this.datePicker.dateFormatParts; + + for (let i = 0; i < dateFormatParts.length; i++) { + if (dateFormatParts[i].type === DATE_PARTS.WEEKDAY) { + if (dateFormatParts[i].formatType === FORMAT_DESC.LONG) { + offset += DatePickerUtil.MAX_WEEKDAY_SYMBOLS - 4; + for (let j = dayName.length; j < DatePickerUtil.MAX_WEEKDAY_SYMBOLS; j++) { + dateArray.splice(j, 0, '_'); + } + dateArray.join(''); + } + } + + if (dateFormatParts[i].type === DATE_PARTS.MONTH) { + if (dateFormatParts[i].formatType === FORMAT_DESC.LONG) { + const startPos = offset + dateFormatParts[i].initialPosition + monthName.length; + const endPos = startPos + DatePickerUtil.MAX_MONTH_SYMBOLS - monthName.length; + offset += DatePickerUtil.MAX_MONTH_SYMBOLS - 4; + for (let j = startPos; j < endPos; j++) { + dateArray.splice(j, 0, '_'); + } + dateArray.join(''); + } + if (dateFormatParts[i].formatType === FORMAT_DESC.NUMERIC + || dateFormatParts[i].formatType === FORMAT_DESC.TWO_DIGITS) { + const isOneDigit = this.datePicker.isOneDigit(DATE_CHARS.MONTH_CHAR, this.datePicker.value.getMonth() + 1); + if (isOneDigit) { + const startPos = offset + dateFormatParts[i].initialPosition; + dateArray.splice(startPos, 0, DatePickerUtil.getNumericFormatPrefix(dateFormatParts[i].formatType)); + } + offset += 1; + dateArray.join(''); + } + } + + if (dateFormatParts[i].type === DATE_PARTS.DAY) { + if (dateFormatParts[i].formatType === FORMAT_DESC.NUMERIC + || dateFormatParts[i].formatType === FORMAT_DESC.TWO_DIGITS) { + const isOneDigit = this.datePicker.isOneDigit(DATE_CHARS.DAY_CHAR, this.datePicker.value.getDate()); + if (isOneDigit) { + const startPos = offset + dateFormatParts[i].initialPosition; + dateArray.splice(startPos, 0, DatePickerUtil.getNumericFormatPrefix(dateFormatParts[i].formatType)); + } + offset += 1; + dateArray.join(''); + } + } + } + + result = dateArray.join(''); + + return result; + } + + return DatePickerUtil.trimMaskSymbols(this.datePicker.mask); + } +} diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts index d6f87ae6aba..225c66ce26e 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -44,8 +44,9 @@ export enum DATE_PARTS { export abstract class DatePickerUtil { public static MAX_MONTH_SYMBOLS = 9; public static MAX_WEEKDAY_SYMBOLS = 9; + public static SEPARATOR = 'separator'; - public static getYearFormatType(format: string) { + public static getYearFormatType(format: string): string { let type; const occurences = format.match(new RegExp(DATE_CHARS.YEAR_CHAR, 'g')).length; @@ -69,7 +70,7 @@ export abstract class DatePickerUtil { return type; } - public static getMonthFormatType(format: string) { + public static getMonthFormatType(format: string): string { let type; const occurences = format.match(new RegExp(DATE_CHARS.MONTH_CHAR, 'g')).length; @@ -103,7 +104,7 @@ export abstract class DatePickerUtil { return type; } - public static getDayFormatType(format: string) { + public static getDayFormatType(format: string): string { let type; const occurences = format.match(new RegExp(DATE_CHARS.DAY_CHAR, 'g')).length; @@ -122,7 +123,7 @@ export abstract class DatePickerUtil { return type; } - public static getWeekDayFormatType(format: string) { + public static getWeekDayFormatType(format: string): string { let type; const occurences = format.match(new RegExp(DATE_CHARS.WEEKDAY_CHAR, 'g')).length; @@ -146,7 +147,7 @@ export abstract class DatePickerUtil { return type; } - public static parseDateFormat(format: string) { + public static parseDateFormat(format: string): any[] { const dateStruct = []; const maskArray = Array.from(format); const weekdayInitPosition = format.indexOf(DATE_CHARS.WEEKDAY_CHAR); @@ -189,7 +190,7 @@ export abstract class DatePickerUtil { for (let i = 0; i < maskArray.length; i++) { if (!DatePickerUtil.isSpecialSymbol(maskArray[i])) { dateStruct.push({ - type: 'literal', + type: DatePickerUtil.SEPARATOR, initialPosition: i, value: maskArray[i] }); @@ -202,7 +203,7 @@ export abstract class DatePickerUtil { return dateStruct; } - private static fillDatePartsPositions(dateArray) { + private static fillDatePartsPositions(dateArray: any[]) { let offset = 0; for (let i = 0; i < dateArray.length; i++) { @@ -237,7 +238,7 @@ export abstract class DatePickerUtil { } } - if (dateArray[i].type === 'literal') { + if (dateArray[i].type === DatePickerUtil.SEPARATOR) { dateArray[i].position = DatePickerUtil.fillValues(offset, 1); offset++; } @@ -268,14 +269,14 @@ export abstract class DatePickerUtil { return array; } - public static isSpecialSymbol(char: string) { + public static isSpecialSymbol(char: string): boolean { return (char !== DATE_CHARS.YEAR_CHAR && char !== DATE_CHARS.MONTH_CHAR && char !== DATE_CHARS.DAY_CHAR && char !== DATE_CHARS.WEEKDAY_CHAR) ? false : true; } - public static getFormatMask(format: string) { + public static getFormatMask(format: string): string { const mask = []; const dateStruct = DatePickerUtil.parseDateFormat(format); @@ -332,7 +333,7 @@ export abstract class DatePickerUtil { } } } - if (dateStruct[i].type === 'literal') { + if (dateStruct[i].type === DatePickerUtil.SEPARATOR) { mask.push(dateStruct[i].value); } } @@ -340,7 +341,7 @@ export abstract class DatePickerUtil { return mask.join(''); } - public static createDate(day, month, year) { + public static createDate(day: number, month: number, year: number): Date { const date = new Date(); date.setDate(day); date.setMonth(month); @@ -348,23 +349,90 @@ export abstract class DatePickerUtil { return date; } - public static trimMaskSymbols(mask) { + public static trimMaskSymbols(mask: string): string { return mask.replace(/0|L/g, '_'); } - public static trimUnderlines(value: string) { + public static trimUnderlines(value: string): string { return value.replace(/_/g, ''); } - public static getLongMonthName(value) { + public static getLongMonthName(value: Date): string { return value.toLocaleString('en', { month: 'long' }); } - public static getLongDayName(value) { + public static getLongDayName(value: Date): string { return value.toLocaleString('en', { weekday: 'long' }); } + + public static getNumericFormatPrefix(formatType: string): string { + return (formatType === FORMAT_DESC.TWO_DIGITS) ? '0' : '_'; + } + + public static getSpinnedDateInput(dateFormatParts: any[], inputValue: string, position: number, delta: number): string { + let datePart = DatePickerUtil.getDatePartOnPosition(dateFormatParts, position); + if ((datePart && datePart.length > 0 && datePart[0].type === DatePickerUtil.SEPARATOR) + || inputValue.length === position) { + datePart = this.getDatePartOnPosition(dateFormatParts, position - 1); + } + + const positionsArray = datePart[0].position; + const startIdx = positionsArray[0]; + const endIdx = positionsArray[0] + positionsArray.length; + const datePartType = datePart[0].type; + const datePartFormatType = datePart[0].formatType; + + let newValue = parseInt(DatePickerUtil.trimUnderlines(inputValue.substring(startIdx, endIdx)), 10); + + let maxValue, minValue = 1; + if (!isNaN(newValue)) { + switch (datePartType) { + case DATE_PARTS.MONTH: { + // Max 12 months + maxValue = 12; + break; + } + case DATE_PARTS.DAY: { + // Max 31 days + maxValue = 31; + break; + } + case DATE_PARTS.YEAR: { + if (datePartFormatType === FORMAT_DESC.TWO_DIGITS) { + minValue = 0; + maxValue = 99; + } else { + // Infinite loop + minValue = -1; + maxValue = -1; + } + break; + } + } + } + + let tempValue = newValue; + tempValue += delta; + if ((tempValue <= maxValue && tempValue >= minValue) || maxValue === -1 || minValue === -1) { + newValue = tempValue; + } + + const start = inputValue.slice(0, startIdx); + const end = inputValue.slice(endIdx, inputValue.length); + let changedPart: string; + + // Handling leading zero format + const prefix = DatePickerUtil.getNumericFormatPrefix(datePartFormatType); + changedPart = (newValue < 10) ? `${prefix}${newValue}` : `${newValue}`; + + return `${start}${changedPart}${end}`; + } + + private static getDatePartOnPosition(dateFormatParts: any[], position: number) { + return dateFormatParts.filter((element) => element.position.some(pos => pos === position)); + } } From a41e57464fcab867d2aea066bae70eee6ee43c94 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Tue, 8 Jan 2019 11:58:04 +0200 Subject: [PATCH 07/57] feat(date-picker): Added spinning functionality and other fixes #3034 --- .../src/lib/date-picker/date-picker.common.ts | 11 ++ .../lib/date-picker/date-picker.component.ts | 180 ++++++++++-------- .../src/lib/date-picker/date-picker.pipes.ts | 45 ++--- .../src/lib/date-picker/date-picker.utils.ts | 145 +++++++------- 4 files changed, 199 insertions(+), 182 deletions(-) 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 e69de29bb2d..fd8c6dffefe 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 @@ -0,0 +1,11 @@ +/** @hidden */ +export const IGX_DATE_PICKER_COMPONENT = 'IgxDatePickerComponentToken'; + +/** @hidden */ +export interface IgxDatePickerBase { + value: Date; + format: string; + mask: string; + rawData: string; + dateFormatParts: any[]; +} 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 eff01000982..e927b100014 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,4 +1,4 @@ -import { CommonModule, DatePipe } from '@angular/common'; +import { CommonModule } from '@angular/common'; import { Component, ContentChild, @@ -11,13 +11,12 @@ import { Output, ViewChild, ViewContainerRef, - HostListener, ElementRef, TemplateRef, Directive, Inject, - Pipe, - PipeTransform + NgZone, + AfterViewInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { @@ -29,8 +28,8 @@ import { } from '../calendar/index'; import { IgxIconModule } from '../icon/index'; import { IgxInputGroupModule } from '../input-group/index'; -import { Subject } from 'rxjs'; -import { filter, takeUntil } from 'rxjs/operators'; +import { Subject, fromEvent, interval, animationFrameScheduler } from 'rxjs'; +import { filter, takeUntil, throttle } from 'rxjs/operators'; import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive'; import { OverlaySettings, @@ -55,6 +54,8 @@ import { DATE_PARTS } from './date-picker.utils'; import { DisplayValuePipe, InputValuePipe, DateFormatPipe } from './date-picker.pipes'; +import { IgxDatePickerBase } from './date-picker.common'; +import { KEYS } from '../core/utils'; @Directive({ selector: '[igxDatePickerTemplate]' @@ -103,7 +104,7 @@ export enum DatePickerInteractionMode { }) @DeprecateClass('\'igx-datePicker\' selector is deprecated. Use \'igx-date-picker\' selector instead.') -export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvider, OnInit, OnDestroy { +export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAccessor, EditorProvider, OnInit, OnDestroy, AfterViewInit { /** *Returns the format options of the `IgxDatePickerComponent`. *```typescript @@ -289,16 +290,12 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi */ public get transformedDate() { if (this._value) { - return this.transformDate(this._value); + return this._transformDate(this._value); } return ''; } - public set transformedDate(value: string) { - this._transformedDate = value; - } - - constructor(@Inject(IgxOverlayService) private overlayService: IgxOverlayService) { } + constructor(@Inject(IgxOverlayService) private _overlayService: IgxOverlayService, private _zone: NgZone) { } /** * Gets the input group template. @@ -378,22 +375,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } public set value(date: Date) { - switch (this.mode) { - case DatePickerInteractionMode.EDITABLE: { - if (date !== null) { - this._value = date; - this._transformedDate = this.transformDate(date); - } else { - this._value = null; - this._transformedDate = ''; - } - break; - } - case DatePickerInteractionMode.READONLY: { - this._value = date; - break; - } - } + this._value = date; } /** @@ -519,47 +501,56 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi @ViewChild('editableDatePickerTemplate', { read: TemplateRef }) protected editableDatePickerTemplate: TemplateRef; - /** - *@hidden + /* + * @hidden */ - @ContentChild(IgxDatePickerTemplateDirective, { read: IgxDatePickerTemplateDirective }) - protected datePickerTemplateDirective: IgxDatePickerTemplateDirective; - @ViewChild('editableInputGroup', { read: ElementRef }) protected editableInputGroup: ElementRef; + /* + * @hidden + */ @ViewChild('editableInput', { read: ElementRef }) protected editableInput: ElementRef; + /* + * @hidden + */ @ViewChild('readonlyInput', { read: ElementRef }) protected readonlyInput: ElementRef; @ViewChild('calendar') public calendar: IgxCalendarComponent; + /** + *@hidden + */ + @ViewChild('container', { read: ViewContainerRef }) + public container: ViewContainerRef; + /** *@hidden */ - @ContentChild(IgxCalendarHeaderTemplateDirective, { read: IgxCalendarHeaderTemplateDirective }) + @ViewChild('calendarContainer') + public calendarContainer: ElementRef; - public headerTemplate: IgxCalendarHeaderTemplateDirective; /** *@hidden */ - @ContentChild(IgxCalendarSubheaderTemplateDirective, { read: IgxCalendarSubheaderTemplateDirective }) - public subheaderTemplate: IgxCalendarSubheaderTemplateDirective; + @ContentChild(IgxDatePickerTemplateDirective, { read: IgxDatePickerTemplateDirective }) + protected datePickerTemplateDirective: IgxDatePickerTemplateDirective; /** *@hidden */ - @ViewChild('container', { read: ViewContainerRef }) - public container: ViewContainerRef; + @ContentChild(IgxCalendarHeaderTemplateDirective, { read: IgxCalendarHeaderTemplateDirective }) + public headerTemplate: IgxCalendarHeaderTemplateDirective; /** *@hidden */ - @ViewChild('calendarContainer') - public calendarContainer: ElementRef; + @ContentChild(IgxCalendarSubheaderTemplateDirective, { read: IgxCalendarSubheaderTemplateDirective }) + public subheaderTemplate: IgxCalendarSubheaderTemplateDirective; public hasHeader = true; public collapsed = true; @@ -594,7 +585,6 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi private _format; private _value; - private _transformedDate; private DEFAULT_DATE_FORMAT = PREDEFINED_FORMAT_OPTIONS.SHORT_DATE; /** @@ -651,16 +641,16 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi outlet: this.outlet }; - this.overlayService.onOpened.pipe( + this._overlayService.onOpened.pipe( filter(overlay => overlay.id === this._componentID), takeUntil(this._destroy$)).subscribe(() => { - this.onOpened(); + this._onOpened(); }); - this.overlayService.onClosed.pipe( + this._overlayService.onClosed.pipe( filter(overlay => overlay.id === this._componentID), takeUntil(this._destroy$)).subscribe(() => { - this.onClosed(); + this._onClosed(); }); if (this.calendarContainer) { @@ -668,16 +658,39 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } if (this.mode === DatePickerInteractionMode.EDITABLE) { - this.getFormatOptions(this.format); + this._getFormatOptions(this.format); this.dateFormatParts = DatePickerUtil.parseDateFormat(this.format); } } + /** + *@hidden + */ + public ngAfterViewInit(): void { + if (this.mode === DatePickerInteractionMode.EDITABLE) { + this._zone.runOutsideAngular(() => { + fromEvent(this.getEditElement(), 'keydown').pipe( + throttle(() => interval(0, animationFrameScheduler)), + takeUntil(this._destroy$)) + .subscribe((res) => { + this.onKeydown(res); + }); + + fromEvent(this.getEditElement(), 'mousewheel').pipe( + throttle(() => interval(0, animationFrameScheduler)), + takeUntil(this._destroy$)) + .subscribe((res) => { + this.onMouseWheel(res); + }); + }); + } + } + /** *@hidden */ public ngOnDestroy(): void { - this.overlayService.hideAll(); + this._overlayService.hideAll(); this._destroy$.next(true); this._destroy$.complete(); } @@ -744,14 +757,14 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi switch (this.mode) { case DatePickerInteractionMode.READONLY: { this.hasHeader = true; - this._componentID = this.overlayService.show(this.calendarContainer, this._modalOverlaySettings); + this._componentID = this._overlayService.show(this.calendarContainer, this._modalOverlaySettings); break; } case DatePickerInteractionMode.EDITABLE: { if (this.collapsed) { this._dropDownOverlaySettings.positionStrategy.settings.target = this.editableInputGroup.nativeElement; this.hasHeader = false; - this._componentID = this.overlayService.show(this.calendarContainer, this._dropDownOverlaySettings); + this._componentID = this._overlayService.show(this.calendarContainer, this._dropDownOverlaySettings); } break; } @@ -759,7 +772,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } public closeCalendar() { - this.overlayService.hide(this._componentID); + this._overlayService.hide(this._componentID); } public clear() { @@ -787,6 +800,9 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi const yearStartIdx = yearPartPosition[0]; const yearEndIdx = yearStartIdx + yearPartPosition.length; + if (this.rawData === undefined) { + this.rawData = data; + } const day = DatePickerUtil.trimUnderlines(this.rawData.substring(dayStartIdx, dayEndIdx)); const month = DatePickerUtil.trimUnderlines(this.rawData.substring(monthStartIdx, monthEndIdx)); const year = this.rawData.substring(yearStartIdx, yearEndIdx); @@ -837,21 +853,11 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi this.calculateDate(eventArgs.target.value); } - public isOneDigit(char, index) { - return (this.format.match(new RegExp(char, 'g')).length === 1 && index < 10); - } - - @HostListener('keydown.alt.arrowdown', ['$event']) - public onAltArrowDownKeydown(event: KeyboardEvent) { - this.calculateDate(this.editableInput.nativeElement.value); - this.openCalendar(event); - } - // @HostListener('keydown.esc', ['$event']) // public onEscKeydown(event) { // this.closeCalendar(); - // event.preventDefault(); - // event.stopPropagation(); + // event.preventDefault(); + // event.stopPropagation(); // } // @HostListener('keydown.spacebar', ['$event']) @@ -861,33 +867,45 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi // event.preventDefault(); // } - @HostListener('keydown.arrowdown', ['$event']) - public onArrowDownKeydown(event) { - event.preventDefault(); + private onKeydown(event) { event.stopPropagation(); - const cursorPos = this._getCursorPosition(); const inputValue = event.target.value; - this.editableInput.nativeElement.value = DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, -1); + + switch (event.key) { + case KEYS.UP_ARROW: + case KEYS.UP_ARROW_IE: + this.editableInput.nativeElement.value = DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, 1); + break; + case KEYS.DOWN_ARROW: + case KEYS.DOWN_ARROW_IE: + if (event.altKey) { + this.calculateDate(this.editableInput.nativeElement.value); + this.openCalendar(event); + } else { + this.editableInput.nativeElement.value = + DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, -1); + } + break; + default: + return; + } + + this._setCursorPosition(cursorPos); requestAnimationFrame(() => { this._setCursorPosition(cursorPos); }); } - @HostListener('keydown.arrowup', ['$event']) - public onArrowUpKeydown(event) { - event.preventDefault(); + private onMouseWheel(event) { event.stopPropagation(); - const cursorPos = this._getCursorPosition(); const inputValue = event.target.value; - this.editableInput.nativeElement.value = DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, 1); - requestAnimationFrame(() => { - this._setCursorPosition(cursorPos); - }); + + // console.log('deltaX ' + event.deltaX + ' deltaY ' + event.deltaY + ' deltaMode ' + event.deltaMode); } - private onOpened() { + private _onOpened() { this.collapsed = false; this.calendarContainer.nativeElement.style.display = 'block'; @@ -904,7 +922,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } } - private onClosed() { + private _onClosed() { this.collapsed = true; this.calendarContainer.nativeElement.style.display = 'none'; this.onClose.emit(this); @@ -933,7 +951,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi this.editableInput.nativeElement.setSelectionRange(start, end); } - private getFormatOptions(format) { + private _getFormatOptions(format) { switch (format) { case PREDEFINED_FORMAT_OPTIONS.SHORT_DATE: { this.mask = PREDEFINED_MASKS.SHORT_DATE_MASK; @@ -976,7 +994,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi private _onChangeCallback: (_: Date) => void = () => { }; - private transformDate(date) { + private _transformDate(date) { return new DateFormatPipe(Constants.DEFAULT_LOCALE_DATE).transform(date, this.format); } } 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 index 7d495030289..be02da03a5a 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts @@ -1,33 +1,29 @@ -import { PipeTransform, Pipe } from '@angular/core'; -import { IgxDatePickerComponent } from 'igniteui-angular'; +import { PipeTransform, Pipe, Inject } from '@angular/core'; import { DatePickerUtil, DATE_PARTS, FORMAT_DESC, DATE_CHARS } from './date-picker.utils'; import { DatePipe } from '@angular/common'; +import { IGX_DATE_PICKER_COMPONENT, IgxDatePickerBase } from './date-picker.common'; @Pipe({ name: 'format' }) - export class DateFormatPipe extends DatePipe implements PipeTransform { transform(value: any, args?: any): any { return super.transform(value, args); } } - @Pipe({ name: 'displayValue' }) export class DisplayValuePipe implements PipeTransform { - constructor(public datePicker: IgxDatePickerComponent) { } - // on blur + constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IgxDatePickerBase) { } transform(value: any, args?: any): any { if (value !== '') { - if (value === DatePickerUtil.trimMaskSymbols(this.datePicker.mask)) { + if (value === DatePickerUtil.trimMaskSymbols(this._datePicker.mask)) { return ''; } - this.datePicker.rawData = value; + this._datePicker.rawData = value; return DatePickerUtil.trimUnderlines(value); } - return ''; } } @@ -36,24 +32,23 @@ export class DisplayValuePipe implements PipeTransform { name: 'inputValue' }) export class InputValuePipe implements PipeTransform { - constructor(public datePicker: IgxDatePickerComponent) { } - // on focus + private PROMPT_CHAR = '_'; + constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IgxDatePickerBase) { } transform(value: any, args?: any): any { - if (this.datePicker.value !== null && this.datePicker.value !== undefined) { - let result; + if (this._datePicker.value !== null && this._datePicker.value !== undefined) { let offset = 0; const dateArray = Array.from(value); - const monthName = DatePickerUtil.getLongMonthName(this.datePicker.value); - const dayName = DatePickerUtil.getLongDayName(this.datePicker.value); - - const dateFormatParts = this.datePicker.dateFormatParts; + const monthName = DatePickerUtil.getLongMonthName(this._datePicker.value); + const dayName = DatePickerUtil.getLongDayName(this._datePicker.value); + const dateFormatParts = this._datePicker.dateFormatParts; + const datePickerFormat = this._datePicker.format; for (let i = 0; i < dateFormatParts.length; i++) { if (dateFormatParts[i].type === DATE_PARTS.WEEKDAY) { if (dateFormatParts[i].formatType === FORMAT_DESC.LONG) { offset += DatePickerUtil.MAX_WEEKDAY_SYMBOLS - 4; for (let j = dayName.length; j < DatePickerUtil.MAX_WEEKDAY_SYMBOLS; j++) { - dateArray.splice(j, 0, '_'); + dateArray.splice(j, 0, this.PROMPT_CHAR); } dateArray.join(''); } @@ -65,13 +60,14 @@ export class InputValuePipe implements PipeTransform { const endPos = startPos + DatePickerUtil.MAX_MONTH_SYMBOLS - monthName.length; offset += DatePickerUtil.MAX_MONTH_SYMBOLS - 4; for (let j = startPos; j < endPos; j++) { - dateArray.splice(j, 0, '_'); + dateArray.splice(j, 0, this.PROMPT_CHAR); } dateArray.join(''); } if (dateFormatParts[i].formatType === FORMAT_DESC.NUMERIC || dateFormatParts[i].formatType === FORMAT_DESC.TWO_DIGITS) { - const isOneDigit = this.datePicker.isOneDigit(DATE_CHARS.MONTH_CHAR, this.datePicker.value.getMonth() + 1); + const isOneDigit = + DatePickerUtil.isOneDigit(datePickerFormat, DATE_CHARS.MONTH_CHAR, this._datePicker.value.getMonth() + 1); if (isOneDigit) { const startPos = offset + dateFormatParts[i].initialPosition; dateArray.splice(startPos, 0, DatePickerUtil.getNumericFormatPrefix(dateFormatParts[i].formatType)); @@ -84,7 +80,8 @@ export class InputValuePipe implements PipeTransform { if (dateFormatParts[i].type === DATE_PARTS.DAY) { if (dateFormatParts[i].formatType === FORMAT_DESC.NUMERIC || dateFormatParts[i].formatType === FORMAT_DESC.TWO_DIGITS) { - const isOneDigit = this.datePicker.isOneDigit(DATE_CHARS.DAY_CHAR, this.datePicker.value.getDate()); + const isOneDigit = + DatePickerUtil.isOneDigit(datePickerFormat, DATE_CHARS.DAY_CHAR, this._datePicker.value.getDate()); if (isOneDigit) { const startPos = offset + dateFormatParts[i].initialPosition; dateArray.splice(startPos, 0, DatePickerUtil.getNumericFormatPrefix(dateFormatParts[i].formatType)); @@ -95,11 +92,9 @@ export class InputValuePipe implements PipeTransform { } } - result = dateArray.join(''); - - return result; + return dateArray.join(''); } - return DatePickerUtil.trimMaskSymbols(this.datePicker.mask); + return DatePickerUtil.trimMaskSymbols(this._datePicker.mask); } } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts index 225c66ce26e..58a21b1854d 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -202,80 +202,12 @@ export abstract class DatePickerUtil { return dateStruct; } - - private static fillDatePartsPositions(dateArray: any[]) { - let offset = 0; - - for (let i = 0; i < dateArray.length; i++) { - if (dateArray[i].type === DATE_PARTS.DAY) { - dateArray[i].position = DatePickerUtil.fillValues(offset, 2); - offset += 2; - } - - if (dateArray[i].type === DATE_PARTS.MONTH) { - switch (dateArray[i].formatType) { - case FORMAT_DESC.SHORT: { - dateArray[i].position = DatePickerUtil.fillValues(offset, 3); - offset += 3; - break; - } - case FORMAT_DESC.LONG: { - dateArray[i].position = DatePickerUtil.fillValues(offset, 9); - offset += 9; - break; - } - case FORMAT_DESC.NARROW: { - dateArray[i].position = DatePickerUtil.fillValues(offset, 1); - offset++; - break; - } - default: { - // FORMAT_DESC.NUMERIC || FORMAT_DESC.TWO_DIGITS - dateArray[i].position = DatePickerUtil.fillValues(offset, 2); - offset += 2; - break; - } - } - } - - if (dateArray[i].type === DatePickerUtil.SEPARATOR) { - dateArray[i].position = DatePickerUtil.fillValues(offset, 1); - offset++; - } - - if (dateArray[i].type === DATE_PARTS.YEAR) { - switch (dateArray[i].formatType) { - case FORMAT_DESC.NUMERIC: { - dateArray[i].position = DatePickerUtil.fillValues(offset, 4); - offset += 4; - break; - } - case FORMAT_DESC.TWO_DIGITS: { - dateArray[i].position = DatePickerUtil.fillValues(offset, 2); - offset += 2; - break; - } - } - } - } - } - - private static fillValues(start: number, offset: number) { - const array = []; - for (let i = start; i < start + offset; i++) { - array.push(i); - } - - return array; - } - public static isSpecialSymbol(char: string): boolean { return (char !== DATE_CHARS.YEAR_CHAR && char !== DATE_CHARS.MONTH_CHAR && char !== DATE_CHARS.DAY_CHAR && char !== DATE_CHARS.WEEKDAY_CHAR) ? false : true; } - public static getFormatMask(format: string): string { const mask = []; const dateStruct = DatePickerUtil.parseDateFormat(format); @@ -340,7 +272,6 @@ export abstract class DatePickerUtil { return mask.join(''); } - public static createDate(day: number, month: number, year: number): Date { const date = new Date(); date.setDate(day); @@ -348,31 +279,25 @@ export abstract class DatePickerUtil { date.setFullYear(year); return date; } - public static trimMaskSymbols(mask: string): string { return mask.replace(/0|L/g, '_'); } - public static trimUnderlines(value: string): string { return value.replace(/_/g, ''); } - public static getLongMonthName(value: Date): string { return value.toLocaleString('en', { month: 'long' }); } - public static getLongDayName(value: Date): string { return value.toLocaleString('en', { weekday: 'long' }); } - public static getNumericFormatPrefix(formatType: string): string { return (formatType === FORMAT_DESC.TWO_DIGITS) ? '0' : '_'; } - public static getSpinnedDateInput(dateFormatParts: any[], inputValue: string, position: number, delta: number): string { let datePart = DatePickerUtil.getDatePartOnPosition(dateFormatParts, position); if ((datePart && datePart.length > 0 && datePart[0].type === DatePickerUtil.SEPARATOR) @@ -431,8 +356,76 @@ export abstract class DatePickerUtil { return `${start}${changedPart}${end}`; } - + public static isOneDigit(input: string, char: string, index: number): boolean { + return input.match(new RegExp(char, 'g')).length === 1 && index < 10; + } + public static daysInMonth(date: Date): number { + return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); + } private static getDatePartOnPosition(dateFormatParts: any[], position: number) { return dateFormatParts.filter((element) => element.position.some(pos => pos === position)); } + private static fillDatePartsPositions(dateArray: any[]): void { + let offset = 0; + + for (let i = 0; i < dateArray.length; i++) { + if (dateArray[i].type === DATE_PARTS.DAY) { + dateArray[i].position = DatePickerUtil.fillValues(offset, 2); + offset += 2; + } + + if (dateArray[i].type === DATE_PARTS.MONTH) { + switch (dateArray[i].formatType) { + case FORMAT_DESC.SHORT: { + dateArray[i].position = DatePickerUtil.fillValues(offset, 3); + offset += 3; + break; + } + case FORMAT_DESC.LONG: { + dateArray[i].position = DatePickerUtil.fillValues(offset, 9); + offset += 9; + break; + } + case FORMAT_DESC.NARROW: { + dateArray[i].position = DatePickerUtil.fillValues(offset, 1); + offset++; + break; + } + default: { + // FORMAT_DESC.NUMERIC || FORMAT_DESC.TWO_DIGITS + dateArray[i].position = DatePickerUtil.fillValues(offset, 2); + offset += 2; + break; + } + } + } + + if (dateArray[i].type === DatePickerUtil.SEPARATOR) { + dateArray[i].position = DatePickerUtil.fillValues(offset, 1); + offset++; + } + + if (dateArray[i].type === DATE_PARTS.YEAR) { + switch (dateArray[i].formatType) { + case FORMAT_DESC.NUMERIC: { + dateArray[i].position = DatePickerUtil.fillValues(offset, 4); + offset += 4; + break; + } + case FORMAT_DESC.TWO_DIGITS: { + dateArray[i].position = DatePickerUtil.fillValues(offset, 2); + offset += 2; + break; + } + } + } + } + } + private static fillValues(start: number, offset: number) { + const array = []; + for (let i = start; i < start + offset; i++) { + array.push(i); + } + return array; + } } From e24418dd6fefadb54f3bd15e8f7cb6ee109a7c06 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Thu, 10 Jan 2019 10:42:00 +0200 Subject: [PATCH 08/57] feat(date-picker): Spinning issues fixes #3034 --- .../lib/date-picker/date-picker.component.ts | 91 +++++++++++-------- .../src/lib/date-picker/date-picker.utils.ts | 2 +- src/app/date-picker/date-picker.sample.html | 27 +++++- src/app/date-picker/date-picker.sample.ts | 28 +++++- 4 files changed, 102 insertions(+), 46 deletions(-) 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 dde74456cf2..fc1efb3a4ff 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 @@ -16,7 +16,8 @@ import { Directive, Inject, NgZone, - AfterViewInit + AfterViewInit, + HostListener } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { @@ -320,6 +321,24 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc openCalendar: (eventArgs) => { this.openCalendar(eventArgs); } }; } + + /** + *An @Input property that sets the selected date. + *```typescript + *public date: Date = new Date(); + *``` + *```html + * + *``` + */ + @Input() + public get value(): Date { + return this._value; + } + + public set value(date: Date) { + this._value = date; + } /** *An @Input property that sets the value of `id` attribute. If not provided it will be automatically generated. *```html @@ -358,24 +377,6 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc @Input() public disabled: boolean; - /** - *An @Input property that sets the selected date. - *```typescript - *public date: Date = new Date(); - *``` - *```html - * - *``` - */ - @Input() - public get value(): Date { - return this._value; - } - - public set value(date: Date) { - this._value = date; - } - /** * An @Input property that sets the `IgxDatePickerComponent` label. * The default label is 'Date'. @@ -585,6 +586,26 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc private _value; private DEFAULT_DATE_FORMAT = PREDEFINED_FORMAT_OPTIONS.SHORT_DATE; + // @HostListener('keydown.esc', ['$event']) + // public onEscKeydown(event) { + // this.closeCalendar(); + // event.preventDefault(); + // event.stopPropagation(); + // } + + // @HostListener('keydown.spacebar', ['$event']) + // @HostListener('keydown.space', ['$event']) + // public onSpaceClick(event) { + // this.openCalendar(); + // event.preventDefault(); + // } + + + @HostListener('keyup', ['$event']) + public test(event) { + // debugger; + } + /** *Method that sets the selected date. *```typescript @@ -674,12 +695,12 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc this.onKeydown(res); }); - fromEvent(this.getEditElement(), 'mousewheel').pipe( - throttle(() => interval(0, animationFrameScheduler)), - takeUntil(this._destroy$)) - .subscribe((res) => { - this.onMouseWheel(res); - }); + // fromEvent(this.getEditElement(), 'mousewheel').pipe( + // throttle(() => interval(0, animationFrameScheduler)), + // takeUntil(this._destroy$)) + // .subscribe((res) => { + // this.onMouseWheel(res); + // }); }); } } @@ -851,29 +872,20 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc this.calculateDate(eventArgs.target.value); } - // @HostListener('keydown.esc', ['$event']) - // public onEscKeydown(event) { - // this.closeCalendar(); - // event.preventDefault(); - // event.stopPropagation(); - // } - - // @HostListener('keydown.spacebar', ['$event']) - // @HostListener('keydown.space', ['$event']) - // public onSpaceClick(event) { - // this.openCalendar(); - // event.preventDefault(); - // } private onKeydown(event) { + console.log('onKeydown 1 '); + event.preventDefault(); event.stopPropagation(); const cursorPos = this._getCursorPosition(); const inputValue = event.target.value; + console.log('repeat ' + event.repeat); switch (event.key) { case KEYS.UP_ARROW: case KEYS.UP_ARROW_IE: this.editableInput.nativeElement.value = DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, 1); + console.log('onKeydown 2 '); break; case KEYS.DOWN_ARROW: case KEYS.DOWN_ARROW_IE: @@ -892,7 +904,10 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc this._setCursorPosition(cursorPos); requestAnimationFrame(() => { this._setCursorPosition(cursorPos); + console.log('onKeydown 4'); }); + + console.log('onKeydown 3'); } private onMouseWheel(event) { diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts index 58a21b1854d..9b8ab92edb5 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -41,7 +41,7 @@ export enum DATE_PARTS { WEEKDAY = 'weekday' } -export abstract class DatePickerUtil { +export class DatePickerUtil { public static MAX_MONTH_SYMBOLS = 9; public static MAX_WEEKDAY_SYMBOLS = 9; public static SEPARATOR = 'separator'; diff --git a/src/app/date-picker/date-picker.sample.html b/src/app/date-picker/date-picker.sample.html index 45826804442..1016c8c690d 100644 --- a/src/app/date-picker/date-picker.sample.html +++ b/src/app/date-picker/date-picker.sample.html @@ -11,22 +11,31 @@

Default Date Picker.

-

Editable Date Picker

+

Editable Date Picker bind with ngModel

+
- +
+
+

Editable Date Picker

+ + +
+ +
+
+

Date Picker with passed date.

Detailed description to be added.

-
--> +

Date Picker with passed date and custom formatter.

Detailed description to be added.

@@ -35,6 +44,14 @@

Date Picker with passed date and custom formatter.

+

Vertical Date Picker

+

Detailed description to be added.

+
+ +
+
+ +
\ No newline at end of file diff --git a/src/app/date-picker/date-picker.sample.ts b/src/app/date-picker/date-picker.sample.ts index c2f75a1ae80..678f72d3fd1 100644 --- a/src/app/date-picker/date-picker.sample.ts +++ b/src/app/date-picker/date-picker.sample.ts @@ -1,5 +1,6 @@ -import { Component, ViewChild } from '@angular/core'; +import { Component, ViewChild, PipeTransform, Pipe } from '@angular/core'; import { IgxDatePickerComponent } from 'igniteui-angular'; +import { DatePipe } from '@angular/common'; @Component({ selector: 'app-date-picker-sample', @@ -11,7 +12,7 @@ export class DatePickerSampleComponent { @ViewChild('datePicker') datePicker: IgxDatePickerComponent; date = new Date('10/3/2018'); - date1 = new Date(); + public date1; formatter = (_: Date) => { return _.toDateString(); @@ -20,4 +21,27 @@ export class DatePickerSampleComponent { public deselect() { this.datePicker.deselectDate(); } + + constructor() { + const date = new Date(); + date.setDate(10); + date.setMonth(2); + date.setFullYear(2018); + + this.date1 = date; + + // this.date1.setDate(10); + // this.date1.setMonth(2); + // this.date1.setFullYear(2018); + + const test = new DateFormatPipe('en').transform(date, 'd.M.y'); + } +} +@Pipe({ + name: 'format' +}) +export class DateFormatPipe extends DatePipe implements PipeTransform { + transform(value: any, args?: any): any { + return super.transform(value, args); + } } From 8dcabb42ccde3ef2fa22a916fa5201c973d05146 Mon Sep 17 00:00:00 2001 From: Borislav Kulov Date: Thu, 10 Jan 2019 13:15:06 +0200 Subject: [PATCH 09/57] chore(*): Some code changes --- .../date-picker/date-picker.component.html | 2 +- .../lib/date-picker/date-picker.component.ts | 47 ++++++++++++------- .../src/lib/date-picker/date-picker.utils.ts | 3 +- 3 files changed, 32 insertions(+), 20 deletions(-) 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 dab167b1195..958e7d322b6 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 @@ -17,7 +17,7 @@ + (blur)="onBlur($event)" (keydown)="onKeydown($event)" /> clear 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 fc1efb3a4ff..faa6eb398b2 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 @@ -287,7 +287,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc * *``` */ - public get transformedDate() { + public get transformedDate(): string { if (this._value) { return this._transformDate(this._value); } @@ -559,6 +559,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc public dateFormatParts = []; public rawData; + private enDateFormatPipe = new DateFormatPipe(Constants.DEFAULT_LOCALE_DATE); private _destroy$ = new Subject(); private _componentID; @@ -687,13 +688,21 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc */ public ngAfterViewInit(): void { if (this.mode === DatePickerInteractionMode.EDITABLE) { - this._zone.runOutsideAngular(() => { - fromEvent(this.getEditElement(), 'keydown').pipe( - throttle(() => interval(0, animationFrameScheduler)), - takeUntil(this._destroy$)) - .subscribe((res) => { - this.onKeydown(res); - }); + // this._zone.runOutsideAngular(() => { + // fromEvent(this.getEditElement(), 'keydown').pipe( + // throttle(() => interval(0, animationFrameScheduler)), + // takeUntil(this._destroy$)) + // .subscribe((res) => { + // this.onKeydown(res); + // }); + + // fromEvent(this.getEditElement(), 'keydown').pipe( + // throttle(() => interval(0, animationFrameScheduler)), + // takeUntil(this._destroy$)) + // .subscribe((res) => { + // this.onKeydown(res); + // }); + // fromEvent(this.getEditElement(), 'mousewheel').pipe( // throttle(() => interval(0, animationFrameScheduler)), @@ -701,7 +710,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc // .subscribe((res) => { // this.onMouseWheel(res); // }); - }); + //}); } } @@ -875,8 +884,8 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc private onKeydown(event) { console.log('onKeydown 1 '); - event.preventDefault(); - event.stopPropagation(); + //event.preventDefault(); + //event.stopPropagation(); const cursorPos = this._getCursorPosition(); const inputValue = event.target.value; @@ -885,6 +894,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc case KEYS.UP_ARROW: case KEYS.UP_ARROW_IE: this.editableInput.nativeElement.value = DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, 1); + //this._value = DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, 1); console.log('onKeydown 2 '); break; case KEYS.DOWN_ARROW: @@ -901,11 +911,11 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc return; } - this._setCursorPosition(cursorPos); - requestAnimationFrame(() => { - this._setCursorPosition(cursorPos); - console.log('onKeydown 4'); - }); + // this._setCursorPosition(cursorPos); + // requestAnimationFrame(() => { + // this._setCursorPosition(cursorPos); + // console.log('onKeydown 4'); + // }); console.log('onKeydown 3'); } @@ -1007,8 +1017,9 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc private _onChangeCallback: (_: Date) => void = () => { }; - private _transformDate(date) { - return new DateFormatPipe(Constants.DEFAULT_LOCALE_DATE).transform(date, this.format); + private _transformDate(date: string) { + //return new DateFormatPipe(Constants.DEFAULT_LOCALE_DATE).transform(date, this.format); + return this.enDateFormatPipe.transform(date, this.format); } } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts index 9b8ab92edb5..ed0c6635a90 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -307,7 +307,8 @@ export class DatePickerUtil { const positionsArray = datePart[0].position; const startIdx = positionsArray[0]; - const endIdx = positionsArray[0] + positionsArray.length; + //const endIdx = positionsArray[0] + positionsArray.length; + const endIdx = positionsArray[positionsArray.length - 1]; const datePartType = datePart[0].type; const datePartFormatType = datePart[0].formatType; From fcdc1dd820d5b8aa2db6e21536c0e6d7a44263dd Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Thu, 10 Jan 2019 13:47:17 +0200 Subject: [PATCH 10/57] feat(date-picker): Resolved merged changes #3034 --- .../date-picker/date-picker.component.html | 2 +- .../lib/date-picker/date-picker.component.ts | 91 +-- .../src/lib/date-picker/date-picker.pipes.ts | 45 +- .../src/lib/date-picker/date-picker.utils.ts | 772 ++++++++++-------- projects/igniteui-angular/src/public_api.ts | 1 + src/app/date-picker/date-picker.sample.html | 20 +- 6 files changed, 510 insertions(+), 421 deletions(-) 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 958e7d322b6..87b586dcdfe 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 @@ -16,7 +16,7 @@ clear 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 faa6eb398b2..31cc96730a4 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 @@ -16,8 +16,7 @@ import { Directive, Inject, NgZone, - AfterViewInit, - HostListener + AfterViewInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { @@ -49,9 +48,13 @@ import { PREDEFINED_FORMAT_OPTIONS, PREDEFINED_FORMATS, PREDEFINED_MASKS, - DatePickerUtil, FORMAT_DESC, - DATE_PARTS + DATE_PARTS, + parseDateFormat, + trimUnderlines, + createDate, + getSpinnedDateInput, + getFormatMask } from './date-picker.utils'; import { DisplayValuePipe, InputValuePipe, DateFormatPipe } from './date-picker.pipes'; import { IgxDatePickerBase } from './date-picker.common'; @@ -554,8 +557,8 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc public hasHeader = true; public collapsed = true; public mask; - public displayValue = new DisplayValuePipe(this); - public inputValue = new InputValuePipe(this); + public displayValuePipe = new DisplayValuePipe(this); + public inputValuePipe = new InputValuePipe(this); public dateFormatParts = []; public rawData; @@ -601,12 +604,6 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc // event.preventDefault(); // } - - @HostListener('keyup', ['$event']) - public test(event) { - // debugger; - } - /** *Method that sets the selected date. *```typescript @@ -679,7 +676,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc if (this.mode === DatePickerInteractionMode.EDITABLE) { this._getFormatOptions(this.format); - this.dateFormatParts = DatePickerUtil.parseDateFormat(this.format); + this.dateFormatParts = parseDateFormat(this.format); } } @@ -687,31 +684,23 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc *@hidden */ public ngAfterViewInit(): void { - if (this.mode === DatePickerInteractionMode.EDITABLE) { - // this._zone.runOutsideAngular(() => { - // fromEvent(this.getEditElement(), 'keydown').pipe( - // throttle(() => interval(0, animationFrameScheduler)), - // takeUntil(this._destroy$)) - // .subscribe((res) => { - // this.onKeydown(res); - // }); - - // fromEvent(this.getEditElement(), 'keydown').pipe( - // throttle(() => interval(0, animationFrameScheduler)), - // takeUntil(this._destroy$)) - // .subscribe((res) => { - // this.onKeydown(res); - // }); - - - // fromEvent(this.getEditElement(), 'mousewheel').pipe( - // throttle(() => interval(0, animationFrameScheduler)), - // takeUntil(this._destroy$)) - // .subscribe((res) => { - // this.onMouseWheel(res); - // }); - //}); - } + // if (this.mode === DatePickerInteractionMode.EDITABLE) { + // this._zone.runOutsideAngular(() => { + // fromEvent(this.getEditElement(), 'keydown').pipe( + // throttle(() => interval(0, animationFrameScheduler)), + // takeUntil(this._destroy$)) + // .subscribe((res) => { + // this.onKeydown(res); + // }); + + // fromEvent(this.getEditElement(), 'mousewheel').pipe( + // throttle(() => interval(0, animationFrameScheduler)), + // takeUntil(this._destroy$)) + // .subscribe((res) => { + // this.onMouseWheel(res); + // }); + // }); + // } } /** @@ -807,9 +796,9 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc this.deselectDate(); } - public calculateDate(data: string) { + public calculateDate(data: string): void { if (data !== '') { - const trimmedData = DatePickerUtil.trimUnderlines(data); + const trimmedData = trimUnderlines(data); const monthPart = this.dateFormatParts.filter(part => part.type === DATE_PARTS.MONTH); if (monthPart[0].formatType === FORMAT_DESC.NUMERIC @@ -831,8 +820,8 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc if (this.rawData === undefined) { this.rawData = data; } - const day = DatePickerUtil.trimUnderlines(this.rawData.substring(dayStartIdx, dayEndIdx)); - const month = DatePickerUtil.trimUnderlines(this.rawData.substring(monthStartIdx, monthEndIdx)); + const day = trimUnderlines(this.rawData.substring(dayStartIdx, dayEndIdx)); + const month = trimUnderlines(this.rawData.substring(monthStartIdx, monthEndIdx)); const year = this.rawData.substring(yearStartIdx, yearEndIdx); if (yearFormat === FORMAT_DESC.TWO_DIGITS) { fullYear = '20'.concat(year); @@ -840,7 +829,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc fullYear = year; } - this.value = DatePickerUtil.createDate(Number(day), Number(month) - 1, Number(fullYear)); + this.value = createDate(Number(day), Number(month) - 1, Number(fullYear)); } else { this.value = new Date(trimmedData); } @@ -881,11 +870,10 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc this.calculateDate(eventArgs.target.value); } - private onKeydown(event) { console.log('onKeydown 1 '); - //event.preventDefault(); - //event.stopPropagation(); + // event.preventDefault(); + // event.stopPropagation(); const cursorPos = this._getCursorPosition(); const inputValue = event.target.value; @@ -893,8 +881,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc switch (event.key) { case KEYS.UP_ARROW: case KEYS.UP_ARROW_IE: - this.editableInput.nativeElement.value = DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, 1); - //this._value = DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, 1); + this.editableInput.nativeElement.value = getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, 1); console.log('onKeydown 2 '); break; case KEYS.DOWN_ARROW: @@ -904,7 +891,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc this.openCalendar(event); } else { this.editableInput.nativeElement.value = - DatePickerUtil.getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, -1); + getSpinnedDateInput(this.dateFormatParts, inputValue, cursorPos, -1); } break; default: @@ -925,7 +912,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc const cursorPos = this._getCursorPosition(); const inputValue = event.target.value; - // console.log('deltaX ' + event.deltaX + ' deltaY ' + event.deltaY + ' deltaMode ' + event.deltaMode); + console.log('deltaX ' + event.deltaX + ' deltaY ' + event.deltaY + ' deltaMode ' + event.deltaMode); } private _onOpened() { @@ -997,7 +984,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc break; } default: { - this.mask = DatePickerUtil.getFormatMask(format); + this.mask = getFormatMask(format); this.format = format; break; } @@ -1018,7 +1005,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc private _onChangeCallback: (_: Date) => void = () => { }; private _transformDate(date: string) { - //return new DateFormatPipe(Constants.DEFAULT_LOCALE_DATE).transform(date, this.format); + // return new DateFormatPipe(Constants.DEFAULT_LOCALE_DATE).transform(date, this.format); return this.enDateFormatPipe.transform(date, this.format); } } 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 index be02da03a5a..a23cdc5479f 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts @@ -1,7 +1,20 @@ import { PipeTransform, Pipe, Inject } from '@angular/core'; -import { DatePickerUtil, DATE_PARTS, FORMAT_DESC, DATE_CHARS } from './date-picker.utils'; import { DatePipe } from '@angular/common'; import { IGX_DATE_PICKER_COMPONENT, IgxDatePickerBase } from './date-picker.common'; +import { + trimMaskSymbols, + trimUnderlines, + getLongMonthName, + getLongDayName, + getNumericFormatPrefix, + isOneDigit, + DATE_PARTS, + FORMAT_DESC, + MAX_WEEKDAY_SYMBOLS, + MAX_MONTH_SYMBOLS, + DATE_CHARS, + +} from './date-picker.utils'; @Pipe({ name: 'format' @@ -18,11 +31,11 @@ export class DisplayValuePipe implements PipeTransform { constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IgxDatePickerBase) { } transform(value: any, args?: any): any { if (value !== '') { - if (value === DatePickerUtil.trimMaskSymbols(this._datePicker.mask)) { + if (value === trimMaskSymbols(this._datePicker.mask)) { return ''; } this._datePicker.rawData = value; - return DatePickerUtil.trimUnderlines(value); + return trimUnderlines(value); } return ''; } @@ -38,16 +51,16 @@ export class InputValuePipe implements PipeTransform { if (this._datePicker.value !== null && this._datePicker.value !== undefined) { let offset = 0; const dateArray = Array.from(value); - const monthName = DatePickerUtil.getLongMonthName(this._datePicker.value); - const dayName = DatePickerUtil.getLongDayName(this._datePicker.value); + const monthName = getLongMonthName(this._datePicker.value); + const dayName = getLongDayName(this._datePicker.value); const dateFormatParts = this._datePicker.dateFormatParts; const datePickerFormat = this._datePicker.format; for (let i = 0; i < dateFormatParts.length; i++) { if (dateFormatParts[i].type === DATE_PARTS.WEEKDAY) { if (dateFormatParts[i].formatType === FORMAT_DESC.LONG) { - offset += DatePickerUtil.MAX_WEEKDAY_SYMBOLS - 4; - for (let j = dayName.length; j < DatePickerUtil.MAX_WEEKDAY_SYMBOLS; j++) { + offset += MAX_WEEKDAY_SYMBOLS - 4; + for (let j = dayName.length; j < MAX_WEEKDAY_SYMBOLS; j++) { dateArray.splice(j, 0, this.PROMPT_CHAR); } dateArray.join(''); @@ -57,8 +70,8 @@ export class InputValuePipe implements PipeTransform { if (dateFormatParts[i].type === DATE_PARTS.MONTH) { if (dateFormatParts[i].formatType === FORMAT_DESC.LONG) { const startPos = offset + dateFormatParts[i].initialPosition + monthName.length; - const endPos = startPos + DatePickerUtil.MAX_MONTH_SYMBOLS - monthName.length; - offset += DatePickerUtil.MAX_MONTH_SYMBOLS - 4; + const endPos = startPos + MAX_MONTH_SYMBOLS - monthName.length; + offset += MAX_MONTH_SYMBOLS - 4; for (let j = startPos; j < endPos; j++) { dateArray.splice(j, 0, this.PROMPT_CHAR); } @@ -66,11 +79,9 @@ export class InputValuePipe implements PipeTransform { } if (dateFormatParts[i].formatType === FORMAT_DESC.NUMERIC || dateFormatParts[i].formatType === FORMAT_DESC.TWO_DIGITS) { - const isOneDigit = - DatePickerUtil.isOneDigit(datePickerFormat, DATE_CHARS.MONTH_CHAR, this._datePicker.value.getMonth() + 1); - if (isOneDigit) { + if (isOneDigit(datePickerFormat, DATE_CHARS.MONTH_CHAR, this._datePicker.value.getMonth() + 1)) { const startPos = offset + dateFormatParts[i].initialPosition; - dateArray.splice(startPos, 0, DatePickerUtil.getNumericFormatPrefix(dateFormatParts[i].formatType)); + dateArray.splice(startPos, 0, getNumericFormatPrefix(dateFormatParts[i].formatType)); } offset += 1; dateArray.join(''); @@ -80,11 +91,9 @@ export class InputValuePipe implements PipeTransform { if (dateFormatParts[i].type === DATE_PARTS.DAY) { if (dateFormatParts[i].formatType === FORMAT_DESC.NUMERIC || dateFormatParts[i].formatType === FORMAT_DESC.TWO_DIGITS) { - const isOneDigit = - DatePickerUtil.isOneDigit(datePickerFormat, DATE_CHARS.DAY_CHAR, this._datePicker.value.getDate()); - if (isOneDigit) { + if (isOneDigit(datePickerFormat, DATE_CHARS.DAY_CHAR, this._datePicker.value.getDate())) { const startPos = offset + dateFormatParts[i].initialPosition; - dateArray.splice(startPos, 0, DatePickerUtil.getNumericFormatPrefix(dateFormatParts[i].formatType)); + dateArray.splice(startPos, 0, getNumericFormatPrefix(dateFormatParts[i].formatType)); } offset += 1; dateArray.join(''); @@ -95,6 +104,6 @@ export class InputValuePipe implements PipeTransform { return dateArray.join(''); } - return DatePickerUtil.trimMaskSymbols(this._datePicker.mask); + return trimMaskSymbols(this._datePicker.mask); } } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts index ed0c6635a90..2220c781935 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -1,25 +1,37 @@ -export enum PREDEFINED_FORMAT_OPTIONS { +/** + *@hidden + */ +export const enum PREDEFINED_FORMAT_OPTIONS { SHORT_DATE = 'shortDate', MEDIUM_DATE = 'mediumDate', LONG_DATE = 'longDate', FULL_DATE = 'fullDate' } -export enum PREDEFINED_FORMATS { +/** + *@hidden + */ +export const enum PREDEFINED_FORMATS { SHORT_DATE_FORMAT = 'M/d/yy', MEDIUM_DATE_FORMAT = 'MMM d, y', LONG_DATE_FORMAT = 'MMMM d, y', FULL_DATE_FORMAT = 'EEEE, MMMM d, y' } -export enum PREDEFINED_MASKS { +/** + *@hidden + */ +export const enum PREDEFINED_MASKS { SHORT_DATE_MASK = '00/00/00', MEDIUM_DATE_MASK = 'LLL 00, 0000', LONG_DATE_MASK = 'LLLLLLLLL 00, 0000', // longest month - sep - 9 chars FULL_DATE_MASK = 'LLLLLLLLL, LLLLLLLLL 00, 0000' // longest month - sep - 9 characters, longest week day - wed - 9 chars } -export enum FORMAT_DESC { +/** + *@hidden + */ +export const enum FORMAT_DESC { NUMERIC = 'numeric', TWO_DIGITS = 'twoDigits', SHORT = 'short', @@ -27,406 +39,486 @@ export enum FORMAT_DESC { NARROW = 'narrow' } -export enum DATE_CHARS { +/** + *@hidden + */ +export const enum DATE_CHARS { YEAR_CHAR = 'y', MONTH_CHAR = 'M', DAY_CHAR = 'd', WEEKDAY_CHAR = 'E' } -export enum DATE_PARTS { +/** + *@hidden + */ +export const enum DATE_PARTS { DAY = 'day', MONTH = 'month', YEAR = 'year', WEEKDAY = 'weekday' } -export class DatePickerUtil { - public static MAX_MONTH_SYMBOLS = 9; - public static MAX_WEEKDAY_SYMBOLS = 9; - public static SEPARATOR = 'separator'; +export const MAX_MONTH_SYMBOLS = 9; +export const MAX_WEEKDAY_SYMBOLS = 9; +export const SEPARATOR = 'separator'; + +/** + *@hidden + */ +export function getYearFormatType(format: string): string { + let type; + const occurences = format.match(new RegExp(DATE_CHARS.YEAR_CHAR, 'g')).length; + + switch (occurences) { + case 1: { + // y (2020) + type = FORMAT_DESC.NUMERIC; + break; + } + case 4: { + // yyyy (2020) + type = FORMAT_DESC.NUMERIC; + break; + } + case 2: { + // yy (20) + type = FORMAT_DESC.TWO_DIGITS; + break; + } + } - public static getYearFormatType(format: string): string { - let type; - const occurences = format.match(new RegExp(DATE_CHARS.YEAR_CHAR, 'g')).length; + return type; +} - switch (occurences) { - case 1: { - // y (2020) - type = FORMAT_DESC.NUMERIC; - break; - } - case 4: { - // yyyy (2020) - type = FORMAT_DESC.NUMERIC; - break; - } - case 2: { - // yy (20) - type = FORMAT_DESC.TWO_DIGITS; - break; - } +/** + *@hidden + */ +export function getMonthFormatType(format: string): string { + let type; + const occurences = format.match(new RegExp(DATE_CHARS.MONTH_CHAR, 'g')).length; + + switch (occurences) { + case 1: { + // M + type = FORMAT_DESC.NUMERIC; + break; + } + case 2: { + // MM + type = FORMAT_DESC.TWO_DIGITS; + break; + } + case 3: { + // MMM + type = FORMAT_DESC.SHORT; + break; + } + case 4: { + // MMMM + type = FORMAT_DESC.LONG; + break; + } + case 5: { + // MMMMM + type = FORMAT_DESC.NARROW; + break; } + } + + return type; +} - return type; +/** + *@hidden + */ +export function getDayFormatType(format: string): string { + let type; + const occurences = format.match(new RegExp(DATE_CHARS.DAY_CHAR, 'g')).length; + + switch (occurences) { + case 1: { + // d + type = FORMAT_DESC.NUMERIC; + break; + } + case 2: { + // dd + type = FORMAT_DESC.TWO_DIGITS; + break; + } } - public static getMonthFormatType(format: string): string { - let type; - const occurences = format.match(new RegExp(DATE_CHARS.MONTH_CHAR, 'g')).length; - - switch (occurences) { - case 1: { - // M - type = FORMAT_DESC.NUMERIC; - break; - } - case 2: { - // MM - type = FORMAT_DESC.TWO_DIGITS; - break; - } - case 3: { - // MMM - type = FORMAT_DESC.SHORT; - break; - } - case 4: { - // MMMM - type = FORMAT_DESC.LONG; - break; - } - case 5: { - // MMMMM - type = FORMAT_DESC.NARROW; - break; - } + + return type; +} + +/** + *@hidden + */ +export function getWeekDayFormatType(format: string): string { + let type; + const occurences = format.match(new RegExp(DATE_CHARS.WEEKDAY_CHAR, 'g')).length; + + switch (occurences) { + case 3: { + // EEE (Tue) + type = FORMAT_DESC.SHORT; + break; + } + case 4: { + // EEEE (Tuesday) + type = FORMAT_DESC.LONG; + break; } + case 5: { + // EEEEE (T) + type = FORMAT_DESC.NARROW; + break; + } + } + + return type; +} - return type; +/** + *@hidden + */ +export function parseDateFormat(format: string): any[] { + const dateStruct = []; + const maskArray = Array.from(format); + const weekdayInitPosition = format.indexOf(DATE_CHARS.WEEKDAY_CHAR); + const monthInitPosition = format.indexOf(DATE_CHARS.MONTH_CHAR); + const dayInitPosition = format.indexOf(DATE_CHARS.DAY_CHAR); + const yearInitPosition = format.indexOf(DATE_CHARS.YEAR_CHAR); + + if (yearInitPosition !== -1) { + dateStruct.push({ + type: DATE_PARTS.YEAR, + initialPosition: yearInitPosition, + formatType: getYearFormatType(format) + }); } - public static getDayFormatType(format: string): string { - let type; - const occurences = format.match(new RegExp(DATE_CHARS.DAY_CHAR, 'g')).length; - - switch (occurences) { - case 1: { - // d - type = FORMAT_DESC.NUMERIC; - break; - } - case 2: { - // dd - type = FORMAT_DESC.TWO_DIGITS; - break; - } - } - return type; + if (weekdayInitPosition !== -1) { + dateStruct.push({ + type: DATE_PARTS.WEEKDAY, + initialPosition: weekdayInitPosition, + formatType: getWeekDayFormatType(format) + }); } - public static getWeekDayFormatType(format: string): string { - let type; - const occurences = format.match(new RegExp(DATE_CHARS.WEEKDAY_CHAR, 'g')).length; - - switch (occurences) { - case 3: { - // EEE (Tue) - type = FORMAT_DESC.SHORT; - break; - } - case 4: { - // EEEE (Tuesday) - type = FORMAT_DESC.LONG; - break; - } - case 5: { - // EEEEE (T) - type = FORMAT_DESC.NARROW; - break; - } - } - return type; + if (monthInitPosition !== -1) { + dateStruct.push({ + type: DATE_PARTS.MONTH, + initialPosition: monthInitPosition, + formatType: getMonthFormatType(format) + }); } - public static parseDateFormat(format: string): any[] { - const dateStruct = []; - const maskArray = Array.from(format); - const weekdayInitPosition = format.indexOf(DATE_CHARS.WEEKDAY_CHAR); - const monthInitPosition = format.indexOf(DATE_CHARS.MONTH_CHAR); - const dayInitPosition = format.indexOf(DATE_CHARS.DAY_CHAR); - const yearInitPosition = format.indexOf(DATE_CHARS.YEAR_CHAR); - - if (yearInitPosition !== -1) { - dateStruct.push({ - type: DATE_PARTS.YEAR, - initialPosition: yearInitPosition, - formatType: this.getYearFormatType(format) - }); - } - if (weekdayInitPosition !== -1) { - dateStruct.push({ - type: DATE_PARTS.WEEKDAY, - initialPosition: weekdayInitPosition, - formatType: this.getWeekDayFormatType(format) - }); - } + if (dayInitPosition !== -1) { + dateStruct.push({ + type: DATE_PARTS.DAY, + initialPosition: dayInitPosition, + formatType: getDayFormatType(format) + }); + } - if (monthInitPosition !== -1) { + for (let i = 0; i < maskArray.length; i++) { + if (!isSpecialSymbol(maskArray[i])) { dateStruct.push({ - type: DATE_PARTS.MONTH, - initialPosition: monthInitPosition, - formatType: this.getMonthFormatType(format) + type: SEPARATOR, + initialPosition: i, + value: maskArray[i] }); } + } - if (dayInitPosition !== -1) { - dateStruct.push({ - type: DATE_PARTS.DAY, - initialPosition: dayInitPosition, - formatType: this.getDayFormatType(format) - }); - } + dateStruct.sort((a, b) => a.initialPosition - b.initialPosition); + fillDatePartsPositions(dateStruct); - for (let i = 0; i < maskArray.length; i++) { - if (!DatePickerUtil.isSpecialSymbol(maskArray[i])) { - dateStruct.push({ - type: DatePickerUtil.SEPARATOR, - initialPosition: i, - value: maskArray[i] - }); - } - } + return dateStruct; +} - dateStruct.sort((a, b) => a.initialPosition - b.initialPosition); - DatePickerUtil.fillDatePartsPositions(dateStruct); +/** + *@hidden + */ +export function isSpecialSymbol(char: string): boolean { + return (char !== DATE_CHARS.YEAR_CHAR + && char !== DATE_CHARS.MONTH_CHAR + && char !== DATE_CHARS.DAY_CHAR + && char !== DATE_CHARS.WEEKDAY_CHAR) ? false : true; +} - return dateStruct; - } - public static isSpecialSymbol(char: string): boolean { - return (char !== DATE_CHARS.YEAR_CHAR - && char !== DATE_CHARS.MONTH_CHAR - && char !== DATE_CHARS.DAY_CHAR - && char !== DATE_CHARS.WEEKDAY_CHAR) ? false : true; - } - public static getFormatMask(format: string): string { - const mask = []; - const dateStruct = DatePickerUtil.parseDateFormat(format); +/** + *@hidden + */ +export function getFormatMask(format: string): string { + const mask = []; + const dateStruct = parseDateFormat(format); - for (let i = 0; i < dateStruct.length; i++) { - if (dateStruct[i].type === DATE_PARTS.DAY) { - mask.push('00'); - } - if (dateStruct[i].type === DATE_PARTS.MONTH) { - switch (dateStruct[i].formatType) { - case FORMAT_DESC.SHORT: { - mask.push('LLL'); - break; - } - case FORMAT_DESC.LONG: { - mask.push('LLLLLLLLL'); - break; - } - case FORMAT_DESC.NARROW: { - mask.push('L'); - break; - } - default: { - // M && MM - mask.push('00'); - break; - } + for (let i = 0; i < dateStruct.length; i++) { + if (dateStruct[i].type === DATE_PARTS.DAY) { + mask.push('00'); + } + if (dateStruct[i].type === DATE_PARTS.MONTH) { + switch (dateStruct[i].formatType) { + case FORMAT_DESC.SHORT: { + mask.push('LLL'); + break; } - } - if (dateStruct[i].type === DATE_PARTS.YEAR) { - switch (dateStruct[i].formatType) { - case FORMAT_DESC.NUMERIC: { - mask.push('0000'); - break; - } - case FORMAT_DESC.TWO_DIGITS: { - mask.push('00'); - break; - } + case FORMAT_DESC.LONG: { + mask.push('LLLLLLLLL'); + break; } - } - if (dateStruct[i].type === DATE_PARTS.WEEKDAY) { - switch (dateStruct[i].formatType) { - case FORMAT_DESC.SHORT: { - mask.push('LLL'); - break; - } - case FORMAT_DESC.LONG: { - mask.push('LLLLLLLLL'); - break; - } - case FORMAT_DESC.NARROW: { - mask.push('L'); - break; - } + case FORMAT_DESC.NARROW: { + mask.push('L'); + break; + } + default: { + // M && MM + mask.push('00'); + break; } - } - if (dateStruct[i].type === DatePickerUtil.SEPARATOR) { - mask.push(dateStruct[i].value); } } - - return mask.join(''); - } - public static createDate(day: number, month: number, year: number): Date { - const date = new Date(); - date.setDate(day); - date.setMonth(month); - date.setFullYear(year); - return date; - } - public static trimMaskSymbols(mask: string): string { - return mask.replace(/0|L/g, '_'); - } - public static trimUnderlines(value: string): string { - return value.replace(/_/g, ''); - } - public static getLongMonthName(value: Date): string { - return value.toLocaleString('en', { - month: 'long' - }); - } - public static getLongDayName(value: Date): string { - return value.toLocaleString('en', { - weekday: 'long' - }); - } - public static getNumericFormatPrefix(formatType: string): string { - return (formatType === FORMAT_DESC.TWO_DIGITS) ? '0' : '_'; - } - public static getSpinnedDateInput(dateFormatParts: any[], inputValue: string, position: number, delta: number): string { - let datePart = DatePickerUtil.getDatePartOnPosition(dateFormatParts, position); - if ((datePart && datePart.length > 0 && datePart[0].type === DatePickerUtil.SEPARATOR) - || inputValue.length === position) { - datePart = this.getDatePartOnPosition(dateFormatParts, position - 1); + if (dateStruct[i].type === DATE_PARTS.YEAR) { + switch (dateStruct[i].formatType) { + case FORMAT_DESC.NUMERIC: { + mask.push('0000'); + break; + } + case FORMAT_DESC.TWO_DIGITS: { + mask.push('00'); + break; + } + } } - - const positionsArray = datePart[0].position; - const startIdx = positionsArray[0]; - //const endIdx = positionsArray[0] + positionsArray.length; - const endIdx = positionsArray[positionsArray.length - 1]; - const datePartType = datePart[0].type; - const datePartFormatType = datePart[0].formatType; - - let newValue = parseInt(DatePickerUtil.trimUnderlines(inputValue.substring(startIdx, endIdx)), 10); - - let maxValue, minValue = 1; - if (!isNaN(newValue)) { - switch (datePartType) { - case DATE_PARTS.MONTH: { - // Max 12 months - maxValue = 12; + if (dateStruct[i].type === DATE_PARTS.WEEKDAY) { + switch (dateStruct[i].formatType) { + case FORMAT_DESC.SHORT: { + mask.push('LLL'); break; } - case DATE_PARTS.DAY: { - // Max 31 days - maxValue = 31; + case FORMAT_DESC.LONG: { + mask.push('LLLLLLLLL'); break; } - case DATE_PARTS.YEAR: { - if (datePartFormatType === FORMAT_DESC.TWO_DIGITS) { - minValue = 0; - maxValue = 99; - } else { - // Infinite loop - minValue = -1; - maxValue = -1; - } + case FORMAT_DESC.NARROW: { + mask.push('L'); break; } } } - - let tempValue = newValue; - tempValue += delta; - if ((tempValue <= maxValue && tempValue >= minValue) || maxValue === -1 || minValue === -1) { - newValue = tempValue; + if (dateStruct[i].type === SEPARATOR) { + mask.push(dateStruct[i].value); } + } + + return mask.join(''); +} - const start = inputValue.slice(0, startIdx); - const end = inputValue.slice(endIdx, inputValue.length); - let changedPart: string; +/** + *@hidden + */ +export function createDate(day: number, month: number, year: number): Date { + const date = new Date(); + date.setDate(day); + date.setMonth(month); + date.setFullYear(year); + return date; +} - // Handling leading zero format - const prefix = DatePickerUtil.getNumericFormatPrefix(datePartFormatType); - changedPart = (newValue < 10) ? `${prefix}${newValue}` : `${newValue}`; +/** + *@hidden + */ +export function trimMaskSymbols(mask: string): string { + return mask.replace(/0|L/g, '_'); +} - return `${start}${changedPart}${end}`; - } - public static isOneDigit(input: string, char: string, index: number): boolean { - return input.match(new RegExp(char, 'g')).length === 1 && index < 10; +/** + *@hidden + */ +export function trimUnderlines(value: string): string { + return value.replace(/_/g, ''); +} + +/** + *@hidden + */ +export function getLongMonthName(value: Date): string { + return value.toLocaleString('en', { + month: 'long' + }); +} + +/** + *@hidden + */ +export function getLongDayName(value: Date): string { + return value.toLocaleString('en', { + weekday: 'long' + }); +} + +/** + *@hidden + */ +export function getNumericFormatPrefix(formatType: string): string { + return (formatType === FORMAT_DESC.TWO_DIGITS) ? '0' : '_'; +} + +/** + *@hidden + */ +export function getSpinnedDateInput(dateFormatParts: any[], inputValue: string, position: number, delta: number): string { + let datePart = getDatePartOnPosition(dateFormatParts, position); + if ((datePart && datePart.length > 0 && datePart[0].type === SEPARATOR) + || inputValue.length === position) { + datePart = getDatePartOnPosition(dateFormatParts, position - 1); } - public static daysInMonth(date: Date): number { - return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); + + // TODO: change positions array + const positionsArray = datePart[0].position; + const startIdx = positionsArray[0]; + const endIdx = positionsArray[0] + positionsArray.length; + const datePartType = datePart[0].type; + const datePartFormatType = datePart[0].formatType; + + let newValue = parseInt(trimUnderlines(inputValue.substring(startIdx, endIdx)), 10); + + let maxValue, minValue = 1; + if (!isNaN(newValue)) { + switch (datePartType) { + case DATE_PARTS.MONTH: { + // Max 12 months + maxValue = 12; + break; + } + case DATE_PARTS.DAY: { + // Max 31 days + maxValue = 31; + break; + } + case DATE_PARTS.YEAR: { + if (datePartFormatType === FORMAT_DESC.TWO_DIGITS) { + minValue = 0; + maxValue = 99; + } else { + // Infinite loop + minValue = -1; + maxValue = -1; + } + break; + } + } } - private static getDatePartOnPosition(dateFormatParts: any[], position: number) { - return dateFormatParts.filter((element) => element.position.some(pos => pos === position)); + + let tempValue = newValue; + tempValue += delta; + if ((tempValue <= maxValue && tempValue >= minValue) || maxValue === -1 || minValue === -1) { + newValue = tempValue; } - private static fillDatePartsPositions(dateArray: any[]): void { - let offset = 0; - for (let i = 0; i < dateArray.length; i++) { - if (dateArray[i].type === DATE_PARTS.DAY) { - dateArray[i].position = DatePickerUtil.fillValues(offset, 2); - offset += 2; - } + const start = inputValue.slice(0, startIdx); + const end = inputValue.slice(endIdx, inputValue.length); + let changedPart: string; + + // Handling leading zero format + const prefix = getNumericFormatPrefix(datePartFormatType); + changedPart = (newValue < 10) ? `${prefix}${newValue}` : `${newValue}`; + + return `${start}${changedPart}${end}`; +} + +/** + *@hidden + */ +export function isOneDigit(input: string, char: string, index: number): boolean { + return input.match(new RegExp(char, 'g')).length === 1 && index < 10; +} + +/** + *@hidden + */ +export function daysInMonth(date: Date): number { + return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); +} + +/** + *@hidden + */ +export function getDatePartOnPosition(dateFormatParts: any[], position: number) { + return dateFormatParts.filter((element) => element.position.some(pos => pos === position)); +} + +/** + *@hidden + */ +function fillDatePartsPositions(dateArray: any[]): void { + let offset = 0; - if (dateArray[i].type === DATE_PARTS.MONTH) { - switch (dateArray[i].formatType) { - case FORMAT_DESC.SHORT: { - dateArray[i].position = DatePickerUtil.fillValues(offset, 3); - offset += 3; - break; - } - case FORMAT_DESC.LONG: { - dateArray[i].position = DatePickerUtil.fillValues(offset, 9); - offset += 9; - break; - } - case FORMAT_DESC.NARROW: { - dateArray[i].position = DatePickerUtil.fillValues(offset, 1); - offset++; - break; - } - default: { - // FORMAT_DESC.NUMERIC || FORMAT_DESC.TWO_DIGITS - dateArray[i].position = DatePickerUtil.fillValues(offset, 2); - offset += 2; - break; - } + for (let i = 0; i < dateArray.length; i++) { + if (dateArray[i].type === DATE_PARTS.DAY) { + dateArray[i].position = fillValues(offset, 2); + offset += 2; + } + + if (dateArray[i].type === DATE_PARTS.MONTH) { + switch (dateArray[i].formatType) { + case FORMAT_DESC.SHORT: { + dateArray[i].position = fillValues(offset, 3); + offset += 3; + break; + } + case FORMAT_DESC.LONG: { + dateArray[i].position = fillValues(offset, 9); + offset += 9; + break; + } + case FORMAT_DESC.NARROW: { + dateArray[i].position = fillValues(offset, 1); + offset++; + break; + } + default: { + // FORMAT_DESC.NUMERIC || FORMAT_DESC.TWO_DIGITS + dateArray[i].position = fillValues(offset, 2); + offset += 2; + break; } } + } - if (dateArray[i].type === DatePickerUtil.SEPARATOR) { - dateArray[i].position = DatePickerUtil.fillValues(offset, 1); - offset++; - } + if (dateArray[i].type === SEPARATOR) { + dateArray[i].position = fillValues(offset, 1); + offset++; + } - if (dateArray[i].type === DATE_PARTS.YEAR) { - switch (dateArray[i].formatType) { - case FORMAT_DESC.NUMERIC: { - dateArray[i].position = DatePickerUtil.fillValues(offset, 4); - offset += 4; - break; - } - case FORMAT_DESC.TWO_DIGITS: { - dateArray[i].position = DatePickerUtil.fillValues(offset, 2); - offset += 2; - break; - } + if (dateArray[i].type === DATE_PARTS.YEAR) { + switch (dateArray[i].formatType) { + case FORMAT_DESC.NUMERIC: { + dateArray[i].position = fillValues(offset, 4); + offset += 4; + break; + } + case FORMAT_DESC.TWO_DIGITS: { + dateArray[i].position = fillValues(offset, 2); + offset += 2; + break; } } } } - private static fillValues(start: number, offset: number) { - const array = []; - for (let i = start; i < start + offset; i++) { - array.push(i); - } - return array; +} + +/** + *@hidden + */ +function fillValues(start: number, offset: number) { + const array = []; + for (let i = start; i < start + offset; i++) { + array.push(i); } + return array; } + diff --git a/projects/igniteui-angular/src/public_api.ts b/projects/igniteui-angular/src/public_api.ts index 25e716ae5ec..be1129140ea 100644 --- a/projects/igniteui-angular/src/public_api.ts +++ b/projects/igniteui-angular/src/public_api.ts @@ -91,4 +91,5 @@ export { changei18n, getCurrentResourceStrings, IResourceStrings } from './lib/c export { IGridResourceStrings } from './lib/core/i18n/grid-resources'; export { TimePickerInteractionMode } from './lib/time-picker/time-picker.common'; export { ITimePickerResourceStrings } from './lib/core/i18n/time-picker-resources'; +export * from './lib/date-picker/date-picker.utils'; diff --git a/src/app/date-picker/date-picker.sample.html b/src/app/date-picker/date-picker.sample.html index 1016c8c690d..1acdde7e7b9 100644 --- a/src/app/date-picker/date-picker.sample.html +++ b/src/app/date-picker/date-picker.sample.html @@ -1,14 +1,14 @@
Informs and requires a response from the user.
-
+

Editable Date Picker bind with ngModel

@@ -20,36 +20,36 @@

Editable Date Picker bind with ngModel

-
+ -
+ + + -
+ + + + + + + + + + + + + +
+

Readonly Date Picker

+
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/src/app/date-picker/date-picker.sample.ts b/src/app/date-picker/date-picker.sample.ts index 678f72d3fd1..39babd33a58 100644 --- a/src/app/date-picker/date-picker.sample.ts +++ b/src/app/date-picker/date-picker.sample.ts @@ -1,6 +1,10 @@ -import { Component, ViewChild, PipeTransform, Pipe } from '@angular/core'; -import { IgxDatePickerComponent } from 'igniteui-angular'; -import { DatePipe } from '@angular/common'; +import { Component, ViewChild, PipeTransform, Pipe, OnInit } from '@angular/core'; +import { IgxDatePickerComponent, DateRangeType } from 'igniteui-angular'; +import { DatePipe, formatDate } from '@angular/common'; + +// import { registerLocaleData } from '@angular/common'; +// import localeDE from '@angular/common/locales/de'; +// import localeJA from '@angular/common/locales/ja'; @Component({ selector: 'app-date-picker-sample', @@ -8,33 +12,85 @@ import { DatePipe } from '@angular/common'; templateUrl: 'date-picker.sample.html' }) -export class DatePickerSampleComponent { - @ViewChild('datePicker') datePicker: IgxDatePickerComponent; - date = new Date('10/3/2018'); +export class DatePickerSampleComponent implements OnInit { + // @ViewChild('datePicker') datePicker: IgxDatePickerComponent; + // @ViewChild('editableDatePicker') editableDatePicker: IgxDatePickerComponent; + // date = new Date('10/3/2018'); + + // public labelVisibility = true; + // public testDate = new Date(2030, 1, 1, 15, 16, 17, 18); + + // public date99: Date = new Date(2017, 7, 7); + public formatOptions = { + day: 'numeric', + month: 'long', + weekday: 'short', + year: 'numeric' + }; + // @ViewChild('dp99') public datePicker99: IgxDatePickerComponent; public date1; + public date2; + public date3; + public date4; + + 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) + ]; formatter = (_: Date) => { - return _.toDateString(); + return _.toLocaleString('en'); } - public deselect() { - this.datePicker.deselectDate(); + // public deselect(event) { + // event.sender.deselectDate(); + // } + public ngOnInit() { + + // this.editableDatePicker.disabledDates = [{ type: DateRangeType.Specific, dateRange: this.range }]; + // this.testdatePicker.value = this.testDate; + + // const test = formatDate(this.date1, 'shortDate', 'ja'); + // console.log('test ' + test); + } + + // public test(event) { + // console.log('open'); + // } + + public onClose(event) { + console.log('activeElement ' + document.activeElement); } constructor() { - const date = new Date(); - date.setDate(10); - date.setMonth(2); - date.setFullYear(2018); + // registerLocaleData(localeJA); + // registerLocaleData(localeDE); + const date1 = new Date(); + date1.setDate(8); + date1.setMonth(5); + date1.setFullYear(1978); - this.date1 = date; + 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; // this.date1.setDate(10); - // this.date1.setMonth(2); + // this.date1.setMonth(10); // this.date1.setFullYear(2018); - - const test = new DateFormatPipe('en').transform(date, 'd.M.y'); } } @Pipe({ From 0685d77562d7aaecdcad277bcbc825ed1e18b7a2 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Tue, 12 Feb 2019 14:59:36 +0200 Subject: [PATCH 27/57] feat(date-picker): Fixed styles bindings #3583 --- .../calendar-container.component.html | 22 +++++++------------ .../calendar-container.component.ts | 16 +++++++++++++- .../lib/date-picker/date-picker.component.ts | 1 - 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.html b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.html index a3a1b3652b9..7fa6805f8f4 100644 --- a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.html +++ b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.html @@ -1,16 +1,10 @@ -
- - -
- - -
+ +
\ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts index 3f6b971a680..fe4c686cab0 100644 --- a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts +++ b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts @@ -1,9 +1,10 @@ -import { Component, ViewChild, Input, Output, EventEmitter, HostListener } from '@angular/core'; +import { Component, ViewChild, Input, Output, EventEmitter, HostListener, HostBinding } from '@angular/core'; import { IgxCalendarComponent } from '../calendar'; import { DatePickerInteractionMode } from 'igniteui-angular'; @Component({ selector: 'igx-calendar-container', + styles: [':host {display: block;}'], templateUrl: 'calendar-container.component.html' }) export class IgxCalendarContainerComponent { @@ -28,6 +29,19 @@ export class IgxCalendarContainerComponent { @Output() public onTodaySelection = new EventEmitter(); + @HostBinding('class.igx-date-picker') + public styleClass = 'igx-date-picker'; + + @HostBinding('class.igx-date-picker--dropdown') + get dropdownCSS(): boolean { + return this.mode === DatePickerInteractionMode.EDITABLE; + } + + @HostBinding('class.igx-date-picker--vertical') + get verticalCSS(): boolean { + return this.vertical && this.mode === DatePickerInteractionMode.READONLY; + } + @HostListener('keydown.esc', ['$event']) public onSpaceClick(event) { event.preventDefault(); 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 6404ccd4929..2ea178641aa 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 @@ -99,7 +99,6 @@ export interface IgxDatePickerDisabledDateEventArgs { }], // tslint:disable-next-line:component-selector selector: 'igx-date-picker', - styles: [':host {display: block;}'], templateUrl: 'date-picker.component.html' }) export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAccessor, EditorProvider, OnInit, OnDestroy { From 28ffae47c1931ede4d885685ac004260c405a51c Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Tue, 12 Feb 2019 18:28:37 +0200 Subject: [PATCH 28/57] fix(date-picker): Fixes in edit mode #3583 --- .../src/lib/date-picker/date-picker.common.ts | 1 + .../igniteui-angular/src/lib/date-picker/date-picker.pipes.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) 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 3970b8f9798..fa113c10118 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 @@ -7,4 +7,5 @@ export interface IgxDatePickerBase { mask: string; inputMask: string; rawDateString: string; + dateFormatParts: any[]; } 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 index ca8c8d109fd..dda6d7c0c32 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts @@ -3,6 +3,7 @@ import { IGX_DATE_PICKER_COMPONENT, IgxDatePickerBase } from './date-picker.comm import { trimUnderlines, maskToPromptChars, + addPromptCharsEditMode, } from './date-picker.utils'; @@ -31,7 +32,8 @@ export class DatePickerInputValuePipe implements PipeTransform { transform(value: any, args?: any): any { if (this._datePicker.value === null || this._datePicker.value === undefined) { return maskToPromptChars(this._datePicker.inputMask); + } else { + return addPromptCharsEditMode(this._datePicker.dateFormatParts, this._datePicker.value, value); } - return ''; } } From 50db6a3970098feb49db470091db8d440ca58f4e Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Wed, 13 Feb 2019 09:10:51 +0200 Subject: [PATCH 29/57] fix(date-picker): Fixes, added tests for edit mode #3583 --- .../src/lib/date-picker/date-picker.common.ts | 1 + .../date-picker/date-picker.component.spec.ts | 55 +++++++++++++- .../lib/date-picker/date-picker.component.ts | 76 +++++++++++-------- .../src/lib/date-picker/date-picker.pipes.ts | 10 ++- .../src/lib/date-picker/date-picker.utils.ts | 14 +++- 5 files changed, 118 insertions(+), 38 deletions(-) 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 fa113c10118..8f98c7f692f 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 @@ -8,4 +8,5 @@ export interface IgxDatePickerBase { inputMask: string; rawDateString: string; dateFormatParts: any[]; + invalidDate: string; } 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 8fb07796e42..6638613628b 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 @@ -22,7 +22,8 @@ describe('IgxDatePicker', () => { IgxDatePickerWithPassedDateComponent, IgxDatePickerWIthLocaleComponent, IgxDatePickerNgModelComponent, - IgxDatePickerRetemplatedComponent + IgxDatePickerRetemplatedComponent, + IgxDatePickerEditableComponent ], imports: [IgxDatePickerModule, FormsModule, NoopAnimationsModule, IgxInputGroupModule] }) @@ -402,6 +403,46 @@ describe('IgxDatePicker', () => { expect(month.innerText.trim()).toBe(expectedResult.trim()); })); + describe('Editable mode', () => { + configureTestSuite(); + let fixture: ComponentFixture; + let datePicker: IgxDatePickerComponent; + + beforeEach(() => { + fixture = TestBed.createComponent(IgxDatePickerEditableComponent); + datePicker = fixture.componentInstance.datePicker; + fixture.detectChanges(); + }); + + fit('should be able to apply display format (editable mode)', (() => { + const input = fixture.debugElement.query(By.directive(IgxInputDirective)); + expect(input.nativeElement.value).toBe('20.10.2011'); + + input.nativeElement.dispatchEvent(new Event('focus')); + fixture.detectChanges(); + + input.nativeElement.dispatchEvent(new Event('blur')); + fixture.detectChanges(); + + expect(input.nativeElement.value).toBe('20.10.2011'); + + datePicker.value = null; + fixture.detectChanges(); + + input.nativeElement.dispatchEvent(new Event('blur')); + fixture.detectChanges(); + expect(input.nativeElement.value).toBe('dd.MM.y'); + })); + + it('should be able to apply editor mask (editable 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'); + })); + }); + describe('EditorProvider', () => { it('Should return correct edit element', () => { const fixture = TestBed.createComponent(IgxDatePickerTestComponent); @@ -499,3 +540,15 @@ export class IgxDatePickerNgModelComponent { ` }) export class IgxDatePickerRetemplatedComponent { } + +@Component({ + template: ` + + ` +}) +export class IgxDatePickerEditableComponent { + public date: Date = new Date(2011, 9, 20); + @ViewChild(IgxDatePickerComponent) public datePicker: IgxDatePickerComponent; +} + + 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 2ea178641aa..0f9a037e815 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 @@ -44,24 +44,20 @@ import { IgxRippleModule } from '../directives/ripple/ripple.directive'; import { IgxMaskModule } from '../directives/mask/mask.directive'; import { PREDEFINED_FORMAT_OPTIONS, - FORMAT_DESC, - DATE_PARTS, parseDateFormat, - trimUnderlines, - createDate, IFormatOptions, IFormatViews, DatePickerInteractionMode, DEFAULT_LOCALE_DATE, SPIN_DELTA, addPromptCharsEditMode, - getDateFormatPart, getModifiedDateInput, isDateInRanges, maskToPromptChars, checkForCompleteDateInput, getInputMask, - getMask + getMask, + parseDateArray } from './date-picker.utils'; import { DatePickerDisplayValuePipe, DatePickerInputValuePipe } from './date-picker.pipes'; import { IgxDatePickerBase } from './date-picker.common'; @@ -78,6 +74,10 @@ export interface IgxDatePickerDisabledDateEventArgs { datePicker: IgxDatePickerComponent; currentValue: Date; } +export interface IgxDatePickerValidationFailedEventArgs { + datePicker: IgxDatePickerComponent; + prevValue: Date; +} /** * **Ignite UI for Angular Date Picker** - @@ -398,6 +398,9 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc if (this._value && !this._isInEditMode) { this._transformedDate = this._getDisplayDate(this._value); } + if (this._value === null) { + this._transformedDate = ''; + } this._onChangeCallback(date); } @@ -546,6 +549,9 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc @Output() public onDisabledDate = new EventEmitter(); + @Output() + public onValidationFailed = new EventEmitter(); + /* * @hidden */ @@ -638,6 +644,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc private _dropDownOverlaySettings: OverlaySettings; private _modalOverlaySettings: OverlaySettings; private _transformedDate; + public invalidDate = ''; @HostListener('keydown.spacebar', ['$event']) @@ -827,42 +834,47 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc public clear(): void { this.isEmpty = true; + this.invalidDate = ''; this.deselectDate(); this._setCursorPosition(0); } public calculateDate(dateString: string, invokedByEvent: string): void { if (dateString !== '') { - const trimmedData = trimUnderlines(dateString); - const monthFormatType = getDateFormatPart(this.dateFormatParts, DATE_PARTS.MONTH).formatType; const prevDateValue = this.value; - let newValue; - if (monthFormatType === FORMAT_DESC.NUMERIC - || monthFormatType === FORMAT_DESC.TWO_DIGITS) { - const inputValue = (invokedByEvent === 'blur') ? this.rawDateString : dateString; - newValue = createDate(this.dateFormatParts, prevDateValue, inputValue); - } else { - newValue = new Date(trimmedData); - } - - // Restore the time part if any - if (prevDateValue !== null && prevDateValue !== undefined) { - newValue.setHours(prevDateValue.getHours()); - newValue.setMinutes(prevDateValue.getMinutes()); - newValue.setSeconds(prevDateValue.getSeconds()); - newValue.setMilliseconds(prevDateValue.getMilliseconds()); - } + const inputValue = (invokedByEvent === 'blur') ? this.rawDateString : dateString; + const newDateArray = parseDateArray(this.dateFormatParts, prevDateValue, inputValue); + + if (newDateArray[0] === 'valid') { + const newValue = newDateArray[1] as Date; + // Restore the time part if any + if (prevDateValue !== null && prevDateValue !== undefined) { + 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))) { - this.value = newValue; - this._onChangeCallback(newValue); + if (this.disabledDates === null + || (this.disabledDates !== null && !isDateInRanges(newValue, this.disabledDates))) { + this.value = newValue; + this.invalidDate = ''; + this._onChangeCallback(newValue); + } else { + const args: IgxDatePickerDisabledDateEventArgs = { + datePicker: this, + currentValue: newValue, + }; + this.onDisabledDate.emit(args); + } } else { - const args: IgxDatePickerDisabledDateEventArgs = { + const args: IgxDatePickerValidationFailedEventArgs = { datePicker: this, - currentValue: newValue, + prevValue: prevDateValue }; - this.onDisabledDate.emit(args); + this.invalidDate = dateString; + console.log('calculate date this.invalidDate ' + this.invalidDate); + this.onValidationFailed.emit(args); } } } @@ -896,7 +908,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc } public onFocus(event): void { - if (this.value) { + if (this.value && this.invalidDate === '') { this._transformedDate = this._getEditorDate(this.value); } } 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 index dda6d7c0c32..5abe6e24365 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts @@ -30,10 +30,14 @@ export class DatePickerDisplayValuePipe implements PipeTransform { export class DatePickerInputValuePipe implements PipeTransform { constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IgxDatePickerBase) { } transform(value: any, args?: any): any { - if (this._datePicker.value === null || this._datePicker.value === undefined) { - return maskToPromptChars(this._datePicker.inputMask); + if (this._datePicker.invalidDate !== '') { + return this._datePicker.invalidDate; } else { - return addPromptCharsEditMode(this._datePicker.dateFormatParts, this._datePicker.value, value); + if (this._datePicker.value === null || this._datePicker.value === undefined) { + return maskToPromptChars(this._datePicker.inputMask); + } else { + return addPromptCharsEditMode(this._datePicker.dateFormatParts, this._datePicker.value, value); + } } } } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts index d5e645ed3e5..70f23c799e8 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -321,13 +321,14 @@ export function getMask(dateStruct: any[]): string { /** *@hidden */ -export function createDate(dateFormatParts: any[], prevDateValue: Date, inputValue: string): Date { +export function parseDateArray(dateFormatParts: any[], prevDateValue: Date, inputValue: string): any[] { const dayStr = getDayValueFromInput(dateFormatParts, inputValue); const monthStr = getMonthValueFromInput(dateFormatParts, inputValue); const yearStr = getYearValueFromInput(dateFormatParts, inputValue); const yearFormat = getDateFormatPart(dateFormatParts, DATE_PARTS.YEAR).formatType; const day = (dayStr !== '') ? Number(dayStr) : 1; const month = (monthStr !== '') ? Number(monthStr) - 1 : 0; + let year; if (yearStr === '') { year = (yearFormat === FORMAT_DESC.TWO_DIGITS) ? '00' : '2000'; @@ -344,12 +345,21 @@ export function createDate(dateFormatParts: any[], prevDateValue: Date, inputVal yearPrefix = '20'; } const fullYear = (yearFormat === FORMAT_DESC.TWO_DIGITS) ? yearPrefix.concat(year) : year; + + if ((month < 0) || (month > 11)) { + return ['invalid', inputValue]; + } + + if ((day < 1) || (day > daysInMonth(fullYear, month))) { + return ['invalid', inputValue]; + } + const date = new Date(); date.setDate(day); date.setMonth(month); date.setFullYear(fullYear); - return date; + return ['valid', date]; } /** From 9772793a23b1146b630822523fb1e02d51d26a6a Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Wed, 13 Feb 2019 11:32:46 +0200 Subject: [PATCH 30/57] fix(date-picker): Added alt+up key behavior, added tests #3583 --- .../calendar-container.component.ts | 2 + .../date-picker/date-picker.component.spec.ts | 103 ++++++++++++++++-- .../lib/date-picker/date-picker.component.ts | 1 - 3 files changed, 98 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts index fe4c686cab0..9d6bf6fb056 100644 --- a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts +++ b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts @@ -1,6 +1,7 @@ import { Component, ViewChild, Input, Output, EventEmitter, HostListener, HostBinding } from '@angular/core'; import { IgxCalendarComponent } from '../calendar'; import { DatePickerInteractionMode } from 'igniteui-angular'; +import { KEYS } from '../core/utils'; @Component({ selector: 'igx-calendar-container', @@ -43,6 +44,7 @@ export class IgxCalendarContainerComponent { } @HostListener('keydown.esc', ['$event']) + @HostListener('keydown.alt.arrowup', ['$event']) public onSpaceClick(event) { event.preventDefault(); this.onClose.emit(); 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 6638613628b..081be15c918 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 @@ -414,24 +414,101 @@ describe('IgxDatePicker', () => { fixture.detectChanges(); }); - fit('should be able to apply display format (editable mode)', (() => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - expect(input.nativeElement.value).toBe('20.10.2011'); + it('Editable Datepicker open/close event', async () => { + const dom = fixture.debugElement; + const iconDate = dom.query(By.css('.igx-icon')); + expect(iconDate).toBeDefined(); - input.nativeElement.dispatchEvent(new Event('focus')); + spyOn(datePicker.onOpen, 'emit'); + spyOn(datePicker.onClose, 'emit'); + + UIInteractions.clickElement(iconDate); fixture.detectChanges(); + await wait(); - input.nativeElement.dispatchEvent(new Event('blur')); + expect(datePicker.onOpen.emit).toHaveBeenCalled(); + expect(datePicker.onOpen.emit).toHaveBeenCalledWith(datePicker); + + const dropDown = dom.query(By.css('.igx-date-picker--dropdown')); + expect(dropDown).toBeDefined(); + + dom.nativeElement.dispatchEvent(new Event('click')); + + fixture.detectChanges(); + await wait(); + + expect(datePicker.onClose.emit).toHaveBeenCalled(); + expect(datePicker.onClose.emit).toHaveBeenCalledWith(datePicker); + }); + + 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('should open the dropdown when click on the date icon', (() => { + const dom = fixture.debugElement; + fixture.detectChanges(); + + const iconDate = dom.query(By.css('.igx-icon')); + expect(iconDate).toBeDefined(); + + UIInteractions.clickElement(iconDate); + fixture.detectChanges(); + + const dropDown = dom.query(By.css('.igx-date-picker--dropdown')); + expect(dropDown).toBeDefined(); + })); + + it('should have correctly selected date', (() => { + const dom = fixture.debugElement; + fixture.detectChanges(); + + const iconDate = dom.query(By.css('.igx-icon')); + expect(iconDate).toBeDefined(); + + UIInteractions.clickElement(iconDate); + fixture.detectChanges(); + + const dropDown = dom.query(By.css('.igx-date-picker--dropdown')); + expect(dropDown).toBeDefined(); + + const selectedSpans = dom.nativeElement.getElementsByClassName('igx-calendar__date--selected'); + expect(selectedSpans.length).toBe(1); + expect(selectedSpans[0].innerHTML.trim()).toBe('20'); + + const dateHeader = dom.nativeElement.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 (editable mode)', (() => { + const input = fixture.debugElement.query(By.directive(IgxInputDirective)); expect(input.nativeElement.value).toBe('20.10.2011'); - datePicker.value = null; + input.nativeElement.dispatchEvent(new Event('focus')); fixture.detectChanges(); input.nativeElement.dispatchEvent(new Event('blur')); fixture.detectChanges(); - expect(input.nativeElement.value).toBe('dd.MM.y'); + + // Check for formatted input on blur + expect(input.nativeElement.value).toBe('20.10.2011'); })); it('should be able to apply editor mask (editable mode)', (() => { @@ -440,6 +517,18 @@ describe('IgxDatePicker', () => { 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'); })); }); 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 0f9a037e815..6fc0fc778d6 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 @@ -922,7 +922,6 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc case KEYS.DOWN_ARROW: case KEYS.DOWN_ARROW_IE: if (event.altKey) { - this.calculateDate(this.getEditElement().value, event.type); this.openCalendar(event); } else { this.spinValue(event); From 71f51ca54dd74eff43750688e37f4b1474c24b96 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Wed, 13 Feb 2019 11:57:45 +0200 Subject: [PATCH 31/57] fix(date-picker): Fixed check for days in month #3583 --- .../src/lib/date-picker/date-picker.component.ts | 1 - .../igniteui-angular/src/lib/date-picker/date-picker.utils.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) 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 6fc0fc778d6..67ae8e43dff 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 @@ -873,7 +873,6 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc prevValue: prevDateValue }; this.invalidDate = dateString; - console.log('calculate date this.invalidDate ' + this.invalidDate); this.onValidationFailed.emit(args); } } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts index 70f23c799e8..c0065ef7978 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -350,7 +350,7 @@ export function parseDateArray(dateFormatParts: any[], prevDateValue: Date, inpu return ['invalid', inputValue]; } - if ((day < 1) || (day > daysInMonth(fullYear, month))) { + if ((day < 1) || (day > daysInMonth(fullYear, month + 1))) { return ['invalid', inputValue]; } From 690dbdff2ffbe36cb939d6c35f6bb96c9a4b6afc Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Wed, 13 Feb 2019 15:59:01 +0200 Subject: [PATCH 32/57] fix(date-picker): Fixes in edit mode #3583 --- .../lib/date-picker/calendar-container.component.ts | 4 +--- .../src/lib/date-picker/date-picker.component.ts | 12 +++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts index 9d6bf6fb056..3553b2f0046 100644 --- a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts +++ b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts @@ -1,7 +1,5 @@ import { Component, ViewChild, Input, Output, EventEmitter, HostListener, HostBinding } from '@angular/core'; -import { IgxCalendarComponent } from '../calendar'; -import { DatePickerInteractionMode } from 'igniteui-angular'; -import { KEYS } from '../core/utils'; +import { DatePickerInteractionMode, IgxCalendarComponent } from 'igniteui-angular'; @Component({ selector: 'igx-calendar-container', 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 67ae8e43dff..3c342039e6c 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 @@ -358,7 +358,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc * ```typescript * let template = this.template(); * ``` - * @memberof IgxTimePickerComponent + * @memberof IgxDatePickerComponent */ get template(): TemplateRef { if (this.datePickerTemplateDirective) { @@ -369,7 +369,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc /** * Gets the context passed to the input group template. - * @memberof IgxTimePickerComponent + * @memberof IgxDatePickerComponent */ get context() { return { @@ -395,14 +395,16 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc public set value(date: Date) { this._value = date; - if (this._value && !this._isInEditMode) { + this._onChangeCallback(date); + + if (this._value + && !this._isInEditMode + && this.mode === DatePickerInteractionMode.EDITABLE) { this._transformedDate = this._getDisplayDate(this._value); } if (this._value === null) { this._transformedDate = ''; } - - this._onChangeCallback(date); } /** From 12e2f46fb20766546a3f0694ed8f8e309bd6f06e Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Wed, 13 Feb 2019 18:25:42 +0200 Subject: [PATCH 33/57] fix(date-picker): Changed date-picker mode in grid #3583 --- .../date-picker/date-picker.component.spec.ts | 39 +++++++++++-------- .../lib/date-picker/date-picker.component.ts | 7 ++-- .../src/lib/date-picker/date-picker.utils.ts | 21 +++++----- .../src/lib/grids/cell.component.html | 19 ++++++--- 4 files changed, 51 insertions(+), 35 deletions(-) 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 081be15c918..be82cebae62 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 @@ -441,22 +441,27 @@ describe('IgxDatePicker', () => { expect(datePicker.onClose.emit).toHaveBeenCalledWith(datePicker); }); - 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(); + it('Handling keyboard navigation with `space`(open) and `esc`(close) buttons', () => { + const input = fixture.debugElement.query(By.directive(IgxInputDirective)); + expect(input).toBeDefined(); - const overlayDiv = document.getElementsByClassName('igx-overlay__wrapper--modal')[0]; - expect(overlayDiv).toBeDefined(); - expect(overlayDiv.classList.contains('igx-overlay__wrapper--modal')).toBeTruthy(); + // UIInteractions.triggerKeyDownEvtUponElem('space', input, false); + // fixture.detectChanges(); - UIInteractions.triggerKeyDownEvtUponElem('Escape', overlayDiv, true); - flush(); - fixture.detectChanges(); + // const dropDown = dom.query(By.css('.igx-date-picker--dropdown')); + // expect(dropDown).toBeDefined(); - const overlays = document.getElementsByClassName('igx-overlay__wrapper--modal'); - expect(overlays.length).toEqual(0); - })); + // 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('should open the dropdown when click on the date icon', (() => { const dom = fixture.debugElement; @@ -497,19 +502,19 @@ describe('IgxDatePicker', () => { expect(year).toBe('2011'); })); - it('should be able to apply display format (editable mode)', (() => { + it('should be able to apply display format (editable mode)', async () => { const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - expect(input.nativeElement.value).toBe('20.10.2011'); + expect(input).toBeDefined(); input.nativeElement.dispatchEvent(new Event('focus')); fixture.detectChanges(); input.nativeElement.dispatchEvent(new Event('blur')); fixture.detectChanges(); + await wait(); - // Check for formatted input on blur expect(input.nativeElement.value).toBe('20.10.2011'); - })); + }); it('should be able to apply editor mask (editable mode)', (() => { const input = fixture.debugElement.query(By.directive(IgxInputDirective)); 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 3c342039e6c..51f82828cee 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 @@ -57,7 +57,8 @@ import { checkForCompleteDateInput, getInputMask, getMask, - parseDateArray + parseDateArray, + DATE_STATE } from './date-picker.utils'; import { DatePickerDisplayValuePipe, DatePickerInputValuePipe } from './date-picker.pipes'; import { IgxDatePickerBase } from './date-picker.common'; @@ -847,8 +848,8 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc const inputValue = (invokedByEvent === 'blur') ? this.rawDateString : dateString; const newDateArray = parseDateArray(this.dateFormatParts, prevDateValue, inputValue); - if (newDateArray[0] === 'valid') { - const newValue = newDateArray[1] as Date; + if (newDateArray.state === DATE_STATE.VALID) { + const newValue = newDateArray.date; // Restore the time part if any if (prevDateValue !== null && prevDateValue !== undefined) { newValue.setHours(prevDateValue.getHours()); diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts index c0065ef7978..a850cb586ed 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -56,6 +56,14 @@ export const enum DATE_PARTS { YEAR = 'year' } +/** + *@hidden + */ +export const enum DATE_STATE { + VALID = 'valid', + INVALID = 'invalid', +} + /** *@hidden */ @@ -321,7 +329,7 @@ export function getMask(dateStruct: any[]): string { /** *@hidden */ -export function parseDateArray(dateFormatParts: any[], prevDateValue: Date, inputValue: string): any[] { +export function parseDateArray(dateFormatParts: any[], prevDateValue: Date, inputValue: string): any { const dayStr = getDayValueFromInput(dateFormatParts, inputValue); const monthStr = getMonthValueFromInput(dateFormatParts, inputValue); const yearStr = getYearValueFromInput(dateFormatParts, inputValue); @@ -347,19 +355,14 @@ export function parseDateArray(dateFormatParts: any[], prevDateValue: Date, inpu const fullYear = (yearFormat === FORMAT_DESC.TWO_DIGITS) ? yearPrefix.concat(year) : year; if ((month < 0) || (month > 11)) { - return ['invalid', inputValue]; + return { state: DATE_STATE.INVALID, value: inputValue }; } if ((day < 1) || (day > daysInMonth(fullYear, month + 1))) { - return ['invalid', inputValue]; + return { state: DATE_STATE.INVALID, value: inputValue }; } - const date = new Date(); - date.setDate(day); - date.setMonth(month); - date.setFullYear(fullYear); - - return ['valid', date]; + return { state: DATE_STATE.VALID, date: new Date(fullYear, month, day) }; } /** diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.html b/projects/igniteui-angular/src/lib/grids/cell.component.html index f5c00e29050..8fc6462c993 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.html +++ b/projects/igniteui-angular/src/lib/grids/cell.component.html @@ -5,23 +5,30 @@ class="igx-grid__td-text">{{ formatter ? formatter(value) : column.dataType === 'number' ? (value | igxdecimal: grid.locale) : column.dataType === 'date' ? (value | igxdate: grid.locale) : value }} - + - + - + - + - + + - + \ No newline at end of file From f925e4d8f21dcb8a51d42a7219af877799ee5b6b Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Thu, 14 Feb 2019 09:51:35 +0200 Subject: [PATCH 34/57] fix(date-picker): Fixes in code and tests #3583 --- .../date-picker/date-picker.component.spec.ts | 27 ++- .../lib/date-picker/date-picker.component.ts | 27 ++- src/app/date-picker/date-picker.sample.html | 167 ++++-------------- src/app/date-picker/date-picker.sample.ts | 43 +---- 4 files changed, 67 insertions(+), 197 deletions(-) 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 be82cebae62..b0a31af3bf1 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 @@ -417,7 +417,7 @@ describe('IgxDatePicker', () => { it('Editable Datepicker open/close event', async () => { const dom = fixture.debugElement; const iconDate = dom.query(By.css('.igx-icon')); - expect(iconDate).toBeDefined(); + expect(iconDate).not.toBeNull(); spyOn(datePicker.onOpen, 'emit'); spyOn(datePicker.onClose, 'emit'); @@ -429,8 +429,9 @@ describe('IgxDatePicker', () => { expect(datePicker.onOpen.emit).toHaveBeenCalled(); expect(datePicker.onOpen.emit).toHaveBeenCalledWith(datePicker); - const dropDown = dom.query(By.css('.igx-date-picker--dropdown')); - expect(dropDown).toBeDefined(); + const dropDown = document.getElementsByClassName('igx-date-picker--dropdown'); + expect(dropDown.length).toBe(1); + expect(dropDown[0]).not.toBeNull(); dom.nativeElement.dispatchEvent(new Event('click')); @@ -441,26 +442,24 @@ describe('IgxDatePicker', () => { expect(datePicker.onClose.emit).toHaveBeenCalledWith(datePicker); }); - it('Handling keyboard navigation with `space`(open) and `esc`(close) buttons', () => { + it('Handling keyboard navigation with `space`(open) and `esc`(close) buttons', async () => { const input = fixture.debugElement.query(By.directive(IgxInputDirective)); expect(input).toBeDefined(); + expect(input).not.toBeNull(); - // UIInteractions.triggerKeyDownEvtUponElem('space', input, false); + // UIInteractions.triggerKeyDownEvtUponElem('space', input.nativeElement, false); // fixture.detectChanges(); + // await wait(100); - // const dropDown = dom.query(By.css('.igx-date-picker--dropdown')); - // expect(dropDown).toBeDefined(); - - // const overlayDiv = document.getElementsByClassName('igx-overlay__wrapper--modal')[0]; - // expect(overlayDiv).toBeDefined(); - // expect(overlayDiv.classList.contains('igx-overlay__wrapper--modal')).toBeTruthy(); + // const dropDown = document.getElementsByClassName('igx-date-picker--dropdown'); + // expect(dropDown.length).toBe(1); + // expect(dropDown[0]).not.toBeNull(); - // UIInteractions.triggerKeyDownEvtUponElem('Escape', overlayDiv, true); + // UIInteractions.triggerKeyDownEvtUponElem('Escape', input.nativeElement, true); // flush(); // fixture.detectChanges(); - // const overlays = document.getElementsByClassName('igx-overlay__wrapper--modal'); - // expect(overlays.length).toEqual(0); + // expect(dom.query(By.css('.igx-date-picker--dropdown'))).not.toBeNull(); }); it('should open the dropdown when click on the date icon', (() => { 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 51f82828cee..433c13be165 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 @@ -25,7 +25,7 @@ import { WEEKDAYS } from '../calendar/index'; import { IgxIconModule } from '../icon/index'; -import { IgxInputGroupModule } from '../input-group/index'; +import { IgxInputGroupModule, IgxInputDirective } from '../input-group/index'; import { Subject } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive'; @@ -342,6 +342,12 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc */ public get transformedDate(): string { if (this._value) { + if (this._isInEditMode) { + this._transformedDate = this._getEditorDate(this._value); + } else { + this._transformedDate = this._getDisplayDate(this._value); + } + this.isEmpty = false; } return this._transformedDate; @@ -397,12 +403,6 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc public set value(date: Date) { this._value = date; this._onChangeCallback(date); - - if (this._value - && !this._isInEditMode - && this.mode === DatePickerInteractionMode.EDITABLE) { - this._transformedDate = this._getDisplayDate(this._value); - } if (this._value === null) { this._transformedDate = ''; } @@ -585,6 +585,9 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc @ViewChild('readonlyInput', { read: ElementRef }) protected readonlyInput: ElementRef; + @ViewChild(IgxInputDirective) + protected input: IgxInputDirective; + /** * @hidden */ @@ -687,7 +690,14 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc /** @hidden */ public getEditElement() { - return ((this.mode === DatePickerInteractionMode.READONLY) ? this.readonlyInput : this.editableInput).nativeElement; + let inputElement; + if (this.mode === DatePickerInteractionMode.EDITABLE) { + inputElement = (this.editableInput) ? this.editableInput : this.input; + } else { + inputElement = (this.readonlyInput) ? this.readonlyInput : this.input; + } + + return (inputElement) ? inputElement.nativeElement : null; } /** @@ -910,6 +920,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc } public onFocus(event): void { + this._isInEditMode = true; if (this.value && this.invalidDate === '') { this._transformedDate = this._getEditorDate(this.value); } diff --git a/src/app/date-picker/date-picker.sample.html b/src/app/date-picker/date-picker.sample.html index 7c487549d8d..323b8cbb6e4 100644 --- a/src/app/date-picker/date-picker.sample.html +++ b/src/app/date-picker/date-picker.sample.html @@ -1,126 +1,34 @@
Informs and requires a response from the user.
- - - - - - - - - - - - - - - - - - -
-

Readonly Date Picker

-
- +
- - - - - - - - +
- - +

Editable Date Picker

- -
- -
-
--> - - +
\ No newline at end of file diff --git a/src/app/date-picker/date-picker.sample.ts b/src/app/date-picker/date-picker.sample.ts index 39babd33a58..33b3e6648a8 100644 --- a/src/app/date-picker/date-picker.sample.ts +++ b/src/app/date-picker/date-picker.sample.ts @@ -12,15 +12,9 @@ import { DatePipe, formatDate } from '@angular/common'; templateUrl: 'date-picker.sample.html' }) -export class DatePickerSampleComponent implements OnInit { - // @ViewChild('datePicker') datePicker: IgxDatePickerComponent; - // @ViewChild('editableDatePicker') editableDatePicker: IgxDatePickerComponent; - // date = new Date('10/3/2018'); +export class DatePickerSampleComponent { + date = new Date('10/3/2018'); - // public labelVisibility = true; - // public testDate = new Date(2030, 1, 1, 15, 16, 17, 18); - - // public date99: Date = new Date(2017, 7, 7); public formatOptions = { day: 'numeric', month: 'long', @@ -33,6 +27,7 @@ export class DatePickerSampleComponent implements OnInit { public date2; public date3; public date4; + public testDate = new Date(2000, 3, 5); public date5 = new Date('10/5/2020'); @@ -45,24 +40,8 @@ export class DatePickerSampleComponent implements OnInit { return _.toLocaleString('en'); } - // public deselect(event) { - // event.sender.deselectDate(); - // } - public ngOnInit() { - - // this.editableDatePicker.disabledDates = [{ type: DateRangeType.Specific, dateRange: this.range }]; - // this.testdatePicker.value = this.testDate; - - // const test = formatDate(this.date1, 'shortDate', 'ja'); - // console.log('test ' + test); - } - - // public test(event) { - // console.log('open'); - // } - - public onClose(event) { - console.log('activeElement ' + document.activeElement); + public deselect(datePicker) { + datePicker.deselectDate(); } constructor() { @@ -87,17 +66,5 @@ export class DatePickerSampleComponent implements OnInit { this.date2 = date2; this.date3 = date3; this.date4 = date3; - - // this.date1.setDate(10); - // this.date1.setMonth(10); - // this.date1.setFullYear(2018); - } -} -@Pipe({ - name: 'format' -}) -export class DateFormatPipe extends DatePipe implements PipeTransform { - transform(value: any, args?: any): any { - return super.transform(value, args); } } From 9e31a3ebbab879f82156d826546340ed46e822db Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Thu, 14 Feb 2019 10:36:55 +0200 Subject: [PATCH 35/57] feat(date-picker): Added some API comments and fixes #3583 --- .../calendar-container.component.html | 2 +- .../calendar-container.component.ts | 18 ++++++ .../lib/date-picker/date-picker.component.ts | 61 +++++++++++++++++-- .../src/lib/date-picker/date-picker.pipes.ts | 6 ++ .../src/lib/date-picker/date-picker.utils.ts | 9 +++ 5 files changed, 90 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.html b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.html index 7fa6805f8f4..f439f6e1880 100644 --- a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.html +++ b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.html @@ -1,5 +1,5 @@ -
-

Editable Date Picker

From f927f39a74abb79bf19a6534ba7b550ecc14addb Mon Sep 17 00:00:00 2001 From: Stefan Stoyanov Date: Thu, 14 Feb 2019 15:42:16 +0200 Subject: [PATCH 37/57] fix(igx-grid): Add filter-row date-picker in grid's outlet, #3034 --- .../src/lib/grids/filtering/grid-filtering-row.component.html | 4 ++-- .../src/lib/grids/filtering/grid-filtering-row.component.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering-row.component.html b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering-row.component.html index bb0b821d90d..ae499381fe9 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering-row.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering-row.component.html @@ -40,7 +40,7 @@ - + Date: Fri, 15 Feb 2019 11:10:56 +0200 Subject: [PATCH 38/57] feat(date-picker): fix broken behaviors after calendar merge #3583 --- .../lib/calendar/days-view/days-view.component.ts | 14 ++++++++++---- .../months-view/months-view.component.html | 2 +- .../calendar/months-view/months-view.component.ts | 8 ++++++++ .../calendar/years-view/years-view.component.html | 2 +- .../calendar/years-view/years-view.component.ts | 7 +++++++ 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/src/lib/calendar/days-view/days-view.component.ts b/projects/igniteui-angular/src/lib/calendar/days-view/days-view.component.ts index 6aec4f7c6c2..5979aec2d5f 100644 --- a/projects/igniteui-angular/src/lib/calendar/days-view/days-view.component.ts +++ b/projects/igniteui-angular/src/lib/calendar/days-view/days-view.component.ts @@ -1014,10 +1014,8 @@ export class IgxDaysViewComponent implements ControlValueAccessor, DoCheck { setTimeout(() => { date.nativeElement.focus(); }, parseInt(slideInRight.options.params.duration, 10)); - } else if (this.callback) { - setTimeout(() => { - this.callback(this.dates, this._nextDate); - }, parseInt(slideInRight.options.params.duration, 10)); + } else if (this.callback && (event.toState === 'next' || event.toState === 'prev')) { + this.callback(this.dates, this._nextDate); } } } @@ -1028,6 +1026,8 @@ export class IgxDaysViewComponent implements ControlValueAccessor, DoCheck { @HostListener('keydown.arrowup', ['$event']) public onKeydownArrowUp(event: KeyboardEvent) { event.preventDefault(); + event.stopPropagation(); + this.focusPreviousUpDate(event.target); } @@ -1037,6 +1037,8 @@ export class IgxDaysViewComponent implements ControlValueAccessor, DoCheck { @HostListener('keydown.arrowdown', ['$event']) public onKeydownArrowDown(event: KeyboardEvent) { event.preventDefault(); + event.stopPropagation(); + this.focusNextDownDate(event.target); } @@ -1055,6 +1057,8 @@ export class IgxDaysViewComponent implements ControlValueAccessor, DoCheck { @HostListener('keydown.arrowright', ['$event']) public onKeydownArrowRight(event: KeyboardEvent) { event.preventDefault(); + event.stopPropagation(); + this.focusNextDate(event.target); } @@ -1064,6 +1068,7 @@ export class IgxDaysViewComponent implements ControlValueAccessor, DoCheck { @HostListener('keydown.home', ['$event']) public onKeydownHome(event: KeyboardEvent) { event.preventDefault(); + event.stopPropagation(); const dates = this.dates.filter(d => d.isCurrentMonth); for (let i = 0; i < dates.length; i++) { @@ -1080,6 +1085,7 @@ export class IgxDaysViewComponent implements ControlValueAccessor, DoCheck { @HostListener('keydown.end', ['$event']) public onKeydownEnd(event: KeyboardEvent) { event.preventDefault(); + event.stopPropagation(); const dates = this.dates.filter(d => d.isCurrentMonth); for (let i = dates.length - 1; i >= 0; i--) { diff --git a/projects/igniteui-angular/src/lib/calendar/months-view/months-view.component.html b/projects/igniteui-angular/src/lib/calendar/months-view/months-view.component.html index dfc19af574d..f806ff9b403 100644 --- a/projects/igniteui-angular/src/lib/calendar/months-view/months-view.component.html +++ b/projects/igniteui-angular/src/lib/calendar/months-view/months-view.component.html @@ -1,6 +1,6 @@
-
+
{{ formattedMonth(month) | titlecase }}
diff --git a/projects/igniteui-angular/src/lib/calendar/months-view/months-view.component.ts b/projects/igniteui-angular/src/lib/calendar/months-view/months-view.component.ts index eef7b559751..a1aaf99e7da 100644 --- a/projects/igniteui-angular/src/lib/calendar/months-view/months-view.component.ts +++ b/projects/igniteui-angular/src/lib/calendar/months-view/months-view.component.ts @@ -228,6 +228,14 @@ export class IgxMonthsViewComponent implements ControlValueAccessor { } } + /** + * @hidden + */ + public monthTracker(index, item): string { + return `${item.getMonth()}}`; + } + + /** *@hidden */ diff --git a/projects/igniteui-angular/src/lib/calendar/years-view/years-view.component.html b/projects/igniteui-angular/src/lib/calendar/years-view/years-view.component.html index 4f67b37afe9..be3ebf28d0e 100644 --- a/projects/igniteui-angular/src/lib/calendar/years-view/years-view.component.html +++ b/projects/igniteui-angular/src/lib/calendar/years-view/years-view.component.html @@ -1,6 +1,6 @@
- + {{ formattedYear(year) }}
diff --git a/projects/igniteui-angular/src/lib/calendar/years-view/years-view.component.ts b/projects/igniteui-angular/src/lib/calendar/years-view/years-view.component.ts index 3180b841897..02bbf04405e 100644 --- a/projects/igniteui-angular/src/lib/calendar/years-view/years-view.component.ts +++ b/projects/igniteui-angular/src/lib/calendar/years-view/years-view.component.ts @@ -238,6 +238,13 @@ export class IgxYearsViewComponent implements ControlValueAccessor { this._onTouchedCallback = fn; } + /** + * @hidden + */ + public yearTracker(index, item): string { + return `${item.getFullYear()}}`; + } + /** * @hidden */ From 160bbacec4a4cc1c95f4a778ab26ad7037e8eac8 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Mon, 18 Feb 2019 10:24:52 +0200 Subject: [PATCH 39/57] fix(date-picker): Requested changes done #3583 --- .../src/lib/date-picker/README.md | 4 +- .../calendar-container.component.ts | 2 +- .../src/lib/date-picker/date-picker.common.ts | 2 +- .../date-picker/date-picker.component.html | 4 +- .../date-picker/date-picker.component.spec.ts | 4 +- .../lib/date-picker/date-picker.component.ts | 201 ++-- .../lib/date-picker/date-picker.directives.ts | 6 + .../src/lib/date-picker/date-picker.pipes.ts | 21 +- .../src/lib/date-picker/date-picker.utils.ts | 983 ++++++++---------- src/app/date-picker/date-picker.sample.html | 2 +- 10 files changed, 575 insertions(+), 654 deletions(-) diff --git a/projects/igniteui-angular/src/lib/date-picker/README.md b/projects/igniteui-angular/src/lib/date-picker/README.md index 61a7afcae86..96849a80edd 100644 --- a/projects/igniteui-angular/src/lib/date-picker/README.md +++ b/projects/igniteui-angular/src/lib/date-picker/README.md @@ -108,8 +108,8 @@ The DatePicker input group could be retemplated. | `onSelection` | `Date` | Fired when selection is made in the calendar. The event contains the selected value(s) based on the type of selection the component is set to | | `onOpen` | `datePicker` | Emitted when a datePicker calendar is being opened. | | `onClose` | `datePicker` | Emitted when a datePicker calendar is being closed. | -| `onDisabledDate` | `IgxDatePickerDisabledDateEventArgs` | Emitted when a disabled date is entered in editable mode. | -| `onValidationFailed` | `IgxDatePickerValidationFailedEventArgs` | Emitted when an invalid date is entered in editable mode. | +| `onDisabledDate` | `IDatePickerDisabledDateEventArgs` | Emitted when a disabled date is entered in editable mode. | +| `onValidationFailed` | `IDatePickerValidationFailedEventArgs` | Emitted when an invalid date is entered in editable mode. | ### Methods | Name | Arguments | Return Type | Description | diff --git a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts index 45b0774f005..aa77d3e4e85 100644 --- a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts +++ b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts @@ -14,7 +14,7 @@ export class IgxCalendarContainerComponent { public calendar: IgxCalendarComponent; @Input() - public mode = DatePickerInteractionMode.READONLY; + public mode: DatePickerInteractionMode = DatePickerInteractionMode.READONLY; @Input() public vertical = false; 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 8f98c7f692f..c14a64bcf29 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 @@ -2,7 +2,7 @@ export const IGX_DATE_PICKER_COMPONENT = 'IgxDatePickerComponentToken'; /** @hidden */ -export interface IgxDatePickerBase { +export interface IDatePicker { value: Date; mask: string; inputMask: string; 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 288c50e9d57..d6bf3f95b63 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,5 +1,5 @@ - + today @@ -11,7 +11,7 @@ - + today 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 cf270654f40..e37048a8dba 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 @@ -442,7 +442,7 @@ describe('IgxDatePicker', () => { expect(datePicker.onClose.emit).toHaveBeenCalledWith(datePicker); }); - it('Handling keyboard navigation with `space`(open) and `esc`(close) buttons', async () => { + xit('Handling keyboard navigation with `space`(open) and `esc`(close) buttons', async () => { const input = fixture.debugElement.query(By.directive(IgxInputDirective)); expect(input).toBeDefined(); expect(input).not.toBeNull(); @@ -491,7 +491,7 @@ describe('IgxDatePicker', () => { const selectedSpans = dom.nativeElement.getElementsByClassName('igx-calendar__date--selected'); expect(selectedSpans.length).toBe(1); - expect(selectedSpans[0].innerHTML.trim()).toBe('20'); + expect(selectedSpans[0].innerText.trim()).toBe('20'); const dateHeader = dom.nativeElement.getElementsByClassName('igx-calendar-picker__date'); expect(dateHeader.length).toBe(2); 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 5528da97af5..61f974f670b 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 @@ -44,42 +44,78 @@ import { IgxButtonModule } from '../directives/button/button.directive'; import { IgxRippleModule } from '../directives/ripple/ripple.directive'; import { IgxMaskModule } from '../directives/mask/mask.directive'; import { - PREDEFINED_FORMAT_OPTIONS, - parseDateFormat, - IFormatOptions, - IFormatViews, - DatePickerInteractionMode, - DEFAULT_LOCALE_DATE, - SPIN_DELTA, - addPromptCharsEditMode, - getModifiedDateInput, - maskToPromptChars, - checkForCompleteDateInput, - getInputMask, - getMask, - parseDateArray, - DATE_STATE + DatePickerUtil, + DateState } from './date-picker.utils'; import { DatePickerDisplayValuePipe, DatePickerInputValuePipe } from './date-picker.pipes'; -import { IgxDatePickerBase } from './date-picker.common'; +import { IDatePicker } from './date-picker.common'; import { KEYS } from '../core/utils'; import { IgxDatePickerTemplateDirective } from './date-picker.directives'; import { IgxCalendarContainerComponent } from './calendar-container.component'; 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 IgxDatePickerDisabledDateEventArgs { +export interface IDatePickerDisabledDateEventArgs { datePicker: IgxDatePickerComponent; currentValue: Date; } -export interface IgxDatePickerValidationFailedEventArgs { + +/** + * This interface is used to provide information about date picker reference and its previously valid value + * when onValidationFailed event is fired. + */ +export interface IDatePickerValidationFailedEventArgs { 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 whether the date picker has an editable input + * or is readonly - the date is selected only through a popup calendar. + */ +export const enum DatePickerInteractionMode { + EDITABLE = 'editable', + READONLY = 'readonly' +} + +/** + * 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 enum PredefinedFormatOptions { + SHORT_DATE = 'shortDate', + MEDIUM_DATE = 'mediumDate', + LONG_DATE = 'longDate', + FULL_DATE = 'fullDate' +} + /** * **Ignite UI for Angular Date Picker** - * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/date_picker.html) @@ -102,7 +138,7 @@ export interface IgxDatePickerValidationFailedEventArgs { selector: 'igx-date-picker', templateUrl: 'date-picker.component.html' }) -export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAccessor, EditorProvider, OnInit, OnDestroy { +export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor, EditorProvider, OnInit, OnDestroy { /** * An @Input property that sets the `IgxDatePickerComponent` label. * The default label is 'Date'. @@ -185,7 +221,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc */ @Input() public get format(): string { - return (this._format === undefined) ? this._defaultDateFormat : this._format; + return (this._format === undefined) ? PredefinedFormatOptions.SHORT_DATE : this._format; } /** @@ -342,14 +378,10 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc */ public get transformedDate(): string { if (this._value) { - if (this._isInEditMode) { - this._transformedDate = this._getEditorDate(this._value); - } else { - this._transformedDate = this._getDisplayDate(this._value); - } - + this._transformedDate = (this._isInEditMode) ? this._getEditorDate(this._value) : this._getDisplayDate(this._value); this.isEmpty = false; } + return this._transformedDate; } @@ -380,14 +412,24 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc */ get context() { return { - value: this.value, + disabled: this.disabledDates, + disabledDates: this.disabledDates, displayData: this.displayData, - openDialog: (event) => { this.openDialog(event); } + format: this.format, + isSpinLoop: this.isSpinLoop, + label: this.label, + labelVisibility: this.labelVisibility, + locale: this.locale, + mask: this.mask, + mode: this.mode, + specialDates: this.specialDates, + value: this.value, + openDialog: () => { this.openDialog(); } }; } /** - *An @Input property that sets the selected date. + *An @Input property that gets/sets the selected date. *```typescript *public date: Date = new Date(); *``` @@ -419,7 +461,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc public id = `igx-date-picker-${NEXT_ID++}`; /** - *An @Input property that applies custom formatter on the selected or passed date. + *An @Input property that applies a custom formatter function on the selected or passed date. *```typescript *public date: Date = new Date(); *private dayFormatter = new Intl.DateTimeFormat("en", { weekday: "long" }); @@ -550,7 +592,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc *``` */ @Output() - public onDisabledDate = new EventEmitter(); + public onDisabledDate = new EventEmitter(); /** *An @Output property that fires when the user types/spins invalid date in the date-picker editor. @@ -564,7 +606,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc *``` */ @Output() - public onValidationFailed = new EventEmitter(); + public onValidationFailed = new EventEmitter(); /* * @hidden @@ -637,6 +679,9 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc public isEmpty = true; public invalidDate = ''; + private readonly SPIN_DELTA = 1; + private readonly DEFAULT_LOCALE = 'en'; + private _formatOptions = { day: 'numeric', month: 'short', @@ -653,13 +698,10 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc private _format: string; private _value: Date; private _isInEditMode: boolean; - private _defaultDateFormat: string = PREDEFINED_FORMAT_OPTIONS.SHORT_DATE; - private _disabledDates: DateRangeDescriptor[] = null; private _specialDates: DateRangeDescriptor[] = null; private _modalOverlay: OverlaySettings; private _dropDownOverlay: OverlaySettings; - private _positionSettings: PositionSettings; private _dropDownOverlaySettings: OverlaySettings; private _modalOverlaySettings: OverlaySettings; @@ -668,7 +710,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc @HostListener('keydown.spacebar', ['$event']) @HostListener('keydown.space', ['$event']) public onSpaceClick(event) { - this.openDialog(event); + this.openDialog(); event.preventDefault(); } @@ -755,11 +797,11 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc }); if (this.mode === DatePickerInteractionMode.EDITABLE) { - this.dateFormatParts = parseDateFormat(this.mask, this.locale); + this.dateFormatParts = DatePickerUtil.parseDateFormat(this.mask, this.locale); if (this.mask === undefined) { - this.mask = getMask(this.dateFormatParts); + this.mask = DatePickerUtil.getMask(this.dateFormatParts); } - this.inputMask = getInputMask(this.dateFormatParts); + this.inputMask = DatePickerUtil.getInputMask(this.dateFormatParts); } } @@ -831,8 +873,10 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc * * @hidden */ - public openDialog(event): void { - event.stopPropagation(); + public openDialog(): void { + if (!this.collapsed) { + return; + } switch (this.mode) { case DatePickerInteractionMode.READONLY: { this.hasHeader = true; @@ -841,13 +885,11 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc break; } case DatePickerInteractionMode.EDITABLE: { - if (this.collapsed) { - this.hasHeader = false; - const dropDownOverlay = - (this.dropDownOverlaySettings !== undefined) ? this._dropDownOverlay : this._dropDownOverlaySettings; - dropDownOverlay.positionStrategy.settings.target = this.editableInputGroup.nativeElement; - this._componentID = this._overlayService.show(IgxCalendarContainerComponent, dropDownOverlay); - } + this.hasHeader = false; + const dropDownOverlay = + (this.dropDownOverlaySettings !== undefined) ? this._dropDownOverlay : this._dropDownOverlaySettings; + dropDownOverlay.positionStrategy.settings.target = this.editableInputGroup.nativeElement; + this._componentID = this._overlayService.show(IgxCalendarContainerComponent, dropDownOverlay); break; } } @@ -915,7 +957,6 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc * and re-calculate the editor text. * * @param event - * * @hidden */ public onFocus(event): void { @@ -942,7 +983,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc case KEYS.DOWN_ARROW: case KEYS.DOWN_ARROW_IE: if (event.altKey) { - this.openDialog(event); + this.openDialog(); } else { this.spinValue(event); } @@ -962,7 +1003,6 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc */ public onWheel(event) { this.spinValue(event); - } /** @@ -975,33 +1015,29 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc public onInput(event) { const targetValue = event.target.value; const cursorPosition = this._getCursorPosition(); - const checkInput = checkForCompleteDateInput(this.dateFormatParts, targetValue); + const checkInput = DatePickerUtil.checkForCompleteDateInput(this.dateFormatParts, targetValue); - if (targetValue !== maskToPromptChars(this.mask)) { + if (targetValue !== DatePickerUtil.maskToPromptChars(this.mask)) { this.isEmpty = false; } - // While editing, if the input is deleted, total clean up - if (checkInput === 'empty') { - this.isEmpty = true; - this.deselectDate(); - } - - // While editing, if one date part is deleted, date-picker value is set to null, the remaining input stays intact - if (checkInput === 'partial') { - this.deselectDate(); - requestAnimationFrame(() => { - this.getEditElement().value = targetValue; - this._setCursorPosition(cursorPosition); - }); - } - // If all date parts are completed, change the date-picker value, stay in edit mode if (checkInput === 'complete' && event.inputType !== 'deleteContentBackward') { this._isInEditMode = true; 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(); } } @@ -1009,12 +1045,12 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc if (dateString !== '') { const prevDateValue = this.value; const inputValue = (invokedByEvent === 'blur') ? this.rawDateString : dateString; - const newDateArray = parseDateArray(this.dateFormatParts, prevDateValue, inputValue); + const newDateArray = DatePickerUtil.parseDateArray(this.dateFormatParts, prevDateValue, inputValue); - if (newDateArray.state === DATE_STATE.VALID) { + if (newDateArray.state === DateState.VALID) { const newValue = newDateArray.date; // Restore the time part if any - if (prevDateValue !== null && prevDateValue !== undefined) { + if (prevDateValue) { newValue.setHours(prevDateValue.getHours()); newValue.setMinutes(prevDateValue.getMinutes()); newValue.setSeconds(prevDateValue.getSeconds()); @@ -1027,14 +1063,14 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc this.invalidDate = ''; this._onChangeCallback(newValue); } else { - const args: IgxDatePickerDisabledDateEventArgs = { + const args: IDatePickerDisabledDateEventArgs = { datePicker: this, currentValue: newValue, }; this.onDisabledDate.emit(args); } } else { - const args: IgxDatePickerValidationFailedEventArgs = { + const args: IDatePickerValidationFailedEventArgs = { datePicker: this, prevValue: prevDateValue }; @@ -1053,18 +1089,17 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc let sign = 0; if (event.key) { sign = (event.key === KEYS.UP_ARROW || event.key === KEYS.UP_ARROW_IE) ? 1 : -1; - } - if (event.deltaY) { + } else if (event.deltaY) { sign = (event.deltaY > 0) ? -1 : 1; } const modifiedInputValue = - getModifiedDateInput(this.dateFormatParts, inputValue, cursorPosition, SPIN_DELTA * sign, this.isSpinLoop); + DatePickerUtil.getModifiedDateInput(this.dateFormatParts, inputValue, cursorPosition, this.SPIN_DELTA * sign, this.isSpinLoop); this.getEditElement().value = modifiedInputValue; this._setCursorPosition(cursorPosition); - const checkInput = checkForCompleteDateInput(this.dateFormatParts, modifiedInputValue); + const checkInput = DatePickerUtil.checkForCompleteDateInput(this.dateFormatParts, modifiedInputValue); if (checkInput === 'complete') { this._isInEditMode = true; this.calculateDate(modifiedInputValue, event.type); @@ -1098,7 +1133,7 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc private _initializeCalendarContainer(event) { const containerComponent = event.componentRef.instance; this.calendar = containerComponent.calendar; - const isVertical = (this.vertical && this.mode !== DatePickerInteractionMode.EDITABLE) ? true : false; + const isVertical = (this.vertical && this.mode !== DatePickerInteractionMode.EDITABLE); this.calendar.hasHeader = this.hasHeader; this.calendar.formatOptions = this.formatOptions; this.calendar.formatViews = this.formatViews; @@ -1160,8 +1195,8 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc * @returns formatted string */ private _getDisplayDate(value: any): string { - if (this.format !== undefined && !this.formatter) { - const locale = (this.locale) ? this.locale : DEFAULT_LOCALE_DATE; + if (this.format && !this.formatter) { + const locale = this.locale || this.DEFAULT_LOCALE; return formatDate(value, this.format, locale); } else { return this._customFormatChecker(this.formatter, value); @@ -1169,9 +1204,9 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc } private _getEditorDate(value: any) { - const locale = (this.locale) ? this.locale : DEFAULT_LOCALE_DATE; - const changedValue = (value !== undefined && value !== null) ? formatDate(value, this.mask, locale) : ''; - return addPromptCharsEditMode(this.dateFormatParts, this.value, changedValue); + const locale = this.locale || this.DEFAULT_LOCALE; + const changedValue = (value) ? formatDate(value, this.mask, locale) : ''; + return DatePickerUtil.addPromptCharsEditMode(this.dateFormatParts, this.value, changedValue); } private _onTouchedCallback: () => void = () => { }; diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.directives.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.directives.ts index 35e9e64b69d..a227875a82d 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.directives.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.directives.ts @@ -3,6 +3,12 @@ import { Directive, TemplateRef } from '@angular/core'; @Directive({ selector: '[igxDatePickerTemplate]' }) + +/** + * IgxDatePickerTemplateDirective can be used to re-template the date-picker input-group. + * + * @hidden + */ export class IgxDatePickerTemplateDirective { constructor(public template: TemplateRef) { } } 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 index 3e0b0667787..1ac31332707 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts @@ -1,11 +1,6 @@ import { PipeTransform, Pipe, Inject } from '@angular/core'; -import { IGX_DATE_PICKER_COMPONENT, IgxDatePickerBase } from './date-picker.common'; -import { - trimUnderlines, - maskToPromptChars, - addPromptCharsEditMode, - -} from './date-picker.utils'; +import { IGX_DATE_PICKER_COMPONENT, IDatePicker } from './date-picker.common'; +import { DatePickerUtil } from './date-picker.utils'; /** * @hidden @@ -14,14 +9,14 @@ import { name: 'displayValue' }) export class DatePickerDisplayValuePipe implements PipeTransform { - constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IgxDatePickerBase) { } + constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IDatePicker) { } transform(value: any, args?: any): any { if (value !== '') { - if (value === maskToPromptChars(this._datePicker.inputMask)) { + if (value === DatePickerUtil.maskToPromptChars(this._datePicker.inputMask)) { return ''; } this._datePicker.rawDateString = value; - return trimUnderlines(value); + return DatePickerUtil.trimUnderlines(value); } return ''; } @@ -34,15 +29,15 @@ export class DatePickerDisplayValuePipe implements PipeTransform { name: 'inputValue' }) export class DatePickerInputValuePipe implements PipeTransform { - constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IgxDatePickerBase) { } + constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IDatePicker) { } transform(value: any, args?: any): any { if (this._datePicker.invalidDate !== '') { return this._datePicker.invalidDate; } else { if (this._datePicker.value === null || this._datePicker.value === undefined) { - return maskToPromptChars(this._datePicker.inputMask); + return DatePickerUtil.maskToPromptChars(this._datePicker.inputMask); } else { - return addPromptCharsEditMode(this._datePicker.dateFormatParts, this._datePicker.value, value); + return DatePickerUtil.addPromptCharsEditMode(this._datePicker.dateFormatParts, this._datePicker.value, value); } } } diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts index 1b7d7eab809..7a373946962 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -1,39 +1,19 @@ -import { DateRangeDescriptor, DateRangeType } from 'igniteui-angular'; import { isIE } from '../core/utils'; /** - * This enumeration is used to configure whether the date picker has an editable input - * or is readonly - the date is selected only through a popup calendar. - */ -export enum DatePickerInteractionMode { - EDITABLE = 'editable', - READONLY = 'readonly' -} - -/** - * 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 enum PREDEFINED_FORMAT_OPTIONS { - SHORT_DATE = 'shortDate', - MEDIUM_DATE = 'mediumDate', - LONG_DATE = 'longDate', - FULL_DATE = 'fullDate' -} - -/** + * This enum is used to keep the date validation result. + * *@hidden */ -export const SHORT_DATE_MASK = 'MM/dd/yy'; +export const enum DateState { + VALID = 'valid', + INVALID = 'invalid', +} /** *@hidden */ -export const enum FORMAT_DESC { +const enum FormatDesc { NUMERIC = 'numeric', TWO_DIGITS = '2-digit' } @@ -41,7 +21,7 @@ export const enum FORMAT_DESC { /** *@hidden */ -export const enum DATE_CHARS { +const enum DateChars { YEAR_CHAR = 'y', MONTH_CHAR = 'M', DAY_CHAR = 'd' @@ -50,7 +30,7 @@ export const enum DATE_CHARS { /** *@hidden */ -export const enum DATE_PARTS { +const enum DateParts { DAY = 'day', MONTH = 'month', YEAR = 'year' @@ -59,609 +39,514 @@ export const enum DATE_PARTS { /** *@hidden */ -export const enum DATE_STATE { - VALID = 'valid', - INVALID = 'invalid', -} +export abstract class DatePickerUtil { + private static readonly SHORT_DATE_MASK = 'MM/dd/yy'; + private static readonly SEPARATOR = 'literal'; + private static readonly NUMBER_OF_MONTHS = 12; + private static readonly PROMPT_CHAR = '_'; + private static readonly DEFAULT_LOCALE = 'en'; -/** - *@hidden - */ -export const SEPARATOR = 'literal'; -/** - *@hidden - */ -export const DEFAULT_LOCALE_DATE = 'en'; - -/** - *@hidden - */ -export const SPIN_DELTA = 1; + /** + * This method generates date parts structure based on editor mask and locale. + * @param maskValue: string + * @param locale: string + * @returns array containing information about date parts - type, position, format + */ + public static parseDateFormat(maskValue: string, locale: string = DatePickerUtil.DEFAULT_LOCALE): any[] { + let dateStruct = []; + if (maskValue === undefined && !isIE()) { + dateStruct = DatePickerUtil.getDefaultLocaleMask(locale); + } else { + const mask = (maskValue) ? maskValue : DatePickerUtil.SHORT_DATE_MASK; + const maskArray = Array.from(mask); + const monthInitPosition = mask.indexOf(DateChars.MONTH_CHAR); + const dayInitPosition = mask.indexOf(DateChars.DAY_CHAR); + const yearInitPosition = mask.indexOf(DateChars.YEAR_CHAR); -/** - *@hidden - */ -export const NUMBER_OF_MONTHS = 12; + if (yearInitPosition !== -1) { + dateStruct.push({ + type: DateParts.YEAR, + initialPosition: yearInitPosition, + formatType: DatePickerUtil.getYearFormatType(mask) + }); + } -/** - *@hidden - */ -export const PROMPT_CHAR = '_'; + if (monthInitPosition !== -1) { + dateStruct.push({ + type: DateParts.MONTH, + initialPosition: monthInitPosition, + formatType: DatePickerUtil.getMonthFormatType(mask) + }); + } -/** - * This interface is used to configure calendar format view options. - */ -export interface IFormatViews { - day?: boolean; - month?: boolean; - year?: boolean; -} + if (dayInitPosition !== -1) { + dateStruct.push({ + type: DateParts.DAY, + initialPosition: dayInitPosition, + formatType: DatePickerUtil.getDayFormatType(mask) + }); + } -/** - * This interface is used to configure calendar format options. - */ -export interface IFormatOptions { - day?: string; - month?: string; - weekday?: string; - year?: string; -} + for (let i = 0; i < maskArray.length; i++) { + if (!DatePickerUtil.isDateChar(maskArray[i])) { + dateStruct.push({ + type: DatePickerUtil.SEPARATOR, + initialPosition: i, + value: maskArray[i] + }); + } + } -/** - *@hidden - */ -export function getYearFormatType(format: string): string { - switch (format.match(new RegExp(DATE_CHARS.YEAR_CHAR, 'g')).length) { - case 1: { - // y (2020) - return FORMAT_DESC.NUMERIC; - } - case 4: { - // yyyy (2020) - return FORMAT_DESC.NUMERIC; - } - case 2: { - // yy (20) - return FORMAT_DESC.TWO_DIGITS; + dateStruct.sort((a, b) => a.initialPosition - b.initialPosition); + DatePickerUtil.fillDatePartsPositions(dateStruct); } + return dateStruct; } -} -/** - *@hidden - */ -export function getMonthFormatType(format: string): string { - switch (format.match(new RegExp(DATE_CHARS.MONTH_CHAR, 'g')).length) { - case 1: { - // M (8) - return FORMAT_DESC.NUMERIC; - } - case 2: { - // MM (08) - return FORMAT_DESC.TWO_DIGITS; + /** + * This method generates input mask based on date parts. + * @param dateStruct array + * @returns input mask + */ + public static getInputMask(dateStruct: any[]): string { + const inputMask = []; + for (let i = 0; i < dateStruct.length; i++) { + if (dateStruct[i].type === DatePickerUtil.SEPARATOR) { + inputMask.push(dateStruct[i].value); + } else if (dateStruct[i].type === DateParts.DAY || dateStruct[i].type === DateParts.MONTH) { + inputMask.push('00'); + } else if (dateStruct[i].type === DateParts.YEAR) { + switch (dateStruct[i].formatType) { + case FormatDesc.NUMERIC: { + inputMask.push('0000'); + break; + } + case FormatDesc.TWO_DIGITS: { + inputMask.push('00'); + break; + } + } + } } + return inputMask.join(''); } -} -/** - *@hidden - */ -export function getDayFormatType(format: string): string { - switch (format.match(new RegExp(DATE_CHARS.DAY_CHAR, 'g')).length) { - case 1: { - // d (6) - return FORMAT_DESC.NUMERIC; - } - case 2: { - // dd (06) - return FORMAT_DESC.TWO_DIGITS; - } - } -} + /** + * This method generates editor mask. + * @param dateStruct + * @returns editor mask + */ + public static getMask(dateStruct: any[]): string { + const mask = []; + for (let i = 0; i < dateStruct.length; i++) { + switch (dateStruct[i].formatType) { + case FormatDesc.NUMERIC: { + if (dateStruct[i].type === DateParts.DAY) { + mask.push('d'); + } else if (dateStruct[i].type === DateParts.MONTH) { + mask.push('M'); + } else { + mask.push('yyyy'); + } + break; + } + case FormatDesc.TWO_DIGITS: { + if (dateStruct[i].type === DateParts.DAY) { + mask.push('dd'); + } else if (dateStruct[i].type === DateParts.MONTH) { + mask.push('MM'); + } else { + mask.push('yy'); + } + } + } -/** - *@hidden - */ -export function parseDateFormat(maskValue: string, locale: string = DEFAULT_LOCALE_DATE): any[] { - let dateStruct = []; - if (maskValue === undefined && !isIE()) { - dateStruct = getDefaultLocaleMask(locale); - } else { - const mask = (maskValue !== undefined) ? maskValue : SHORT_DATE_MASK; - const maskArray = Array.from(mask); - const monthInitPosition = mask.indexOf(DATE_CHARS.MONTH_CHAR); - const dayInitPosition = mask.indexOf(DATE_CHARS.DAY_CHAR); - const yearInitPosition = mask.indexOf(DATE_CHARS.YEAR_CHAR); - - if (yearInitPosition !== -1) { - dateStruct.push({ - type: DATE_PARTS.YEAR, - initialPosition: yearInitPosition, - formatType: getYearFormatType(mask) - }); + if (dateStruct[i].type === DatePickerUtil.SEPARATOR) { + mask.push(dateStruct[i].value); + } } - if (monthInitPosition !== -1) { - dateStruct.push({ - type: DATE_PARTS.MONTH, - initialPosition: monthInitPosition, - formatType: getMonthFormatType(mask) - }); + return mask.join(''); + } + /** + * This method parses an input string base on date parts and returns a date and its validation state. + * @param dateFormatParts + * @param prevDateValue + * @param inputValue + * @returns object containing a date and its validation state + */ + public static parseDateArray(dateFormatParts: any[], prevDateValue: Date, inputValue: string): any { + const dayStr = DatePickerUtil.getDayValueFromInput(dateFormatParts, inputValue); + const monthStr = DatePickerUtil.getMonthValueFromInput(dateFormatParts, inputValue); + const yearStr = DatePickerUtil.getYearValueFromInput(dateFormatParts, inputValue); + const yearFormat = DatePickerUtil.getDateFormatPart(dateFormatParts, DateParts.YEAR).formatType; + const day = (dayStr !== '') ? parseInt(dayStr, 10) : 1; + const month = (monthStr !== '') ? parseInt(monthStr, 10) - 1 : 0; + + let year; + if (yearStr === '') { + year = (yearFormat === FormatDesc.TWO_DIGITS) ? '00' : '2000'; + } else { + year = yearStr; } + let yearPrefix; + if (prevDateValue) { + const originalYear = prevDateValue.getFullYear().toString(); + if (originalYear.length === 4) { + yearPrefix = originalYear.substring(0, 2); + } + } else { + yearPrefix = '20'; + } + const fullYear = (yearFormat === FormatDesc.TWO_DIGITS) ? yearPrefix.concat(year) : year; - if (dayInitPosition !== -1) { - dateStruct.push({ - type: DATE_PARTS.DAY, - initialPosition: dayInitPosition, - formatType: getDayFormatType(mask) - }); + if ((month < 0) || (month > 11) || (month === NaN)) { + return { state: DateState.INVALID, value: inputValue }; } - for (let i = 0; i < maskArray.length; i++) { - if (!isSpecialSymbol(maskArray[i])) { - dateStruct.push({ - type: SEPARATOR, - initialPosition: i, - value: maskArray[i] - }); - } + if ((day < 1) || (day > DatePickerUtil.daysInMonth(fullYear, month + 1)) || (day === NaN)) { + return { state: DateState.INVALID, value: inputValue }; } - dateStruct.sort((a, b) => a.initialPosition - b.initialPosition); - fillDatePartsPositions(dateStruct); + return { state: DateState.VALID, date: new Date(fullYear, month, day) }; } - return dateStruct; -} -/** - *@hidden - */ -export function getDefaultLocaleMask(locale: string) { - const dateStruct = []; - const formatter = new Intl.DateTimeFormat(locale); - const formatToParts = formatter.formatToParts(new Date()); - for (let i = 0; i < formatToParts.length; i++) { - if (formatToParts[i].type === SEPARATOR) { - dateStruct.push({ - type: SEPARATOR, - value: formatToParts[i].value - }); - } else { - dateStruct.push({ - type: formatToParts[i].type, - }); - } + public static maskToPromptChars(mask: string): string { + return mask.replace(/0|L/g, DatePickerUtil.PROMPT_CHAR); } - const formatterOptions = formatter.resolvedOptions(); - for (let i = 0; i < dateStruct.length; i++) { - switch (dateStruct[i].type) { - case DATE_PARTS.DAY: { - dateStruct[i].formatType = formatterOptions.day; - break; - } - case DATE_PARTS.MONTH: { - dateStruct[i].formatType = formatterOptions.month; - break; - } - case DATE_PARTS.YEAR: { - dateStruct[i].formatType = formatterOptions.month; - break; - } - } - } - fillDatePartsPositions(dateStruct); - return dateStruct; -} -/** - *@hidden - */ -export function isSpecialSymbol(char: string): boolean { - return (char !== DATE_CHARS.YEAR_CHAR - && char !== DATE_CHARS.MONTH_CHAR - && char !== DATE_CHARS.DAY_CHAR) ? false : true; -} + /** + * This method replaces prompt chars with empty string. + * @param value + */ + public static trimUnderlines(value: string): string { + return value.replace(/_/g, ''); + } -/** - *@hidden - */ -export function getInputMask(dateStruct: any[]): string { - const inputMask = []; - for (let i = 0; i < dateStruct.length; i++) { - if (dateStruct[i].type === DATE_PARTS.DAY || dateStruct[i].type === DATE_PARTS.MONTH) { - inputMask.push('00'); - } - if (dateStruct[i].type === DATE_PARTS.YEAR) { - switch (dateStruct[i].formatType) { - case FORMAT_DESC.NUMERIC: { - inputMask.push('0000'); - break; - } - case FORMAT_DESC.TWO_DIGITS: { - inputMask.push('00'); - break; - } + /** + * This method is used for spinning date parts. + * @param dateFormatParts + * @param inputValue + * @param position + * @param delta + * @param isSpinLoop + * @return modified text input + */ + public static getModifiedDateInput(dateFormatParts: any[], + inputValue: string, + position: number, + delta: number, + isSpinLoop: boolean): string { + const datePart = DatePickerUtil.getDatePartOnPosition(dateFormatParts, position); + const datePartType = datePart.type; + const datePartFormatType = datePart.formatType; + let newValue; + + const datePartValue = DatePickerUtil.getDateValueFromInput(dateFormatParts, datePartType, inputValue); + newValue = parseInt(datePartValue, 10); + + let maxValue, minValue; + const minMax = DatePickerUtil.getMinMaxValue(dateFormatParts, datePart, inputValue); + minValue = minMax.min; + maxValue = minMax.max; + + if (isNaN(newValue)) { + if (minValue === 'infinite') { + newValue = 2000; + } else { + newValue = minValue; } } + let tempValue = newValue; + tempValue += delta; - if (dateStruct[i].type === SEPARATOR) { - inputMask.push(dateStruct[i].value); + // Infinite loop for full years + if (maxValue === 'infinite' && minValue === 'infinite') { + newValue = tempValue; } - } - - return inputMask.join(''); -} -/** - *@hidden - */ -export function getMask(dateStruct: any[]): string { - const mask = []; - for (let i = 0; i < dateStruct.length; i++) { - switch (dateStruct[i].formatType) { - case FORMAT_DESC.NUMERIC: { - if (dateStruct[i].type === DATE_PARTS.DAY) { - mask.push('d'); - } - if (dateStruct[i].type === DATE_PARTS.MONTH) { - mask.push('M'); - } - if (dateStruct[i].type === DATE_PARTS.YEAR) { - mask.push('yyyy'); - } - break; + if (isSpinLoop) { + if (tempValue > maxValue) { + tempValue = minValue; } - case FORMAT_DESC.TWO_DIGITS: { - if (dateStruct[i].type === DATE_PARTS.DAY) { - mask.push('dd'); - } - if (dateStruct[i].type === DATE_PARTS.MONTH) { - mask.push('MM'); - } - if (dateStruct[i].type === DATE_PARTS.YEAR) { - mask.push('yy'); - } + if (tempValue < minValue) { + tempValue = maxValue; + } + newValue = tempValue; + } else { + if (tempValue <= maxValue && tempValue >= minValue) { + newValue = tempValue; } } - if (dateStruct[i].type === SEPARATOR) { - mask.push(dateStruct[i].value); - } - } + const startIdx = datePart.position[0]; + const endIdx = datePart.position[1]; + const start = inputValue.slice(0, startIdx); + const end = inputValue.slice(endIdx, inputValue.length); + let changedPart: string; - return mask.join(''); -} + const prefix = DatePickerUtil.getNumericFormatPrefix(datePartFormatType); + changedPart = (newValue < 10) ? `${prefix}${newValue}` : `${newValue}`; -/** - *@hidden - */ -export function parseDateArray(dateFormatParts: any[], prevDateValue: Date, inputValue: string): any { - const dayStr = getDayValueFromInput(dateFormatParts, inputValue); - const monthStr = getMonthValueFromInput(dateFormatParts, inputValue); - const yearStr = getYearValueFromInput(dateFormatParts, inputValue); - const yearFormat = getDateFormatPart(dateFormatParts, DATE_PARTS.YEAR).formatType; - const day = (dayStr !== '') ? Number(dayStr) : 1; - const month = (monthStr !== '') ? Number(monthStr) - 1 : 0; - - let year; - if (yearStr === '') { - year = (yearFormat === FORMAT_DESC.TWO_DIGITS) ? '00' : '2000'; - } else { - year = yearStr; - } - let yearPrefix; - if (prevDateValue !== null && prevDateValue !== undefined) { - const originalYear = prevDateValue.getFullYear().toString(); - if (originalYear.length === 4) { - yearPrefix = originalYear.substring(0, 2); - } - } else { - yearPrefix = '20'; + return `${start}${changedPart}${end}`; } - const fullYear = (yearFormat === FORMAT_DESC.TWO_DIGITS) ? yearPrefix.concat(year) : year; - if ((month < 0) || (month > 11)) { - return { state: DATE_STATE.INVALID, value: inputValue }; + /** + * This method returns date input with prompt chars. + * @param dateFormatParts + * @param date + * @param inputValue + * @returns date input including prompt chars + */ + public static addPromptCharsEditMode(dateFormatParts: any[], date: Date, inputValue: string): string { + const dateArray = Array.from(inputValue); + for (let i = 0; i < dateFormatParts.length; i++) { + if (dateFormatParts[i].formatType === FormatDesc.NUMERIC) { + if ((dateFormatParts[i].type === DateParts.DAY && date.getDate() < 10) + || (dateFormatParts[i].type === DateParts.MONTH && date.getMonth() + 1 < 10)) { + dateArray.splice(dateFormatParts[i].position[0], 0, DatePickerUtil.PROMPT_CHAR); + dateArray.join(''); + } + } + } + return dateArray.join(''); } - if ((day < 1) || (day > daysInMonth(fullYear, month + 1))) { - return { state: DATE_STATE.INVALID, value: inputValue }; + /** + * This method checks if date input is done. + * @param dateFormatParts + * @param input + * @returns input completeness + */ + public static checkForCompleteDateInput(dateFormatParts: any[], input: string): string { + const dayValue = DatePickerUtil.getDayValueFromInput(dateFormatParts, input); + const monthValue = DatePickerUtil.getMonthValueFromInput(dateFormatParts, input); + const yearValue = DatePickerUtil.getYearValueFromInput(dateFormatParts, input); + const dayStr = DatePickerUtil.getDayValueFromInput(dateFormatParts, input, false); + const monthStr = DatePickerUtil.getMonthValueFromInput(dateFormatParts, input, false); + + if (DatePickerUtil.isFullInput(dayValue, dayStr) + && DatePickerUtil.isFullInput(monthValue, monthStr) + && DatePickerUtil.isFullYearInput(dateFormatParts, yearValue)) { + return 'complete'; + } else if (dayValue === '' && monthValue === '' && yearValue === '') { + return 'empty'; + } else if (dayValue === '' || monthValue === '' || yearValue === '') { + return 'partial'; + } + return ''; } - return { state: DATE_STATE.VALID, date: new Date(fullYear, month, day) }; -} - -/** - *@hidden - */ -export function maskToPromptChars(mask: string): string { - return mask.replace(/0|L/g, PROMPT_CHAR); -} - -/** - *@hidden - */ -export function trimUnderlines(value: string): string { - return value.replace(/_/g, ''); -} - -/** - *@hidden - */ -export function getNumericFormatPrefix(formatType: string): string { - switch (formatType) { - case FORMAT_DESC.TWO_DIGITS: { - return '0'; - } - case FORMAT_DESC.NUMERIC: { - return PROMPT_CHAR; + private static getYearFormatType(format: string): string { + switch (format.match(new RegExp(DateChars.YEAR_CHAR, 'g')).length) { + case 1: { + // y (2020) + return FormatDesc.NUMERIC; + } + case 4: { + // yyyy (2020) + return FormatDesc.NUMERIC; + } + case 2: { + // yy (20) + return FormatDesc.TWO_DIGITS; + } } } -} -/** - *@hidden - */ -export function getModifiedDateInput(dateFormatParts: any[], - inputValue: string, - position: number, - delta: number, - isSpinLoop: boolean): string { - const datePart = getDatePartOnPosition(dateFormatParts, position); - const datePartType = datePart.type; - const datePartFormatType = datePart.formatType; - let newValue; - - const datePartValue = getDateValueFromInput(dateFormatParts, datePartType, inputValue); - newValue = parseInt(datePartValue, 10); - - let maxValue, minValue; - const minMax = getMinMaxValue(dateFormatParts, datePart, inputValue); - minValue = minMax[0]; - maxValue = minMax[1]; - - if (isNaN(newValue)) { - if (minValue === 'infinite') { - newValue = 2000; - } else { - newValue = minValue; + private static getMonthFormatType(format: string): string { + switch (format.match(new RegExp(DateChars.MONTH_CHAR, 'g')).length) { + case 1: { + // M (8) + return FormatDesc.NUMERIC; + } + case 2: { + // MM (08) + return FormatDesc.TWO_DIGITS; + } } } - let tempValue = newValue; - tempValue += delta; - // Infinite loop for full years - if (maxValue === 'infinite' && minValue === 'infinite') { - newValue = tempValue; + private static getDayFormatType(format: string): string { + switch (format.match(new RegExp(DateChars.DAY_CHAR, 'g')).length) { + case 1: { + // d (6) + return FormatDesc.NUMERIC; + } + case 2: { + // dd (06) + return FormatDesc.TWO_DIGITS; + } + } } - if (isSpinLoop) { - if (tempValue > maxValue) { - tempValue = minValue; - } - if (tempValue < minValue) { - tempValue = maxValue; + private static getDefaultLocaleMask(locale: string) { + const dateStruct = []; + const formatter = new Intl.DateTimeFormat(locale); + const formatToParts = formatter.formatToParts(new Date()); + for (let i = 0; i < formatToParts.length; i++) { + if (formatToParts[i].type === DatePickerUtil.SEPARATOR) { + dateStruct.push({ + type: DatePickerUtil.SEPARATOR, + value: formatToParts[i].value + }); + } else { + dateStruct.push({ + type: formatToParts[i].type, + }); + } } - newValue = tempValue; - } else { - if (tempValue <= maxValue && tempValue >= minValue) { - newValue = tempValue; + const formatterOptions = formatter.resolvedOptions(); + for (let i = 0; i < dateStruct.length; i++) { + switch (dateStruct[i].type) { + case DateParts.DAY: { + dateStruct[i].formatType = formatterOptions.day; + break; + } + case DateParts.MONTH: { + dateStruct[i].formatType = formatterOptions.month; + break; + } + case DateParts.YEAR: { + dateStruct[i].formatType = formatterOptions.month; + break; + } + } } + DatePickerUtil.fillDatePartsPositions(dateStruct); + return dateStruct; } - const startIdx = datePart.position[0]; - const endIdx = datePart.position[1]; - const start = inputValue.slice(0, startIdx); - const end = inputValue.slice(endIdx, inputValue.length); - let changedPart: string; - - const prefix = getNumericFormatPrefix(datePartFormatType); - changedPart = (newValue < 10) ? `${prefix}${newValue}` : `${newValue}`; - - return `${start}${changedPart}${end}`; -} + private static isDateChar(char: string): boolean { + return (char === DateChars.YEAR_CHAR || char === DateChars.MONTH_CHAR || char === DateChars.DAY_CHAR); + } -/** - *@hidden - */ -export function getMinMaxValue(dateFormatParts: any[], datePart, inputValue: string): any[] { - let maxValue, minValue; - switch (datePart.type) { - case DATE_PARTS.MONTH: { - minValue = 1; - maxValue = NUMBER_OF_MONTHS; - break; - } - case DATE_PARTS.DAY: { - minValue = 1; - maxValue = daysInMonth( - getFullYearFromString(getDateFormatPart(dateFormatParts, DATE_PARTS.YEAR), inputValue), - Number(getMonthValueFromInput(dateFormatParts, inputValue))); - break; - } - case DATE_PARTS.YEAR: { - if (datePart.formatType === FORMAT_DESC.TWO_DIGITS) { - minValue = 0; - maxValue = 99; - } else { - // Infinite loop - minValue = 'infinite'; - maxValue = 'infinite'; + private static getNumericFormatPrefix(formatType: string): string { + switch (formatType) { + case FormatDesc.TWO_DIGITS: { + return '0'; + } + case FormatDesc.NUMERIC: { + return DatePickerUtil.PROMPT_CHAR; } - break; } } - return [minValue, maxValue]; -} - -/** - *@hidden - */ -export function daysInMonth(fullYear: number, month: number): number { - return new Date(fullYear, month, 0).getDate(); -} - -/** - *@hidden - */ -export function addPromptCharsEditMode(dateFormatParts: any[], date: Date, inputValue: string): string { - const dateArray = Array.from(inputValue); - for (let i = 0; i < dateFormatParts.length; i++) { - if (dateFormatParts[i].formatType === FORMAT_DESC.NUMERIC) { - if ((dateFormatParts[i].type === DATE_PARTS.DAY && date.getDate() < 10) - || (dateFormatParts[i].type === DATE_PARTS.MONTH && date.getMonth() + 1 < 10)) { - dateArray.splice(dateFormatParts[i].position[0], 0, PROMPT_CHAR); - dateArray.join(''); + private static getMinMaxValue(dateFormatParts: any[], datePart, inputValue: string): any { + let maxValue, minValue; + switch (datePart.type) { + case DateParts.MONTH: { + minValue = 1; + maxValue = DatePickerUtil.NUMBER_OF_MONTHS; + break; + } + case DateParts.DAY: { + minValue = 1; + maxValue = DatePickerUtil.daysInMonth( + DatePickerUtil.getFullYearFromString(DatePickerUtil.getDateFormatPart(dateFormatParts, DateParts.YEAR), inputValue), + parseInt(DatePickerUtil.getMonthValueFromInput(dateFormatParts, inputValue), 10)); + break; + } + case DateParts.YEAR: { + if (datePart.formatType === FormatDesc.TWO_DIGITS) { + minValue = 0; + maxValue = 99; + } else { + // Infinite loop + minValue = 'infinite'; + maxValue = 'infinite'; + } + break; } } + return { min: minValue, max: maxValue }; } - return dateArray.join(''); -} - -/** - *@hidden - */ -export function getDateValueFromInput(dateFormatParts: any[], type: string, inputValue: string, trim: boolean = true): string { - const partPosition = getDateFormatPart(dateFormatParts, type).position; - const result = inputValue.substring(partPosition[0], partPosition[1]); - return (trim) ? trimUnderlines(result) : result; -} -/** - *@hidden - */ -export function getDayValueFromInput(dateFormatParts: any[], inputValue: string, trim: boolean = true): string { - return getDateValueFromInput(dateFormatParts, DATE_PARTS.DAY, inputValue, trim); -} - -/** - *@hidden - */ -export function getMonthValueFromInput(dateFormatParts: any[], inputValue: string, trim: boolean = true): string { - return getDateValueFromInput(dateFormatParts, DATE_PARTS.MONTH, inputValue, trim); -} - -/** - *@hidden - */ -export function getYearValueFromInput(dateFormatParts: any[], inputValue: string, trim: boolean = true): string { - return getDateValueFromInput(dateFormatParts, DATE_PARTS.YEAR, inputValue, trim); -} - -/** - *@hidden - */ -export function getDateFormatPart(dateFormatParts: any[], type: string): any { - return dateFormatParts.filter((datePart) => (datePart.type === type))[0]; -} - -/** - *@hidden - */ -export function isFullInput(value: any, input: string): boolean { - if (value !== '' && input.length === 2 && input.charAt(1) !== PROMPT_CHAR) { - return true; + private static daysInMonth(fullYear: number, month: number): number { + return new Date(fullYear, month, 0).getDate(); } - return false; -} -/** - *@hidden - */ -export function isFullYearInput(dateFormatParts: any[], value: any): boolean { - switch (getDateFormatPart(dateFormatParts, DATE_PARTS.YEAR).formatType) { - case FORMAT_DESC.NUMERIC: { - return (value !== '' && value.length === 4) ? true : false; - } - case FORMAT_DESC.TWO_DIGITS: { - return (value !== '' && value.length === 2) ? true : false; - } - default: { - return false; - } + private static getDateValueFromInput(dateFormatParts: any[], type: DateParts, inputValue: string, trim: boolean = true): string { + const partPosition = DatePickerUtil.getDateFormatPart(dateFormatParts, type).position; + const result = inputValue.substring(partPosition[0], partPosition[1]); + return (trim) ? DatePickerUtil.trimUnderlines(result) : result; } -} -/** - *@hidden - */ -export function getDatePartOnPosition(dateFormatParts: any[], position: number) { - return dateFormatParts.filter((element) => - element.position[0] <= position && position <= element.position[1] && element.type !== SEPARATOR)[0]; -} + private static getDayValueFromInput(dateFormatParts: any[], inputValue: string, trim: boolean = true): string { + return DatePickerUtil.getDateValueFromInput(dateFormatParts, DateParts.DAY, inputValue, trim); + } -/** - *@hidden - */ -export function checkForCompleteDateInput(dateFormatParts: any[], input: string): string { - const dayValue = getDayValueFromInput(dateFormatParts, input); - const monthValue = getMonthValueFromInput(dateFormatParts, input); - const yearValue = getYearValueFromInput(dateFormatParts, input); - const dayStr = getDayValueFromInput(dateFormatParts, input, false); - const monthStr = getMonthValueFromInput(dateFormatParts, input, false); - - if (isFullInput(dayValue, dayStr) - && isFullInput(monthValue, monthStr) - && isFullYearInput(dateFormatParts, yearValue)) { - return 'complete'; + private static getMonthValueFromInput(dateFormatParts: any[], inputValue: string, trim: boolean = true): string { + return DatePickerUtil.getDateValueFromInput(dateFormatParts, DateParts.MONTH, inputValue, trim); } - if (dayValue === '' && monthValue === '' && yearValue === '') { - return 'empty'; + private static getYearValueFromInput(dateFormatParts: any[], inputValue: string, trim: boolean = true): string { + return DatePickerUtil.getDateValueFromInput(dateFormatParts, DateParts.YEAR, inputValue, trim); } - if (dayValue === '' || monthValue === '' || yearValue === '') { - return 'partial'; + private static getDateFormatPart(dateFormatParts: any[], type: DateParts): any { + return dateFormatParts.filter((datePart) => (datePart.type === type))[0]; } - return ''; -} + private static isFullInput(value: any, input: string): boolean { + return (value !== '' && input.length === 2 && input.charAt(1) !== DatePickerUtil.PROMPT_CHAR); + } -/** - *@hidden - */ -function fillDatePartsPositions(dateArray: any[]): void { - let currentPos = 0; - - for (let i = 0; i < dateArray.length; i++) { - // Day|Month part positions - if (dateArray[i].type === DATE_PARTS.DAY || dateArray[i].type === DATE_PARTS.MONTH) { - // Offset 2 positions for number - dateArray[i].position = [currentPos, currentPos + 2]; - currentPos += 2; + private static isFullYearInput(dateFormatParts: any[], value: any): boolean { + switch (DatePickerUtil.getDateFormatPart(dateFormatParts, DateParts.YEAR).formatType) { + case FormatDesc.NUMERIC: { + return (value !== '' && value.length === 4); + } + case FormatDesc.TWO_DIGITS: { + return (value !== '' && value.length === 2); + } + default: { + return false; + } } + } - // Separator positions - if (dateArray[i].type === SEPARATOR) { - dateArray[i].position = [currentPos, currentPos + 1]; - currentPos++; - } + private static getDatePartOnPosition(dateFormatParts: any[], position: number) { + return dateFormatParts.filter((element) => + element.position[0] <= position && position <= element.position[1] && element.type !== DatePickerUtil.SEPARATOR)[0]; + } - // Year part positions - if (dateArray[i].type === DATE_PARTS.YEAR) { - switch (dateArray[i].formatType) { - case FORMAT_DESC.NUMERIC: { - // Offset 4 positions for full year - dateArray[i].position = [currentPos, currentPos + 4]; - currentPos += 4; - break; - } - case FORMAT_DESC.TWO_DIGITS: { - // Offset 2 positions for short year - dateArray[i].position = [currentPos, currentPos + 2]; - currentPos += 2; - break; + private static getFullYearFromString(yearPart, inputValue): number { + return parseInt(inputValue.substring(yearPart.position[0], yearPart.position[1]), 10); + } + + private static fillDatePartsPositions(dateArray: any[]): void { + let currentPos = 0; + + for (let i = 0; i < dateArray.length; i++) { + // Day|Month part positions + if (dateArray[i].type === DateParts.DAY || dateArray[i].type === DateParts.MONTH) { + // Offset 2 positions for number + dateArray[i].position = [currentPos, currentPos + 2]; + currentPos += 2; + } else if (dateArray[i].type === DateParts.YEAR) { + // Year part positions + switch (dateArray[i].formatType) { + case FormatDesc.NUMERIC: { + // Offset 4 positions for full year + dateArray[i].position = [currentPos, currentPos + 4]; + currentPos += 4; + break; + } + case FormatDesc.TWO_DIGITS: { + // Offset 2 positions for short year + dateArray[i].position = [currentPos, currentPos + 2]; + currentPos += 2; + break; + } } + } else if (dateArray[i].type === DatePickerUtil.SEPARATOR) { + // Separator positions + dateArray[i].position = [currentPos, currentPos + 1]; + currentPos++; } } } } -/** - *@hidden - */ -function getFullYearFromString(yearPart, inputValue): number { - return Number(inputValue.substring(yearPart.position[0], yearPart.position[1])); -} diff --git a/src/app/date-picker/date-picker.sample.html b/src/app/date-picker/date-picker.sample.html index 4f97bc6d758..e4f1ad25161 100644 --- a/src/app/date-picker/date-picker.sample.html +++ b/src/app/date-picker/date-picker.sample.html @@ -31,7 +31,7 @@

Date Picker with retemplated input group.

- + From d8cf1a98cd732a662a8607fdc50623ea6da6283a Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Mon, 18 Feb 2019 13:47:59 +0200 Subject: [PATCH 40/57] fix(date-picker): Fixes in date-picker #3583 --- .../lib/date-picker/date-picker.component.ts | 27 +++++++++---------- .../src/lib/date-picker/date-picker.utils.ts | 12 ++++++--- 2 files changed, 20 insertions(+), 19 deletions(-) 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 61f974f670b..a9d4c6abeac 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 @@ -381,7 +381,6 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor this._transformedDate = (this._isInEditMode) ? this._getEditorDate(this._value) : this._getDisplayDate(this._value); this.isEmpty = false; } - return this._transformedDate; } @@ -412,7 +411,7 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor */ get context() { return { - disabled: this.disabledDates, + disabled: this.disabled, disabledDates: this.disabledDates, displayData: this.displayData, format: this.format, @@ -641,7 +640,7 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor /* * @hidden */ - @ViewChild(IgxInputDirective) + @ContentChild(IgxInputDirective) protected input: IgxInputDirective; /** @@ -750,7 +749,6 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor } else { inputElement = (this.readonlyInput) ? this.readonlyInput : this.input; } - return (inputElement) ? inputElement.nativeElement : null; } @@ -1016,6 +1014,7 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor const targetValue = event.target.value; const cursorPosition = this._getCursorPosition(); const checkInput = DatePickerUtil.checkForCompleteDateInput(this.dateFormatParts, targetValue); + this._isInEditMode = true; if (targetValue !== DatePickerUtil.maskToPromptChars(this.mask)) { this.isEmpty = false; @@ -1023,7 +1022,6 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor // If all date parts are completed, change the date-picker value, stay in edit mode if (checkInput === 'complete' && event.inputType !== 'deleteContentBackward') { - this._isInEditMode = true; this._transformedDate = targetValue; this.calculateDate(targetValue, event.type); this._setCursorPosition(cursorPosition); @@ -1108,7 +1106,7 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor } private _onOpening(event) { - this._initializeCalendarContainer(event); + this._initializeCalendarContainer(event.componentRef.instance); this.collapsed = false; } @@ -1130,9 +1128,8 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor } } - private _initializeCalendarContainer(event) { - const containerComponent = event.componentRef.instance; - this.calendar = containerComponent.calendar; + private _initializeCalendarContainer(componentInstance: IgxCalendarContainerComponent) { + this.calendar = componentInstance.calendar; const isVertical = (this.vertical && this.mode !== DatePickerInteractionMode.EDITABLE); this.calendar.hasHeader = this.hasHeader; this.calendar.formatOptions = this.formatOptions; @@ -1149,13 +1146,13 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor this.calendar.viewDate = this.value; } - containerComponent.mode = this.mode; - containerComponent.vertical = isVertical; - containerComponent.cancelButtonLabel = this.cancelButtonLabel; - containerComponent.todayButtonLabel = this.todayButtonLabel; + componentInstance.mode = this.mode; + componentInstance.vertical = isVertical; + componentInstance.cancelButtonLabel = this.cancelButtonLabel; + componentInstance.todayButtonLabel = this.todayButtonLabel; - containerComponent.onClose.pipe(takeUntil(this._destroy$)).subscribe(() => this.closeCalendar()); - containerComponent.onTodaySelection.pipe(takeUntil(this._destroy$)).subscribe(() => this.triggerTodaySelection()); + componentInstance.onClose.pipe(takeUntil(this._destroy$)).subscribe(() => this.closeCalendar()); + componentInstance.onTodaySelection.pipe(takeUntil(this._destroy$)).subscribe(() => this.triggerTodaySelection()); } // Focus a date, after the calendar appearance into DOM. diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts index 7a373946962..566b2a1b873 100644 --- a/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -212,7 +212,8 @@ export abstract class DatePickerUtil { } public static maskToPromptChars(mask: string): string { - return mask.replace(/0|L/g, DatePickerUtil.PROMPT_CHAR); + const result = mask.replace(/0|L/g, DatePickerUtil.PROMPT_CHAR); + return result; } /** @@ -220,7 +221,8 @@ export abstract class DatePickerUtil { * @param value */ public static trimUnderlines(value: string): string { - return value.replace(/_/g, ''); + const result = value.replace(/_/g, ''); + return result; } /** @@ -485,7 +487,8 @@ export abstract class DatePickerUtil { } private static getDateFormatPart(dateFormatParts: any[], type: DateParts): any { - return dateFormatParts.filter((datePart) => (datePart.type === type))[0]; + const result = dateFormatParts.filter((datePart) => (datePart.type === type))[0]; + return result; } private static isFullInput(value: any, input: string): boolean { @@ -507,8 +510,9 @@ export abstract class DatePickerUtil { } private static getDatePartOnPosition(dateFormatParts: any[], position: number) { - return dateFormatParts.filter((element) => + const result = dateFormatParts.filter((element) => element.position[0] <= position && position <= element.position[1] && element.type !== DatePickerUtil.SEPARATOR)[0]; + return result; } private static getFullYearFromString(yearPart, inputValue): number { From b738ad749e399af0ba8aaf17e4194a7500c81d2f Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Mon, 18 Feb 2019 14:30:43 +0200 Subject: [PATCH 41/57] fix(date-picker): Fixes in code #3583 --- .../date-picker/date-picker.component.html | 2 +- .../lib/date-picker/date-picker.component.ts | 26 ++++++++----------- 2 files changed, 12 insertions(+), 16 deletions(-) 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 d6bf3f95b63..d5408de90a2 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 @@ -18,7 +18,7 @@ + (input)="onInput($event)" (keydown)="onKeyDown($event)" (focus)=onFocus() /> clear 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 a9d4c6abeac..6f7f26e2963 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 @@ -708,7 +708,7 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor @HostListener('keydown.spacebar', ['$event']) @HostListener('keydown.space', ['$event']) - public onSpaceClick(event) { + public onSpaceClick(event: KeyboardEvent) { this.openDialog(); event.preventDefault(); } @@ -957,7 +957,7 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor * @param event * @hidden */ - public onFocus(event): void { + public onFocus(): void { this._isInEditMode = true; if (this.value && this.invalidDate === '') { this._transformedDate = this._getEditorDate(this.value); @@ -976,14 +976,16 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor switch (event.key) { case KEYS.UP_ARROW: case KEYS.UP_ARROW_IE: - this.spinValue(event); + event.preventDefault(); + this.spinValue(event.target.value, 1, event.type); break; case KEYS.DOWN_ARROW: case KEYS.DOWN_ARROW_IE: if (event.altKey) { this.openDialog(); } else { - this.spinValue(event); + event.preventDefault(); + this.spinValue(event.target.value, -1, event.type); } break; default: @@ -1000,7 +1002,9 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor * @hidden */ public onWheel(event) { - this.spinValue(event); + event.preventDefault(); + const sign = (event.deltaY > 0) ? -1 : 1; + this.spinValue(event.target.value, sign, event.type); } /** @@ -1078,18 +1082,10 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor } } - private spinValue(event) { - event.preventDefault(); + private spinValue(inputValue: string, sign: number, eventType: string): void { this._isInEditMode = true; this.isEmpty = false; - const inputValue = event.target.value; const cursorPosition = this._getCursorPosition(); - let sign = 0; - if (event.key) { - sign = (event.key === KEYS.UP_ARROW || event.key === KEYS.UP_ARROW_IE) ? 1 : -1; - } else if (event.deltaY) { - sign = (event.deltaY > 0) ? -1 : 1; - } const modifiedInputValue = DatePickerUtil.getModifiedDateInput(this.dateFormatParts, inputValue, cursorPosition, this.SPIN_DELTA * sign, this.isSpinLoop); @@ -1100,7 +1096,7 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor const checkInput = DatePickerUtil.checkForCompleteDateInput(this.dateFormatParts, modifiedInputValue); if (checkInput === 'complete') { this._isInEditMode = true; - this.calculateDate(modifiedInputValue, event.type); + this.calculateDate(modifiedInputValue, eventType); this._setCursorPosition(cursorPosition); } } From a6188fa2bbc19ab21d4c2b3a655fb5aadc4ef070 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Mon, 18 Feb 2019 14:52:24 +0200 Subject: [PATCH 42/57] fix(date-picker): Fix in calendar opening #3583 --- .../src/lib/date-picker/date-picker.component.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 6f7f26e2963..7a45e2b2a11 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 @@ -776,8 +776,7 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor }; this._overlayService.onOpening.pipe( - // TODO - // filter((overlay) => overlay.id === this._componentID), + filter((overlay) => overlay.id === this._componentID), takeUntil(this._destroy$)).subscribe((eventArgs) => { this._onOpening(eventArgs); }); @@ -879,7 +878,8 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor case DatePickerInteractionMode.READONLY: { this.hasHeader = true; const modalOverlay = (this.modalOverlaySettings !== undefined) ? this._modalOverlay : this._modalOverlaySettings; - this._componentID = this._overlayService.show(IgxCalendarContainerComponent, modalOverlay); + this._componentID = this._overlayService.attach(IgxCalendarContainerComponent, modalOverlay); + this._overlayService.show(this._componentID, modalOverlay); break; } case DatePickerInteractionMode.EDITABLE: { @@ -887,7 +887,8 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor const dropDownOverlay = (this.dropDownOverlaySettings !== undefined) ? this._dropDownOverlay : this._dropDownOverlaySettings; dropDownOverlay.positionStrategy.settings.target = this.editableInputGroup.nativeElement; - this._componentID = this._overlayService.show(IgxCalendarContainerComponent, dropDownOverlay); + this._componentID = this._overlayService.attach(IgxCalendarContainerComponent, dropDownOverlay); + this._overlayService.show(this._componentID, dropDownOverlay); break; } } From e01737c371703f814796f19dc62f84e8e3b3db39 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Mon, 18 Feb 2019 16:35:35 +0200 Subject: [PATCH 43/57] fix(grid): Enabled key down event for date-picker editor #3583 --- .../src/lib/date-picker/calendar-container.component.ts | 2 +- .../igniteui-angular/src/lib/grids/cell.component.ts | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts index aa77d3e4e85..e3d5f0a2abf 100644 --- a/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts +++ b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts @@ -46,7 +46,7 @@ export class IgxCalendarContainerComponent { @HostListener('keydown.esc', ['$event']) @HostListener('keydown.alt.arrowup', ['$event']) - public onSpaceClick(event) { + public onEscape(event) { event.preventDefault(); this.onClose.emit(); } diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 9bdc62625ef..a404a157b43 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -269,7 +269,7 @@ export class IgxGridCellComponent implements OnInit { get inEditMode(): boolean { const editableCell = this.gridAPI.get_cell_inEditMode(this.gridID); return editableCell ? this.cellID.rowID === editableCell.cellID.rowID && - this.cellID.columnID === editableCell.cellID.columnID : false; + this.cellID.columnID === editableCell.cellID.columnID : false; } /** @@ -713,8 +713,7 @@ export class IgxGridCellComponent implements OnInit { const column = this.gridAPI.get(this.gridID).columns[editCell.cellID.columnID]; if (column.inlineEditorTemplate === undefined && ( - (column.dataType === DataType.Boolean && (key !== KEYS.SPACE && key !== KEYS.SPACE_IE)) - || column.dataType === DataType.Date)) { + (column.dataType === DataType.Boolean && (key !== KEYS.SPACE && key !== KEYS.SPACE_IE)))) { event.preventDefault(); } return; @@ -728,9 +727,9 @@ export class IgxGridCellComponent implements OnInit { if (event.altKey) { if (this.row.nativeElement.tagName.toLowerCase() === 'igx-tree-grid-row' && this.isToggleKey(key)) { const collapse = (this.row as any).expanded && - (key === 'left' || key === 'arrowleft' || key === 'up' || key === 'arrowup'); + (key === 'left' || key === 'arrowleft' || key === 'up' || key === 'arrowup'); const expand = !(this.row as any).expanded && - (key === 'right' || key === 'arrowright' || key === 'down' || key === 'arrowdown'); + (key === 'right' || key === 'arrowright' || key === 'down' || key === 'arrowdown'); if (collapse) { (this.gridAPI as any).trigger_row_expansion_toggle( this.gridID, this.row.treeRow, !this.row.expanded, event, this.visibleColumnIndex); From e68d2e9fff917028b7f902e86576e7cfe7ef278a Mon Sep 17 00:00:00 2001 From: Stefana Andreeva Date: Mon, 18 Feb 2019 16:58:03 +0200 Subject: [PATCH 44/57] fix(grid): failing tests #3583 --- .../lib/grids/grid/grid-filtering-ui.spec.ts | 68 ++++++++++++------- .../src/lib/services/overlay/overlay.spec.ts | 3 +- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index e3711629b83..3ff2da9efdf 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -1134,9 +1134,13 @@ describe('IgxGrid - Filtering actions', () => { tick(); fix.detectChanges(); - const calendar = fix.debugElement.query(By.css('igx-calendar')); - const currentDay = calendar.query(By.css('igx-day-item.igx-calendar__date--current')); - currentDay.nativeElement.click(); + const outlet = document.getElementsByClassName('igx-grid__outlet')[0]; + const calendar = outlet.getElementsByClassName('igx-calendar')[0]; + + const currentDay = calendar.querySelector('.igx-calendar__date--current'); + + currentDay.dispatchEvent(new Event('click')); + flush(); fix.detectChanges(); @@ -1169,12 +1173,15 @@ describe('IgxGrid - Filtering actions', () => { GridFunctions.selectFilteringCondition('Does Not Equal', ddList); input.nativeElement.click(); - tick(); + tick(100); fix.detectChanges(); - const calendar = fix.debugElement.query(By.css('igx-calendar')); - const currentDay = calendar.query(By.css('igx-day-item.igx-calendar__date--current')); - currentDay.nativeElement.click(); + const outlet = document.getElementsByClassName('igx-grid__outlet')[0]; + const calendar = outlet.getElementsByClassName('igx-calendar')[0]; + + const currentDay = calendar.querySelector('.igx-calendar__date--current'); + + currentDay.dispatchEvent(new Event('click')); flush(); fix.detectChanges(); @@ -1257,9 +1264,12 @@ describe('IgxGrid - Filtering actions', () => { tick(); fix.detectChanges(); - const calendar = fix.debugElement.query(By.css('igx-calendar')); - const currentDay = calendar.query(By.css('igx-day-item.igx-calendar__date--current')); - currentDay.nativeElement.click(); + const outlet = document.getElementsByClassName('igx-grid__outlet')[0]; + const calendar = outlet.getElementsByClassName('igx-calendar')[0]; + + const currentDay = calendar.querySelector('.igx-calendar__date--current'); + + currentDay.dispatchEvent(new Event('click')); flush(); fix.detectChanges(); @@ -1289,21 +1299,25 @@ describe('IgxGrid - Filtering actions', () => { tick(); fix.detectChanges(); - let calendar = fix.debugElement.query(By.css('igx-calendar')); - const monthView = calendar.queryAll(By.css('.igx-calendar-picker__date'))[0]; - monthView.nativeElement.click(); + const outlet = document.getElementsByClassName('igx-grid__outlet')[0]; + let calendar = outlet.getElementsByClassName('igx-calendar')[0]; + + calendar.querySelector('.igx-calendar__date--current'); + const monthView = calendar.querySelector('.igx-calendar-picker__date'); + + monthView.dispatchEvent(new Event('click')); tick(); fix.detectChanges(); - const firstMonth = calendar.queryAll(By.css(`[class*='igx-calendar__month']`))[0]; - firstMonth.nativeElement.click(); + const firstMonth = calendar.querySelector('.igx-calendar__month'); + firstMonth.dispatchEvent(new Event('click')); tick(); fix.detectChanges(); - calendar = fix.debugElement.query(By.css('igx-calendar')); - const month = calendar.queryAll(By.css('.igx-calendar-picker__date'))[0]; + calendar = outlet.getElementsByClassName('igx-calendar')[0]; + const month = calendar.querySelector('.igx-calendar-picker__date'); - expect(month.nativeElement.textContent.trim()).toEqual('Jan'); + expect(month.innerHTML.trim()).toEqual('Jan'); })); it('Should correctly select year from year view datepicker/calendar component', fakeAsync(() => { @@ -1325,24 +1339,26 @@ describe('IgxGrid - Filtering actions', () => { tick(); fix.detectChanges(); - let calendar = fix.debugElement.query(By.css('igx-calendar')); - const monthView = calendar.queryAll(By.css('.igx-calendar-picker__date'))[1]; - monthView.nativeElement.click(); + const outlet = document.getElementsByClassName('igx-grid__outlet')[0]; + let calendar = outlet.getElementsByClassName('igx-calendar')[0]; + + const monthView = calendar.querySelectorAll('.igx-calendar-picker__date')[1]; + monthView.dispatchEvent(new Event('click')); tick(); fix.detectChanges(); - const firstMonth = calendar.queryAll(By.css('.igx-calendar__year'))[0]; - firstMonth.nativeElement.click(); + const firstMonth = calendar.querySelectorAll('.igx-calendar__year')[0]; + firstMonth.dispatchEvent(new Event('click')); tick(); fix.detectChanges(); - calendar = fix.debugElement.query(By.css('igx-calendar')); - const month = calendar.queryAll(By.css('.igx-calendar-picker__date'))[1]; + calendar = outlet.getElementsByClassName('igx-calendar')[0]; + const month = calendar.querySelectorAll('.igx-calendar-picker__date')[1]; const today = new Date(Date.now()); const expectedResult = today.getFullYear() - 3; - expect(month.nativeElement.textContent.trim()).toEqual(expectedResult.toString()); + expect(month.innerHTML.trim()).toEqual(expectedResult.toString()); })); // UI tests custom column 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 23f833cb9e1..03887860361 100644 --- a/projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts +++ b/projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts @@ -38,6 +38,7 @@ import { IgxCalendarComponent, IgxCalendarModule } from '../../calendar/index'; import { IgxAvatarComponent, IgxAvatarModule } from '../../avatar/avatar.component'; import { IgxDatePickerComponent, IgxDatePickerModule } from '../../date-picker/date-picker.component'; import { IPositionStrategy } from './position/IPositionStrategy'; +import { IgxCalendarContainerComponent } from '../../date-picker/calendar-container.component'; const CLASS_OVERLAY_CONTENT = 'igx-overlay__content'; const CLASS_OVERLAY_CONTENT_MODAL = 'igx-overlay__content--modal'; @@ -3281,7 +3282,7 @@ describe('igxOverlay', () => { tick(); fixture.detectChanges(); expect(document.querySelectorAll((IGX_AVATAR_CLASS)).length).toEqual(0); - overlay.show(IgxDatePickerComponent); + overlay.show(IgxCalendarContainerComponent); fixture.detectChanges(); expect(document.querySelectorAll((IGX_DATEPICKER_CLASS)).length).toEqual(1); overlay.hideAll(); From 318756a34600514074b43496c03f6200e0ad2dc3 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Mon, 18 Feb 2019 17:07:23 +0200 Subject: [PATCH 45/57] fix(grid): Removed not needed brackets #3583 --- projects/igniteui-angular/src/lib/grids/cell.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index a404a157b43..555295d570f 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -712,8 +712,8 @@ export class IgxGridCellComponent implements OnInit { const editCell = this.gridAPI.get_cell_inEditMode(this.gridID); const column = this.gridAPI.get(this.gridID).columns[editCell.cellID.columnID]; - if (column.inlineEditorTemplate === undefined && ( - (column.dataType === DataType.Boolean && (key !== KEYS.SPACE && key !== KEYS.SPACE_IE)))) { + if (column.inlineEditorTemplate === undefined && + column.dataType === DataType.Boolean && key !== KEYS.SPACE && key !== KEYS.SPACE_IE) { event.preventDefault(); } return; From 9c8fd5c32a96563f799b8fc89e7ebbf6326a0188 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Mon, 18 Feb 2019 17:20:55 +0200 Subject: [PATCH 46/57] fix(grid): Removed unused openDialog event argument #3583 --- .../src/lib/grids/filtering/grid-filtering-row.component.html | 2 +- .../src/lib/grids/filtering/grid-filtering-row.component.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering-row.component.html b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering-row.component.html index ae499381fe9..980237d61da 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering-row.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering-row.component.html @@ -53,7 +53,7 @@ Date: Mon, 18 Feb 2019 17:29:50 +0200 Subject: [PATCH 47/57] fix(date-picker): Stopped propagation of spinning events #3583 --- .../src/lib/date-picker/date-picker.component.ts | 4 ++++ 1 file changed, 4 insertions(+) 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 7a45e2b2a11..35ce72572df 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 @@ -978,6 +978,7 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor 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: @@ -986,6 +987,7 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor this.openDialog(); } else { event.preventDefault(); + event.stopPropagation(); this.spinValue(event.target.value, -1, event.type); } break; @@ -1004,8 +1006,10 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor */ public onWheel(event) { event.preventDefault(); + event.stopPropagation(); const sign = (event.deltaY > 0) ? -1 : 1; this.spinValue(event.target.value, sign, event.type); + } /** From c5a0234af344f73c50b61dc12932f1f9a3839e26 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Mon, 18 Feb 2019 18:05:15 +0200 Subject: [PATCH 48/57] fix(date-picker): Fixed empty mask #3583 --- .../src/lib/date-picker/date-picker.component.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 35ce72572df..02dad6d86d6 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 @@ -380,6 +380,8 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor 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; } @@ -444,9 +446,6 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor public set value(date: Date) { this._value = date; this._onChangeCallback(date); - if (this._value === null) { - this._transformedDate = ''; - } } /** @@ -1025,7 +1024,7 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor const checkInput = DatePickerUtil.checkForCompleteDateInput(this.dateFormatParts, targetValue); this._isInEditMode = true; - if (targetValue !== DatePickerUtil.maskToPromptChars(this.mask)) { + if (targetValue !== DatePickerUtil.maskToPromptChars(this.inputMask)) { this.isEmpty = false; } From c7c7ed1c1a2b9a628bc0b1a513243fdcaf703ec6 Mon Sep 17 00:00:00 2001 From: Stefana Andreeva Date: Mon, 18 Feb 2019 18:07:39 +0200 Subject: [PATCH 49/57] fix(grid): failing test for locale #3583 --- .../src/lib/grids/grid/grid-filtering-ui.spec.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index 3ff2da9efdf..64b882d0916 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -2788,10 +2788,12 @@ describe('IgxGrid - Filtering Row UI actions', () => { tick(); fix.detectChanges(); - const calendar = fix.debugElement.query(By.css('igx-calendar')); - const sundayLabel = calendar.nativeElement.children[1].children[1].children[0].children[0].innerText; + const outlet = document.getElementsByClassName('igx-grid__outlet')[0]; + const calendar = outlet.getElementsByClassName('igx-calendar')[0]; + + const sundayLabel = calendar.querySelectorAll('.igx-calendar__label')[0].innerHTML; - expect(sundayLabel).toEqual('So'); + expect(sundayLabel.trim()).toEqual('So'); })); }); From 21bb2b078abd422c6548f1d7a04a1aeee51f4e22 Mon Sep 17 00:00:00 2001 From: Stefana Andreeva Date: Mon, 18 Feb 2019 19:17:52 +0200 Subject: [PATCH 50/57] chore(*): try fixing flickering test #3583 --- .../hierarchical-grid/hierarchical-grid.navigation.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.navigation.spec.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.navigation.spec.ts index 3ee894819b0..a332062b800 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.navigation.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.navigation.spec.ts @@ -659,12 +659,12 @@ describe('IgxHierarchicalGrid Multi-layout Navigation', () => { const child2Cell = child2.dataRowList.toArray()[0].cells.toArray()[0]; child2Cell.nativeElement.focus(); - await wait(100); + await wait(240); fixture.detectChanges(); // Shift + Tab from 2nd child child2Cell.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', shiftKey: true })); - await wait(100); + await wait(240); fixture.detectChanges(); const child1Cell = child1.getCellByColumn(9, 'childData'); From 5eabd70560300eacffadc38d15d0bf1572bed411 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Tue, 19 Feb 2019 10:41:02 +0200 Subject: [PATCH 51/57] test(date-picker): Added new tests, fixes #3583 --- .../date-picker/date-picker.component.spec.ts | 208 +++++++++- .../src/lib/grids/grid/grid.component.spec.ts | 388 +++++++++--------- 2 files changed, 397 insertions(+), 199 deletions(-) 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 e37048a8dba..52dfae1bf70 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 @@ -336,7 +336,8 @@ describe('IgxDatePicker', () => { expect(datePicker.value.getMilliseconds()).toBe(date.getMilliseconds()); }); - it('Should focus the today date', async () => { + // TO DO + xit('Should focus the today date', async () => { const fixture = TestBed.createComponent(IgxDatePickerTestComponent); const datePicker = fixture.componentInstance.datePicker; fixture.detectChanges(); @@ -346,9 +347,11 @@ describe('IgxDatePicker', () => { UIInteractions.clickElement(target); fixture.detectChanges(); - await wait(); + await wait(100); const todayDate = datePicker.calendar.daysView.dates.find(d => d.isToday); + await wait(100); + expect(document.activeElement).toEqual(todayDate.nativeElement); }); @@ -442,24 +445,39 @@ describe('IgxDatePicker', () => { expect(datePicker.onClose.emit).toHaveBeenCalledWith(datePicker); }); - xit('Handling keyboard navigation with `space`(open) and `esc`(close) buttons', async () => { - const input = fixture.debugElement.query(By.directive(IgxInputDirective)); - expect(input).toBeDefined(); - expect(input).not.toBeNull(); + 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__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('space', input.nativeElement, false); - // fixture.detectChanges(); - // await wait(100); + UIInteractions.triggerKeyDownEvtUponElem('Escape', dropDown[0], false); + flush(); + fixture.detectChanges(); - // const dropDown = document.getElementsByClassName('igx-date-picker--dropdown'); - // expect(dropDown.length).toBe(1); - // expect(dropDown[0]).not.toBeNull(); + const overlays = document.getElementsByClassName('igx-overlay__content'); + expect(overlays.length).toEqual(0); + })); - // UIInteractions.triggerKeyDownEvtUponElem('Escape', input.nativeElement, true); - // flush(); - // fixture.detectChanges(); + it('Datepicker onSelection event and selectDate method propagation', () => { + spyOn(datePicker.onSelection, 'emit'); + const newDate: Date = new Date(2016, 4, 6); + datePicker.selectDate(newDate); + fixture.detectChanges(); + + expect(datePicker.onSelection.emit).toHaveBeenCalled(); + expect(datePicker.value).toBe(newDate); - // expect(dom.query(By.css('.igx-date-picker--dropdown'))).not.toBeNull(); + 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', (() => { @@ -534,6 +552,164 @@ describe('IgxDatePicker', () => { expect(input.nativeNode.placeholder).toBe('dd-MM-yy'); })); + + it('Should be able to deselect using the API.', () => { + 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', 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.focus(); + 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', 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.focus(); + 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', 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.focus(); + 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'); + })); }); describe('EditorProvider', () => { diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts index 39c5549baa2..e99890e604e 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts @@ -1,5 +1,7 @@ -import { AfterViewInit, ChangeDetectorRef, Component, DebugElement, Injectable, - OnInit, ViewChild, ViewChildren, QueryList, TemplateRef } from '@angular/core'; +import { + AfterViewInit, ChangeDetectorRef, Component, DebugElement, Injectable, + OnInit, ViewChild, ViewChildren, QueryList, TemplateRef +} from '@angular/core'; import { async, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { BehaviorSubject, Observable } from 'rxjs'; import { By } from '@angular/platform-browser'; @@ -687,36 +689,36 @@ describe('IgxGrid Component Tests', () => { it(`should account for columns with set width when determining default column width when grid has px width and there are enough rows to cover the grid's height and enough columns to cover the grid's width`, fakeAsync(() => { - const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); - const grid = fix.componentInstance.grid; - grid.width = '800px'; - fix.componentInstance.initColumnsRows(1000, 30); - fix.componentInstance.changeInitColumns = true; - tick(); - fix.detectChanges(); + const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); + const grid = fix.componentInstance.grid; + grid.width = '800px'; + fix.componentInstance.initColumnsRows(1000, 30); + fix.componentInstance.changeInitColumns = true; + tick(); + fix.detectChanges(); - expect(grid.width).toEqual('800px'); - expect(grid.columns[0].width).toEqual('200px'); - expect(grid.columns[3].width).toEqual('200px'); - expect(grid.columns[5].width).toEqual('200px'); - expect(grid.columns[10].width).toEqual('200px'); - expect(grid.columns[25].width).toEqual('200px'); + expect(grid.width).toEqual('800px'); + expect(grid.columns[0].width).toEqual('200px'); + expect(grid.columns[3].width).toEqual('200px'); + expect(grid.columns[5].width).toEqual('200px'); + expect(grid.columns[10].width).toEqual('200px'); + expect(grid.columns[25].width).toEqual('200px'); - const actualGridWidth = grid.nativeElement.clientWidth; - const expectedDefWidth = Math.max(Math.floor((actualGridWidth - 5 * 200) / 25), parseInt(MIN_COL_WIDTH, 10)); - expect(parseInt(grid.columnWidth, 10)).toEqual(expectedDefWidth); + const actualGridWidth = grid.nativeElement.clientWidth; + const expectedDefWidth = Math.max(Math.floor((actualGridWidth - 5 * 200) / 25), parseInt(MIN_COL_WIDTH, 10)); + expect(parseInt(grid.columnWidth, 10)).toEqual(expectedDefWidth); - grid.columns.forEach((column) => { - const width = parseInt(column.width, 10); - const minWidth = parseInt(grid.columnWidth, 10); - if (column.index !== 0 && column.index !== 3 && column.index !== 5 && - column.index !== 10 && column.index !== 25) { - expect(width).toEqual(minWidth); - } - }); - expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true); - expect(grid.rowList.length).toBeGreaterThan(0); - })); + grid.columns.forEach((column) => { + const width = parseInt(column.width, 10); + const minWidth = parseInt(grid.columnWidth, 10); + if (column.index !== 0 && column.index !== 3 && column.index !== 5 && + column.index !== 10 && column.index !== 25) { + expect(width).toEqual(minWidth); + } + }); + expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true); + expect(grid.rowList.length).toBeGreaterThan(0); + })); it(`should account for columns with set width when determining default column width when grid has 100% width and there are 10000 rows and 150 columns`, () => { @@ -748,33 +750,33 @@ describe('IgxGrid Component Tests', () => { it(`should account for columns with set width when determining default column width when grid has px width and there are 10000 rows and 150 columns`, fakeAsync(() => { - const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); - const grid = fix.componentInstance.grid; - grid.width = '800px'; - fix.componentInstance.initColumnsRows(10000, 150); - fix.componentInstance.changeInitColumns = true; - tick(); - fix.detectChanges(); + const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); + const grid = fix.componentInstance.grid; + grid.width = '800px'; + fix.componentInstance.initColumnsRows(10000, 150); + fix.componentInstance.changeInitColumns = true; + tick(); + fix.detectChanges(); - expect(grid.width).toEqual('800px'); - expect(grid.columns[0].width).toEqual('500px'); - expect(grid.columns[3].width).toEqual('500px'); - expect(grid.columns[5].width).toEqual('500px'); - expect(grid.columns[10].width).toEqual('500px'); - expect(grid.columns[50].width).toEqual('500px'); + expect(grid.width).toEqual('800px'); + expect(grid.columns[0].width).toEqual('500px'); + expect(grid.columns[3].width).toEqual('500px'); + expect(grid.columns[5].width).toEqual('500px'); + expect(grid.columns[10].width).toEqual('500px'); + expect(grid.columns[50].width).toEqual('500px'); - grid.columns.forEach((column) => { - const width = parseInt(column.width, 10); - const minWidth = parseInt(grid.columnWidth, 10); - if (column.index !== 0 && column.index !== 3 && column.index !== 5 && - column.index !== 10 && column.index !== 50) { - expect(width).toEqual(minWidth); - } - }); + grid.columns.forEach((column) => { + const width = parseInt(column.width, 10); + const minWidth = parseInt(grid.columnWidth, 10); + if (column.index !== 0 && column.index !== 3 && column.index !== 5 && + column.index !== 10 && column.index !== 50) { + expect(width).toEqual(minWidth); + } + }); - expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true); - expect(grid.rowList.length).toBeGreaterThan(0); - })); + expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true); + expect(grid.rowList.length).toBeGreaterThan(0); + })); it('should render all records if height is explicitly set to null.', fakeAsync(() => { const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); @@ -830,58 +832,58 @@ describe('IgxGrid Component Tests', () => { it(`should render all records exactly if height is 100% and parent container\'s height is unset and there are fewer than 10 records in the data view`, fakeAsync(() => { - const fix = TestBed.createComponent(IgxGridWrappedInContComponent); - fix.componentInstance.grid.height = '100%'; - fix.componentInstance.data = fix.componentInstance.data.slice(0, 5); - tick(); - fix.detectChanges(); - const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height; - expect(defaultHeight).not.toBeNull(); - expect(parseInt(defaultHeight, 10)).toBeGreaterThan(200); - expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeFalsy(); - expect(fix.componentInstance.grid.rowList.length).toEqual(5); - })); + const fix = TestBed.createComponent(IgxGridWrappedInContComponent); + fix.componentInstance.grid.height = '100%'; + fix.componentInstance.data = fix.componentInstance.data.slice(0, 5); + tick(); + fix.detectChanges(); + const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height; + expect(defaultHeight).not.toBeNull(); + expect(parseInt(defaultHeight, 10)).toBeGreaterThan(200); + expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeFalsy(); + expect(fix.componentInstance.grid.rowList.length).toEqual(5); + })); it(`should render 10 records if height is 100% and parent container\'s height is unset and display density is changed`, fakeAsync(() => { - const fix = TestBed.createComponent(IgxGridWrappedInContComponent); - fix.componentInstance.grid.height = '100%'; - fix.componentInstance.data = fix.componentInstance.data.slice(0, 11); - fix.componentInstance.density = DisplayDensity.compact; - tick(); - fix.detectChanges(); - const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height; - const defaultHeightNum = parseInt(defaultHeight, 10); - expect(defaultHeight).not.toBeNull(); - expect(defaultHeightNum).toBe(320); - expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeTruthy(); - expect(fix.componentInstance.grid.rowList.length).toEqual(11); - })); + const fix = TestBed.createComponent(IgxGridWrappedInContComponent); + fix.componentInstance.grid.height = '100%'; + fix.componentInstance.data = fix.componentInstance.data.slice(0, 11); + fix.componentInstance.density = DisplayDensity.compact; + tick(); + fix.detectChanges(); + const defaultHeight = fix.debugElement.query(By.css(TBODY_CLASS)).styles.height; + const defaultHeightNum = parseInt(defaultHeight, 10); + expect(defaultHeight).not.toBeNull(); + expect(defaultHeightNum).toBe(320); + expect(fix.componentInstance.isVerticalScrollbarVisible()).toBeTruthy(); + expect(fix.componentInstance.grid.rowList.length).toEqual(11); + })); it('should render correct columns if after scrolling right container size changes so that all columns become visible.', - async () => { - const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); - fix.detectChanges(); - const grid = fix.componentInstance.grid; - grid.width = '500px'; - fix.componentInstance.initColumnsRows(5, 5); - fix.detectChanges(); - // tick(); - await wait(); - expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true); - const scrollbar = grid.parentVirtDir.getHorizontalScroll(); - scrollbar.scrollLeft = 10000; - grid.width = '1500px'; + async () => { + const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); + fix.detectChanges(); + const grid = fix.componentInstance.grid; + grid.width = '500px'; + fix.componentInstance.initColumnsRows(5, 5); + fix.detectChanges(); + // tick(); + await wait(); + expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true); + const scrollbar = grid.parentVirtDir.getHorizontalScroll(); + scrollbar.scrollLeft = 10000; + grid.width = '1500px'; - fix.detectChanges(); - await wait(100); - expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(false); - const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS)); - expect(headers.length).toEqual(5); - for (let i = 0; i < headers.length; i++) { - expect(headers[i].context.column.field).toEqual(grid.columns[i].field); - } - }); + fix.detectChanges(); + await wait(100); + expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(false); + const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS)); + expect(headers.length).toEqual(5); + for (let i = 0; i < headers.length; i++) { + expect(headers[i].context.column.field).toEqual(grid.columns[i].field); + } + }); it('Should render date and number values based on default formatting', () => { const fixture = TestBed.createComponent(IgxGridFormattingComponent); @@ -927,7 +929,7 @@ describe('IgxGrid Component Tests', () => { }); }); - it('Should calculate default column width when a column has width in %', async() => { + it('Should calculate default column width when a column has width in %', async () => { const fix = TestBed.createComponent(IgxGridColumnPercentageWidthComponent); fix.componentInstance.initColumnsRows(5, 3); fix.detectChanges(); @@ -958,7 +960,7 @@ describe('IgxGrid Component Tests', () => { expect(virtDir.getSizeAt(1)).toEqual(136); expect(virtDir.getSizeAt(2)).toEqual(136); }); - it('Should re-calculate column width when a column has width in % and grid width changes.', async() => { + it('Should re-calculate column width when a column has width in % and grid width changes.', async () => { const fix = TestBed.createComponent(IgxGridColumnPercentageWidthComponent); fix.componentInstance.initColumnsRows(5, 3); fix.detectChanges(); @@ -987,35 +989,35 @@ describe('IgxGrid Component Tests', () => { expect(virtDir.getSizeAt(1)).toEqual(150); expect(virtDir.getSizeAt(2)).toEqual(150); }); - it('Should calculate column width when a column has width in % and row selectors are enabled.', async() => { - const fix = TestBed.createComponent(IgxGridColumnPercentageWidthComponent); - fix.componentInstance.initColumnsRows(5, 3); - fix.detectChanges(); - const grid = fix.componentInstance.grid; - const hScroll = fix.debugElement.query(By.css('.igx-grid__scroll')); - grid.rowSelectable = true; - fix.detectChanges(); - grid.columns[0].width = '70%'; - - fix.detectChanges(); - await wait(16); - // check UI - const header0 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[0]; - const header1 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[1]; - const header2 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[2]; - expect(header0.nativeElement.offsetWidth).toEqual(Math.round(0.7 * grid.unpinnedWidth)); - expect(header1.nativeElement.offsetWidth).toEqual(136); - expect(header2.nativeElement.offsetWidth).toEqual(136); - expect(hScroll.nativeElement.hidden).toBe(false); - - // check virtualization cache is valid - const virtDir = grid.getRowByIndex(0).virtDirRow; - expect(virtDir.getSizeAt(0)).toEqual(Math.floor(0.7 * grid.unpinnedWidth)); - expect(virtDir.getSizeAt(1)).toEqual(136); - expect(virtDir.getSizeAt(2)).toEqual(136); - - }); - it('Should render correct column widths when having mixed width setting - px, %, null', async() => { + it('Should calculate column width when a column has width in % and row selectors are enabled.', async () => { + const fix = TestBed.createComponent(IgxGridColumnPercentageWidthComponent); + fix.componentInstance.initColumnsRows(5, 3); + fix.detectChanges(); + const grid = fix.componentInstance.grid; + const hScroll = fix.debugElement.query(By.css('.igx-grid__scroll')); + grid.rowSelectable = true; + fix.detectChanges(); + grid.columns[0].width = '70%'; + + fix.detectChanges(); + await wait(16); + // check UI + const header0 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[0]; + const header1 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[1]; + const header2 = fix.debugElement.queryAll(By.css('igx-grid-header-group'))[2]; + expect(header0.nativeElement.offsetWidth).toEqual(Math.round(0.7 * grid.unpinnedWidth)); + expect(header1.nativeElement.offsetWidth).toEqual(136); + expect(header2.nativeElement.offsetWidth).toEqual(136); + expect(hScroll.nativeElement.hidden).toBe(false); + + // check virtualization cache is valid + const virtDir = grid.getRowByIndex(0).virtDirRow; + expect(virtDir.getSizeAt(0)).toEqual(Math.floor(0.7 * grid.unpinnedWidth)); + expect(virtDir.getSizeAt(1)).toEqual(136); + expect(virtDir.getSizeAt(2)).toEqual(136); + + }); + it('Should render correct column widths when having mixed width setting - px, %, null', async () => { const fix = TestBed.createComponent(IgxGridColumnPercentageWidthComponent); fix.componentInstance.initColumnsRows(5, 3); const grid = fix.componentInstance.grid; @@ -1097,7 +1099,7 @@ describe('IgxGrid Component Tests', () => { tick(); expect(trans.add).toHaveBeenCalled(); expect(trans.add).toHaveBeenCalledTimes(1); - expect(trans.add).toHaveBeenCalledWith({id: 100, type: 'add', newValue: addRowData}); + expect(trans.add).toHaveBeenCalledWith({ id: 100, type: 'add', newValue: addRowData }); expect(grid.data.length).toBe(10); })); @@ -1113,7 +1115,7 @@ describe('IgxGrid Component Tests', () => { tick(); expect(trans.add).toHaveBeenCalled(); expect(trans.add).toHaveBeenCalledTimes(1); - expect(trans.add).toHaveBeenCalledWith({id: 5, type: 'delete', newValue: null}, grid.data[4]); + expect(trans.add).toHaveBeenCalledWith({ id: 5, type: 'delete', newValue: null }, grid.data[4]); expect(grid.data.length).toBe(10); })); @@ -1129,7 +1131,7 @@ describe('IgxGrid Component Tests', () => { tick(); expect(trans.add).toHaveBeenCalled(); expect(trans.add).toHaveBeenCalledTimes(1); - expect(trans.add).toHaveBeenCalledWith({id: 3, type: 'update', newValue: { ProductName: 'Updated Cell'}}, grid.data[2]); + expect(trans.add).toHaveBeenCalledWith({ id: 3, type: 'update', newValue: { ProductName: 'Updated Cell' } }, grid.data[2]); expect(grid.data.length).toBe(10); })); @@ -1154,7 +1156,7 @@ describe('IgxGrid Component Tests', () => { tick(); expect(trans.add).toHaveBeenCalled(); expect(trans.add).toHaveBeenCalledTimes(1); - expect(trans.add).toHaveBeenCalledWith({id: 3, type: 'update', newValue: updateRowData}, oldRowData); + expect(trans.add).toHaveBeenCalledWith({ id: 3, type: 'update', newValue: updateRowData }, oldRowData); expect(grid.data[2]).toBe(oldRowData); })); }); @@ -1386,7 +1388,7 @@ describe('IgxGrid Component Tests', () => { }); describe('Row Editing - Navigation - Keyboard', () => { - it(`Should jump from first editable columns to overlay buttons`, (async() => { + it(`Should jump from first editable columns to overlay buttons`, (async () => { const fixture = TestBed.createComponent(IgxGridWithEditingAndFeaturesComponent); fixture.detectChanges(); const grid = fixture.componentInstance.grid; @@ -1937,8 +1939,10 @@ describe('IgxGrid Component Tests', () => { cell.inEditMode = true; tick(); - grid.sort({ fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: true, - strategy: DefaultSortingStrategy.instance() }); + grid.sort({ + fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: true, + strategy: DefaultSortingStrategy.instance() + }); fix.detectChanges(); // expect(gridAPI.submit_value).toHaveBeenCalled(); @@ -2402,8 +2406,10 @@ describe('IgxGrid Component Tests', () => { targetCell.inEditMode = true; tick(); - grid.groupBy({ fieldName: 'OrderDate', dir: SortingDirection.Desc, ignoreCase: true, - strategy: DefaultSortingStrategy.instance() }); + grid.groupBy({ + fieldName: 'OrderDate', dir: SortingDirection.Desc, ignoreCase: true, + strategy: DefaultSortingStrategy.instance() + }); expect(gridAPI.escape_editMode).toHaveBeenCalled(); expect(gridAPI.submit_value).toHaveBeenCalled(); @@ -2428,8 +2434,10 @@ describe('IgxGrid Component Tests', () => { fix.detectChanges(); cell.update(111); // Do not exit edit mode - grid.sort({ fieldName: 'Downloads', dir: SortingDirection.Desc, ignoreCase: true, - strategy: DefaultSortingStrategy.instance() }); + grid.sort({ + fieldName: 'Downloads', dir: SortingDirection.Desc, ignoreCase: true, + strategy: DefaultSortingStrategy.instance() + }); tick(); fix.detectChanges(); @@ -2453,8 +2461,10 @@ describe('IgxGrid Component Tests', () => { tick(); cell.update(newValue); - grid.sort({ fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: true, - strategy: DefaultSortingStrategy.instance() }); + grid.sort({ + fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: true, + strategy: DefaultSortingStrategy.instance() + }); tick(); fix.detectChanges(); @@ -2473,8 +2483,10 @@ describe('IgxGrid Component Tests', () => { const grid = fix.componentInstance.grid; - grid.sort({ fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: true, - strategy: DefaultSortingStrategy.instance() }); + grid.sort({ + fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: true, + strategy: DefaultSortingStrategy.instance() + }); tick(); fix.detectChanges(); @@ -2827,8 +2839,10 @@ describe('IgxGrid Component Tests', () => { component.cellInEditMode.editValue = 1337; fixture.detectChanges(); // On sort - grid.sort({ fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: true, - strategy: DefaultSortingStrategy.instance() }); + grid.sort({ + fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: true, + strategy: DefaultSortingStrategy.instance() + }); fixture.detectChanges(); expect(grid.onRowEdit.emit).toHaveBeenCalled(); expect(grid.onRowEdit.emit).toHaveBeenCalledWith({ @@ -2902,7 +2916,7 @@ describe('IgxGrid Component Tests', () => { })); }); - describe('Row Editing - Custom overlay', () => { + describe('Row Editing - Custom overlay', () => { it('Custom overlay', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridCustomOverlayComponent); fixture.detectChanges(); @@ -2934,7 +2948,7 @@ describe('IgxGrid Component Tests', () => { })); }); - describe('Row Editing - Transaction', () => { + describe('Row Editing - Transaction', () => { it('Transaction Update, Delete, Add, Undo, Redo, Commit check transaction and grid state', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridRowEditingTransactionComponent); fixture.detectChanges(); @@ -3100,7 +3114,7 @@ describe('IgxGrid Component Tests', () => { expect(cell.value).toBe('Updated value'); const expectedTransaction: Transaction = { id: 1, - newValue: {ProductName: 'Updated value'}, + newValue: { ProductName: 'Updated value' }, type: TransactionType.UPDATE }; expect(grid.transactions.getAggregatedChanges(false)).toEqual([expectedTransaction]); @@ -3117,11 +3131,11 @@ describe('IgxGrid Component Tests', () => { const initialState = grid.transactions.getAggregatedChanges(false); // Enter edit mode - cellDate.onKeydownEnterEditMode({ stopPropagation: () => {}, preventDefault: () => {}}); + cellDate.onKeydownEnterEditMode({ stopPropagation: () => { }, preventDefault: () => { } }); tick(); fixture.detectChanges(); // Perform Shift + Tab to UnitsInStock - cellDate.nativeElement.dispatchEvent(new KeyboardEvent('keydown', {key: 'tab', shiftKey: true, code: 'tab'})); + cellDate.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'tab', shiftKey: true, code: 'tab' })); tick(); fixture.detectChanges(); // Exit edit mode @@ -3132,14 +3146,13 @@ describe('IgxGrid Component Tests', () => { const newValue = new Date('01/01/2000'); cellDate.update(newValue); - tick(); + tick(200); fixture.detectChanges(); - const expectedTransaction: Transaction = { - id: 1, - newValue: {OrderDate: newValue}, - type: TransactionType.UPDATE - }; - expect(grid.transactions.getAggregatedChanges(false)).toEqual([expectedTransaction]); + + const updatedTransaction = grid.transactions.getAggregatedChanges(false)[0]; + expect(updatedTransaction.id).toEqual(1); + expect(updatedTransaction.type).toEqual(TransactionType.UPDATE); + expect(updatedTransaction.newValue.OrderDate.getTime()).toEqual(newValue.getTime()); })); it('Should allow to change of a cell in added row in grid with transactions', fakeAsync(() => { @@ -3152,7 +3165,8 @@ describe('IgxGrid Component Tests', () => { ProductName: 'Added product', InStock: false, UnitsInStock: 0, - OrderDate: new Date()}; + OrderDate: new Date() + }; grid.addRow(addRowData); tick(); fixture.detectChanges(); @@ -3287,14 +3301,16 @@ describe('IgxGrid Component Tests', () => { })); }); - describe('Row Editing - Grouping', () => { + describe('Row Editing - Grouping', () => { it('Hide/show row editing dialog with group collapsing/expanding', fakeAsync(() => { const fix = TestBed.createComponent(IgxGridRowEditingWithFeaturesComponent); const grid = fix.componentInstance.instance; grid.primaryKey = 'ID'; fix.detectChanges(); - grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false, - strategy: DefaultSortingStrategy.instance() }); + grid.groupBy({ + fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false, + strategy: DefaultSortingStrategy.instance() + }); tick(); fix.detectChanges(); const cell = grid.getCellByColumn(1, 'ProductName'); @@ -3323,8 +3339,10 @@ describe('IgxGrid Component Tests', () => { const grid = fix.componentInstance.instance; grid.primaryKey = 'ID'; fix.detectChanges(); - grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false, - strategy: DefaultSortingStrategy.instance() }); + grid.groupBy({ + fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false, + strategy: DefaultSortingStrategy.instance() + }); tick(); fix.detectChanges(); let row: HTMLElement; @@ -3370,7 +3388,7 @@ describe('IgxGrid Component Tests', () => { expect(overlayContent.style.display).toEqual(''); row = grid.getRowByIndex(7).nativeElement; expect(row.getBoundingClientRect().bottom === overlayElem.getBoundingClientRect().top).toBeTruthy(); - })); + })); it('Hide/show row editing dialog when hierarchical group is collapsed/expanded', fakeAsync(() => { @@ -3378,12 +3396,16 @@ describe('IgxGrid Component Tests', () => { const grid = fix.componentInstance.instance; grid.primaryKey = 'ID'; fix.detectChanges(); - grid.groupBy({ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false, - strategy: DefaultSortingStrategy.instance() }); + grid.groupBy({ + fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false, + strategy: DefaultSortingStrategy.instance() + }); tick(); fix.detectChanges(); - grid.groupBy({ fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false, - strategy: DefaultSortingStrategy.instance() }); + grid.groupBy({ + fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false, + strategy: DefaultSortingStrategy.instance() + }); tick(); fix.detectChanges(); const cell = grid.getCellByColumn(2, 'ProductName'); @@ -3401,7 +3423,7 @@ describe('IgxGrid Component Tests', () => { tick(); fix.detectChanges(); expect(overlayContent.style.display).toEqual(''); - })); + })); }); }); @@ -3417,7 +3439,7 @@ describe('IgxGrid Component Tests', () => { }).compileComponents(); })); - it('IgxTabs: should initialize a grid with correct width/height', async() => { + it('IgxTabs: should initialize a grid with correct width/height', async () => { const fix = TestBed.createComponent(IgxGridInsideIgxTabsComponent); fix.detectChanges(); const grid = fix.componentInstance.grid3; @@ -4132,10 +4154,10 @@ export class IgxGridInsideIgxTabsComponent { public tabs: IgxTabsComponent; public columns = [ - { field: 'id', width: 100}, - { field: '1', width: 100}, - { field: '2', width: 100}, - { field: '3', width: 100} + { field: 'id', width: 100 }, + { field: '1', width: 100 }, + { field: '2', width: 100 }, + { field: '3', width: 100 } ]; public data = []; @@ -4143,13 +4165,13 @@ export class IgxGridInsideIgxTabsComponent { constructor() { const data = []; for (let j = 1; j <= 10; j++) { - const item = {}; - item['id'] = j; - for (let k = 2, len = this.columns.length; k <= len; k++) { - const field = this.columns[k - 1].field; - item[field] = `item${j}-${k}`; - } - data.push(item); + const item = {}; + item['id'] = j; + for (let k = 2, len = this.columns.length; k <= len; k++) { + const field = this.columns[k - 1].field; + item[field] = `item${j}-${k}`; + } + data.push(item); } this.data = data; } From f0fed13eccca2778851edb0c0770d386a339f05a Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Tue, 19 Feb 2019 11:56:16 +0200 Subject: [PATCH 52/57] test(date-picker): Fixed failing focus test #3583 --- .../src/lib/date-picker/date-picker.component.spec.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 52dfae1bf70..fc5e821ff43 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 @@ -336,24 +336,21 @@ describe('IgxDatePicker', () => { expect(datePicker.value.getMilliseconds()).toBe(date.getMilliseconds()); }); - // TO DO - xit('Should focus the today date', async () => { + 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.clickElement(target); fixture.detectChanges(); - await wait(100); + tick(200); const todayDate = datePicker.calendar.daysView.dates.find(d => d.isToday); - await wait(100); expect(document.activeElement).toEqual(todayDate.nativeElement); - }); + })); it('#3595 - Should be able to change year', fakeAsync(() => { const fixture = TestBed.createComponent(IgxDatePickerTestComponent); From e5032376b9a46aabea22d520c176becec77d1123 Mon Sep 17 00:00:00 2001 From: NikolayAlipiev Date: Tue, 19 Feb 2019 16:35:44 +0200 Subject: [PATCH 53/57] test(grid): remove detectChanges #3583 --- .../src/lib/grids/grid/grid.component.spec.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts index e99890e604e..95bceaafb18 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts @@ -3125,9 +3125,7 @@ describe('IgxGrid Component Tests', () => { fixture.detectChanges(); const grid = fixture.componentInstance.grid; - const cellStock = grid.getCellByColumn(0, 'UnitsInStock'); const cellDate = grid.getCellByColumn(0, 'OrderDate'); - const initialCellValue = cellDate.value; const initialState = grid.transactions.getAggregatedChanges(false); // Enter edit mode @@ -3146,13 +3144,13 @@ describe('IgxGrid Component Tests', () => { const newValue = new Date('01/01/2000'); cellDate.update(newValue); - tick(200); - fixture.detectChanges(); - const updatedTransaction = grid.transactions.getAggregatedChanges(false)[0]; - expect(updatedTransaction.id).toEqual(1); - expect(updatedTransaction.type).toEqual(TransactionType.UPDATE); - expect(updatedTransaction.newValue.OrderDate.getTime()).toEqual(newValue.getTime()); + const expectedTransaction: Transaction = { + id: 1, + newValue: {OrderDate: newValue}, + type: TransactionType.UPDATE + }; + expect(grid.transactions.getAggregatedChanges(false)).toEqual([expectedTransaction]); })); it('Should allow to change of a cell in added row in grid with transactions', fakeAsync(() => { From f7cce0b2772c18f5e01bdead0e2edc8ba047055f Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Tue, 19 Feb 2019 16:48:19 +0200 Subject: [PATCH 54/57] test(date-picker): Added tests for editable date-picker #3583 --- .../date-picker/date-picker.component.spec.ts | 84 ++++++++++++++++--- 1 file changed, 73 insertions(+), 11 deletions(-) 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 c64f30260ca..efdf6deabb2 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 @@ -11,7 +11,7 @@ import { IgxInputGroupModule } from '../input-group'; import { configureTestSuite } from '../test-utils/configure-suite'; -fdescribe('IgxDatePicker', () => { +describe('IgxDatePicker', () => { configureTestSuite(); beforeEach(async(() => { TestBed.configureTestingModule({ @@ -414,7 +414,7 @@ fdescribe('IgxDatePicker', () => { fixture.detectChanges(); }); - it('Editable Datepicker open/close event', async () => { + it('Editable Datepicker open/close event - edit mode', async () => { const dom = fixture.debugElement; const iconDate = dom.query(By.css('.igx-icon')); expect(iconDate).not.toBeNull(); @@ -442,7 +442,7 @@ fdescribe('IgxDatePicker', () => { expect(datePicker.onClose.emit).toHaveBeenCalledWith(datePicker); }); - it('Handling keyboard navigation with `space`(open) and `esc`(close) buttons', fakeAsync(() => { + it('Handling keyboard navigation with `space`(open) and `esc`(close) buttons - edit mode', fakeAsync(() => { const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker')); UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); fixture.detectChanges(); @@ -463,7 +463,29 @@ fdescribe('IgxDatePicker', () => { expect(overlays.length).toEqual(0); })); - it('Datepicker onSelection event and selectDate method propagation', () => { + it('Open/close drop-down with `alt + down`(open) and `alt + up`(close) - edit 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 event and selectDate method propagation - edit mode', () => { spyOn(datePicker.onSelection, 'emit'); const newDate: Date = new Date(2016, 4, 6); datePicker.selectDate(newDate); @@ -477,7 +499,7 @@ fdescribe('IgxDatePicker', () => { expect(input.nativeElement.value).toBe('06.05.2016'); }); - it('should open the dropdown when click on the date icon', (() => { + it('should open the dropdown when click on the date icon - edit mode', (() => { const dom = fixture.debugElement; fixture.detectChanges(); @@ -516,7 +538,7 @@ fdescribe('IgxDatePicker', () => { expect(year).toBe('2011'); })); - it('should be able to apply display format (editable mode)', async () => { + it('should be able to apply display format - edit mode', async () => { const input = fixture.debugElement.query(By.directive(IgxInputDirective)); expect(input).toBeDefined(); @@ -530,7 +552,7 @@ fdescribe('IgxDatePicker', () => { expect(input.nativeElement.value).toBe('20.10.2011'); }); - it('should be able to apply editor mask (editable mode)', (() => { + it('should be able to apply editor mask - editable mode', (() => { const input = fixture.debugElement.query(By.directive(IgxInputDirective)); input.nativeElement.dispatchEvent(new Event('focus')); fixture.detectChanges(); @@ -550,7 +572,7 @@ fdescribe('IgxDatePicker', () => { expect(input.nativeNode.placeholder).toBe('dd-MM-yy'); })); - it('Should be able to deselect using the API.', () => { + it('Should be able to deselect using the API - edit mode', () => { const date = new Date(Date.now()); datePicker.selectDate(date); fixture.detectChanges(); @@ -563,7 +585,7 @@ fdescribe('IgxDatePicker', () => { expect(datePicker.value).toBe(null); }); - it('should increase date parts using arrows', fakeAsync(() => { + it('should increase date parts using arrows - edit mode', fakeAsync(() => { const input = fixture.debugElement.query(By.directive(IgxInputDirective)); expect(input).toBeDefined(); input.nativeElement.dispatchEvent(new Event('focus')); @@ -608,7 +630,7 @@ fdescribe('IgxDatePicker', () => { expect(input.nativeElement.value).toBe('21.11.2012'); })); - it('should decrease date parts using arrows', fakeAsync(() => { + it('should decrease date parts using arrows - edit mode', fakeAsync(() => { const input = fixture.debugElement.query(By.directive(IgxInputDirective)); expect(input).toBeDefined(); input.nativeElement.dispatchEvent(new Event('focus')); @@ -660,7 +682,7 @@ fdescribe('IgxDatePicker', () => { expect(input.nativeElement.value).toBe('18.09.2010'); })); - it('should increase/decrease date parts using mouse wheel', fakeAsync(() => { + it('should increase/decrease date parts using mouse wheel - edit mode', fakeAsync(() => { const input = fixture.debugElement.query(By.directive(IgxInputDirective)); expect(input).toBeDefined(); input.nativeElement.dispatchEvent(new Event('focus')); @@ -707,6 +729,46 @@ fdescribe('IgxDatePicker', () => { // format dd.MM.y expect(input.nativeElement.value).toBe('21.09.2013'); })); + + it('should reset value on clear button click - edit mode', () => { + const input = fixture.debugElement.query(By.directive(IgxInputDirective)); + const dom = fixture.debugElement; + const clear = dom.queryAll(By.css('.igx-icon'))[1]; + UIInteractions.clickElement(clear); + fixture.detectChanges(); + + 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 - edit 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.sendInput(input, '28-02-19'); + fixture.detectChanges(); + + UIInteractions.sendInput(input, '29-02-19'); + fixture.detectChanges(); + + // invalid date + expect(input.nativeElement.value).toBe('29-02-19'); + + expect(datePicker.onValidationFailed.emit).toHaveBeenCalledTimes(1); + }); + + xit('should emit onDisabledDate event when entered disabled date - edit mode', fakeAsync(() => { + + })); }); describe('EditorProvider', () => { From 342b8d6995b28260bc61c603f884a69a40822958 Mon Sep 17 00:00:00 2001 From: NikolayAlipiev Date: Tue, 19 Feb 2019 17:08:53 +0200 Subject: [PATCH 55/57] test(grid): enter edit mode -> update -> exit edit mode -> check #3583 --- .../src/lib/grids/grid/grid.component.spec.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts index 95bceaafb18..538c18dd35c 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts @@ -3142,8 +3142,16 @@ describe('IgxGrid Component Tests', () => { fixture.detectChanges(); expect(grid.transactions.getAggregatedChanges(true)).toEqual(initialState); + // Enter edit mode + cellDate.onKeydownEnterEditMode({ stopPropagation: () => { }, preventDefault: () => { } }); + tick(); + fixture.detectChanges(); const newValue = new Date('01/01/2000'); cellDate.update(newValue); + // Exit edit mode + grid.endEdit(true); + tick(); + fixture.detectChanges(); const expectedTransaction: Transaction = { id: 1, From 170ac12b1cf4aa3f00909ab76fab9ef879fdc1f2 Mon Sep 17 00:00:00 2001 From: NikolayAlipiev Date: Tue, 19 Feb 2019 19:32:19 +0200 Subject: [PATCH 56/57] test(grid): use API calls to methods instead of using shift+tab #3583 --- .../src/lib/grids/grid/grid.component.spec.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts index 538c18dd35c..3bee7df3bdd 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts @@ -3125,31 +3125,27 @@ describe('IgxGrid Component Tests', () => { fixture.detectChanges(); const grid = fixture.componentInstance.grid; - const cellDate = grid.getCellByColumn(0, 'OrderDate'); + let cellDate = grid.getCellByColumn(0, 'OrderDate'); const initialState = grid.transactions.getAggregatedChanges(false); // Enter edit mode cellDate.onKeydownEnterEditMode({ stopPropagation: () => { }, preventDefault: () => { } }); tick(); fixture.detectChanges(); - // Perform Shift + Tab to UnitsInStock - cellDate.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'tab', shiftKey: true, code: 'tab' })); + // Exit edit mode without change + cellDate.onKeydownExitEditMode({ stopPropagation: () => { }, preventDefault: () => { } }); tick(); fixture.detectChanges(); - // Exit edit mode - grid.endEdit(true); + cellDate = grid.getCellByColumn(0, 'UnitsInStock'); + cellDate.onKeydownEnterEditMode({ stopPropagation: () => { }, preventDefault: () => { } }); tick(); fixture.detectChanges(); expect(grid.transactions.getAggregatedChanges(true)).toEqual(initialState); + cellDate.onKeydownExitEditMode({ stopPropagation: () => { }, preventDefault: () => { } }); - // Enter edit mode - cellDate.onKeydownEnterEditMode({ stopPropagation: () => { }, preventDefault: () => { } }); - tick(); - fixture.detectChanges(); + cellDate = grid.getCellByColumn(0, 'OrderDate'); const newValue = new Date('01/01/2000'); cellDate.update(newValue); - // Exit edit mode - grid.endEdit(true); tick(); fixture.detectChanges(); From d8e623aba4592045937231e8d4465af082853f77 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Wed, 20 Feb 2019 08:46:37 +0200 Subject: [PATCH 57/57] test(date-picker): Added more tests for editable date picker #3583 --- .../date-picker/date-picker.component.spec.ts | 96 ++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) 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 efdf6deabb2..812afc6ff62 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 @@ -10,6 +10,7 @@ import { UIInteractions, wait } from '../test-utils/ui-interactions.spec'; import { IgxInputGroupModule } from '../input-group'; import { configureTestSuite } from '../test-utils/configure-suite'; +import { DateRangeType } from 'igniteui-angular'; describe('IgxDatePicker', () => { configureTestSuite(); @@ -762,12 +763,103 @@ describe('IgxDatePicker', () => { // invalid date expect(input.nativeElement.value).toBe('29-02-19'); - expect(datePicker.onValidationFailed.emit).toHaveBeenCalledTimes(1); }); - xit('should emit onDisabledDate event when entered disabled date - edit mode', fakeAsync(() => { + it('should emit onDisabledDate event when entered disabled date - edit 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.sendInput(input, '03-05-19'); + fixture.detectChanges(); + + // disabled date + UIInteractions.sendInput(input, '03-09-18'); + fixture.detectChanges(); + expect(input.nativeElement.value).toBe('03-09-18'); + + UIInteractions.sendInput(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 - edit mode', fakeAsync(() => { + const input = fixture.debugElement.query(By.directive(IgxInputDirective)); + expect(input).toBeDefined(); + datePicker.isSpinLoop = false; + + input.nativeElement.focus(); + UIInteractions.sendInput(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.sendInput(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.sendInput(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.sendInput(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'); })); });