Skip to content

Commit

Permalink
fix(datepicker): not revalidating after value is changed through the …
Browse files Browse the repository at this point in the history
…calendar (#19695)

Fixes an issue where the datepicker wouldn't revalidate, if the user typed in something invalid and then selected a value through the calendar. This seems to have regressed after things were moved around to accommodate the date range picker.
  • Loading branch information
crisbeto authored and mmalerba committed Jun 19, 2020
1 parent 613606d commit 10888f3
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 29 deletions.
10 changes: 8 additions & 2 deletions src/material/datepicker/datepicker-input-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export abstract class MatDatepickerInputBase<S, D = ExtractDateTypeFromSelection
}
set value(value: D | null) {
value = this._dateAdapter.deserialize(value);
this._lastValueValid = !value || this._dateAdapter.isValid(value);
this._lastValueValid = this._isValidValue(value);
value = this._getValidDateOrNull(value);
const oldDate = this.value;
this._assignValue(value);
Expand Down Expand Up @@ -194,6 +194,7 @@ export abstract class MatDatepickerInputBase<S, D = ExtractDateTypeFromSelection
this._valueChangesSubscription = this._model.selectionChanged.subscribe(event => {
if (event.source !== this) {
const value = this._getValueFromModel(event.selection);
this._lastValueValid = this._isValidValue(value);
this._cvaOnChange(value);
this._onTouched();
this._formatValue(value);
Expand Down Expand Up @@ -298,7 +299,7 @@ export abstract class MatDatepickerInputBase<S, D = ExtractDateTypeFromSelection
_onInput(value: string) {
const lastValueWasValid = this._lastValueValid;
let date = this._dateAdapter.parse(value, this._dateFormats.parse.dateInput);
this._lastValueValid = !date || this._dateAdapter.isValid(date);
this._lastValueValid = this._isValidValue(date);
date = this._getValidDateOrNull(date);

if (!this._dateAdapter.sameDate(date, this.value)) {
Expand Down Expand Up @@ -351,6 +352,11 @@ export abstract class MatDatepickerInputBase<S, D = ExtractDateTypeFromSelection
}
}

/** Whether a value is considered valid. */
private _isValidValue(value: D | null): boolean {
return !value || this._dateAdapter.isValid(value);
}

/**
* Checks whether a parent control is disabled. This is in place so that it can be overridden
* by inputs extending this one which can be placed inside of a group that can be disabled.
Expand Down
67 changes: 40 additions & 27 deletions src/material/datepicker/datepicker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1241,6 +1241,12 @@ describe('MatDatepicker', () => {
testComponent = fixture.componentInstance;
}));

function revalidate() {
fixture.detectChanges();
flush();
fixture.detectChanges();
}

afterEach(fakeAsync(() => {
testComponent.datepicker.close();
fixture.detectChanges();
Expand All @@ -1253,50 +1259,39 @@ describe('MatDatepicker', () => {

it('should mark invalid when value is before min', fakeAsync(() => {
testComponent.date = new Date(2009, DEC, 31);
fixture.detectChanges();
flush();
fixture.detectChanges();
revalidate();

expect(fixture.debugElement.query(By.css('input'))!.nativeElement.classList)
.toContain('ng-invalid');
}));

it('should mark invalid when value is after max', fakeAsync(() => {
testComponent.date = new Date(2020, JAN, 2);
fixture.detectChanges();
flush();

fixture.detectChanges();
revalidate();

expect(fixture.debugElement.query(By.css('input'))!.nativeElement.classList)
.toContain('ng-invalid');
}));

it('should not mark invalid when value equals min', fakeAsync(() => {
testComponent.date = testComponent.datepicker._minDate;
fixture.detectChanges();
flush();
fixture.detectChanges();
revalidate();

expect(fixture.debugElement.query(By.css('input'))!.nativeElement.classList)
.not.toContain('ng-invalid');
}));

it('should not mark invalid when value equals max', fakeAsync(() => {
testComponent.date = testComponent.datepicker._maxDate;
fixture.detectChanges();
flush();
fixture.detectChanges();
revalidate();

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', fakeAsync(() => {
testComponent.date = new Date(2010, JAN, 2);
fixture.detectChanges();
flush();
fixture.detectChanges();
revalidate();

expect(fixture.debugElement.query(By.css('input'))!.nativeElement.classList)
.not.toContain('ng-invalid');
Expand All @@ -1306,31 +1301,49 @@ describe('MatDatepicker', () => {
const inputEl = fixture.debugElement.query(By.css('input'))!.nativeElement;
inputEl.value = '';
dispatchFakeEvent(inputEl, 'input');

fixture.detectChanges();
flush();
fixture.detectChanges();
revalidate();

expect(testComponent.model.valid).toBe(true);

inputEl.value = 'abcdefg';
dispatchFakeEvent(inputEl, 'input');

fixture.detectChanges();
flush();
fixture.detectChanges();
revalidate();

expect(testComponent.model.valid).toBe(false);

inputEl.value = '';
dispatchFakeEvent(inputEl, 'input');
revalidate();

fixture.detectChanges();
flush();
fixture.detectChanges();
expect(testComponent.model.valid).toBe(true);
}));

it('should update validity when a value is assigned', fakeAsync(() => {
const inputEl = fixture.debugElement.query(By.css('input'))!.nativeElement;
inputEl.value = '';
dispatchFakeEvent(inputEl, 'input');
revalidate();

expect(testComponent.model.valid).toBe(true);

inputEl.value = 'abcdefg';
dispatchFakeEvent(inputEl, 'input');
revalidate();

expect(testComponent.model.valid).toBe(false);

const validDate = new Date(2010, JAN, 2);

// Assigning through the selection model simulates the user doing it via the calendar.
const model = fixture.debugElement.query(By.directive(MatDatepicker))
.injector.get<MatDateSelectionModel<Date>>(MatDateSelectionModel);
model.updateSelection(validDate, null);
revalidate();

expect(testComponent.model.valid).toBe(true);
expect(testComponent.date).toBe(validDate);
}));

});

describe('datepicker with filter and validation', () => {
Expand Down

0 comments on commit 10888f3

Please sign in to comment.