From 947bf9093fed6f9b97dba42a149e693b3d23c2c4 Mon Sep 17 00:00:00 2001 From: NikolayAlipiev Date: Wed, 13 Feb 2019 18:02:56 +0200 Subject: [PATCH 1/3] docs(*): hide all modules --- .../igniteui-angular/src/lib/avatar/avatar.component.ts | 3 ++- projects/igniteui-angular/src/lib/badge/badge.component.ts | 3 +-- .../igniteui-angular/src/lib/banner/banner.component.ts | 4 ++++ .../src/lib/buttonGroup/buttonGroup.component.ts | 2 +- .../igniteui-angular/src/lib/calendar/calendar.module.ts | 3 +++ projects/igniteui-angular/src/lib/card/card.component.ts | 3 ++- .../src/lib/carousel/carousel.component.ts | 3 ++- .../src/lib/checkbox/checkbox.component.ts | 3 ++- .../igniteui-angular/src/lib/core/navigation/directives.ts | 3 +++ .../src/lib/date-picker/date-picker.component.ts | 2 +- .../igniteui-angular/src/lib/dialog/dialog.component.ts | 3 +-- .../lib/directives/autocomplete/autocomplete.directive.ts | 3 +++ .../src/lib/directives/button/button.directive.ts | 3 ++- .../src/lib/directives/dragdrop/dragdrop.directive.ts | 2 +- .../src/lib/directives/filter/filter.directive.ts | 4 +++- .../src/lib/directives/focus/focus.directive.ts | 3 ++- .../src/lib/directives/for-of/for_of.directive.ts | 4 ++-- .../src/lib/directives/layout/layout.directive.ts | 3 +++ .../src/lib/directives/mask/mask.directive.ts | 4 ++++ .../src/lib/directives/radio/radio-group.directive.ts | 2 +- .../src/lib/directives/ripple/ripple.directive.ts | 3 ++- .../directives/text-highlight/text-highlight.directive.ts | 3 +++ .../directives/text-selection/text-selection.directive.ts | 3 +++ .../src/lib/directives/toggle/toggle.directive.ts | 3 +++ .../src/lib/directives/tooltip/tooltip.directive.ts | 3 +++ projects/igniteui-angular/src/lib/drop-down/index.ts | 4 +++- .../src/lib/grids/column-hiding.component.ts | 3 ++- .../src/lib/grids/column-pinning.component.ts | 2 +- .../igniteui-angular/src/lib/grids/grid-common.module.ts | 3 +++ .../grids/hierarchical-grid/hierarchical-grid.module.ts | 3 +++ .../src/lib/grids/tree-grid/tree-grid.module.ts | 3 +++ projects/igniteui-angular/src/lib/icon/index.ts | 3 +++ .../src/lib/input-group/input-group.component.ts | 2 +- projects/igniteui-angular/src/lib/list/list.component.ts | 2 +- .../igniteui-angular/src/lib/navbar/navbar.component.ts | 7 ++++--- .../src/lib/navigation-drawer/navigation-drawer.module.ts | 3 +++ .../src/lib/progressbar/progressbar.component.ts | 3 +-- .../igniteui-angular/src/lib/slider/slider.component.ts | 7 +++---- .../src/lib/snackbar/snackbar.component.ts | 3 ++- .../igniteui-angular/src/lib/switch/switch.component.ts | 3 ++- .../igniteui-angular/src/lib/tabbar/tabbar.component.ts | 4 ++-- projects/igniteui-angular/src/lib/tabs/tabs.component.ts | 6 ++---- .../src/lib/time-picker/time-picker.component.ts | 2 +- projects/igniteui-angular/src/lib/toast/toast.component.ts | 3 ++- 44 files changed, 99 insertions(+), 42 deletions(-) diff --git a/projects/igniteui-angular/src/lib/avatar/avatar.component.ts b/projects/igniteui-angular/src/lib/avatar/avatar.component.ts index 4ee0d2ddcae..45569b6a181 100644 --- a/projects/igniteui-angular/src/lib/avatar/avatar.component.ts +++ b/projects/igniteui-angular/src/lib/avatar/avatar.component.ts @@ -356,8 +356,9 @@ export class IgxAvatarComponent implements OnInit, AfterViewInit { return `url(${this.src})`; } } + /** - * The `IgxAvatarModule` provides the {@link IgxAvatarComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxAvatarComponent], diff --git a/projects/igniteui-angular/src/lib/badge/badge.component.ts b/projects/igniteui-angular/src/lib/badge/badge.component.ts index 668672d1dcf..f2d4d24be48 100644 --- a/projects/igniteui-angular/src/lib/badge/badge.component.ts +++ b/projects/igniteui-angular/src/lib/badge/badge.component.ts @@ -182,9 +182,8 @@ export class IgxBadgeComponent { } /** - * The IgxBadgeComponent provides the {@link IgxBadgeComponent} inside your application. + * @hidden */ - @NgModule({ declarations: [IgxBadgeComponent], exports: [IgxBadgeComponent], diff --git a/projects/igniteui-angular/src/lib/banner/banner.component.ts b/projects/igniteui-angular/src/lib/banner/banner.component.ts index 8c6ed056525..e6a9ee519a6 100644 --- a/projects/igniteui-angular/src/lib/banner/banner.component.ts +++ b/projects/igniteui-angular/src/lib/banner/banner.component.ts @@ -258,6 +258,10 @@ export class IgxBannerComponent implements IToggleView { this.onClosed.emit(this._bannerEvent); } } + +/** + * @hidden + */ @NgModule({ declarations: [IgxBannerComponent, IgxBannerActionsDirective], exports: [IgxBannerComponent, IgxBannerActionsDirective], diff --git a/projects/igniteui-angular/src/lib/buttonGroup/buttonGroup.component.ts b/projects/igniteui-angular/src/lib/buttonGroup/buttonGroup.component.ts index c671ac5f36f..0a996e33f75 100644 --- a/projects/igniteui-angular/src/lib/buttonGroup/buttonGroup.component.ts +++ b/projects/igniteui-angular/src/lib/buttonGroup/buttonGroup.component.ts @@ -412,7 +412,7 @@ export interface IButtonGroupEventArgs { } /** - * The IgxButtonGroupModule provides the {@link IgxButtonGroupComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxButtonGroupComponent], diff --git a/projects/igniteui-angular/src/lib/calendar/calendar.module.ts b/projects/igniteui-angular/src/lib/calendar/calendar.module.ts index 761072dd774..cdad80cbc77 100644 --- a/projects/igniteui-angular/src/lib/calendar/calendar.module.ts +++ b/projects/igniteui-angular/src/lib/calendar/calendar.module.ts @@ -11,6 +11,9 @@ import { IgxCalendarYearDirective } from './calendar.directives'; +/** + * @hidden + */ @NgModule({ declarations: [ IgxCalendarComponent, diff --git a/projects/igniteui-angular/src/lib/card/card.component.ts b/projects/igniteui-angular/src/lib/card/card.component.ts index 0e60594c291..32ab063620b 100644 --- a/projects/igniteui-angular/src/lib/card/card.component.ts +++ b/projects/igniteui-angular/src/lib/card/card.component.ts @@ -89,8 +89,9 @@ export class IgxCardComponent { @Input() public id = `igx-card-${NEXT_ID++}`; } + /** - * The `IgxCardModule` provides the {@link IgxCardComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxCardComponent, IgxCardHeaderDirective, diff --git a/projects/igniteui-angular/src/lib/carousel/carousel.component.ts b/projects/igniteui-angular/src/lib/carousel/carousel.component.ts index 32ac39db074..365964287b1 100644 --- a/projects/igniteui-angular/src/lib/carousel/carousel.component.ts +++ b/projects/igniteui-angular/src/lib/carousel/carousel.component.ts @@ -562,8 +562,9 @@ export interface ISlideEventArgs { carousel: IgxCarouselComponent; slide: IgxSlideComponent; } + /** - * The `IgxCarouselModule` provides the {@link IgxCarouselComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxCarouselComponent, IgxSlideComponent], diff --git a/projects/igniteui-angular/src/lib/checkbox/checkbox.component.ts b/projects/igniteui-angular/src/lib/checkbox/checkbox.component.ts index b9ddd5c1d3e..54a101322f5 100644 --- a/projects/igniteui-angular/src/lib/checkbox/checkbox.component.ts +++ b/projects/igniteui-angular/src/lib/checkbox/checkbox.component.ts @@ -403,8 +403,9 @@ export const IGX_CHECKBOX_REQUIRED_VALIDATOR: Provider = { providers: [IGX_CHECKBOX_REQUIRED_VALIDATOR] }) export class IgxCheckboxRequiredDirective extends CheckboxRequiredValidator { } + /** - *The IgxCheckboxModule provides the {@link IgxCheckboxComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxCheckboxComponent, IgxCheckboxRequiredDirective], diff --git a/projects/igniteui-angular/src/lib/core/navigation/directives.ts b/projects/igniteui-angular/src/lib/core/navigation/directives.ts index 2022bc5a318..ae1702fb06a 100644 --- a/projects/igniteui-angular/src/lib/core/navigation/directives.ts +++ b/projects/igniteui-angular/src/lib/core/navigation/directives.ts @@ -51,6 +51,9 @@ export class IgxNavigationCloseDirective { } } +/** + * @hidden + */ @NgModule({ declarations: [IgxNavigationCloseDirective, IgxNavigationToggleDirective], exports: [IgxNavigationCloseDirective, IgxNavigationToggleDirective], 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 5a1e8b2fa17..7865190fc44 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 @@ -696,7 +696,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi } /** - * The IgxDatePickerModule provides the {@link IgxDatePickerComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxDatePickerComponent, IgxDatePickerTemplateDirective], diff --git a/projects/igniteui-angular/src/lib/dialog/dialog.component.ts b/projects/igniteui-angular/src/lib/dialog/dialog.component.ts index c4fa941ca64..d5d19da21e1 100644 --- a/projects/igniteui-angular/src/lib/dialog/dialog.component.ts +++ b/projects/igniteui-angular/src/lib/dialog/dialog.component.ts @@ -480,9 +480,8 @@ export interface IDialogEventArgs { } /** - * The IgxDialogComponent provides {@link IgxDialogComponent} inside your application. + * @hidden */ - @NgModule({ declarations: [IgxDialogComponent, IgxDialogTitleDirective, IgxDialogActionsDirective], exports: [IgxDialogComponent, IgxDialogTitleDirective, IgxDialogActionsDirective], diff --git a/projects/igniteui-angular/src/lib/directives/autocomplete/autocomplete.directive.ts b/projects/igniteui-angular/src/lib/directives/autocomplete/autocomplete.directive.ts index d5fe0973d37..e207b94a253 100644 --- a/projects/igniteui-angular/src/lib/directives/autocomplete/autocomplete.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/autocomplete/autocomplete.directive.ts @@ -364,6 +364,9 @@ export class IgxAutocompleteDirective extends IgxDropDownItemNavigationDirective } } +/** + * @hidden + */ @NgModule({ imports: [IgxDropDownModule, CommonModule], declarations: [IgxAutocompleteDirective], diff --git a/projects/igniteui-angular/src/lib/directives/button/button.directive.ts b/projects/igniteui-angular/src/lib/directives/button/button.directive.ts index 58aa4b84924..b9886a75ab8 100644 --- a/projects/igniteui-angular/src/lib/directives/button/button.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/button/button.directive.ts @@ -129,8 +129,9 @@ export class IgxButtonDirective { this.buttonClick.emit(ev); } } + /** - * The IgxButtonModule provides the {@link IgxButtonDirective} inside your application. + * @hidden */ @NgModule({ declarations: [IgxButtonDirective], diff --git a/projects/igniteui-angular/src/lib/directives/dragdrop/dragdrop.directive.ts b/projects/igniteui-angular/src/lib/directives/dragdrop/dragdrop.directive.ts index 502be6d07ad..0eeb0113366 100644 --- a/projects/igniteui-angular/src/lib/directives/dragdrop/dragdrop.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/dragdrop/dragdrop.directive.ts @@ -943,7 +943,7 @@ export class IgxDropDirective implements OnInit, OnDestroy { /** - * The IgxDragDropModule provides the {@link IgxDragDirective}, {@link IgxDropDirective} inside your application. + * @hidden */ @NgModule({ declarations: [IgxDragDirective, IgxDropDirective], diff --git a/projects/igniteui-angular/src/lib/directives/filter/filter.directive.ts b/projects/igniteui-angular/src/lib/directives/filter/filter.directive.ts index eb1019fe24a..62ed397c59e 100644 --- a/projects/igniteui-angular/src/lib/directives/filter/filter.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/filter/filter.directive.ts @@ -159,7 +159,9 @@ export class IgxFilterPipe implements PipeTransform { } } - +/** + * @hidden + */ @NgModule({ declarations: [IgxFilterDirective, IgxFilterPipe], exports: [IgxFilterDirective, IgxFilterPipe], diff --git a/projects/igniteui-angular/src/lib/directives/focus/focus.directive.ts b/projects/igniteui-angular/src/lib/directives/focus/focus.directive.ts index 15c094e391a..4307065eb64 100644 --- a/projects/igniteui-angular/src/lib/directives/focus/focus.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/focus/focus.directive.ts @@ -67,8 +67,9 @@ export class IgxFocusDirective { } } } + /** - * The IgxFocusModule provides the {@link IgxFocusDirective} inside your application. + * @hidden */ @NgModule({ declarations: [IgxFocusDirective], diff --git a/projects/igniteui-angular/src/lib/directives/for-of/for_of.directive.ts b/projects/igniteui-angular/src/lib/directives/for-of/for_of.directive.ts index 0d0daabacf5..d6e6ba255d0 100644 --- a/projects/igniteui-angular/src/lib/directives/for-of/for_of.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/for-of/for_of.directive.ts @@ -1411,10 +1411,10 @@ export class IgxGridForOfDirective extends IgxForOfDirective implements On this._updateViews(prevChunkSize); } } + /** - * The IgxForOfModule provides the {@link IgxForOfDirective}, inside your application. + * @hidden */ - @NgModule({ declarations: [IgxForOfDirective, IgxGridForOfDirective, DisplayContainerComponent, VirtualHelperComponent, HVirtualHelperComponent], entryComponents: [DisplayContainerComponent, VirtualHelperComponent, HVirtualHelperComponent], diff --git a/projects/igniteui-angular/src/lib/directives/layout/layout.directive.ts b/projects/igniteui-angular/src/lib/directives/layout/layout.directive.ts index 95cbdad3738..616f8f8b8b6 100644 --- a/projects/igniteui-angular/src/lib/directives/layout/layout.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/layout/layout.directive.ts @@ -224,6 +224,9 @@ export class IgxFlexDirective { } } +/** + * @hidden + */ @NgModule({ declarations: [IgxFlexDirective, IgxLayoutDirective], exports: [IgxFlexDirective, IgxLayoutDirective] diff --git a/projects/igniteui-angular/src/lib/directives/mask/mask.directive.ts b/projects/igniteui-angular/src/lib/directives/mask/mask.directive.ts index 4b0ac45b2d0..a0210f6a02d 100644 --- a/projects/igniteui-angular/src/lib/directives/mask/mask.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/mask/mask.directive.ts @@ -367,6 +367,10 @@ export interface IMaskEventArgs { rawValue: string; formattedValue: string; } + +/** + * @hidden + */ @NgModule({ declarations: [IgxMaskDirective], exports: [IgxMaskDirective], diff --git a/projects/igniteui-angular/src/lib/directives/radio/radio-group.directive.ts b/projects/igniteui-angular/src/lib/directives/radio/radio-group.directive.ts index ebe47b070dc..168e13d1b78 100644 --- a/projects/igniteui-angular/src/lib/directives/radio/radio-group.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/radio/radio-group.directive.ts @@ -354,7 +354,7 @@ export class IgxRadioGroupDirective implements AfterContentInit, ControlValueAcc } /** - *The IgxRadioModule provides the {@link IgxRadioGroupDirective} and {@link IgxRadioComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxRadioGroupDirective, IgxRadioComponent], diff --git a/projects/igniteui-angular/src/lib/directives/ripple/ripple.directive.ts b/projects/igniteui-angular/src/lib/directives/ripple/ripple.directive.ts index b4cf23ca09f..c2880f74e95 100644 --- a/projects/igniteui-angular/src/lib/directives/ripple/ripple.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/ripple/ripple.directive.ts @@ -160,8 +160,9 @@ export class IgxRippleDirective { } } + /** - * The IgxRippleModule provides the {@link IgxRippleDirective} inside your application. + * @hidden */ @NgModule({ declarations: [IgxRippleDirective], diff --git a/projects/igniteui-angular/src/lib/directives/text-highlight/text-highlight.directive.ts b/projects/igniteui-angular/src/lib/directives/text-highlight/text-highlight.directive.ts index ac396ef2dcc..318ec2852fd 100644 --- a/projects/igniteui-angular/src/lib/directives/text-highlight/text-highlight.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/text-highlight/text-highlight.directive.ts @@ -494,6 +494,9 @@ export class IgxTextHighlightDirective implements AfterViewInit, AfterViewChecke } } +/** + * @hidden + */ @NgModule({ declarations: [IgxTextHighlightDirective], exports: [IgxTextHighlightDirective] diff --git a/projects/igniteui-angular/src/lib/directives/text-selection/text-selection.directive.ts b/projects/igniteui-angular/src/lib/directives/text-selection/text-selection.directive.ts index 799a4e92bd0..efb3bb0359d 100644 --- a/projects/igniteui-angular/src/lib/directives/text-selection/text-selection.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/text-selection/text-selection.directive.ts @@ -113,6 +113,9 @@ export class IgxTextSelectionDirective { } } +/** + * @hidden + */ @NgModule({ declarations: [IgxTextSelectionDirective], exports: [IgxTextSelectionDirective] diff --git a/projects/igniteui-angular/src/lib/directives/toggle/toggle.directive.ts b/projects/igniteui-angular/src/lib/directives/toggle/toggle.directive.ts index 6ffc2e72560..b1cb3707c03 100644 --- a/projects/igniteui-angular/src/lib/directives/toggle/toggle.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/toggle/toggle.directive.ts @@ -443,6 +443,9 @@ export class IgxOverlayOutletDirective { } } +/** + * @hidden + */ @NgModule({ declarations: [IgxToggleDirective, IgxToggleActionDirective, IgxOverlayOutletDirective], exports: [IgxToggleDirective, IgxToggleActionDirective, IgxOverlayOutletDirective], diff --git a/projects/igniteui-angular/src/lib/directives/tooltip/tooltip.directive.ts b/projects/igniteui-angular/src/lib/directives/tooltip/tooltip.directive.ts index f2b29a0677d..ab35fb0508b 100644 --- a/projects/igniteui-angular/src/lib/directives/tooltip/tooltip.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/tooltip/tooltip.directive.ts @@ -534,6 +534,9 @@ export class IgxTooltipDirective extends IgxToggleDirective { } } +/** + * @hidden + */ @NgModule({ declarations: [IgxTooltipDirective, IgxTooltipTargetDirective], exports: [IgxTooltipDirective, IgxTooltipTargetDirective], diff --git a/projects/igniteui-angular/src/lib/drop-down/index.ts b/projects/igniteui-angular/src/lib/drop-down/index.ts index 68131bfab70..f7fc9da50a9 100644 --- a/projects/igniteui-angular/src/lib/drop-down/index.ts +++ b/projects/igniteui-angular/src/lib/drop-down/index.ts @@ -15,7 +15,9 @@ export * from './drop-down.base'; export * from './drop-down-item.base'; export * from './drop-down-group.component'; - +/** + * @hidden + */ @NgModule({ declarations: [IgxDropDownComponent, IgxDropDownItemComponent, IgxDropDownGroupComponent, IgxDropDownItemNavigationDirective], exports: [IgxDropDownComponent, IgxDropDownItemComponent, IgxDropDownGroupComponent, IgxDropDownItemNavigationDirective], diff --git a/projects/igniteui-angular/src/lib/grids/column-hiding.component.ts b/projects/igniteui-angular/src/lib/grids/column-hiding.component.ts index 2d1693484cb..4a228f9207e 100644 --- a/projects/igniteui-angular/src/lib/grids/column-hiding.component.ts +++ b/projects/igniteui-angular/src/lib/grids/column-hiding.component.ts @@ -179,8 +179,9 @@ export class IgxColumnHidingComponent extends ColumnChooserBase implements OnDes this.destroy$.complete(); } } + /** - *The `IgxColumnHidingModule` provides the {@link IgxColumnHidingComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxColumnHidingComponent, IgxColumnHidingItemDirective], diff --git a/projects/igniteui-angular/src/lib/grids/column-pinning.component.ts b/projects/igniteui-angular/src/lib/grids/column-pinning.component.ts index 98a6bb2000e..ad744193f6c 100644 --- a/projects/igniteui-angular/src/lib/grids/column-pinning.component.ts +++ b/projects/igniteui-angular/src/lib/grids/column-pinning.component.ts @@ -69,7 +69,7 @@ export class IgxColumnPinningComponent extends ColumnChooserBase { } /** - * The IgxColumnPinningModule provides the {@link IgxColumnPinningComponent}, {@link IgxColumnPinningItemDirective} inside your application. + * @hidden */ @NgModule({ declarations: [IgxColumnPinningComponent, IgxColumnPinningItemDirective], diff --git a/projects/igniteui-angular/src/lib/grids/grid-common.module.ts b/projects/igniteui-angular/src/lib/grids/grid-common.module.ts index 0af15e63db7..f451a880153 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-common.module.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-common.module.ts @@ -63,6 +63,9 @@ import { IgxSummaryDataPipe } from './summaries/grid-root-summary.pipe'; import { IgxGridSummaryService } from './summaries/grid-summary.service'; import { IgxProgressBarModule } from '../progressbar/progressbar.component'; +/** + * @hidden + */ @NgModule({ declarations: [ IgxGridCellComponent, diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts index 8d09e8d7b64..0f146948d03 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.module.ts @@ -12,6 +12,9 @@ import { IgxChildGridRowComponent } from './child-grid-row.component'; import { IgxHierarchicalGridCellComponent } from './hierarchical-cell.component'; import { IgxHierarchicalSelectionAPIService } from './selection'; +/** + * @hidden + */ @NgModule({ declarations: [ IgxHierarchicalGridComponent, diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts index 4dc6f8c60ac..2771ccbffa9 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts @@ -11,6 +11,9 @@ import { IgxTreeGridCellComponent } from './tree-cell.component'; import { IgxTreeGridFilteringPipe } from './tree-grid.filtering.pipe'; import { IgxTreeGridSummaryPipe } from './tree-grid.summary.pipe'; +/** + * @hidden + */ @NgModule({ declarations: [ IgxTreeGridComponent, diff --git a/projects/igniteui-angular/src/lib/icon/index.ts b/projects/igniteui-angular/src/lib/icon/index.ts index ba4f43501a8..2880b16e85b 100644 --- a/projects/igniteui-angular/src/lib/icon/index.ts +++ b/projects/igniteui-angular/src/lib/icon/index.ts @@ -5,6 +5,9 @@ import { IgxIconComponent } from './icon.component'; import { HttpClientModule } from '@angular/common/http'; import { DeprecateMethod } from '../core/deprecateDecorators'; +/** + * @hidden + */ @NgModule({ declarations: [IgxIconComponent], exports: [IgxIconComponent], diff --git a/projects/igniteui-angular/src/lib/input-group/input-group.component.ts b/projects/igniteui-angular/src/lib/input-group/input-group.component.ts index 9f5532f5f5a..96a08af708c 100644 --- a/projects/igniteui-angular/src/lib/input-group/input-group.component.ts +++ b/projects/igniteui-angular/src/lib/input-group/input-group.component.ts @@ -357,7 +357,7 @@ export class IgxInputGroupComponent extends DisplayDensityBase implements IgxInp } /** - * The IgxInputGroupModule provides the {@link IgxInputGroupComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxInputGroupComponent, IgxHintDirective, IgxInputDirective, IgxLabelDirective], diff --git a/projects/igniteui-angular/src/lib/list/list.component.ts b/projects/igniteui-angular/src/lib/list/list.component.ts index a2e638f7226..132720564d8 100644 --- a/projects/igniteui-angular/src/lib/list/list.component.ts +++ b/projects/igniteui-angular/src/lib/list/list.component.ts @@ -355,7 +355,7 @@ export class IgxListComponent implements IgxListBase { } /** - * The IgxListModule provides the {@link IgxListComponent} and the {@link IgxListItemComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxListComponent, IgxListItemComponent, diff --git a/projects/igniteui-angular/src/lib/navbar/navbar.component.ts b/projects/igniteui-angular/src/lib/navbar/navbar.component.ts index da9ca2230f0..6c26de2e8fa 100644 --- a/projects/igniteui-angular/src/lib/navbar/navbar.component.ts +++ b/projects/igniteui-angular/src/lib/navbar/navbar.component.ts @@ -137,9 +137,10 @@ export class IgxNavbarComponent { this.onAction.emit(this); } } - /** - *The IgxNavbarModule provides the {@link IgxNavbarComponent} inside your application. - */ + +/** + * @hidden + */ @NgModule({ declarations: [IgxNavbarComponent, IgxActionIconDirective], exports: [IgxNavbarComponent, IgxActionIconDirective], diff --git a/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.module.ts b/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.module.ts index cb2a7ed0618..9f4b3b648b8 100644 --- a/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.module.ts +++ b/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.module.ts @@ -7,6 +7,9 @@ import { IgxNavDrawerTemplateDirective } from './navigation-drawer.directives'; +/** + * @hidden + */ @NgModule({ declarations: [ IgxNavigationDrawerComponent, diff --git a/projects/igniteui-angular/src/lib/progressbar/progressbar.component.ts b/projects/igniteui-angular/src/lib/progressbar/progressbar.component.ts index bc02e2f8137..819a2119c0f 100644 --- a/projects/igniteui-angular/src/lib/progressbar/progressbar.component.ts +++ b/projects/igniteui-angular/src/lib/progressbar/progressbar.component.ts @@ -752,8 +752,7 @@ export function convertInPercentage(value: number, max: number) { } /** - * The IgxProgressBarModule provides the {@link IgxLinearProgressBarComponent}, - * {@link IgxCircularProgressBarComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxLinearProgressBarComponent, IgxCircularProgressBarComponent, IgxProcessBarTextTemplateDirective], diff --git a/projects/igniteui-angular/src/lib/slider/slider.component.ts b/projects/igniteui-angular/src/lib/slider/slider.component.ts index 3f156ccc067..aa7edae5f31 100644 --- a/projects/igniteui-angular/src/lib/slider/slider.component.ts +++ b/projects/igniteui-angular/src/lib/slider/slider.component.ts @@ -928,10 +928,9 @@ export class IgxSliderComponent implements ControlValueAccessor, EditorProvider, } } - /** - *The IgxSliderModule provides the {@link IgxSliderComponent} inside your application. - */ - +/** + * @hidden + */ @NgModule({ declarations: [IgxSliderComponent], exports: [IgxSliderComponent], diff --git a/projects/igniteui-angular/src/lib/snackbar/snackbar.component.ts b/projects/igniteui-angular/src/lib/snackbar/snackbar.component.ts index ab3c0e7a23e..db083db6e9b 100644 --- a/projects/igniteui-angular/src/lib/snackbar/snackbar.component.ts +++ b/projects/igniteui-angular/src/lib/snackbar/snackbar.component.ts @@ -240,8 +240,9 @@ export class IgxSnackbarComponent { } } } + /** - *The IgxSnackbarModule provides the {@link IgxSnackbarComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxSnackbarComponent], diff --git a/projects/igniteui-angular/src/lib/switch/switch.component.ts b/projects/igniteui-angular/src/lib/switch/switch.component.ts index 633971c602a..e8a3905b4e4 100644 --- a/projects/igniteui-angular/src/lib/switch/switch.component.ts +++ b/projects/igniteui-angular/src/lib/switch/switch.component.ts @@ -365,8 +365,9 @@ export const IGX_SWITCH_REQUIRED_VALIDATOR: Provider = { providers: [IGX_SWITCH_REQUIRED_VALIDATOR] }) export class IgxSwitchRequiredDirective extends CheckboxRequiredValidator { } + /** - * The IgxSwitchModule provides the {@link IgxSwitchComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxSwitchComponent, IgxSwitchRequiredDirective], diff --git a/projects/igniteui-angular/src/lib/tabbar/tabbar.component.ts b/projects/igniteui-angular/src/lib/tabbar/tabbar.component.ts index 7c850919898..e494eed6684 100644 --- a/projects/igniteui-angular/src/lib/tabbar/tabbar.component.ts +++ b/projects/igniteui-angular/src/lib/tabbar/tabbar.component.ts @@ -449,9 +449,9 @@ export class IgxTabComponent { this.relatedPanel.select(); } } + /** - * The IgxBottomNavModule provides the {@link IgxBottomNavComponent}, - * the {@link IgxTabPanelComponent} and the {@link IgxTabComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxBottomNavComponent, IgxTabPanelComponent, IgxTabComponent, IgxTabTemplateDirective], diff --git a/projects/igniteui-angular/src/lib/tabs/tabs.component.ts b/projects/igniteui-angular/src/lib/tabs/tabs.component.ts index d1875127eca..fd3ffbb2d03 100644 --- a/projects/igniteui-angular/src/lib/tabs/tabs.component.ts +++ b/projects/igniteui-angular/src/lib/tabs/tabs.component.ts @@ -336,10 +336,8 @@ export class IgxTabsComponent implements IgxTabsBase, AfterViewInit, OnDestroy { } /** -* The IgxTabsModule provides the {@link IgxTabsComponent}, {@link IgxTabsGroupComponent}, -*{@link IgxTabItemComponent}, {@link IgxTabItemTemplateDirective}, {@link IgxRightButtonStyleDirective} -* and {@link IgxLeftButtonStyleDirective} inside your application. -*/ + * @hidden + */ @NgModule({ declarations: [IgxTabsComponent, IgxTabsGroupComponent, diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts index 973d9660811..a14c0dedfa7 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts @@ -1624,7 +1624,7 @@ export class IgxTimePickerComponent implements } /** - * The IgxTimePickerModule provides the {@link IgxTimePickerComponent} inside your application. + * @hidden */ @NgModule({ declarations: [ diff --git a/projects/igniteui-angular/src/lib/toast/toast.component.ts b/projects/igniteui-angular/src/lib/toast/toast.component.ts index bda0ed2a1e6..ca0ffb181d1 100644 --- a/projects/igniteui-angular/src/lib/toast/toast.component.ts +++ b/projects/igniteui-angular/src/lib/toast/toast.component.ts @@ -330,8 +330,9 @@ export enum IgxToastPosition { Middle, Top } + /** - * The IgxToastModule provides the {@link IgxToastComponent} inside your application. + * @hidden */ @NgModule({ declarations: [IgxToastComponent], From 07c7181b298d7dd41b35eb5dd30f58ab86027189 Mon Sep 17 00:00:00 2001 From: NikolayAlipiev Date: Mon, 18 Feb 2019 17:17:14 +0200 Subject: [PATCH 2/3] docs(*): hide new IgxMonthPickerModule --- .../src/lib/month-picker/month-picker.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/igniteui-angular/src/lib/month-picker/month-picker.component.ts b/projects/igniteui-angular/src/lib/month-picker/month-picker.component.ts index 107fb9687cb..487bfb726c8 100644 --- a/projects/igniteui-angular/src/lib/month-picker/month-picker.component.ts +++ b/projects/igniteui-angular/src/lib/month-picker/month-picker.component.ts @@ -220,6 +220,7 @@ export class IgxMonthPickerComponent extends IgxCalendarComponent { } } +/** @hidden */ @NgModule({ declarations: [IgxMonthPickerComponent], exports: [IgxMonthPickerComponent], From a311c0c75bd9d1fe06d118eb195932bed7b6bbc6 Mon Sep 17 00:00:00 2001 From: Svetla Boykova Date: Wed, 20 Feb 2019 09:36:42 +0200 Subject: [PATCH 3/3] editable date picker (#3583) * feat(date-picker): Initial changes to support edit field #3034 * refactor(themes): update date-picker theme after refactor * feat(date-picker): Fixes in editable date-picker #3034 * feat(date-picker): Implementing edit mode #3034 * feat(date-picker): More changes #3034 * feat(date-picker): Added spinning of the date parts #3034 * feat(date-picker): Added spinning functionality and other fixes #3034 * feat(date-picker): Spinning issues fixes #3034 * chore(*): Some code changes * feat(date-picker): Resolved merged changes #3034 * feat(date-picker): Reworked parts in editable mode #3034 * fix(igxCalendar): add trackBy for months and years, #3595 If calendar is shown in overlay, the overlay saves a reference to calendar elementRef. Month and Year templates add click event via host listener. However, both templates redraw themselves. As a result of this elements, where the click event was added, are lost. As a result click does not fire. To fix this we added trackBy in ngFor both for Month and Year templates. * feat(date-picker): Added spinning on mouse wheel #3583 * feat(date-picker): Added more functionality #3583 * feat(date-picker): Fixes in editing and spinning date parts #3583 * feat(date-picker): Added infinite loop when spinning #3583 * feat(date-picker): Fixes in editable date-picker #3583 * feat(date-picker): Fixes in edit mode #3583 * feat(date-picker): Fixes, refactoring #3583 * feat(date-picker): Disabled/special dates handling #3583 * feat(date-picker): Fixes in editable mode #3583 * feat(date-picker): Fixes in tests #3583 * feat(date-picker): Calendar and buttons added to new component #3583 * feat(date-picker): Fixed spinning of long months names #3583 * feat(date-picker): Fixed existing tests, refactoring #3583 * feat(date-picker): Reworked code, overlay settings exposed #3583 * feat(date-picker): Fixed styles bindings #3583 * fix(date-picker): Fixes in edit mode #3583 * fix(date-picker): Fixes, added tests for edit mode #3583 * fix(date-picker): Added alt+up key behavior, added tests #3583 * fix(date-picker): Fixed check for days in month #3583 * fix(date-picker): Fixes in edit mode #3583 * fix(date-picker): Changed date-picker mode in grid #3583 * fix(date-picker): Fixes in code and tests #3583 * feat(date-picker): Added some API comments and fixes #3583 * feat(date-picker): Added info in docs, fixed method name #3583 * fix(igx-grid): Add filter-row date-picker in grid's outlet, #3034 * feat(date-picker): fix broken behaviors after calendar merge #3583 * fix(date-picker): Requested changes done #3583 * fix(date-picker): Fixes in date-picker #3583 * fix(date-picker): Fixes in code #3583 * fix(date-picker): Fix in calendar opening #3583 * fix(grid): Enabled key down event for date-picker editor #3583 * fix(grid): failing tests #3583 * fix(grid): Removed not needed brackets #3583 * fix(grid): Removed unused openDialog event argument #3583 * fix(date-picker): Stopped propagation of spinning events #3583 * fix(date-picker): Fixed empty mask #3583 * fix(grid): failing test for locale #3583 * chore(*): try fixing flickering test #3583 * test(date-picker): Added new tests, fixes #3583 * test(date-picker): Fixed failing focus test #3583 * test(grid): remove detectChanges #3583 * test(date-picker): Added tests for editable date-picker #3583 * test(grid): enter edit mode -> update -> exit edit mode -> check #3583 * test(grid): use API calls to methods instead of using shift+tab #3583 * test(date-picker): Added more tests for editable date picker #3583 --- CHANGELOG.md | 6 + .../src/lib/calendar/calendar.component.html | 46 +- .../src/lib/calendar/calendar.component.ts | 9 +- .../calendar/days-view/days-view.component.ts | 14 +- .../months-view/months-view.component.html | 2 +- .../months-view/months-view.component.ts | 8 + .../years-view/years-view.component.html | 2 +- .../years-view/years-view.component.ts | 7 + .../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 - .../src/lib/date-picker/README.md | 24 +- .../calendar-container.component.html | 10 + .../calendar-container.component.ts | 76 ++ .../src/lib/date-picker/date-picker.common.ts | 12 + .../date-picker/date-picker.component.html | 45 +- .../date-picker/date-picker.component.spec.ts | 620 +++++++++++- .../lib/date-picker/date-picker.component.ts | 935 ++++++++++++++---- .../lib/date-picker/date-picker.directives.ts | 14 + .../src/lib/date-picker/date-picker.pipes.ts | 44 + .../src/lib/date-picker/date-picker.utils.ts | 556 +++++++++++ .../src/lib/grids/cell.component.html | 19 +- .../src/lib/grids/cell.component.ts | 11 +- .../grid-filtering-row.component.html | 2 +- .../lib/grids/grid/grid-filtering-ui.spec.ts | 76 +- .../src/lib/grids/grid/grid.component.spec.ts | 388 ++++---- .../hierarchical-grid.navigation.spec.ts | 4 +- .../src/lib/services/overlay/overlay.spec.ts | 3 +- projects/igniteui-angular/src/public_api.ts | 1 + src/app/date-picker/date-picker.sample.html | 38 +- src/app/date-picker/date-picker.sample.ts | 64 +- 35 files changed, 2602 insertions(+), 611 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 create mode 100644 projects/igniteui-angular/src/lib/date-picker/calendar-container.component.html create mode 100644 projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts 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.directives.ts create mode 100644 projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts create mode 100644 projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 42de7511a43..45b9d19fb9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -104,6 +104,12 @@ All notable changes for each version of this project will be documented in this - `igxOverlay.attach(component, settings?)` method added. Use this method to obtain an unique Id of the created overlay where the provided component will be shown. Then call `igxOverlay.show(id, settings?)` method to show the component in overlay. - `igxOverlay.show(component, settings)` is **depricated**. Use `igxOverlay.attach(component, settings?)` method to obtain an Id, and then call `igxOverlay.show(id, settings)` method to show a component in the overlay. +- `igx-date-picker` + - **Feature** Added editable `mode` to enable the input field value editing and spinning of the date parts. Example: + ```html + + + ``` **Components roundness** - Ignite UI for Angular now allows you to change the shape of components by changing their border-radius. diff --git a/projects/igniteui-angular/src/lib/calendar/calendar.component.html b/projects/igniteui-angular/src/lib/calendar/calendar.component.html index ead1b7594e9..7eb8f05efeb 100644 --- a/projects/igniteui-angular/src/lib/calendar/calendar.component.html +++ b/projects/igniteui-angular/src/lib/calendar/calendar.component.html @@ -4,15 +4,17 @@ - + {{ formattedMonth(viewDate) }} - + {{ formattedYear(viewDate) }} -
+
{{ formattedYear(headerDate) }}

@@ -20,9 +22,11 @@

-
+
-
+
keyboard_arrow_left
@@ -34,31 +38,17 @@

- +
- + - - + + \ 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 bb63c41b7a3..96990db4e34 100644 --- a/projects/igniteui-angular/src/lib/calendar/calendar.component.ts +++ b/projects/igniteui-angular/src/lib/calendar/calendar.component.ts @@ -73,6 +73,9 @@ export class IgxCalendarComponent extends IgxDaysViewComponent { @Input() public id = `igx-calendar-${NEXT_ID++}`; + @Input() + public hasHeader = true; + /** * Gets whether the `day`, `month` and `year` should be rendered * according to the locale and formatOptions, if any. @@ -146,19 +149,19 @@ export class IgxCalendarComponent extends IgxDaysViewComponent { /** * @hidden */ - @ViewChild('decade', {read: IgxYearsViewComponent}) + @ViewChild('decade', { read: IgxYearsViewComponent }) public dacadeView: IgxYearsViewComponent; /** * @hidden */ - @ViewChild('months', {read: IgxMonthsViewComponent}) + @ViewChild('months', { read: IgxMonthsViewComponent }) public monthsView: IgxMonthsViewComponent; /** * @hidden */ - @ViewChild('days', {read: IgxDaysViewComponent}) + @ViewChild('days', { read: IgxDaysViewComponent }) public daysView: IgxDaysViewComponent; /** 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 */ 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 a43ba22ed97..47e5aaef974 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/_index.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/_index.scss @@ -165,7 +165,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 b86c91c73a1..05a033434a4 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 @@ -16,7 +16,6 @@ @import './chip'; @import './column-hiding'; @import './combo'; -@import './date-picker'; @import './dialog'; @import './drop-down'; @import './expansion-panel'; @@ -57,7 +56,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] @@ -98,7 +96,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 eb49679135c..a6f02805100 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/README.md b/projects/igniteui-angular/src/lib/date-picker/README.md index b2bf055892d..96849a80edd 100644 --- a/projects/igniteui-angular/src/lib/date-picker/README.md +++ b/projects/igniteui-angular/src/lib/date-picker/README.md @@ -59,6 +59,12 @@ The DatePicker also supports binding through `ngModel` if two-way date-bind is n ``` +The DatePicker has editable mode as well. Custom display format and editor mask can be configured by setting the `format` and `mask` properties. +```html + + +``` + The DatePicker input group could be retemplated. ```html @@ -84,14 +90,30 @@ The DatePicker input group could be retemplated. | `weekStart`| `Number \| WEEKDAYS` | Sets on which day will the week start. | | `locale` | `string` | Sets the locale used for formatting and displaying the dates in the calendar. For more information check out [this](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) page for valid formats. | | `formatOptions` | `Object` | The format options passed along with the `locale` property used for formatting the dates. | +| `label` | `string` | Configure the input label text. | +| `labelVisibility` | `string` | Configure the input label text visibility. | +| `format` | `string` | Configure the date display format when in edit mode. Accepts formats using the d, M and y symbols, custom separators and the following pre-defined format options - shortDate, mediumDate, longDate and fullDate. | +| `mask` | `string` | Configure the date editor mask. Accepts the d, M and y symbols as mask - for example dd-MM-y. | +| `disabledDates` | `DateRangeDescriptor[]` | Configure the disabled dates. | +| `specialDates` | `DateRangeDescriptor[]` | Configure the special dates. | +| `value` | `Date` | Configure the datePicker value and selected date in the calendar. | +| `vertical` | `boolean` | Configure the calendar mode - horizontal or vertical in read-only datePicker. | +| `mode` | `DatePickerInteractionMode` | Configure the datePicker input mode - READONLY or EDITABLE. In editable mode, the datePicker input is editable, in read-only mode - the input is not editable and popup appears to select a date.| +| `isSpinLoop` | `boolean` | Configure whether the date parts would spin continuously or stop when min/max value is reached in editable mode.| + ### Outputs | Name | Return Type | Description | |:--:|:---|:---| | `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. | +| `onOpen` | `datePicker` | Emitted when a datePicker calendar is being opened. | +| `onClose` | `datePicker` | Emitted when a datePicker calendar is being closed. | +| `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 | |:----------:|:------|:------|:------| | `selectDate` | `date: Date` | `void` | Change the calendar selection. Calling this method will emit the `onSelection` event. | +| `deselectDate` | `void` | Deselects the calendar date and clear input field value. | +| `triggerTodaySelection` | `void` | Selects today's date in calendar and change the input field value. | \ No newline at end of file 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 new file mode 100644 index 00000000000..f439f6e1880 --- /dev/null +++ b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.html @@ -0,0 +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 new file mode 100644 index 00000000000..e3d5f0a2abf --- /dev/null +++ b/projects/igniteui-angular/src/lib/date-picker/calendar-container.component.ts @@ -0,0 +1,76 @@ +import { Component, ViewChild, Input, Output, EventEmitter, HostListener, HostBinding } from '@angular/core'; +import { DatePickerInteractionMode, IgxCalendarComponent } from 'igniteui-angular'; + +/** + * @hidden + */ +@Component({ + selector: 'igx-calendar-container', + styles: [':host {display: block;}'], + templateUrl: 'calendar-container.component.html' +}) +export class IgxCalendarContainerComponent { + @ViewChild('calendar') + public calendar: IgxCalendarComponent; + + @Input() + public mode: DatePickerInteractionMode = DatePickerInteractionMode.READONLY; + + @Input() + public vertical = false; + + @Input() + public cancelButtonLabel: string; + + @Input() + public todayButtonLabel: string; + + @Output() + public onClose = new EventEmitter(); + + @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']) + @HostListener('keydown.alt.arrowup', ['$event']) + public onEscape(event) { + event.preventDefault(); + this.onClose.emit(); + } + + /** + * Returns whether the date-picker is in readonly mode. + * + * @hidden + */ + public get isReadonly() { + return this.mode === DatePickerInteractionMode.READONLY; + } + + /** + * Emits close event for the calendar. + */ + public closeCalendar() { + this.onClose.emit(); + } + + /** + * Emits today selection event for the calendar. + */ + public triggerTodaySelection() { + this.onTodaySelection.emit(); + } +} 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..c14a64bcf29 --- /dev/null +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.common.ts @@ -0,0 +1,12 @@ +/** @hidden */ +export const IGX_DATE_PICKER_COMPONENT = 'IgxDatePickerComponentToken'; + +/** @hidden */ +export interface IDatePicker { + value: Date; + mask: string; + inputMask: string; + rawDateString: string; + dateFormatParts: any[]; + invalidDate: 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 15863f1127f..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 @@ -1,16 +1,29 @@ -
- - - - today - - - - - - - - - -
+ + + + today + + + + + + + + + + today + + + + + clear + + + + + +
\ No newline at end of file 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 25699ebb5da..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 @@ -1,4 +1,4 @@ -import { Component, ViewChild, DebugElement } from '@angular/core'; +import { Component, ViewChild } from '@angular/core'; import { async, fakeAsync, TestBed, tick, flush, ComponentFixture } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; @@ -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(); @@ -22,11 +23,12 @@ describe('IgxDatePicker', () => { IgxDatePickerWithPassedDateComponent, IgxDatePickerWIthLocaleComponent, IgxDatePickerNgModelComponent, - IgxDatePickerRetemplatedComponent + IgxDatePickerRetemplatedComponent, + IgxDatePickerEditableComponent ], imports: [IgxDatePickerModule, FormsModule, NoopAnimationsModule, IgxInputGroupModule] }) - .compileComponents(); + .compileComponents(); })); afterEach(() => { @@ -45,7 +47,7 @@ describe('IgxDatePicker', () => { }); it('Initialize a datepicker component', () => { - expect(fixture.componentInstance).toBeDefined(); + expect(fixture.componentInstance).toBeDefined(); expect(datePicker.displayData).toBe(''); }); @@ -62,24 +64,28 @@ describe('IgxDatePicker', () => { expect(domDatePicker.id).toBe('customDatePicker'); }); - it('Datepicker open/close event', async() => { + it('Datepicker open/close event', async () => { const dom = fixture.debugElement; - const target = dom.query(By.css('.igx-date-picker__input-date')); spyOn(datePicker.onOpen, 'emit'); spyOn(datePicker.onClose, 'emit'); - target.nativeElement.dispatchEvent(new Event('click', { bubbles: true })); + UIInteractions.clickElement(target); fixture.detectChanges(); await wait(); expect(datePicker.onOpen.emit).toHaveBeenCalled(); expect(datePicker.onOpen.emit).toHaveBeenCalledWith(datePicker); - const overlay = dom.query(By.css('.igx-dialog')); - overlay.nativeElement.dispatchEvent(new Event('click', { bubbles: true })); - await wait(350); // destroy timeout... + const overlayDiv = document.getElementsByClassName('igx-overlay__wrapper--modal')[0]; + expect(overlayDiv).toBeDefined(); + expect(overlayDiv.classList.contains('igx-overlay__wrapper--modal')).toBeTruthy(); + overlayDiv.dispatchEvent(new Event('click')); + + fixture.detectChanges(); + await wait(); + expect(datePicker.onClose.emit).toHaveBeenCalled(); expect(datePicker.onClose.emit).toHaveBeenCalledWith(datePicker); }); @@ -134,47 +140,44 @@ describe('IgxDatePicker', () => { it('Handling keyboard navigation with `space`(open) and `esc`(close) buttons', fakeAsync(() => { const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker')); - let overlayToggle = document.getElementsByTagName('igx-toggle'); - expect(overlayToggle.length).toEqual(0); - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - flush(); fixture.detectChanges(); - overlayToggle = document.getElementsByClassName('igx-toggle'); - expect(overlayToggle[0]).not.toBeNull(); + const overlayDiv = document.getElementsByClassName('igx-overlay__wrapper--modal')[0]; + expect(overlayDiv).toBeDefined(); + expect(overlayDiv.classList.contains('igx-overlay__wrapper--modal')).toBeTruthy(); - UIInteractions.triggerKeyDownEvtUponElem('Escape', overlayToggle[0], true); + UIInteractions.triggerKeyDownEvtUponElem('Escape', overlayDiv, true); flush(); fixture.detectChanges(); - overlayToggle = document.getElementsByClassName('igx-toggle'); - expect(overlayToggle.length).toEqual(0); + const overlays = document.getElementsByClassName('igx-overlay__wrapper--modal'); + expect(overlays.length).toEqual(0); })); - it('When datepicker is closed and the dialog disappear the focus should remain on the input', - fakeAsync(() => { - const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker')); - let overlayToggle = document.getElementsByTagName('igx-toggle'); - expect(overlayToggle.length).toEqual(0); + it('When datepicker is closed and the dialog disappear, the focus should remain on the input', + fakeAsync(() => { + const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker')); + let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); + expect(overlayToggle.length).toEqual(0); - UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); - flush(); - fixture.detectChanges(); + UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false); + flush(); + fixture.detectChanges(); - overlayToggle = document.getElementsByClassName('igx-toggle'); - expect(overlayToggle[0]).not.toBeNull(); - expect(overlayToggle[0]).not.toBeUndefined(); + overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); + expect(overlayToggle[0]).not.toBeNull(); + expect(overlayToggle[0]).not.toBeUndefined(); - UIInteractions.triggerKeyDownEvtUponElem('Escape', overlayToggle[0], true); - flush(); - fixture.detectChanges(); + UIInteractions.triggerKeyDownEvtUponElem('Escape', overlayToggle[0], true); + flush(); + fixture.detectChanges(); - const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; - overlayToggle = document.getElementsByClassName('igx-toggle'); - expect(overlayToggle[0]).toEqual(undefined); - expect(input).toEqual(document.activeElement); - })); + const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; + overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal'); + expect(overlayToggle[0]).toEqual(undefined); + expect(input).toEqual(document.activeElement); + })); }); @@ -210,12 +213,11 @@ describe('IgxDatePicker', () => { it('Set formatOptions for month to be numeric', () => { const getMonthFromPickerDate = fixture.componentInstance.date.getMonth() + 1; - inputTarget.dispatchEvent(new Event('click', { bubbles: true })); fixture.detectChanges(); - const getMonthFromCalendarHeader: any = fixture.debugElement.query(By.css('.igx-calendar__header-date')).nativeElement - .children[1].innerText.substring(0, 1); + const headerDate = document.getElementsByClassName('igx-calendar__header-date')[0]; + const getMonthFromCalendarHeader = (headerDate.children[1] as HTMLElement).innerText.substring(0, 1); expect(parseInt(getMonthFromCalendarHeader, 10)).toBe(getMonthFromPickerDate); }); @@ -228,10 +230,10 @@ describe('IgxDatePicker', () => { const dom = fixture.debugElement; const datePickerTarget = dom.query(By.css('.igx-date-picker__input-date')); - datePickerTarget.nativeElement.dispatchEvent(new Event('click', { bubbles: true })); + UIInteractions.clickElement(datePickerTarget); fixture.detectChanges(); - const firstDayValue = dom.query(By.css('.igx-calendar__label')).nativeElement.innerText; + const firstDayValue = (document.getElementsByClassName('igx-calendar__label')[0] as HTMLElement).innerText.trim(); const expectedResult = 'Mon'; expect(firstDayValue).toBe(expectedResult); @@ -335,20 +337,530 @@ describe('IgxDatePicker', () => { expect(datePicker.value.getMilliseconds()).toBe(date.getMilliseconds()); }); - it('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')); - - target.nativeElement.dispatchEvent(new Event('click', { bubbles: true })); + UIInteractions.clickElement(target); fixture.detectChanges(); - await wait(); + tick(200); const todayDate = datePicker.calendar.daysView.dates.find(d => d.isToday); + expect(document.activeElement).toEqual(todayDate.nativeElement); + })); + + it('#3595 - Should be able to change year', fakeAsync(() => { + const fixture = TestBed.createComponent(IgxDatePickerTestComponent); + fixture.detectChanges(); + + const dom = fixture.debugElement; + const target = dom.query(By.css('.igx-date-picker__input-date')); + + UIInteractions.clickElement(target); + fixture.detectChanges(); + + let year = fixture.debugElement.nativeElement.getElementsByClassName('igx-calendar-picker__date')[1]; + year.dispatchEvent(new Event('click')); + tick(); + fixture.detectChanges(); + + const firstYear = document.getElementsByClassName('igx-calendar__year')[1]; + const expectedResult = (firstYear as HTMLElement).innerText; + firstYear.dispatchEvent(new Event('click')); + tick(); + fixture.detectChanges(); + + year = fixture.debugElement.nativeElement.getElementsByClassName('igx-calendar-picker__date')[1]; + expect(year.innerText).toBe(expectedResult); + })); + + it('#3595 - Should be able to change month', fakeAsync(() => { + const fixture = TestBed.createComponent(IgxDatePickerTestComponent); + fixture.detectChanges(); + + const dom = fixture.debugElement; + const target = dom.query(By.css('.igx-date-picker__input-date')); + UIInteractions.clickElement(target); + tick(200); + fixture.detectChanges(); + + let month = fixture.debugElement.nativeElement.getElementsByClassName('igx-calendar-picker__date')[0]; + month.dispatchEvent(new Event('click')); + tick(); + fixture.detectChanges(); + + const firstMonth = document.getElementsByClassName('igx-calendar__month')[1]; + const expectedResult = (firstMonth as HTMLElement).innerText; + + firstMonth.dispatchEvent(new Event('click')); + tick(); + fixture.detectChanges(); + + month = fixture.debugElement.nativeElement.getElementsByClassName('igx-calendar-picker__date')[0]; + 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(); + }); + + 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(); + + spyOn(datePicker.onOpen, 'emit'); + spyOn(datePicker.onClose, 'emit'); + + UIInteractions.clickElement(iconDate); + fixture.detectChanges(); + await wait(); + + expect(datePicker.onOpen.emit).toHaveBeenCalled(); + expect(datePicker.onOpen.emit).toHaveBeenCalledWith(datePicker); + + const dropDown = document.getElementsByClassName('igx-date-picker--dropdown'); + expect(dropDown.length).toBe(1); + expect(dropDown[0]).not.toBeNull(); + + dom.nativeElement.dispatchEvent(new Event('click')); + + fixture.detectChanges(); + await wait(); + + expect(datePicker.onClose.emit).toHaveBeenCalled(); + expect(datePicker.onClose.emit).toHaveBeenCalledWith(datePicker); + }); + + 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(); + + const overlayDiv = document.getElementsByClassName('igx-overlay__content')[0]; + expect(overlayDiv).toBeDefined(); + expect(overlayDiv.classList.contains('igx-overlay__content')).toBeTruthy(); + + const dropDown = document.getElementsByClassName('igx-date-picker--dropdown'); + expect(dropDown.length).toBe(1); + expect(dropDown[0]).not.toBeNull(); + + UIInteractions.triggerKeyDownEvtUponElem('Escape', dropDown[0], false); + flush(); + fixture.detectChanges(); + + const overlays = document.getElementsByClassName('igx-overlay__content'); + expect(overlays.length).toEqual(0); + })); + + it('Open/close drop-down with `alt + down`(open) and `alt + up`(close) - 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); + fixture.detectChanges(); + + expect(datePicker.onSelection.emit).toHaveBeenCalled(); + expect(datePicker.value).toBe(newDate); + + const input = fixture.debugElement.query(By.directive(IgxInputDirective)); + expect(input).toBeDefined(); + expect(input.nativeElement.value).toBe('06.05.2016'); + }); + + it('should open the dropdown when click on the date icon - edit mode', (() => { + 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].innerText.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 - edit mode', async () => { + const input = fixture.debugElement.query(By.directive(IgxInputDirective)); + expect(input).toBeDefined(); + + input.nativeElement.dispatchEvent(new Event('focus')); + fixture.detectChanges(); + + input.nativeElement.dispatchEvent(new Event('blur')); + fixture.detectChanges(); + await wait(); + + expect(input.nativeElement.value).toBe('20.10.2011'); + }); + + it('should be able to apply editor mask - 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'); + + // Check for formatted empty value on blur - placeholder is displayed + datePicker.deselectDate(); + fixture.detectChanges(); + + input.nativeElement.dispatchEvent(new Event('focus')); + fixture.detectChanges(); + + input.nativeElement.dispatchEvent(new Event('blur')); + fixture.detectChanges(); + + expect(input.nativeNode.placeholder).toBe('dd-MM-yy'); + })); + + it('Should be able to deselect using the API - edit mode', () => { + const date = new Date(Date.now()); + datePicker.selectDate(date); + fixture.detectChanges(); + + expect(datePicker.value).toBe(date); + + datePicker.deselectDate(); + fixture.detectChanges(); + + expect(datePicker.value).toBe(null); + }); + + it('should increase date parts using arrows - edit mode', fakeAsync(() => { + const input = fixture.debugElement.query(By.directive(IgxInputDirective)); + expect(input).toBeDefined(); + input.nativeElement.dispatchEvent(new Event('focus')); + fixture.detectChanges(); + expect(input.nativeElement.value).toBe('20-10-11'); + + // initial input value is 20-10-11 / dd-MM-yy + // focus the day part, position the caret at the beginning + input.nativeElement.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 - edit mode', fakeAsync(() => { + const input = fixture.debugElement.query(By.directive(IgxInputDirective)); + expect(input).toBeDefined(); + input.nativeElement.dispatchEvent(new Event('focus')); + fixture.detectChanges(); + expect(input.nativeElement.value).toBe('20-10-11'); + + // initial input value is 20-10-11 / dd-MM-yy + // focus the day part, position the caret at the beginning + input.nativeElement.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 - edit mode', fakeAsync(() => { + const input = fixture.debugElement.query(By.directive(IgxInputDirective)); + expect(input).toBeDefined(); + input.nativeElement.dispatchEvent(new Event('focus')); + fixture.detectChanges(); + expect(input.nativeElement.value).toBe('20-10-11'); + + // initial input value is 20-10-11 / dd-MM-yy + // focus the day part, position the caret at the beginning + input.nativeElement.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'); + })); + + 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); + }); + + 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'); + })); }); describe('EditorProvider', () => { @@ -447,4 +959,16 @@ export class IgxDatePickerNgModelComponent {
` }) -export class IgxDatePickerRetemplatedComponent {} +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 1a6be762fb0..7043952a719 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,8 +1,6 @@ -import { CommonModule } from '@angular/common'; +import { CommonModule, formatDate } from '@angular/common'; import { Component, - ComponentFactoryResolver, - ComponentRef, ContentChild, EventEmitter, HostBinding, @@ -12,12 +10,11 @@ import { OnInit, Output, ViewChild, - ViewContainerRef, - HostListener, ElementRef, TemplateRef, - Directive, - ChangeDetectorRef + Inject, + ChangeDetectorRef, + HostListener } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { @@ -25,39 +22,100 @@ import { IgxCalendarHeaderTemplateDirective, IgxCalendarModule, IgxCalendarSubheaderTemplateDirective, - WEEKDAYS + WEEKDAYS, + isDateInRanges } from '../calendar/index'; -import { IgxDialogComponent, IgxDialogModule } from '../dialog/dialog.component'; import { IgxIconModule } from '../icon/index'; import { IgxInputGroupModule, IgxInputDirective } 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 { DateRangeDescriptor } from '../core/dates/dateRange'; import { EditorProvider } from '../core/edit-provider'; +import { IgxButtonModule } from '../directives/button/button.directive'; +import { IgxRippleModule } from '../directives/ripple/ripple.directive'; +import { IgxMaskModule } from '../directives/mask/mask.directive'; +import { + DatePickerUtil, + DateState +} from './date-picker.utils'; +import { DatePickerDisplayValuePipe, DatePickerInputValuePipe } from './date-picker.pipes'; +import { IDatePicker } from './date-picker.common'; +import { KEYS } from '../core/utils'; +import { IgxDatePickerTemplateDirective } from './date-picker.directives'; +import { IgxCalendarContainerComponent } from './calendar-container.component'; -@Directive({ - selector: '[igxDatePickerTemplate]' -}) -export class IgxDatePickerTemplateDirective { - constructor(public template: TemplateRef) {} +let NEXT_ID = 0; + +/** + * This interface is used to provide information about date picker reference and its current value + * when onDisabledDate event is fired. + */ +export interface IDatePickerDisabledDateEventArgs { + datePicker: IgxDatePickerComponent; + currentValue: Date; } +/** + * This interface is used to provide information about date picker reference and its previously valid value + * when onValidationFailed event is fired. + */ +export interface IDatePickerValidationFailedEventArgs { + 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; } -let NEXT_ID = 0; + +/** + * 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) @@ -71,63 +129,16 @@ let NEXT_ID = 0; */ @Component({ providers: - [{ provide: NG_VALUE_ACCESSOR, useExisting: IgxDatePickerComponent, multi: true }], + [{ + provide: NG_VALUE_ACCESSOR, + useExisting: IgxDatePickerComponent, + multi: true + }], // tslint:disable-next-line:component-selector selector: 'igx-date-picker', - styles: [':host {display: block;}'], templateUrl: 'date-picker.component.html' }) -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; - +export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor, EditorProvider, OnInit, OnDestroy { /** * An @Input property that sets the `IgxDatePickerComponent` label. * The default label is 'Date'. @@ -176,6 +187,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi public get formatOptions(): IFormatOptions { return this._formatOptions; } + /** *Sets the format options of the `IgxDatePickerComponent`. *```typescript @@ -197,6 +209,47 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi this._formatOptions = Object.assign(this._formatOptions, formatOptions); } + /** + *Returns the date display format of the editable `IgxDatePickerComponent`. + *```typescript + *@ViewChild("MyDatePicker") + *public datePicker: IgxDatePickerComponent; + *ngAfterViewInit(){ + * let format = this.datePicker.format; + *} + *``` + */ + @Input() + public get format(): string { + return (this._format === undefined) ? PredefinedFormatOptions.SHORT_DATE : this._format; + } + + /** + *Sets the date format of the `IgxDatePickerComponent` when in edit mode. + *```typescript + *@ViewChild("MyDatePicker") + *public datePicker: IgxDatePickerComponent; + *this.datePicker.format = 'yyyy-M-d'; + *} + *``` + */ + public set format(format: string) { + this._format = format; + } + + /** + *Returns the date mask of the `IgxDatePickerComponent` when in edit mode. + *```typescript + *@ViewChild("MyDatePicker") + *public datePicker: IgxDatePickerComponent; + *ngAfterViewInit(){ + * let mask = this.datePicker.mask; + *} + *``` + */ + @Input() + public mask: string; + /** *Returns the format views of the `IgxDatePickerComponent`. *```typescript @@ -282,8 +335,160 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi this._specialDates = value; } + @Input() + public get modalOverlaySettings(): OverlaySettings { + return this._modalOverlay; + } + + public set modalOverlaySettings(value: OverlaySettings) { + this._modalOverlay = value; + } + + @Input() + public get dropDownOverlaySettings(): OverlaySettings { + return this._dropDownOverlay; + } + + public set dropDownOverlaySettings(value: OverlaySettings) { + this._dropDownOverlay = value; + } + + /** + *Returns the formatted date when `IgxDatePickerComponent` is readonly. + *```typescript + *@ViewChild("MyDatePicker") + *public datePicker: IgxDatePickerComponent; + *public selection(event){ + * let selectedDate = this.datePicker.displayData; + * alert(selectedDate); + *} + *``` + *```html + * + *``` + */ + public get displayData(): string { + if (this.value) { + return this._customFormatChecker(this.formatter, this.value); + } + return ''; + } + + /** + hidden + */ + public get transformedDate(): string { + if (this._value) { + this._transformedDate = (this._isInEditMode) ? this._getEditorDate(this._value) : this._getDisplayDate(this._value); + this.isEmpty = false; + } else { + this._transformedDate = (this._isInEditMode) ? DatePickerUtil.maskToPromptChars(this.inputMask) : ''; + } + return this._transformedDate; + } + + public set transformedDate(value) { + this._transformedDate = value; + } + + constructor(@Inject(IgxOverlayService) private _overlayService: IgxOverlayService, + private cdr: ChangeDetectorRef) { } + /** - *An @Input proeprty that sets the orientation of the `IgxDatePickerComponent` header. + * Gets the input group template. + * ```typescript + * let template = this.template(); + * ``` + * @memberof IgxDatePickerComponent + */ + get template(): TemplateRef { + if (this.datePickerTemplateDirective) { + return this.datePickerTemplateDirective.template; + } + return (this.mode === DatePickerInteractionMode.READONLY) ? this.readOnlyDatePickerTemplate : this.editableDatePickerTemplate; + } + + /** + * Gets the context passed to the input group template. + * @memberof IgxDatePickerComponent + */ + get context() { + return { + disabled: this.disabled, + disabledDates: this.disabledDates, + displayData: this.displayData, + 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 gets/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; + this._onChangeCallback(date); + } + + /** + *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 a custom formatter function 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 orientation of the `IgxDatePickerComponent` header. *```html * *``` @@ -310,10 +515,34 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi public cancelButtonLabel: string; /** - *An event that is emitted when the `IgxDatePickerComponent` is opened. + *An @Input property that sets whether `IgxDatePickerComponent` is readonly or editable. + *```html + * + *``` + */ + @Input() + public mode = DatePickerInteractionMode.READONLY; + + /** + *An @Input property that sets whether the `IgxDatePickerComponent` date parts would spin continuously or stop when min/max is reached. + *```html + * + *``` + */ + @Input() + public isSpinLoop = true; + + /** + *@hidden + */ + @Input() + public outlet: IgxOverlayOutletDirective | ElementRef; + + /** + *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 @@ -351,81 +580,106 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi public onSelection = new EventEmitter(); /** - * @hidden - */ - @ViewChild('defaultDatePickerTemplate', { read: TemplateRef }) - protected defaultDatePickerTemplate: TemplateRef; + *An @Output property that fires when the user types/spins to a disabled date in the date-picker editor. + *```typescript + *public onDisabledDate(event){ + * alert("This date is disabled!"); + *} + *``` + *```html + * + *``` + */ + @Output() + public onDisabledDate = new EventEmitter(); /** - *@hidden - */ - @ContentChild(IgxDatePickerTemplateDirective, { read: IgxDatePickerTemplateDirective }) - protected datePickerTemplateDirective: IgxDatePickerTemplateDirective; + *An @Output property that fires when the user types/spins invalid date in the date-picker editor. + *```typescript + *public onValidationFailed(event){ + * alert("This date is not valid!"); + *} + *``` + *```html + * + *``` + */ + @Output() + public onValidationFailed = new EventEmitter(); - /** - *Returns the formatted date. - *```typescript - *@ViewChild("MyDatePicker") - *public datePicker: IgxDatePickerComponent; - *public selection(event){ - * let selectedDate = this.datePicker.displayData; - * alert(selectedDate); - *} - *``` - *```html - * - *``` + /* + * @hidden */ - public get displayData() { - if (this.value) { - return this._customFormatChecker(this.formatter, this.value); - } - return ''; - } + @ViewChild('readOnlyDatePickerTemplate', { read: TemplateRef }) + protected readOnlyDatePickerTemplate: TemplateRef; - /** - *@hidden + /* + * @hidden */ - @ContentChild(IgxCalendarHeaderTemplateDirective, { read: IgxCalendarHeaderTemplateDirective }) + @ViewChild('editableDatePickerTemplate', { read: TemplateRef }) + protected editableDatePickerTemplate: TemplateRef; - public headerTemplate: IgxCalendarHeaderTemplateDirective; - /** - *@hidden + /* + * @hidden */ - @ContentChild(IgxCalendarSubheaderTemplateDirective, { read: IgxCalendarSubheaderTemplateDirective }) - public subheaderTemplate: IgxCalendarSubheaderTemplateDirective; + @ViewChild('editableInputGroup', { read: ElementRef }) + protected editableInputGroup: ElementRef; - /** - *@hidden + /* + * @hidden */ - @ViewChild('container', { read: ViewContainerRef }) - public container: ViewContainerRef; + @ViewChild('editableInput', { read: ElementRef }) + protected editableInput: ElementRef; + + /* + * @hidden + */ + @ViewChild('readonlyInput', { read: ElementRef }) + protected readonlyInput: ElementRef; + + /* + * @hidden + */ + @ContentChild(IgxInputDirective) + protected input: IgxInputDirective; /** - *@hidden + * @hidden */ - @ViewChild(IgxDialogComponent) - public alert: IgxDialogComponent; + @ViewChild('datePickerOutlet', { read: ElementRef }) + public outletDirective: ElementRef; /** *@hidden */ - public calendarRef: ComponentRef; + @ContentChild(IgxDatePickerTemplateDirective, { read: IgxDatePickerTemplateDirective }) + protected datePickerTemplateDirective: IgxDatePickerTemplateDirective; /** *@hidden */ - @Input() - public outlet: IgxOverlayOutletDirective | ElementRef; + @ContentChild(IgxCalendarHeaderTemplateDirective, { read: IgxCalendarHeaderTemplateDirective }) + public headerTemplate: IgxCalendarHeaderTemplateDirective; /** *@hidden */ - public get calendar() { - return this.calendarRef.instance; - } + @ContentChild(IgxCalendarSubheaderTemplateDirective, { read: IgxCalendarSubheaderTemplateDirective }) + public subheaderTemplate: IgxCalendarSubheaderTemplateDirective; + + public calendar: IgxCalendarComponent; + public hasHeader = true; + public collapsed = true; + public displayValuePipe = new DatePickerDisplayValuePipe(this); + public inputValuePipe = new DatePickerInputValuePipe(this); + public dateFormatParts = []; + public rawDateString: string; + public inputMask: string; + public isEmpty = true; + public invalidDate = ''; - protected destroy$ = new Subject(); + private readonly SPIN_DELTA = 1; + private readonly DEFAULT_LOCALE = 'en'; private _formatOptions = { day: 'numeric', @@ -433,20 +687,31 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi weekday: 'short', year: 'numeric' }; - private _formatViews = { day: false, month: true, year: false }; - + private _destroy$ = new Subject(); + private _componentID: string; + private _format: string; + private _value: Date; + private _isInEditMode: boolean; private _disabledDates: DateRangeDescriptor[] = null; - private _specialDates: DateRangeDescriptor[] = null; + private _modalOverlay: OverlaySettings; + private _dropDownOverlay: OverlaySettings; + private _positionSettings: PositionSettings; + private _dropDownOverlaySettings: OverlaySettings; + private _modalOverlaySettings: OverlaySettings; + private _transformedDate; - @ViewChild(IgxInputDirective) protected input: IgxInputDirective; - - constructor(private resolver: ComponentFactoryResolver, private cdr: ChangeDetectorRef) { } + @HostListener('keydown.spacebar', ['$event']) + @HostListener('keydown.space', ['$event']) + public onSpaceClick(event: KeyboardEvent) { + this.openDialog(); + event.preventDefault(); + } /** *Method that sets the selected date. @@ -477,24 +742,73 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi public registerOnTouched(fn: () => void) { this._onTouchedCallback = fn; } /** @hidden */ - getEditElement() { - return this.input.nativeElement; + public getEditElement() { + 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; } /** *@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, + }; + + const outlet = (this.outlet !== undefined) ? this.outlet : this.outletDirective; + this._dropDownOverlaySettings = { + closeOnOutsideClick: true, + modal: false, + positionStrategy: new ConnectedPositioningStrategy(this._positionSettings), + outlet: outlet + }; + + this._modalOverlaySettings = { + closeOnOutsideClick: true, + modal: true, + outlet: outlet + }; + + this._overlayService.onOpening.pipe( + filter((overlay) => overlay.id === this._componentID), + takeUntil(this._destroy$)).subscribe((eventArgs) => { + this._onOpening(eventArgs); + }); + + this._overlayService.onOpened.pipe( + filter((overlay) => overlay.id === this._componentID), + takeUntil(this._destroy$)).subscribe((eventArgs) => { + this._onOpened(eventArgs); + }); + + this._overlayService.onClosed.pipe( + filter(overlay => overlay.id === this._componentID), + takeUntil(this._destroy$)).subscribe(() => { + this._onClosed(); + }); + + if (this.mode === DatePickerInteractionMode.EDITABLE) { + this.dateFormatParts = DatePickerUtil.parseDateFormat(this.mask, this.locale); + if (this.mask === undefined) { + this.mask = DatePickerUtil.getMask(this.dateFormatParts); + } + this.inputMask = DatePickerUtil.getInputMask(this.dateFormatParts); + } } /** *@hidden */ public ngOnDestroy(): void { - this.destroy$.next(true); - this.destroy$.complete(); + this._overlayService.hideAll(); + this._destroy$.next(true); + this._destroy$.complete(); } /** @@ -508,7 +822,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi *``` *@memberOf {@link IgxDatePickerComponent} */ - public triggerTodaySelection() { + public triggerTodaySelection(): void { const today = new Date(Date.now()); this.handleSelection(today); } @@ -526,7 +840,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi * @param date passed date that has to be set to the calendar. * @memberOf {@link IgxDatePickerComponent} */ - public selectDate(date: Date) { + public selectDate(date: Date): void { this.value = date; this.onSelection.emit(date); this._onChangeCallback(date); @@ -543,52 +857,62 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi * ``` * @memberOf {@link IgxDatePickerComponent} */ - public deselectDate() { + public deselectDate(): void { this.value = null; + if (this.calendar) { + this.calendar.deselectDate(); + } this._onChangeCallback(null); } /** - * Open the dialog and update the calendar. + * Open the calendar. * * @hidden */ public openDialog(): void { - this.createCalendarRef(); - if (this.outlet) { - const overlaySettings: OverlaySettings = { - outlet: this.outlet - }; - this.alert.open(overlaySettings); - } else { - this.alert.open(); + if (!this.collapsed) { + return; + } + switch (this.mode) { + case DatePickerInteractionMode.READONLY: { + this.hasHeader = true; + const modalOverlay = (this.modalOverlaySettings !== undefined) ? this._modalOverlay : this._modalOverlaySettings; + this._componentID = this._overlayService.attach(IgxCalendarContainerComponent, modalOverlay); + this._overlayService.show(this._componentID, modalOverlay); + break; + } + case DatePickerInteractionMode.EDITABLE: { + this.hasHeader = false; + const dropDownOverlay = + (this.dropDownOverlaySettings !== undefined) ? this._dropDownOverlay : this._dropDownOverlaySettings; + dropDownOverlay.positionStrategy.settings.target = this.editableInputGroup.nativeElement; + this._componentID = this._overlayService.attach(IgxCalendarContainerComponent, dropDownOverlay); + this._overlayService.show(this._componentID, dropDownOverlay); + break; + } } - this._onTouchedCallback(); - this.onOpen.emit(this); } - private createCalendarRef(): void { - const factory = this.resolver.resolveComponentFactory(IgxCalendarComponent); - - this.calendarRef = this.container.createComponent(factory); - - this.calendarRef.changeDetectorRef.detach(); - this.updateCalendarInstance(); - this.calendarRef.location.nativeElement.classList.add('igx-date-picker__date--opened'); - this.calendarRef.changeDetectorRef.reattach(); + /** + * Close the calendar. + * + * @hidden + */ + public closeCalendar(): void { + this._overlayService.hide(this._componentID); } /** - * Closes the dialog, after was clearing all calendar items from dom. + * Clear the input field, date picker value and calendar selection. * * @hidden */ - public handleDialogCloseAction() { - this.onClose.emit(this); - this.calendarRef.destroy(); - if (this.input) { - this.input.nativeElement.focus(); - } + public clear(): void { + this.isEmpty = true; + this.invalidDate = ''; + this.deselectDate(); + this._setCursorPosition(0); } /** @@ -599,7 +923,7 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi * * @hidden */ - public handleSelection(date: Date) { + public handleSelection(date: Date): void { if (this.value) { date.setHours(this.value.getHours()); date.setMinutes(this.value.getMinutes()); @@ -610,69 +934,230 @@ 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(); - event.preventDefault(); + /** + * Evaluates when the input blur event was fired + * and re-calculate the date picker value. + * + * @param event + * + * @hidden + */ + public onBlur(event): void { + this._isInEditMode = false; + this.calculateDate(event.target.value, event.type); } /** - * Gets the input group template. - * ```typescript - * let template = this.template(); - * ``` - * @memberof IgxTimePickerComponent - */ - get template(): TemplateRef { - if (this.datePickerTemplateDirective) { - return this.datePickerTemplateDirective.template; + * Evaluates when the input focus event was fired + * and re-calculate the editor text. + * + * @param event + * @hidden + */ + public onFocus(): void { + this._isInEditMode = true; + if (this.value && this.invalidDate === '') { + this._transformedDate = this._getEditorDate(this.value); } - return this.defaultDatePickerTemplate; } /** - * Gets the context passed to the input group template. - * @memberof IgxTimePickerComponent - */ - get context() { - return { - value: this.value, - displayData: this.displayData, - openDialog: () => { this.openDialog(); } - }; + * Evaluates when the keydown event was fired for up/down keys + * to provide spinning of date parts. + * + * @param event + * + * @hidden + */ + public onKeyDown(event) { + switch (event.key) { + case KEYS.UP_ARROW: + case KEYS.UP_ARROW_IE: + event.preventDefault(); + event.stopPropagation(); + this.spinValue(event.target.value, 1, event.type); + break; + case KEYS.DOWN_ARROW: + case KEYS.DOWN_ARROW_IE: + if (event.altKey) { + this.openDialog(); + } else { + event.preventDefault(); + event.stopPropagation(); + this.spinValue(event.target.value, -1, event.type); + } + break; + default: + break; + } } - 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; + /** + * Evaluates when the mouse wheel event was fired + * to provide spinning of date parts. + * + * @param event + * + * @hidden + */ + public onWheel(event) { + event.preventDefault(); + event.stopPropagation(); + const sign = (event.deltaY > 0) ? -1 : 1; + this.spinValue(event.target.value, sign, event.type); + + } + + /** + * Evaluates when input event was fired in editor. + * + * @param event + * + * @hidden + */ + public onInput(event) { + const targetValue = event.target.value; + const cursorPosition = this._getCursorPosition(); + const checkInput = DatePickerUtil.checkForCompleteDateInput(this.dateFormatParts, targetValue); + this._isInEditMode = true; + + if (targetValue !== DatePickerUtil.maskToPromptChars(this.inputMask)) { + this.isEmpty = false; + } + + // If all date parts are completed, change the date-picker value, stay in edit mode + if (checkInput === 'complete' && event.inputType !== 'deleteContentBackward') { + this._transformedDate = targetValue; + this.calculateDate(targetValue, event.type); + this._setCursorPosition(cursorPosition); + } else if (checkInput === 'partial') { + // While editing, if one date part is deleted, date-picker value is set to null, the remaining input stays intact. + this.deselectDate(); + requestAnimationFrame(() => { + this.getEditElement().value = targetValue; + this._setCursorPosition(cursorPosition); + }); + } else if (checkInput === 'empty') { + // Total clean-up as input is deleted. + this.isEmpty = true; + this.deselectDate(); + } + } + + private calculateDate(dateString: string, invokedByEvent: string): void { + if (dateString !== '') { + const prevDateValue = this.value; + const inputValue = (invokedByEvent === 'blur') ? this.rawDateString : dateString; + const newDateArray = DatePickerUtil.parseDateArray(this.dateFormatParts, prevDateValue, inputValue); + + if (newDateArray.state === DateState.VALID) { + const newValue = newDateArray.date; + // Restore the time part if any + if (prevDateValue) { + newValue.setHours(prevDateValue.getHours()); + newValue.setMinutes(prevDateValue.getMinutes()); + newValue.setSeconds(prevDateValue.getSeconds()); + newValue.setMilliseconds(prevDateValue.getMilliseconds()); + } + + if (this.disabledDates === null + || (this.disabledDates !== null && !isDateInRanges(newValue, this.disabledDates))) { + this.value = newValue; + this.invalidDate = ''; + this._onChangeCallback(newValue); + } else { + const args: IDatePickerDisabledDateEventArgs = { + datePicker: this, + currentValue: newValue, + }; + this.onDisabledDate.emit(args); + } + } else { + const args: IDatePickerValidationFailedEventArgs = { + datePicker: this, + prevValue: prevDateValue + }; + this.invalidDate = dateString; + this.onValidationFailed.emit(args); + } + } + } - if (this.headerTemplate) { - this.calendar.headerTemplate = this.headerTemplate; + private spinValue(inputValue: string, sign: number, eventType: string): void { + this._isInEditMode = true; + this.isEmpty = false; + const cursorPosition = this._getCursorPosition(); + + const modifiedInputValue = + DatePickerUtil.getModifiedDateInput(this.dateFormatParts, inputValue, cursorPosition, this.SPIN_DELTA * sign, this.isSpinLoop); + + this.getEditElement().value = modifiedInputValue; + this._setCursorPosition(cursorPosition); + + const checkInput = DatePickerUtil.checkForCompleteDateInput(this.dateFormatParts, modifiedInputValue); + if (checkInput === 'complete') { + this._isInEditMode = true; + this.calculateDate(modifiedInputValue, eventType); + this._setCursorPosition(cursorPosition); } + } + + private _onOpening(event) { + this._initializeCalendarContainer(event.componentRef.instance); + this.collapsed = false; + } - if (this.subheaderTemplate) { - this.calendar.subheaderTemplate = this.subheaderTemplate; + private _onOpened(event): void { + this._onTouchedCallback(); + this.onOpen.emit(this); + + if (this.calendar) { + this._focusCalendarDate(); } + } + + private _onClosed(): void { + this.collapsed = true; + this.onClose.emit(this); + + if (this.getEditElement()) { + this.getEditElement().focus(); + } + } + + 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; + this.calendar.formatViews = this.formatViews; + this.calendar.locale = this.locale; + this.calendar.vertical = isVertical; + this.calendar.weekStart = this.weekStart; + this.calendar.specialDates = this.specialDates; + this.calendar.disabledDates = this.disabledDates; + this.calendar.onSelection.pipe(takeUntil(this._destroy$)).subscribe((ev: Date) => this.handleSelection(ev)); if (this.value) { this.calendar.value = this.value; this.calendar.viewDate = this.value; } - this.calendar.weekStart = this.weekStart; - this.calendar.onSelection.pipe(takeUntil(this.destroy$)).subscribe((ev: Date) => this.handleSelection(ev)); + + componentInstance.mode = this.mode; + componentInstance.vertical = isVertical; + componentInstance.cancelButtonLabel = this.cancelButtonLabel; + componentInstance.todayButtonLabel = this.todayButtonLabel; + + componentInstance.onClose.pipe(takeUntil(this._destroy$)).subscribe(() => this.closeCalendar()); + componentInstance.onTodaySelection.pipe(takeUntil(this._destroy$)).subscribe(() => this.triggerTodaySelection()); } - // Focus a date, after the celendar appearence into DOM. - private _focusCalendarDate() { + // Focus a date, after the calendar appearance into DOM. + private _focusCalendarDate(): void { requestAnimationFrame(() => { this.calendar.daysView.focusActiveDate(); }); @@ -682,6 +1167,16 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi return value.toLocaleDateString(this.locale); } + private _getCursorPosition(): number { + return this.getEditElement().selectionStart; + } + + private _setCursorPosition(start: number, end: number = start): void { + requestAnimationFrame(() => { + this.getEditElement().setSelectionRange(start, end); + }); + } + /** * Apply custom user formatter upon date. * @param formatter custom formatter function. @@ -691,6 +1186,27 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi return this.formatter ? this.formatter(date) : this._setLocaleToDate(date); } + /* + * Transforms the date according to the specified format when `IgxDatePickerComponent` is in edit mode + * using @angular/common formatDate method: https://angular.io/api/common/formatDate + * @param value: string | number | Date + * @returns formatted string + */ + private _getDisplayDate(value: any): string { + if (this.format && !this.formatter) { + const locale = this.locale || this.DEFAULT_LOCALE; + return formatDate(value, this.format, locale); + } else { + return this._customFormatChecker(this.formatter, value); + } + } + + private _getEditorDate(value: any) { + 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 = () => { }; private _onChangeCallback: (_: Date) => void = () => { }; @@ -700,9 +1216,10 @@ export class IgxDatePickerComponent implements ControlValueAccessor, EditorProvi * @hidden */ @NgModule({ - declarations: [IgxDatePickerComponent, IgxDatePickerTemplateDirective], - entryComponents: [IgxCalendarComponent], - exports: [IgxDatePickerComponent, IgxDatePickerTemplateDirective], - imports: [CommonModule, IgxIconModule, IgxInputGroupModule, IgxDialogModule, IgxCalendarModule] + declarations: [IgxDatePickerComponent, IgxCalendarContainerComponent, + IgxDatePickerTemplateDirective, DatePickerDisplayValuePipe, DatePickerInputValuePipe], + exports: [IgxDatePickerComponent, IgxDatePickerTemplateDirective, DatePickerDisplayValuePipe, DatePickerInputValuePipe], + imports: [CommonModule, IgxIconModule, IgxInputGroupModule, IgxCalendarModule, IgxButtonModule, IgxRippleModule, IgxMaskModule], + entryComponents: [IgxCalendarContainerComponent] }) export class IgxDatePickerModule { } 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 new file mode 100644 index 00000000000..a227875a82d --- /dev/null +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.directives.ts @@ -0,0 +1,14 @@ +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 new file mode 100644 index 00000000000..1ac31332707 --- /dev/null +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.pipes.ts @@ -0,0 +1,44 @@ +import { PipeTransform, Pipe, Inject } from '@angular/core'; +import { IGX_DATE_PICKER_COMPONENT, IDatePicker } from './date-picker.common'; +import { DatePickerUtil } from './date-picker.utils'; + +/** + * @hidden + */ +@Pipe({ + name: 'displayValue' +}) +export class DatePickerDisplayValuePipe implements PipeTransform { + constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IDatePicker) { } + transform(value: any, args?: any): any { + if (value !== '') { + if (value === DatePickerUtil.maskToPromptChars(this._datePicker.inputMask)) { + return ''; + } + this._datePicker.rawDateString = value; + return DatePickerUtil.trimUnderlines(value); + } + return ''; + } +} + +/** + * @hidden + */ +@Pipe({ + name: 'inputValue' +}) +export class DatePickerInputValuePipe implements PipeTransform { + constructor(@Inject(IGX_DATE_PICKER_COMPONENT) private _datePicker: IDatePicker) { } + transform(value: any, args?: any): any { + if (this._datePicker.invalidDate !== '') { + return this._datePicker.invalidDate; + } else { + if (this._datePicker.value === null || this._datePicker.value === undefined) { + return DatePickerUtil.maskToPromptChars(this._datePicker.inputMask); + } else { + return 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 new file mode 100644 index 00000000000..566b2a1b873 --- /dev/null +++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts @@ -0,0 +1,556 @@ +import { isIE } from '../core/utils'; + +/** + * This enum is used to keep the date validation result. + * + *@hidden + */ +export const enum DateState { + VALID = 'valid', + INVALID = 'invalid', +} + +/** + *@hidden + */ +const enum FormatDesc { + NUMERIC = 'numeric', + TWO_DIGITS = '2-digit' +} + +/** + *@hidden + */ +const enum DateChars { + YEAR_CHAR = 'y', + MONTH_CHAR = 'M', + DAY_CHAR = 'd' +} + +/** + *@hidden + */ +const enum DateParts { + DAY = 'day', + MONTH = 'month', + YEAR = 'year' +} + +/** + *@hidden + */ +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'; + + /** + * 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); + + if (yearInitPosition !== -1) { + dateStruct.push({ + type: DateParts.YEAR, + initialPosition: yearInitPosition, + formatType: DatePickerUtil.getYearFormatType(mask) + }); + } + + if (monthInitPosition !== -1) { + dateStruct.push({ + type: DateParts.MONTH, + initialPosition: monthInitPosition, + formatType: DatePickerUtil.getMonthFormatType(mask) + }); + } + + if (dayInitPosition !== -1) { + dateStruct.push({ + type: DateParts.DAY, + initialPosition: dayInitPosition, + formatType: DatePickerUtil.getDayFormatType(mask) + }); + } + + for (let i = 0; i < maskArray.length; i++) { + if (!DatePickerUtil.isDateChar(maskArray[i])) { + dateStruct.push({ + type: DatePickerUtil.SEPARATOR, + initialPosition: i, + value: maskArray[i] + }); + } + } + + dateStruct.sort((a, b) => a.initialPosition - b.initialPosition); + DatePickerUtil.fillDatePartsPositions(dateStruct); + } + return dateStruct; + } + + /** + * 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(''); + } + + /** + * 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'); + } + } + } + + if (dateStruct[i].type === DatePickerUtil.SEPARATOR) { + mask.push(dateStruct[i].value); + } + } + + 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 ((month < 0) || (month > 11) || (month === NaN)) { + return { state: DateState.INVALID, value: inputValue }; + } + + if ((day < 1) || (day > DatePickerUtil.daysInMonth(fullYear, month + 1)) || (day === NaN)) { + return { state: DateState.INVALID, value: inputValue }; + } + + return { state: DateState.VALID, date: new Date(fullYear, month, day) }; + } + + public static maskToPromptChars(mask: string): string { + const result = mask.replace(/0|L/g, DatePickerUtil.PROMPT_CHAR); + return result; + } + + /** + * This method replaces prompt chars with empty string. + * @param value + */ + public static trimUnderlines(value: string): string { + const result = value.replace(/_/g, ''); + return result; + } + + /** + * 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; + + // Infinite loop for full years + if (maxValue === 'infinite' && minValue === 'infinite') { + newValue = tempValue; + } + + if (isSpinLoop) { + if (tempValue > maxValue) { + tempValue = minValue; + } + if (tempValue < minValue) { + tempValue = maxValue; + } + newValue = tempValue; + } else { + if (tempValue <= maxValue && tempValue >= minValue) { + newValue = tempValue; + } + } + + 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 = DatePickerUtil.getNumericFormatPrefix(datePartFormatType); + changedPart = (newValue < 10) ? `${prefix}${newValue}` : `${newValue}`; + + return `${start}${changedPart}${end}`; + } + + /** + * 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(''); + } + + /** + * 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 ''; + } + + 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; + } + } + } + + 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; + } + } + } + + 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; + } + } + } + + 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, + }); + } + } + 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; + } + + private static isDateChar(char: string): boolean { + return (char === DateChars.YEAR_CHAR || char === DateChars.MONTH_CHAR || char === DateChars.DAY_CHAR); + } + + private static getNumericFormatPrefix(formatType: string): string { + switch (formatType) { + case FormatDesc.TWO_DIGITS: { + return '0'; + } + case FormatDesc.NUMERIC: { + return DatePickerUtil.PROMPT_CHAR; + } + } + } + + 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 }; + } + + private static daysInMonth(fullYear: number, month: number): number { + return new Date(fullYear, month, 0).getDate(); + } + + 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; + } + + private static getDayValueFromInput(dateFormatParts: any[], inputValue: string, trim: boolean = true): string { + return DatePickerUtil.getDateValueFromInput(dateFormatParts, DateParts.DAY, inputValue, trim); + } + + private static getMonthValueFromInput(dateFormatParts: any[], inputValue: string, trim: boolean = true): string { + return DatePickerUtil.getDateValueFromInput(dateFormatParts, DateParts.MONTH, inputValue, trim); + } + + private static getYearValueFromInput(dateFormatParts: any[], inputValue: string, trim: boolean = true): string { + return DatePickerUtil.getDateValueFromInput(dateFormatParts, DateParts.YEAR, inputValue, trim); + } + + private static getDateFormatPart(dateFormatParts: any[], type: DateParts): any { + const result = dateFormatParts.filter((datePart) => (datePart.type === type))[0]; + return result; + } + + private static isFullInput(value: any, input: string): boolean { + return (value !== '' && input.length === 2 && input.charAt(1) !== DatePickerUtil.PROMPT_CHAR); + } + + 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; + } + } + } + + private static getDatePartOnPosition(dateFormatParts: any[], position: number) { + 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 { + 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++; + } + } + } +} + + 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 diff --git a/projects/igniteui-angular/src/lib/grids/cell.component.ts b/projects/igniteui-angular/src/lib/grids/cell.component.ts index 7b99a16d068..22c2b49c9ab 100644 --- a/projects/igniteui-angular/src/lib/grids/cell.component.ts +++ b/projects/igniteui-angular/src/lib/grids/cell.component.ts @@ -271,7 +271,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges { 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; } /** @@ -727,9 +727,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges { 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)) - || column.dataType === DataType.Date)) { + if (column.inlineEditorTemplate === undefined && + column.dataType === DataType.Boolean && key !== KEYS.SPACE && key !== KEYS.SPACE_IE) { event.preventDefault(); } return; @@ -743,9 +742,9 @@ export class IgxGridCellComponent implements OnInit, OnChanges { 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); 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..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 @@ -40,7 +40,7 @@ - + { 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 @@ -2772,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'); })); }); 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..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 @@ -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]); @@ -3111,29 +3125,30 @@ 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; + let cellDate = grid.getCellByColumn(0, 'OrderDate'); 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'})); + // 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: () => { } }); + cellDate = grid.getCellByColumn(0, 'OrderDate'); const newValue = new Date('01/01/2000'); cellDate.update(newValue); tick(); fixture.detectChanges(); + const expectedTransaction: Transaction = { id: 1, newValue: {OrderDate: newValue}, @@ -3152,7 +3167,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 +3303,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 +3341,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 +3390,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 +3398,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 +3425,7 @@ describe('IgxGrid Component Tests', () => { tick(); fix.detectChanges(); expect(overlayContent.style.display).toEqual(''); - })); + })); }); }); @@ -3417,7 +3441,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 +4156,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 +4167,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; } 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'); 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(); diff --git a/projects/igniteui-angular/src/public_api.ts b/projects/igniteui-angular/src/public_api.ts index d4d33e1b715..ee485bb9fc5 100644 --- a/projects/igniteui-angular/src/public_api.ts +++ b/projects/igniteui-angular/src/public_api.ts @@ -96,4 +96,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 b0de6d16153..e4f1ad25161 100644 --- a/src/app/date-picker/date-picker.sample.html +++ b/src/app/date-picker/date-picker.sample.html @@ -5,8 +5,9 @@

Default Date Picker.

Detailed description to be added.

- - + + +
@@ -27,16 +28,39 @@

Date Picker with passed date and custom formatter.

Date Picker with retemplated input group.

Detailed description to be added.

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

Editable Date Picker

+ + + +
+ + +
+
+ +
+

Editable Date Picker bind with ngModel

+ + +
+ + +
-
+
\ 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 2aa4f00a6ef..33b3e6648a8 100644 --- a/src/app/date-picker/date-picker.sample.ts +++ b/src/app/date-picker/date-picker.sample.ts @@ -1,20 +1,70 @@ -import { Component, ViewChild } from '@angular/core'; -import { IgxDatePickerComponent } from 'igniteui-angular'; +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', 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'); + + public formatOptions = { + day: 'numeric', + month: 'long', + weekday: 'short', + year: 'numeric' + }; + // @ViewChild('dp99') public datePicker99: IgxDatePickerComponent; + + public date1; + public date2; + public date3; + public date4; + public testDate = new Date(2000, 3, 5); + + public date5 = new Date('10/5/2020'); + + public range = [ + new Date(new Date().getFullYear(), new Date().getMonth(), 3), + new Date(new Date().getFullYear(), new Date().getMonth(), 8) + ]; formatter = (_: Date) => { - return _.toDateString(); + return _.toLocaleString('en'); } - public deselect() { - this.datePicker.deselectDate(); + public deselect(datePicker) { + datePicker.deselectDate(); + } + + constructor() { + // registerLocaleData(localeJA); + // registerLocaleData(localeDE); + const date1 = new Date(); + date1.setDate(8); + date1.setMonth(5); + date1.setFullYear(1978); + + const date2 = new Date(); + date2.setDate(6); + date2.setMonth(4); + date2.setFullYear(2020); + + const date3 = new Date(); + date3.setDate(14); + date3.setMonth(10); + date3.setFullYear(2021); + + this.date1 = date1; + this.date2 = date2; + this.date3 = date3; + this.date4 = date3; } }