Skip to content

Commit 54cadfe

Browse files
authored
fix(date-range): allow mix and min value as string #7352 (#7363)
1 parent d119a37 commit 54cadfe

File tree

6 files changed

+138
-38
lines changed

6 files changed

+138
-38
lines changed

projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ export abstract class DatePickerUtil {
252252
* @returns true if provided value is greater than provided maxValue
253253
*/
254254
public static greaterThanMaxValue(value: Date, maxValue: Date, includeTime = true, includeDate = true): boolean {
255+
// TODO: check if provided dates are valid dates and not Invalid Date
256+
// if maxValue is Invalid Date and value is valid date this will return:
257+
// - false if includeDate is true
258+
// - true if includeDate is false
255259
if (includeTime && includeDate) {
256260
return value.getTime() > maxValue.getTime();
257261
}
@@ -277,6 +281,10 @@ export abstract class DatePickerUtil {
277281
* @returns true if provided value is less than provided minValue
278282
*/
279283
public static lessThanMinValue(value: Date, minValue: Date, includeTime = true, includeDate = true): boolean {
284+
// TODO: check if provided dates are valid dates and not Invalid Date
285+
// if value is Invalid Date and minValue is valid date this will return:
286+
// - false if includeDate is true
287+
// - true if includeDate is false
280288
if (includeTime && includeDate) {
281289
return value.getTime() < minValue.getTime();
282290
}
@@ -636,6 +644,46 @@ export abstract class DatePickerUtil {
636644
return new Date(fullYear, month + 1, 0).getDate();
637645
}
638646

647+
/**
648+
* Parse provided input to Date.
649+
* @param value input to parse
650+
* @returns Date if parse succeed or null
651+
*/
652+
public static parseDate(value: any): Date | null {
653+
if (typeof value === 'number') {
654+
return new Date(value);
655+
}
656+
657+
// if value is Invalid Date we should return null
658+
if (this.isDate(value)) {
659+
return this.isValidDate(value) ? value : null;
660+
}
661+
662+
return value ? new Date(Date.parse(value)) : null;
663+
}
664+
665+
/**
666+
* Returns whether provided input is date
667+
* @param value input to check
668+
* @returns true if provided input is date
669+
*/
670+
public static isDate(value: any): boolean {
671+
return Object.prototype.toString.call(value) === '[object Date]';
672+
}
673+
674+
/**
675+
* Returns whether the input is valid date
676+
* @param value input to check
677+
* @returns true if provided input is a valid date
678+
*/
679+
public static isValidDate(value: any): boolean {
680+
if (this.isDate(value)) {
681+
return !isNaN(value.getTime());
682+
}
683+
684+
return false;
685+
}
686+
639687
private static getYearFormatType(format: string): string {
640688
switch (format.match(new RegExp(DateChars.YearChar, 'g')).length) {
641689
case 1: {

projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.spec.ts

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { UIInteractions } from '../test-utils/ui-interactions.spec';
1212
import { configureTestSuite } from '../test-utils/configure-suite';
1313
import { HelperTestFunctions } from '../calendar/calendar-helper-utils';
1414
import { IgxDateTimeEditorModule } from '../directives/date-time-editor';
15+
import { DateRangeType } from '../core/dates';
1516

1617
// The number of milliseconds in one day
1718
const ONE_DAY = 1000 * 60 * 60 * 24;
@@ -248,6 +249,7 @@ describe('IgxDateRangePicker', () => {
248249
fixture.detectChanges();
249250
expect(singleInputElement.nativeElement.placeholder).toEqual(placeholder);
250251
});
252+
251253
it('should close the calendar with the "Done" button', fakeAsync(() => {
252254
fixture.componentInstance.mode = InteractionMode.Dialog;
253255
fixture.detectChanges();
@@ -326,6 +328,46 @@ describe('IgxDateRangePicker', () => {
326328
fixture.detectChanges();
327329
});
328330

331+
it('should disable calendar dates when min and/or max values as dates are provided', fakeAsync(() => {
332+
// TODO: move this to unit tests
333+
const minDate = new Date(2000, 10, 1);
334+
const maxDate = new Date(2000, 10, 20);
335+
fixture.componentInstance.minValue = minDate;
336+
fixture.componentInstance.maxValue = maxDate;
337+
fixture.detectChanges();
338+
339+
dateRange.open();
340+
tick();
341+
fixture.detectChanges();
342+
343+
const calendarComponent: IgxCalendarComponent = calendar.componentInstance;
344+
expect(calendarComponent.disabledDates.length).toEqual(2);
345+
expect(calendarComponent.disabledDates[0].type).toEqual(DateRangeType.Before);
346+
expect(calendarComponent.disabledDates[0].dateRange[0]).toEqual(minDate);
347+
expect(calendarComponent.disabledDates[1].type).toEqual(DateRangeType.After);
348+
expect(calendarComponent.disabledDates[1].dateRange[0]).toEqual(maxDate);
349+
}));
350+
351+
it('should disable calendar dates when min and/or max values as strings are provided', fakeAsync(() => {
352+
// TODO: move this to unit tests
353+
const minDate = '2000/10/1';
354+
const maxDate = '2000/10/30';
355+
fixture.componentInstance.minValue = minDate;
356+
fixture.componentInstance.maxValue = maxDate;
357+
fixture.detectChanges();
358+
359+
dateRange.open();
360+
tick();
361+
fixture.detectChanges();
362+
363+
const calendarComponent: IgxCalendarComponent = calendar.componentInstance;
364+
expect(calendarComponent.disabledDates.length).toEqual(2);
365+
expect(calendarComponent.disabledDates[0].type).toEqual(DateRangeType.Before);
366+
expect(calendarComponent.disabledDates[0].dateRange[0]).toEqual(new Date(minDate));
367+
expect(calendarComponent.disabledDates[1].type).toEqual(DateRangeType.After);
368+
expect(calendarComponent.disabledDates[1].dateRange[0]).toEqual(new Date(maxDate));
369+
}));
370+
329371
it('should emit open/close events - open/close methods', fakeAsync(() => {
330372
spyOn(dateRange.onOpening, 'emit').and.callThrough();
331373
spyOn(dateRange.onOpened, 'emit').and.callThrough();
@@ -701,9 +743,9 @@ describe('IgxDateRangePicker', () => {
701743

702744
}));
703745
it('should render aria attributes properly', fakeAsync(() => {
704-
toggleBtn = fixture.debugElement.query(By.css(`.${ CSS_CLASS_TOGGLE_BUTTON}`));
705-
calendarElement = fixture.debugElement.query(By.css(`.${ CSS_CLASS_CALENDAR}`));
706-
singleInputElement = fixture.debugElement.query(By.css(`.${ CSS_CLASS_INPUT}`));
746+
toggleBtn = fixture.debugElement.query(By.css(`.${CSS_CLASS_TOGGLE_BUTTON}`));
747+
calendarElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_CALENDAR}`));
748+
singleInputElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_INPUT}`));
707749
startDate = new Date(2020, 1, 1);
708750
endDate = new Date(2020, 1, 4);
709751
const expectedLabelID = dateRange.label.id;
@@ -748,6 +790,8 @@ export class DateRangeTestComponent implements OnInit {
748790
public todayButtonText: string;
749791
public doneButtonText: string;
750792
public mode: InteractionMode;
793+
public minValue: Date | String;
794+
public maxValue: Date | String;
751795

752796
@ViewChild(IgxDateRangePickerComponent, { read: IgxDateRangePickerComponent, static: true })
753797
public dateRange: IgxDateRangePickerComponent;
@@ -791,11 +835,11 @@ export class DateRangeDefaultCustomLabelComponent extends DateRangeTestComponent
791835
@Component({
792836
selector: 'igx-date-range-single-input-test',
793837
template: `
794-
<igx-date-range-picker [mode]="mode">
838+
<igx-date-range-picker [mode]="mode" [minValue]="minValue" [maxValue]="maxValue">
795839
</igx-date-range-picker>
796840
`
797841
})
798842
export class DateRangeDefaultComponent extends DateRangeTestComponent {
799843
@ViewChild(IgxDateRangePickerComponent)
800844
public dateRange: IgxDateRangePickerComponent;
801-
}
845+
}

projects/igniteui-angular/src/lib/date-range-picker/date-range-picker.component.ts

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,12 @@ export class IgxDateRangePickerComponent extends DisplayDensityBase
236236
* <igx-date-range-picker [minValue]="minDate"></igx-date-range-picker>
237237
*/
238238
@Input()
239-
public set minValue(value: Date) {
239+
public set minValue(value: Date | string) {
240240
this._minValue = value;
241241
this.onValidatorChange();
242242
}
243243

244-
public get minValue(): Date {
244+
public get minValue(): Date | string {
245245
return this._minValue;
246246
}
247247

@@ -252,12 +252,12 @@ export class IgxDateRangePickerComponent extends DisplayDensityBase
252252
* <igx-date-range-picker [maxValue]="maxDate"></igx-date-range-picker>
253253
*/
254254
@Input()
255-
public set maxValue(value: Date) {
255+
public set maxValue(value: Date | string) {
256256
this._maxValue = value;
257257
this.onValidatorChange();
258258
}
259259

260-
public get maxValue(): Date {
260+
public get maxValue(): Date | string {
261261
return this._maxValue;
262262
}
263263

@@ -419,8 +419,8 @@ export class IgxDateRangePickerComponent extends DisplayDensityBase
419419
private _ngControl: NgControl;
420420
private _statusChanges$: Subscription;
421421
private $destroy = new Subject();
422-
private _minValue: Date;
423-
private _maxValue: Date;
422+
private _minValue: Date | string;
423+
private _maxValue: Date | string;
424424
private $toggleClickNotifier = new Subject();
425425
private _positionSettings: PositionSettings;
426426
private _dialogOverlaySettings: OverlaySettings = {
@@ -568,23 +568,29 @@ export class IgxDateRangePickerComponent extends DisplayDensityBase
568568
}
569569

570570
/** @hidden @internal */
571-
public validate(control: AbstractControl): ValidationErrors {
571+
public validate(control: AbstractControl): ValidationErrors | null {
572572
const value: DateRange = control.value;
573-
if (this.minValue && value && value.start && DatePickerUtil.lessThanMinValue(value.start, this.minValue, false)) {
574-
return { 'minValue': true };
575-
}
576-
if (this.minValue && value && value.end && DatePickerUtil.lessThanMinValue(value.end, this.minValue, false)) {
577-
return { 'minValue': true };
578-
}
579-
if (this.maxValue && value && value.start && DatePickerUtil.greaterThanMaxValue(value.start, this.maxValue, false)) {
580-
return { 'maxValue': true };
581-
}
582-
if (this.maxValue && value && value.end && DatePickerUtil.greaterThanMaxValue(value.end, this.maxValue, false)) {
583-
return { 'maxValue': true };
573+
if (value) {
574+
const min = DatePickerUtil.parseDate(this.minValue);
575+
const max = DatePickerUtil.parseDate(this.maxValue);
576+
const start = DatePickerUtil.parseDate(value.start);
577+
const end = DatePickerUtil.parseDate(value.end);
578+
579+
if (min && start && DatePickerUtil.lessThanMinValue(start, min, false)) {
580+
return { 'minValue': true };
581+
}
582+
if (min && end && DatePickerUtil.lessThanMinValue(end, min, false)) {
583+
return { 'minValue': true };
584+
}
585+
if (max && start && DatePickerUtil.greaterThanMaxValue(start, max, false)) {
586+
return { 'maxValue': true };
587+
}
588+
if (max && end && DatePickerUtil.greaterThanMaxValue(end, max, false)) {
589+
return { 'maxValue': true };
590+
}
584591
}
585592

586-
// TODO: fix what happens on blur and ensure there value is either null or with both start and end filled
587-
593+
// TODO: fix what happens on blur and ensure on blur the value is either null or with both start and end filled
588594
return null;
589595
}
590596

@@ -784,26 +790,22 @@ export class IgxDateRangePickerComponent extends DisplayDensityBase
784790

785791
private updateCalendar(): void {
786792
this.calendar.disabledDates = [];
787-
// TODO: minValue and maxValue type to be Date | String as it is in dateTineEditorDirective
788-
// TODO: move isDate and parseDate to utils
789-
let minValue = this.minValue;
793+
let minValue: Date = DatePickerUtil.parseDate(this.minValue);
790794
if (!minValue && this.hasProjectedInputs) {
791795
const start = this.projectedInputs.filter(i => i instanceof IgxDateRangeStartComponent)[0];
792796
if (start) {
793-
const editor = start.dateTimeEditor;
794-
minValue = editor.isDate(editor.minValue) ? editor.minValue : editor.parseDate(editor.minValue);
797+
minValue = DatePickerUtil.parseDate(start.dateTimeEditor.minValue);
795798
}
796799
}
797800
if (minValue) {
798801
this.calendar.disabledDates.push({ type: DateRangeType.Before, dateRange: [minValue] });
799802
}
800803

801-
let maxValue = this.maxValue;
804+
let maxValue: Date = DatePickerUtil.parseDate(this.maxValue);
802805
if (!maxValue && this.hasProjectedInputs) {
803806
const end = this.projectedInputs.filter(i => i instanceof IgxDateRangeEndComponent)[0];
804807
if (end) {
805-
const editor = end.dateTimeEditor;
806-
maxValue = editor.isDate(editor.maxValue) ? editor.maxValue : editor.parseDate(editor.maxValue);
808+
maxValue = DatePickerUtil.parseDate(end.dateTimeEditor.maxValue);
807809
}
808810
}
809811
if (maxValue) {
@@ -822,6 +824,7 @@ export class IgxDateRangePickerComponent extends DisplayDensityBase
822824

823825
if (range.length > 0) {
824826
this.calendar.selectDate(range);
827+
this.calendar.viewDate = range[0];
825828
} else {
826829
this.calendar.deselectDate();
827830
}

projects/igniteui-angular/src/lib/directives/date-time-editor/date-time-editor.directive.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,8 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnCh
421421
return mask;
422422
}
423423

424-
/** @hidden @internal */
425-
public isDate(value: any): value is Date {
424+
// TODO: move isDate to utils
425+
private isDate(value: any): value is Date {
426426
return value instanceof Date && typeof value === 'object';
427427
}
428428

@@ -569,8 +569,8 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnCh
569569
return date && date.getTime && !isNaN(date.getTime());
570570
}
571571

572-
/** @hidden @internal */
573-
public parseDate(val: string): Date | null {
572+
// TODO: move parseDate to utils
573+
public parseDate(val: string): Date | null {
574574
if (!val) { return null; }
575575
return DatePickerUtil.parseValueFromMask(val, this._inputDateParts, this.promptChar);
576576
}

src/app/date-range/date-range.sample.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ <h5>Without forms</h5>
1818
</igx-date-range-picker>
1919

2020
<p>Drop down two inputs with prefix</p>
21-
<igx-date-range-picker [mode]="'dropdown'">
21+
<igx-date-range-picker [mode]="'dropdown'"
22+
[value]="range"
23+
[minValue]="'2000/10/1'"
24+
[maxValue]="'some invalid value'"
25+
>
2226
<igx-date-range-start>
2327
<input igxInput igxDateTimeEditor type="text">
2428
<igx-picker-toggle igxPrefix>

src/app/date-range/date-range.sample.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { IgxDateRangePickerComponent, DateRange, IChangeRadioEventArgs } from 'i
88
styleUrls: ['./date-range.sample.scss']
99
})
1010
export class DateRangeSampleComponent {
11+
public range: DateRange = { start: new Date('2000,10,1'), end: new Date('2000,10,20') };
1112
public range1: DateRange = { start: new Date(), end: new Date(new Date().setDate(new Date().getDate() + 5)) };
1213
public range2: DateRange;
1314
public range3: DateRange = { start: new Date(), end: new Date(new Date().setDate(new Date().getDate() + 5)) };

0 commit comments

Comments
 (0)