From 3ab84ef24796d74d29f1457639cdccc018b46a52 Mon Sep 17 00:00:00 2001 From: Wilson Zeng Date: Tue, 31 Jul 2018 15:25:05 +0800 Subject: [PATCH] feat(module:date-picker): refactoring and add new feature of year-picker close #416 --- components/date-picker/date-picker.module.ts | 10 +- components/date-picker/demo/basic.ts | 4 +- components/date-picker/doc/index.en-US.md | 14 +- components/date-picker/doc/index.zh-CN.md | 14 +- ...nent.html => header-picker.component.html} | 4 +- .../date-picker/header-picker.component.ts | 80 ++++ .../lib/calendar/calendar-header.component.ts | 26 +- .../lib/lib-packer-supplements.spec.ts | 16 +- .../date-picker/month-picker.component.ts | 56 +-- .../date-picker/year-picker.component.spec.ts | 423 ++++++++++++++++++ .../date-picker/year-picker.component.ts | 23 + 11 files changed, 586 insertions(+), 84 deletions(-) rename components/date-picker/{month-picker.component.html => header-picker.component.html} (91%) create mode 100644 components/date-picker/header-picker.component.ts create mode 100644 components/date-picker/year-picker.component.spec.ts create mode 100644 components/date-picker/year-picker.component.ts diff --git a/components/date-picker/date-picker.module.ts b/components/date-picker/date-picker.module.ts index e1719c18697..0d69ab72471 100644 --- a/components/date-picker/date-picker.module.ts +++ b/components/date-picker/date-picker.module.ts @@ -6,10 +6,12 @@ import { LibPackerModule } from './lib/lib-packer.module'; import { NzDatePickerComponent } from './date-picker.component'; import { DateRangePickerComponent } from './date-range-picker.component'; +import { HeaderPickerComponent } from './header-picker.component'; import { NzMonthPickerComponent } from './month-picker.component'; import { NzPickerComponent } from './picker.component'; import { NzRangePickerComponent } from './range-picker.component'; import { NzWeekPickerComponent } from './week-picker.component'; +import { NzYearPickerComponent } from './year-picker.component'; @NgModule({ imports: [ @@ -22,15 +24,19 @@ import { NzWeekPickerComponent } from './week-picker.component'; NzDatePickerComponent, NzRangePickerComponent, NzMonthPickerComponent, + NzYearPickerComponent, NzWeekPickerComponent ], declarations: [ + HeaderPickerComponent, DateRangePickerComponent, + NzPickerComponent, + NzDatePickerComponent, NzMonthPickerComponent, + NzYearPickerComponent, NzWeekPickerComponent, - NzRangePickerComponent, - NzPickerComponent + NzRangePickerComponent ], providers: [] }) diff --git a/components/date-picker/demo/basic.ts b/components/date-picker/demo/basic.ts index 3e4625af7fe..527f66195d4 100644 --- a/components/date-picker/demo/basic.ts +++ b/components/date-picker/demo/basic.ts @@ -9,6 +9,8 @@ import { en_US, zh_CN, NzI18nService } from 'ng-zorro-antd';

+ +

@@ -16,7 +18,7 @@ import { en_US, zh_CN, NzI18nService } from 'ng-zorro-antd'; `, styles : [ ` - nz-date-picker, nz-month-picker, nz-range-picker, nz-week-picker { + nz-date-picker, nz-month-picker, nz-year-picker, nz-range-picker, nz-week-picker { margin: 0 8px 12px 0; } ` ] diff --git a/components/date-picker/doc/index.en-US.md b/components/date-picker/doc/index.en-US.md index 624941f4706..5a4790f1f20 100644 --- a/components/date-picker/doc/index.en-US.md +++ b/components/date-picker/doc/index.en-US.md @@ -39,9 +39,9 @@ The following APIs are shared by nz-date-picker, nz-month-picker, nz-range-picke | `[nzAllowClear]` | Whether to show clear button | boolean | true | | `[nzAutoFocus]` | get focus when component mounted | boolean | false | | `[nzClassName]` | picker className | string | '' | -| `[nzDateRender]` | custom rendering function for date cells (Not support by nz-month-picker) | TemplateRef<Date> / string or (d: Date) => TemplateRef<Date> / string | - | +| `[nzDateRender]` | custom rendering function for date cells (Not support by month-picker/year-picker) | TemplateRef<Date> / string or (d: Date) => TemplateRef<Date> / string | - | | `[nzDisabled]` | determine whether the nz-date-picker is disabled | boolean | false | -| `[nzDisabledDate]` | specify the date that cannot be selected | (current: Date) => boolean | - | +| `[nzDisabledDate]` | specify the date that cannot be selected (Not support by year-picker) | (current: Date) => boolean | - | | `[nzLocale]` | localization configuration | object | [default](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) | | `[nzOpen]` | open state of picker | boolean | - | | `[nzPopupStyle]` | to customize the style of the popup calendar | object | {} | @@ -64,6 +64,16 @@ The following APIs are shared by nz-date-picker, nz-month-picker, nz-range-picke | `(nzOnOk)` | callback when click ok button | `EventEmitter` | - | | `(ngModelChange)` | Date change callback | `EventEmitter` | - | +### nz-year-picker + +| Property | Description | Type | Default | +| -------- | ----------- | ---- | ------- | +| `[ngModel]` | Date | Date | - | +| `[nzFormat]` | to set the date format, refer to [DatePipe](https://angular.io/api/common/DatePipe) | string | "yyyy" | +| `[nzRenderExtraFooter]` | render extra footer in panel | TemplateRef / string or () => TemplateRef / string | - | +| `[nzPlaceHolder]` | placeholder of date input | string | - | +| `(ngModelChange)` | Date change callback | `EventEmitter` | - | + ### nz-month-picker | Property | Description | Type | Default | diff --git a/components/date-picker/doc/index.zh-CN.md b/components/date-picker/doc/index.zh-CN.md index 70229ec7348..bd59b8e989e 100644 --- a/components/date-picker/doc/index.zh-CN.md +++ b/components/date-picker/doc/index.zh-CN.md @@ -40,9 +40,9 @@ registerLocaleData(zh); | `[nzAllowClear]` | 是否显示清除按钮 | boolean | true | | `[nzAutoFocus]` | 自动获取焦点 | boolean | false | | `[nzClassName]` | 选择器 className | string | '' | -| `[nzDateRender]` | 自定义日期单元格的内容(nz-month-picker不支持) | TemplateRef<Date> / string or (d: Date) => TemplateRef<Date> / string | - | +| `[nzDateRender]` | 自定义日期单元格的内容(month-picker/year-picker不支持) | TemplateRef<Date> / string or (d: Date) => TemplateRef<Date> / string | - | | `[nzDisabled]` | 禁用 | boolean | false | -| `[nzDisabledDate]` | 不可选择的日期 | (current: Date) => boolean | 无 | +| `[nzDisabledDate]` | 不可选择的日期(year-picker不支持) | (current: Date) => boolean | 无 | | `[nzLocale]` | 国际化配置 | object | [默认配置](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) | | `[nzOpen]` | 控制弹层是否展开 | boolean | - | | `[nzPopupStyle]` | 额外的弹出日历样式 | object | {} | @@ -65,6 +65,16 @@ registerLocaleData(zh); | `(nzOnOk)` | 点击确定按钮的回调 | `EventEmitter` | - | | `(ngModelChange)` | 时间发生变化的回调 | `EventEmitter` | 无 | +### nz-year-picker + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| `[ngModel]` | 日期 | Date | 无 | +| `[nzFormat]` | 展示的日期格式,配置参考 [DatePipe](https://angular.io/api/common/DatePipe) | string | "yyyy" | +| `[nzRenderExtraFooter]` | 在面板中添加额外的页脚 | TemplateRef / string or () => TemplateRef / string | - | +| `[nzPlaceHolder]` | 输入框提示文字 | string | - | +| `(ngModelChange)` | 时间发生变化的回调 | `EventEmitter` | 无 | + ### nz-month-picker | 参数 | 说明 | 类型 | 默认值 | diff --git a/components/date-picker/month-picker.component.html b/components/date-picker/header-picker.component.html similarity index 91% rename from components/date-picker/month-picker.component.html rename to components/date-picker/header-picker.component.html index e23c8bbff4b..fed362f5a35 100644 --- a/components/date-picker/month-picker.component.html +++ b/components/date-picker/header-picker.component.html @@ -19,12 +19,12 @@
| string>; + @Input() nzDefaultValue: CandyDate; + @Input() nzFormat: string; // [Canmplemented by sub class] The output format + + endPanelMode: SupportHeaderPanel; // [Implemented by sub class] The final panel for picking a date + panelMode: PanelMode; // Current panel mode + extraFooter: TemplateRef | string; + + private supportPanels: PanelMode[]; + + constructor(i18n: NzI18nService) { + super(i18n); + } + + ngOnInit(): void { + super.ngOnInit(); + + this.panelMode = this.endPanelMode; + + const allHeaderPanels: PanelMode[] = [ 'decade', 'year', 'month' ]; + this.supportPanels = allHeaderPanels.slice(0, allHeaderPanels.indexOf(this.endPanelMode) + 1); + } + + ngOnChanges(changes: SimpleChanges): void { + super.ngOnChanges(changes); + + if (changes.nzRenderExtraFooter) { + this.extraFooter = valueFunctionProp(this.nzRenderExtraFooter); + } + } + + onPanelModeChange(mode: PanelMode): void { + if (this.supportPanels.indexOf(mode) > -1) { + this.panelMode = mode; + } else { // Since the default "click year" logic can be "year panel" -> "date panel", we need force to the end panel otherwise + this.panelMode = this.endPanelMode; + } + } + + onChooseValue(mode: SupportHeaderPanel, value: CandyDate): void { + if (this.endPanelMode === mode) { + super.onValueChange(value); + + this.closeOverlay(); + } + } + + onOpenChange(open: boolean): void { + if (!open) { + this.cleanUp(); + } + this.nzOnOpenChange.emit(open); + } + + // Restore some initial props to let open as new in next time + private cleanUp(): void { + this.panelMode = this.endPanelMode; + } +} + +export type SupportHeaderPanel = 'year' | 'month'; diff --git a/components/date-picker/lib/calendar/calendar-header.component.ts b/components/date-picker/lib/calendar/calendar-header.component.ts index 9334e75843e..5e8ce8fdf2a 100644 --- a/components/date-picker/lib/calendar/calendar-header.component.ts +++ b/components/date-picker/lib/calendar/calendar-header.component.ts @@ -16,7 +16,6 @@ export class CalendarHeaderComponent implements OnInit, OnChanges { @Input() enableNext: boolean = true; @Input() disabledMonth: (date: Date) => boolean; @Input() showTimePicker: boolean = false; - @Input() forceToMonth: boolean = false; // Force change panel to month without value change when choose a year/decade @Input() value: CandyDate; @Output() valueChange = new EventEmitter(); @@ -24,6 +23,10 @@ export class CalendarHeaderComponent implements OnInit, OnChanges { @Input() panelMode: PanelMode; @Output() panelModeChange = new EventEmitter(); + @Output() chooseDecade = new EventEmitter(); + @Output() chooseYear = new EventEmitter(); + @Output() chooseMonth = new EventEmitter(); + prefixCls: string = 'ant-calendar'; yearMonthDaySelectors: YearMonthDaySelector[]; @@ -60,8 +63,6 @@ export class CalendarHeaderComponent implements OnInit, OnChanges { } changePanel(mode: PanelMode, value?: CandyDate): void { - // this.panelMode = mode; - // this.panelModeChange.emit(this.panelMode); this.panelModeChange.emit(mode); if (value) { this.changeValueFromInside(value); @@ -69,27 +70,20 @@ export class CalendarHeaderComponent implements OnInit, OnChanges { } onChooseDecade(value: CandyDate): void { - if (this.forceToMonth) { - this.value = value; // Change internal value only - this.changePanel('year'); - } else { - this.changePanel('year', value); - } + this.changePanel('year', value); + this.chooseDecade.emit(value); } onChooseYear(value: CandyDate): void { - if (this.forceToMonth) { - this.value = value; // Change internal value only - this.changePanel('month'); - } else { - this.changePanel(this.yearToMonth ? 'month' : 'date', value); - this.yearToMonth = false; // Clear - } + this.changePanel(this.yearToMonth ? 'month' : 'date', value); + this.yearToMonth = false; // Clear + this.chooseYear.emit(value); } onChooseMonth(value: CandyDate): void { this.changePanel('date', value); this.yearToMonth = false; // Clear + this.chooseMonth.emit(value); } changeToMonthPanel(): void { diff --git a/components/date-picker/lib/lib-packer-supplements.spec.ts b/components/date-picker/lib/lib-packer-supplements.spec.ts index 8d5b1e98cb3..e1fbf818c61 100644 --- a/components/date-picker/lib/lib-packer-supplements.spec.ts +++ b/components/date-picker/lib/lib-packer-supplements.spec.ts @@ -44,14 +44,14 @@ describe('Coverage supplements', () => { expect(componentInstance.render).not.toHaveBeenCalled(); }); - it('should work with forceToMonth branch', () => { - componentInstance.forceToMonth = true; - spyOn(componentInstance, 'changePanel'); - componentInstance.onChooseDecade(new CandyDate()); - expect(componentInstance.changePanel).toHaveBeenCalledWith('year'); - componentInstance.onChooseYear(new CandyDate()); - expect(componentInstance.changePanel).toHaveBeenCalledWith('month'); - }); + // it('should work with forceToMonth branch', () => { + // componentInstance.forceToMonth = true; + // spyOn(componentInstance, 'changePanel'); + // componentInstance.onChooseDecade(new CandyDate()); + // expect(componentInstance.changePanel).toHaveBeenCalledWith('year'); + // componentInstance.onChooseYear(new CandyDate()); + // expect(componentInstance.changePanel).toHaveBeenCalledWith('month'); + // }); it('should step into yearToMonth branch', () => { const testDate = new CandyDate(); diff --git a/components/date-picker/month-picker.component.ts b/components/date-picker/month-picker.component.ts index e9015c67cec..169f0ab9ea9 100644 --- a/components/date-picker/month-picker.component.ts +++ b/components/date-picker/month-picker.component.ts @@ -1,16 +1,11 @@ -import { forwardRef, Component, Input, OnChanges, SimpleChanges, TemplateRef } from '@angular/core'; +import { forwardRef, Component, Input } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; -import { FunctionProp } from '../core/types/common-wrap'; -import { valueFunctionProp } from '../core/util/convert'; -import { NzI18nService } from '../i18n/nz-i18n.service'; -import { AbstractPickerComponent } from './abstract-picker.component'; -import { CandyDate } from './lib/candy-date'; -import { PanelMode } from './standard-types'; +import { HeaderPickerComponent, SupportHeaderPanel } from './header-picker.component'; @Component({ selector: 'nz-month-picker', - templateUrl: './month-picker.component.html', + templateUrl: './header-picker.component.html', providers: [{ provide: NG_VALUE_ACCESSOR, multi: true, @@ -21,49 +16,8 @@ import { PanelMode } from './standard-types'; } }) -export class NzMonthPickerComponent extends AbstractPickerComponent implements OnChanges { - @Input() nzPlaceHolder: string; - - @Input() nzRenderExtraFooter: FunctionProp | string>; - @Input() nzDefaultValue: CandyDate; +export class NzMonthPickerComponent extends HeaderPickerComponent { @Input() nzFormat: string = 'yyyy-MM'; - panelMode: PanelMode = 'month'; - extraFooter: TemplateRef | string; - - constructor(i18n: NzI18nService) { - super(i18n); - } - - ngOnChanges(changes: SimpleChanges): void { - super.ngOnChanges(changes); - - if (changes.nzRenderExtraFooter) { - this.extraFooter = valueFunctionProp(this.nzRenderExtraFooter); - } - } - - onPanelModeChange(mode: PanelMode): void { - if (mode !== 'date') { - this.panelMode = mode; - } - } - - onValueChange(value: CandyDate): void { - super.onValueChange(value); - - this.closeOverlay(); - } - - onOpenChange(open: boolean): void { - if (!open) { - this.cleanUp(); - } - this.nzOnOpenChange.emit(open); - } - - // Restore some initial props to let open as new in next time - private cleanUp(): void { - this.panelMode = 'month'; - } + endPanelMode: SupportHeaderPanel = 'month'; } diff --git a/components/date-picker/year-picker.component.spec.ts b/components/date-picker/year-picker.component.spec.ts new file mode 100644 index 00000000000..23834df1b32 --- /dev/null +++ b/components/date-picker/year-picker.component.spec.ts @@ -0,0 +1,423 @@ +import { OverlayContainer } from '@angular/cdk/overlay'; +import { registerLocaleData } from '@angular/common'; +import zh from '@angular/common/locales/zh'; +import { Component, DebugElement, TemplateRef, ViewChild } from '@angular/core'; +import { fakeAsync, flush, inject, tick, ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule } from '@angular/forms'; +import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import * as isBefore from 'date-fns/is_before'; +import { dispatchMouseEvent } from '../core/testing'; +import { NzDatePickerModule } from './date-picker.module'; +import { CandyDate } from './lib/candy-date'; +import { PickerResultSingle } from './standard-types'; + +registerLocaleData(zh); + +describe('NzYearPickerComponent', () => { + let fixture: ComponentFixture; + let fixtureInstance: NzTestYearPickerComponent; + let debugElement: DebugElement; + let overlayContainer: OverlayContainer; + let overlayContainerElement: HTMLElement; + + beforeEach(fakeAsync(() => { + TestBed.configureTestingModule({ + imports : [ FormsModule, NoopAnimationsModule, NzDatePickerModule ], + providers : [], + declarations: [ + NzTestYearPickerComponent + ] + }); + + TestBed.compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NzTestYearPickerComponent); + fixtureInstance = fixture.componentInstance; + debugElement = fixture.debugElement; + }); + + beforeEach(inject([ OverlayContainer ], (oc: OverlayContainer) => { + overlayContainer = oc; + overlayContainerElement = oc.getContainerElement(); + })); + + afterEach(() => { + // overlayContainer.ngOnDestroy(); + }); + + describe('general api testing', () => { + beforeEach(() => fixtureInstance.useSuite = 1); + + it('should open by click and close by click at outside', fakeAsync(() => { + fixture.detectChanges(); + dispatchMouseEvent(getPickerTrigger(), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(getPickerContainer()).not.toBeNull(); + + dispatchMouseEvent(queryFromOverlay('.cdk-overlay-backdrop'), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(getPickerContainer()).toBeNull(); + })); + + it('should support nzAllowClear and work properly', fakeAsync(() => { + const clearBtnSelector = By.css('nz-picker i.ant-calendar-picker-clear'); + const initial = fixtureInstance.nzValue = new Date(); + fixtureInstance.nzAllowClear = false; + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(debugElement.query(clearBtnSelector)).toBeFalsy(); + + fixtureInstance.nzAllowClear = true; + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(fixtureInstance.nzValue).toBe(initial); + expect(debugElement.query(clearBtnSelector)).toBeDefined(); + + const nzOnChange = spyOn(fixtureInstance, 'nzOnChange'); + debugElement.query(clearBtnSelector).nativeElement.click(); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(fixtureInstance.nzValue).toBe(initial); + expect(nzOnChange).toHaveBeenCalledWith(null); + expect(debugElement.query(clearBtnSelector)).toBeFalsy(); + })); + + it('should support nzAutoFocus', () => { + fixtureInstance.nzAutoFocus = true; + fixture.detectChanges(); + expect(getPickerTrigger() === document.activeElement).toBeTruthy(); + }); + + it('should support nzDisabled', fakeAsync(() => { + // Make sure picker clear button shown up + fixtureInstance.nzAllowClear = true; + fixtureInstance.nzValue = new Date(); + + fixtureInstance.nzDisabled = true; + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + expect(debugElement.query(By.css('nz-picker .ant-input-disabled'))).toBeDefined(); + expect(debugElement.query(By.css('nz-picker i.ant-calendar-picker-clear'))).toBeNull(); + + fixtureInstance.nzDisabled = false; + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + expect(debugElement.query(By.css('nz-picker .ant-input-disabled'))).toBeNull(); + expect(debugElement.query(By.css('nz-picker i.ant-calendar-picker-clear'))).toBeDefined(); + })); + + it('should support nzOpen if assigned', fakeAsync(() => { + fixtureInstance.useSuite = 2; + + fixture.detectChanges(); + fixture.whenRenderingDone().then(() => { + expect(getPickerContainer()).toBeNull(); + + fixtureInstance.nzOpen = true; + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(getPickerContainer()).not.toBeNull(); + expect(queryFromOverlay('.cdk-overlay-backdrop')).toBeNull(); + // dispatchMouseEvent(queryFromOverlay('.cdk-overlay-backdrop'), 'click'); + // expect(getPickerContainer()).not.toBeNull(); + + fixtureInstance.nzOpen = false; + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(getPickerContainer()).toBeNull(); + }); + })); + + it('should support nzClassName', () => { + const className = fixtureInstance.nzClassName = 'my-test-class'; + fixture.detectChanges(); + const picker = debugElement.query(By.css('.ant-calendar-picker')).nativeElement as HTMLElement; + expect(picker.classList.contains(className)).toBeTruthy(); + }); + + it('should support nzLocale', () => { + const featureKey = 'TEST_PLACEHOLDER'; + fixtureInstance.nzLocale = { lang: { placeholder: featureKey } }; + fixture.detectChanges(); + expect(getPickerTrigger().getAttribute('placeholder')).toBe(featureKey); + }); + + it('should support nzPlaceHolder', () => { + const featureKey = fixtureInstance.nzPlaceHolder = 'TEST_PLACEHOLDER'; + fixture.detectChanges(); + expect(getPickerTrigger().getAttribute('placeholder')).toBe(featureKey); + }); + + it('should support nzPopupStyle', fakeAsync(() => { + fixtureInstance.nzPopupStyle = { color: 'red' }; + fixture.detectChanges(); + dispatchMouseEvent(getPickerTrigger(), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(getPickerContainer().style.color).toBe('red'); + })); + + it('should support nzDropdownClassName', fakeAsync(() => { + const keyCls = fixtureInstance.nzDropdownClassName = 'my-test-class'; + fixture.detectChanges(); + dispatchMouseEvent(getPickerTrigger(), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(getPickerContainer().classList.contains(keyCls)).toBeTruthy(); + })); + + it('should support nzSize', () => { + fixtureInstance.nzSize = 'large'; + fixture.detectChanges(); + expect(getPicker().classList.contains('ant-calendar-picker-large')).toBeTruthy(); + + fixtureInstance.nzSize = 'small'; + fixture.detectChanges(); + expect(getPicker().classList.contains('ant-calendar-picker-small')).toBeTruthy(); + }); + + it('should support nzStyle', () => { + fixtureInstance.nzStyle = { color: 'blue' }; + fixture.detectChanges(); + expect(getPicker().style.color).toBe('blue'); + }); + + it('should support nzOnOpenChange', () => { + const nzOnOpenChange = spyOn(fixtureInstance, 'nzOnOpenChange'); + fixture.detectChanges(); + dispatchMouseEvent(getPickerTrigger(), 'click'); + fixture.detectChanges(); + expect(nzOnOpenChange).toHaveBeenCalledWith(true); + + dispatchMouseEvent(queryFromOverlay('.cdk-overlay-backdrop'), 'click'); + fixture.detectChanges(); + expect(nzOnOpenChange).toHaveBeenCalledWith(false); + expect(nzOnOpenChange).toHaveBeenCalledTimes(2); + }); + it('should support nzValue', fakeAsync(() => { + fixtureInstance.nzValue = new Date('2018-11-22'); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + dispatchMouseEvent(getPickerTrigger(), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(getSelectedYearCell().textContent).toContain('2018'); + })); + + it('should support nzOnChange', fakeAsync(() => { + fixtureInstance.nzValue = new Date('2018-11'); + const nzOnChange = spyOn(fixtureInstance, 'nzOnChange'); + fixture.detectChanges(); + dispatchMouseEvent(getPickerTrigger(), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + + const cell = getSecondYearCell(); // Use the second cell + const cellText = cell.textContent.trim(); + dispatchMouseEvent(cell, 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(nzOnChange).toHaveBeenCalled(); + const result = nzOnChange.calls.allArgs()[ 0 ][ 0 ]; + expect(result.getFullYear()).toBe(parseInt(cellText, 10)); + })); + + }); // /general api testing + + describe('panel switch and move forward/afterward', () => { + beforeEach(() => fixtureInstance.useSuite = 1); + + it('should support decade panel changes', fakeAsync(() => { + fixtureInstance.nzValue = new Date('2018-11'); + fixture.detectChanges(); + openPickerByClickTrigger(); + // Click to show decade panel + dispatchMouseEvent(queryFromOverlay('.ant-calendar-year-panel-decade-select'), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(queryFromOverlay('.ant-calendar-decade-panel')).toBeDefined(); + // Goto previous decade + dispatchMouseEvent(queryFromOverlay('.ant-calendar-decade-panel-prev-century-btn'), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(queryFromOverlay('.ant-calendar-decade-panel-century').textContent).toContain('1900'); + expect(queryFromOverlay('.ant-calendar-decade-panel-century').textContent).toContain('1999'); + // Goto next decade * 2 + dispatchMouseEvent(queryFromOverlay('.ant-calendar-decade-panel-next-century-btn'), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + dispatchMouseEvent(queryFromOverlay('.ant-calendar-decade-panel-next-century-btn'), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(queryFromOverlay('.ant-calendar-decade-panel-century').textContent).toContain('2100'); + expect(queryFromOverlay('.ant-calendar-decade-panel-century').textContent).toContain('2199'); + })); + + }); // /panel switch and move forward/afterward + + describe('specified date picker testing', () => { + beforeEach(() => fixtureInstance.useSuite = 1); + + it('should support nzRenderExtraFooter', fakeAsync(() => { + fixtureInstance.nzRenderExtraFooter = () => fixtureInstance.tplExtraFooter; + fixture.detectChanges(); + + openPickerByClickTrigger(); + expect(overlayContainerElement.textContent.indexOf('TEST_EXTRA_FOOTER') > -1).toBeTruthy(); + + fixtureInstance.nzRenderExtraFooter = 'TEST_EXTRA_FOOTER_STRING'; + fixture.detectChanges(); + expect(overlayContainerElement.textContent.indexOf(fixtureInstance.nzRenderExtraFooter) > -1).toBeTruthy(); + })); + + }); // /specified date picker testing + + describe('ngModel value accesors', () => { + beforeEach(() => fixtureInstance.useSuite = 3); + + it('should specified date provide by "modelValue" be choosed', fakeAsync(() => { + fixtureInstance.modelValue = new Date('2018-11'); + fixture.detectChanges(); + flush(); // Wait writeValue() tobe done + fixture.detectChanges(); + expect(getSelectedYearCell().textContent).toContain('2018'); + + // Click the first cell to change ngModel + const cell = getSecondYearCell(); + const cellText = cell.textContent.trim(); + dispatchMouseEvent(cell, 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect(fixtureInstance.modelValue.getFullYear()).toBe(parseInt(cellText, 10)); + })); + }); + + //////////// + + function getPicker(): HTMLElement { + return debugElement.query(By.css('nz-picker .ant-calendar-picker')).nativeElement as HTMLElement; + } + + function getPickerTrigger(): HTMLInputElement { + return debugElement.query(By.css('nz-picker input.ant-calendar-picker-input')).nativeElement as HTMLInputElement; + } + + function getPickerContainer(): HTMLElement { + return queryFromOverlay('.ant-calendar-picker-container') as HTMLElement; + } + + function getSelectedYearCell(): HTMLElement { + return queryFromOverlay('tbody.ant-calendar-year-panel-tbody td.ant-calendar-year-panel-selected-cell') as HTMLElement; + } + + function getSecondYearCell(): HTMLElement { + return queryFromOverlay('tbody.ant-calendar-year-panel-tbody td.ant-calendar-year-panel-cell:nth-child(2)') as HTMLElement; + } + + function queryFromOverlay(selector: string): HTMLElement { + return overlayContainerElement.querySelector(selector) as HTMLElement; + } + + function openPickerByClickTrigger(): void { + dispatchMouseEvent(getPickerTrigger(), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + } + +}); + +@Component({ + template: ` + + + + + TEST_EXTRA_FOOTER + + + + + + + + + + ` +}) +class NzTestYearPickerComponent { + useSuite: 1 | 2 | 3; + @ViewChild('tplExtraFooter') tplExtraFooter: TemplateRef; + + // --- Suite 1 + nzAllowClear; + nzAutoFocus; + nzDisabled; + nzClassName; + nzLocale; + nzPlaceHolder; + nzPopupStyle; + nzDropdownClassName; + nzSize; + nzStyle; + + nzOnOpenChange(d: CandyDate): void { + } + + nzOnChange(result: PickerResultSingle): void { + } + + nzValue; + + nzRenderExtraFooter; + + // --- Suite 2 + nzOpen; + + // --- Suite 3 + modelValue; +} diff --git a/components/date-picker/year-picker.component.ts b/components/date-picker/year-picker.component.ts new file mode 100644 index 00000000000..5d06a074de7 --- /dev/null +++ b/components/date-picker/year-picker.component.ts @@ -0,0 +1,23 @@ +import { forwardRef, Component, Input } from '@angular/core'; +import { NG_VALUE_ACCESSOR } from '@angular/forms'; + +import { HeaderPickerComponent, SupportHeaderPanel } from './header-picker.component'; + +@Component({ + selector: 'nz-year-picker', + templateUrl: './header-picker.component.html', + providers: [{ + provide: NG_VALUE_ACCESSOR, + multi: true, + useExisting: forwardRef(() => NzYearPickerComponent) + }], + host : { + '[class.ant-checkbox-group]': 'true' + } +}) + +export class NzYearPickerComponent extends HeaderPickerComponent { + @Input() nzFormat: string = 'yyyy'; + + endPanelMode: SupportHeaderPanel = 'year'; +}