diff --git a/demo/src/app/getting-started/getting-started.md b/demo/src/app/getting-started/getting-started.md index e0e92a87dd..587a2217c5 100644 --- a/demo/src/app/getting-started/getting-started.md +++ b/demo/src/app/getting-started/getting-started.md @@ -18,8 +18,6 @@ ngx-bootstrap contains all core (and not only) Bootstrap components powered by Angular. So you don't need to include original JS components, but we are using markup and css provided by Bootstrap. -Additionally to allow reach experience while working with Dates we are using [moment.js](http://momentjs.com/), de facto base date manipulation library at the moment. - # Installation instructions Install `ngx-bootstrap` from `npm` diff --git a/package.json b/package.json index c73fc06f67..aac19421b3 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,6 @@ }, "homepage": "https://github.com/valor-software/ngx-bootstrap#readme", "dependencies": { - "moment": "2.18.1" }, "peerDependencies": { "@angular/common": "^2.3.1 || >=4.0.0", @@ -65,7 +64,7 @@ "@angular/forms": "^2.3.1 || >=4.0.0" }, "devDependencies": { - "@angular/cli": "1.3.0", + "@angular/cli": "1.3.1", "@angular/common": "^2.4.4", "@angular/compiler": "^2.4.4", "@angular/compiler-cli": "^2.4.4", @@ -79,7 +78,7 @@ "@angular/tsc-wrapped": "0.5.1", "@types/jasmine": "2.5.53", "@types/marked": "0.3.0", - "@types/node": "8.0.23", + "@types/node": "8.0.24", "@types/webpack": "3.0.9", "bootstrap": "3.3.7", "classlist-polyfill": "1.2.0", diff --git a/scripts/test.ts b/scripts/test.ts index 9d5e1a16ce..54243ea2ca 100644 --- a/scripts/test.ts +++ b/scripts/test.ts @@ -31,7 +31,7 @@ let context = require.context('../demo/src', true, /\.spec\.ts/); // And load the modules. context.keys().map(context); -let context2 = require.context('../src/spec', true, /\.spec\.ts/); +let context2 = require.context('../src', true, /\.spec\.ts/); context2.keys().map(context2); // Finally, start Karma to run the tests. __karma__.start(); diff --git a/src/baseline.spec.ts b/src/baseline.spec.ts new file mode 100644 index 0000000000..139bd7d56c --- /dev/null +++ b/src/baseline.spec.ts @@ -0,0 +1,46 @@ +import { async, TestBed } from '@angular/core/testing'; +import { AlertModule } from './alert/alert.module'; +import { AccordionModule } from './accordion/accordion.module'; +import { ButtonsModule } from './buttons/buttons.module'; +import { CarouselModule } from './carousel/carousel.module'; +import { CollapseModule } from './collapse/collapse.module'; +import { DatepickerModule } from './datepicker/datepicker.module'; +import { BsDropdownModule } from './dropdown/bs-dropdown.module'; +import { ModalModule } from './modal/modal.module'; +import { PaginationModule } from './pagination/pagination.module'; +import { ProgressbarModule } from './progressbar/progressbar.module'; +import { PopoverModule } from './popover/popover.module'; +import { RatingModule } from './rating/rating.module'; +import { TabsModule } from './tabs/tabs.module'; +import { TimepickerModule } from './timepicker/timepicker.module'; +import { TooltipModule } from './tooltip/tooltip.module'; +import { TypeaheadModule } from './typeahead/typeahead.module'; + +describe('datepicker: [bsDatepickerDayDecorator]', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + AccordionModule.forRoot(), + AlertModule.forRoot(), + ButtonsModule.forRoot(), + CarouselModule.forRoot(), + CollapseModule.forRoot(), + DatepickerModule.forRoot(), + BsDropdownModule.forRoot(), + ModalModule.forRoot(), + PaginationModule.forRoot(), + ProgressbarModule.forRoot(), + PopoverModule.forRoot(), + RatingModule.forRoot(), + TabsModule.forRoot(), + TimepickerModule.forRoot(), + TooltipModule.forRoot(), + TypeaheadModule.forRoot() + ] + }) + .compileComponents(); + })); + + // beforeEach(() => { + // }); +}); diff --git a/src/datepicker/bs-datepicker.module.ts b/src/datepicker/bs-datepicker.module.ts index cdd657b4b2..ec8672d8de 100644 --- a/src/datepicker/bs-datepicker.module.ts +++ b/src/datepicker/bs-datepicker.module.ts @@ -1,12 +1,11 @@ import { CommonModule } from '@angular/common'; -import { ModuleWithProviders, NgModule } from '@angular/core'; +import { isDevMode, ModuleWithProviders, NgModule } from '@angular/core'; import { BsDatepickerActions } from './reducer/bs-datepicker.actions'; import { BsDatepickerStore } from './reducer/bs-datepicker.store'; import { BsDatepickerContainerComponent } from './themes/bs/bs-datepicker-container.component'; import { BsDatepickerMonthViewComponent } from './themes/bs/bs-datepicker-month-view.component'; import { BsDatepickerNavigationViewComponent } from './themes/bs/bs-datepicker-navigation-view.component'; import { BsDatepickerViewComponent } from './themes/bs/bs-datepicker-view.component'; -import { BsDatepickerDayViewComponent } from './themes/bs/bs-datepicker-day-view.component'; import { BsDatepickerConfig } from './bs-datepicker-config'; import { BsDatepickerEffects } from './reducer/bs-datepicker.effects'; import { BsDaterangepickerContainerComponent } from './themes/bs/bs-daterangepicker-container.component'; @@ -22,7 +21,6 @@ import { BsDatepickerDayDecoratorComponent } from './themes/bs/bs-datepicker-day BsDatepickerMonthViewComponent, BsDatepickerViewComponent, BsDatepickerNavigationViewComponent, - BsDatepickerDayViewComponent, BsDatepickerDayDecoratorComponent, BsDatepickerContainerComponent, BsDaterangepickerContainerComponent, @@ -34,6 +32,13 @@ import { BsDatepickerDayDecoratorComponent } from './themes/bs/bs-datepicker-day BsDatepickerComponent, BsDaterangepickerComponent] }) export class BsDatepickerModule { + constructor() { + if (isDevMode()) { + console.warn(`BsDatepickerModule is under development, + BREAKING CHANGES are possible, + PLEASE, read changelog`); + } + } static forRoot(): ModuleWithProviders { return { ngModule: BsDatepickerModule, diff --git a/src/datepicker/date-formatter.ts b/src/datepicker/date-formatter.ts index e88650cec6..027d0afc84 100644 --- a/src/datepicker/date-formatter.ts +++ b/src/datepicker/date-formatter.ts @@ -1,7 +1,7 @@ -import moment from 'moment'; +import { formatDate } from '../bs-moment/format'; export class DateFormatter { - public format(date:Date, format:string):string { - return moment(date.getTime()).format(format); + public format(date: Date, format: string): string { + return formatDate(date, format); } } diff --git a/src/datepicker/themes/bs/bs-datepicker-day-decorator.directive.ts b/src/datepicker/themes/bs/bs-datepicker-day-decorator.directive.ts index 3a2d716769..9337eb765c 100644 --- a/src/datepicker/themes/bs/bs-datepicker-day-decorator.directive.ts +++ b/src/datepicker/themes/bs/bs-datepicker-day-decorator.directive.ts @@ -5,9 +5,6 @@ import { DayHoverEvent, DayViewModel } from '../../models/index'; selector: '[bsDatepickerDayDecorator]', changeDetection: ChangeDetectionStrategy.OnPush, host: { - '(click)': 'selectDay(day)', - '(mouseenter)': 'hoverDay(day, true)', - '(mouseleave)': 'hoverDay(day, false)', '[class.disabled]': 'day.isDisabled', '[class.is-highlighted]': 'day.isHovered', '[class.is-other-month]': 'day.isOtherMonth', @@ -20,15 +17,4 @@ import { DayHoverEvent, DayViewModel } from '../../models/index'; }) export class BsDatepickerDayDecoratorComponent { @Input() day: DayViewModel; - - @Output() onSelect = new EventEmitter(); - @Output() onHover = new EventEmitter(); - - selectDay(day: DayViewModel): void { - this.onSelect.emit(day); - } - - hoverDay(day: DayViewModel, isHovered: boolean): void { - this.onHover.emit({day, isHovered}); - } } diff --git a/src/datepicker/themes/bs/bs-datepicker-day-decorator.spec.ts b/src/datepicker/themes/bs/bs-datepicker-day-decorator.spec.ts new file mode 100644 index 0000000000..2280e7434c --- /dev/null +++ b/src/datepicker/themes/bs/bs-datepicker-day-decorator.spec.ts @@ -0,0 +1,76 @@ +import { Component } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { DayViewModel } from '../../models/index'; +import { BsDatepickerDayDecoratorComponent } from './bs-datepicker-day-decorator.directive'; + +function getDayElement(fixture: ComponentFixture): HTMLElement { + return fixture.nativeElement.querySelector('[bsDatepickerDayDecorator]') as HTMLElement; +} + +function setDay(fixture: ComponentFixture, day: Partial): void { + fixture.componentInstance.day = day as DayViewModel; + fixture.detectChanges(); +} + +describe('datepicker: [bsDatepickerDayDecorator]', () => { + let fixture: ComponentFixture; + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TestComponent, BsDatepickerDayDecoratorComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TestComponent); + }); + + it('should display date', () => { + // arrange + const label = 'some label'; + setDay(fixture, {label}); + const el = getDayElement(fixture); + // assert + expect(el.innerText).toBe(label); + }); + + it('should not add any special classes by default', () => { + fixture.detectChanges(); + const el = getDayElement(fixture); + expect(el).not.toHaveCssClass('disabled'); + expect(el).not.toHaveCssClass('is-highlighted'); + expect(el).not.toHaveCssClass('is-other-month'); + expect(el).not.toHaveCssClass('in-range'); + expect(el).not.toHaveCssClass('select-start'); + expect(el).not.toHaveCssClass('select-end'); + expect(el).not.toHaveCssClass('selected'); + }); + + it('should add classes corresponding to day state', () => { + setDay(fixture, { + isDisabled: true, + isHovered: true, + isOtherMonth: true, + isInRange: true, + isSelectionStart: true, + isSelectionEnd: true, + isSelected: true + }); + const el = getDayElement(fixture); + expect(el).toHaveCssClass('disabled'); + expect(el).toHaveCssClass('is-highlighted'); + expect(el).toHaveCssClass('is-other-month'); + expect(el).toHaveCssClass('in-range'); + expect(el).toHaveCssClass('select-start'); + expect(el).toHaveCssClass('select-end'); + expect(el).toHaveCssClass('selected'); + }); +}); + +@Component({ + selector: 'test-cmp', + template: `{{ day.label }}` +}) +class TestComponent { + day: DayViewModel = { } as DayViewModel; +} diff --git a/src/datepicker/themes/bs/bs-datepicker-day-view.component.ts b/src/datepicker/themes/bs/bs-datepicker-day-view.component.ts deleted file mode 100644 index 4aff0766c4..0000000000 --- a/src/datepicker/themes/bs/bs-datepicker-day-view.component.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { DayHoverEvent, DayViewModel } from '../../models/index'; - -@Component({ - selector: 'bs-datepicker-day-view', - changeDetection: ChangeDetectionStrategy.OnPush, - template: ` - {{day.label}} - ` -}) -export class BsDatepickerDayViewComponent { - @Input() day: DayViewModel; - - @Output() onSelect = new EventEmitter(); - @Output() onHover = new EventEmitter(); - - selectDay(day: DayViewModel): void { - this.onSelect.emit(day); - } - - hoverDay(day: DayViewModel, isHovered: boolean): void { - this.onHover.emit({day, isHovered}); - } -} diff --git a/src/datepicker/themes/bs/bs-datepicker-month-view.component.ts b/src/datepicker/themes/bs/bs-datepicker-month-view.component.ts index e38d03d508..41550c1f9b 100644 --- a/src/datepicker/themes/bs/bs-datepicker-month-view.component.ts +++ b/src/datepicker/themes/bs/bs-datepicker-month-view.component.ts @@ -20,9 +20,11 @@ import { DatepickerRenderOptions, DayHoverEvent, DayViewModel, MonthViewModel } {{ month.weekNumbers[i] }} - {{ day.label }} + {{ day.label }} @@ -40,8 +42,8 @@ export class BsDatepickerMonthViewComponent { this.onSelect.emit(event); } - hoverDay(event: DayHoverEvent): void { - this.onHover.emit(event); + hoverDay(day: DayViewModel, isHovered: boolean): void { + this.onHover.emit({day, isHovered}); } } diff --git a/src/datepicker/themes/bs/bs-datepicker-navigation-view.spec.ts b/src/datepicker/themes/bs/bs-datepicker-navigation-view.spec.ts new file mode 100644 index 0000000000..3326adf124 --- /dev/null +++ b/src/datepicker/themes/bs/bs-datepicker-navigation-view.spec.ts @@ -0,0 +1,108 @@ +import { Component } from '@angular/core'; +import { BsNavigationEvent, MonthViewModel } from '../../models/index'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BsDatepickerNavigationViewComponent } from './bs-datepicker-navigation-view.component'; + +type TestFixture = ComponentFixture; +const titleSelector = '.current'; +const prevNavSelector = '.previous'; +const nextNavSelector = '.next'; + +function getTitles(fixture: TestFixture): string[] { + const elements = fixture.nativeElement + .querySelectorAll(titleSelector); + + return [elements[0].innerText, elements[1].innerText]; +} + +function getPrevNavButton(fixture: TestFixture): HTMLElement { + return fixture.nativeElement + .querySelector(prevNavSelector) as HTMLElement; +} + +function getNextNavButton(fixture: TestFixture): HTMLElement { + return fixture.nativeElement + .querySelector(nextNavSelector) as HTMLElement; +} + +function getNavEvent(fixture: TestFixture): BsNavigationEvent { + return fixture.componentInstance._navTo; +} + +function setMonth(fixture: TestFixture, month: Partial): void { + fixture.componentInstance.month = month as MonthViewModel; + fixture.detectChanges(); +} + +describe('datepicker: bs-datepicker-navigation-view', () => { + let fixture: TestFixture; + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TestComponent, BsDatepickerNavigationViewComponent] + }) + .compileComponents(); + })); + beforeEach(() => { + fixture = TestBed.createComponent(TestComponent); + }); + + it('should display month and year titles', () => { + const monthTitle = 'Some month'; + const yearTitle = 'Some year'; + + setMonth(fixture, {monthTitle, yearTitle}); + const titles = getTitles(fixture); + + expect(titles[0]).toBe(monthTitle); + expect(titles[1]).toBe(yearTitle); + }); + + it('should display navigation buttons by default', () => { + const prev = getPrevNavButton(fixture); + const next = getNextNavButton(fixture); + setMonth(fixture, {}); + expect(prev.style.visibility).toBe('visible'); + expect(next.style.visibility).toBe('visible'); + }); + + it('should hide prev nav button', () => { + const prev = getPrevNavButton(fixture); + setMonth(fixture, {hideLeftArrow: true}); + expect(prev.style.visibility).toBe('hidden'); + }); + + it('should hide next nav button', () => { + const next = getNextNavButton(fixture); + setMonth(fixture, {hideRightArrow: true}); + expect(next.style.visibility).toBe('hidden'); + }); + + it('on prev nav button click should decrease month on 1', () => { + const prev = getPrevNavButton(fixture); + prev.click(); + expect(getNavEvent(fixture).step.month).toBe(-1); + }); + + it('on next nav button click should increase month on 1', () => { + const next = getNextNavButton(fixture); + next.click(); + expect(getNavEvent(fixture).step.month).toBe(1); + }); +}); + +@Component({ + selector: 'test-cmp', + template: ` + ` +}) +class TestComponent { + month: MonthViewModel; + _navTo: BsNavigationEvent; + + navTo(event: BsNavigationEvent): void { + this._navTo = event; + } +} diff --git a/src/datepicker/utils/bs-calendar-utils.ts b/src/datepicker/utils/bs-calendar-utils.ts index 6f28d02fae..545b1d281c 100644 --- a/src/datepicker/utils/bs-calendar-utils.ts +++ b/src/datepicker/utils/bs-calendar-utils.ts @@ -7,5 +7,6 @@ export function getStartingDayOfCalendar(date: Date, options: {firstDayOfWeek?: } const weekDay = getDayOfWeek(date); + return changeDate(date, {day: -weekDay}); } diff --git a/src/modal/modal.component.ts b/src/modal/modal.component.ts index 05e1832e21..c13ceaaebf 100644 --- a/src/modal/modal.component.ts +++ b/src/modal/modal.component.ts @@ -71,8 +71,8 @@ export class ModalDirective implements AfterViewInit, OnDestroy { protected originalBodyPadding: number = 0; protected scrollbarWidth: number = 0; - protected timerHideModal: number = 0; - protected timerRmBackDrop: number = 0; + protected timerHideModal: any = 0; + protected timerRmBackDrop: any = 0; // constructor props protected _element: ElementRef; diff --git a/src/package.json b/src/package.json index 3650a7e8f1..e2a873f781 100644 --- a/src/package.json +++ b/src/package.json @@ -1,8 +1,5 @@ { "name": "ngx-bootstrap", - "dependencies": { - "moment": "*" - }, "peerDependencies": { "@angular/common": "*", "@angular/compiler": "*", diff --git a/src/tsconfig.spec.json b/src/tsconfig.spec.json index af0829c3ec..65cfed7154 100644 --- a/src/tsconfig.spec.json +++ b/src/tsconfig.spec.json @@ -11,7 +11,7 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true, "typeRoots": [ - "node_modules/@types" + "../node_modules/@types" ], "paths": { "ngx-bootstrap*": [ @@ -29,7 +29,7 @@ }, "include": [ "../scripts/typings.d.ts", - "./spec/**/*.spec.ts", + "**/*.spec.ts", "**/*.d.ts" ], "exclude": [