diff --git a/src/lib/datepicker/datepicker.spec.ts b/src/lib/datepicker/datepicker.spec.ts index 81576b717270..85ce44aae18d 100644 --- a/src/lib/datepicker/datepicker.spec.ts +++ b/src/lib/datepicker/datepicker.spec.ts @@ -28,7 +28,6 @@ import {MatDatepickerInput} from './datepicker-input'; import {MatDatepickerToggle} from './datepicker-toggle'; import {MatDatepickerIntl, MatDatepickerModule} from './index'; - describe('MatDatepicker', () => { const SUPPORTS_INTL = typeof Intl != 'undefined'; @@ -113,6 +112,18 @@ describe('MatDatepicker', () => { .not.toBeNull(); }); + it('should open datepicker if opened input is set to true', () => { + testComponent.opened = true; + fixture.detectChanges(); + + expect(document.querySelector('.mat-datepicker-content')).not.toBeNull(); + + testComponent.opened = false; + fixture.detectChanges(); + + expect(document.querySelector('.mat-datepicker-content')).toBeNull(); + }); + it('open in disabled mode should not open the calendar', () => { testComponent.disabled = true; fixture.detectChanges(); @@ -151,9 +162,7 @@ describe('MatDatepicker', () => { testComponent.datepicker.close(); fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(parseInt(getComputedStyle(popup).height as string)).toBe(0); - }); + expect(parseInt(getComputedStyle(popup).height as string)).toBe(0); }); it('should close the popup when pressing ESCAPE', () => { @@ -177,7 +186,7 @@ describe('MatDatepicker', () => { .toBe(true, 'Expected default ESCAPE action to be prevented.'); }); - it('close should close dialog', () => { + it('close should close dialog', async(() => { testComponent.touch = true; fixture.detectChanges(); @@ -192,9 +201,9 @@ describe('MatDatepicker', () => { fixture.whenStable().then(() => { expect(document.querySelector('mat-dialog-container')).toBeNull(); }); - }); + })); - it('setting selected should update input and close calendar', () => { + it('setting selected should update input and close calendar', async(() => { testComponent.touch = true; fixture.detectChanges(); @@ -212,12 +221,13 @@ describe('MatDatepicker', () => { expect(document.querySelector('mat-dialog-container')).toBeNull(); expect(testComponent.datepickerInput.value).toEqual(new Date(2020, JAN, 2)); }); - }); + })); it('clicking the currently selected date should close the calendar ' + 'without firing selectedChanged', () => { const selectedChangedSpy = spyOn(testComponent.datepicker.selectedChanged, 'emit').and.callThrough(); + for (let changeCount = 1; changeCount < 3; changeCount++) { const currentDay = changeCount; testComponent.datepicker.open(); @@ -231,11 +241,9 @@ describe('MatDatepicker', () => { fixture.detectChanges(); } - fixture.whenStable().then(() => { - expect(selectedChangedSpy.calls.count()).toEqual(1); - expect(document.querySelector('mat-dialog-container')).toBeNull(); - expect(testComponent.datepickerInput.value).toEqual(new Date(2020, JAN, 2)); - }); + expect(selectedChangedSpy.calls.count()).toEqual(1); + expect(document.querySelector('mat-dialog-container')).toBeNull(); + expect(testComponent.datepickerInput.value).toEqual(new Date(2020, JAN, 2)); }); it('startAt should fallback to input value', () => { @@ -479,7 +487,7 @@ describe('MatDatepicker', () => { expect(inputEl.classList).toContain('ng-touched'); }); - it('should mark input touched on calendar selection', () => { + it('should mark input touched on calendar selection', async(() => { let inputEl = fixture.debugElement.query(By.css('input')).nativeElement; expect(inputEl.classList).toContain('ng-untouched'); @@ -492,7 +500,7 @@ describe('MatDatepicker', () => { expect(inputEl.classList).toContain('ng-touched'); }); - }); + })); }); describe('datepicker with formControl', () => { @@ -702,7 +710,7 @@ describe('MatDatepicker', () => { expect(testComponent.datepicker._maxDate).toEqual(new Date(2020, JAN, 1)); }); - it('should mark invalid when value is before min', () => { + it('should mark invalid when value is before min', async(() => { testComponent.date = new Date(2009, DEC, 31); fixture.detectChanges(); @@ -712,9 +720,9 @@ describe('MatDatepicker', () => { expect(fixture.debugElement.query(By.css('input')).nativeElement.classList) .toContain('ng-invalid'); }); - }); + })); - it('should mark invalid when value is after max', () => { + it('should mark invalid when value is after max', async(() => { testComponent.date = new Date(2020, JAN, 2); fixture.detectChanges(); @@ -724,9 +732,9 @@ describe('MatDatepicker', () => { expect(fixture.debugElement.query(By.css('input')).nativeElement.classList) .toContain('ng-invalid'); }); - }); + })); - it('should not mark invalid when value equals min', () => { + it('should not mark invalid when value equals min', async(() => { testComponent.date = testComponent.datepicker._minDate; fixture.detectChanges(); @@ -736,9 +744,9 @@ describe('MatDatepicker', () => { expect(fixture.debugElement.query(By.css('input')).nativeElement.classList) .not.toContain('ng-invalid'); }); - }); + })); - it('should not mark invalid when value equals max', () => { + it('should not mark invalid when value equals max', async(() => { testComponent.date = testComponent.datepicker._maxDate; fixture.detectChanges(); @@ -748,9 +756,9 @@ describe('MatDatepicker', () => { expect(fixture.debugElement.query(By.css('input')).nativeElement.classList) .not.toContain('ng-invalid'); }); - }); + })); - it('should not mark invalid when value is between min and max', () => { + it('should not mark invalid when value is between min and max', async(() => { testComponent.date = new Date(2010, JAN, 2); fixture.detectChanges(); @@ -760,7 +768,7 @@ describe('MatDatepicker', () => { expect(fixture.debugElement.query(By.css('input')).nativeElement.classList) .not.toContain('ng-invalid'); }); - }); + })); }); describe('datepicker with filter and validation', () => { @@ -1078,17 +1086,27 @@ describe('MatDatepicker', () => { input = fixture.nativeElement.querySelector('input') as HTMLInputElement; })); - it('should have the correct input value even when inverted date format', () => { - let selected = new Date(2017, SEP, 1); + it('should have the correct input value even when inverted date format', async(() => { + if (typeof Intl === 'undefined') { + // Skip this test if the internationalization API is not supported in the current + // browser. Browsers like Safari 9 do not support the "Intl" API. + return; + } + + const selected = new Date(2017, SEP, 1); testComponent.date = selected; fixture.detectChanges(); fixture.whenStable().then(() => { fixture.detectChanges(); - expect(input.value).toBe('01.09.2017'); + + // Normally the proper date format would 01.09.2017, but some browsers seem format the + // date without the leading zero. (e.g. 1.9.2017). + expect(input.value).toMatch(/0?1\.0?9\.2017/); + expect(testComponent.datepickerInput.value).toBe(selected); }); - }); + })); }); }); @@ -1096,10 +1114,11 @@ describe('MatDatepicker', () => { @Component({ template: ` - + `, }) class StandardDatepicker { + opened = false; touch = false; disabled = false; date: Date | null = new Date(2020, JAN, 1); diff --git a/src/lib/datepicker/datepicker.ts b/src/lib/datepicker/datepicker.ts index ae0bd02aab63..910baac9b011 100644 --- a/src/lib/datepicker/datepicker.ts +++ b/src/lib/datepicker/datepicker.ts @@ -186,7 +186,10 @@ export class MatDatepicker implements OnDestroy { @Output('closed') closedStream: EventEmitter = new EventEmitter(); /** Whether the calendar is open. */ - opened = false; + @Input() + get opened(): boolean { return this._opened; } + set opened(shouldOpen: boolean) { shouldOpen ? this.open() : this.close(); } + private _opened = false; /** The id for the datepicker calendar. */ id = `mat-datepicker-${datepickerUid++}`; @@ -277,7 +280,7 @@ export class MatDatepicker implements OnDestroy { /** Open the calendar. */ open(): void { - if (this.opened || this.disabled) { + if (this._opened || this.disabled) { return; } if (!this._datepickerInput) { @@ -288,13 +291,13 @@ export class MatDatepicker implements OnDestroy { } this.touchUi ? this._openAsDialog() : this._openAsPopup(); - this.opened = true; + this._opened = true; this.openedStream.emit(); } /** Close the calendar. */ close(): void { - if (!this.opened) { + if (!this._opened) { return; } if (this._popupRef && this._popupRef.hasAttached()) { @@ -314,7 +317,7 @@ export class MatDatepicker implements OnDestroy { this._focusedElementBeforeOpen = null; } - this.opened = false; + this._opened = false; this.closedStream.emit(); }