Date picker validation fixed (#2789)

* Date picker validation fixed

* Added date value check to remove unncessary validations

Commented defaultDate validation test as invalid date cannot be saved in date control

* Fixed date selection on user change

Fixed min and max date parsing for date picker control default date

* Added validation for min and max date in validations to handle validations when field is converted to JS
This commit is contained in:
Vicky Bansal 2021-02-02 20:12:49 +05:30 committed by GitHub
parent 9d97645b3c
commit 11f5687b38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 210 additions and 75 deletions

View File

@ -101,18 +101,18 @@ describe("DatePicker Widget Functionality", function() {
);
});
it("Datepicker default date validation", function() {
cy.get(formWidgetsPage.defaultDate).click();
cy.wait(1000);
cy.setDate(-2, "ddd MMM DD YYYY");
cy.get(formWidgetsPage.defaultDate).should(
"have.css",
"border",
"1px solid rgb(206, 66, 87)",
);
// it("Datepicker default date validation", function() {
// cy.get(formWidgetsPage.defaultDate).click();
// cy.wait(1000);
// cy.setDate(-2, "ddd MMM DD YYYY");
// cy.get(formWidgetsPage.defaultDate).should(
// "have.css",
// "border",
// "1px solid rgb(206, 66, 87)",
// );
cy.PublishtheApp();
});
// cy.PublishtheApp();
// });
// it("DatePicker-check Required field validation", function() {
// // Check the required checkbox

View File

@ -10,6 +10,7 @@ import { DatePickerType } from "widgets/DatePickerWidget";
import { WIDGET_PADDING } from "constants/WidgetConstants";
import { TimePrecision } from "@blueprintjs/datetime";
import { Colors } from "constants/Colors";
import { ISO_DATE_FORMAT } from "constants/WidgetValidation";
const StyledControlGroup = styled(ControlGroup)`
&&& {
@ -67,10 +68,11 @@ class DatePickerComponent extends React.Component<
}
componentDidUpdate(prevProps: DatePickerComponentProps) {
const dateFormat = this.props.dateFormat || ISO_DATE_FORMAT;
if (
this.props.selectedDate !== this.state.selectedDate &&
!moment(this.props.selectedDate, this.props.dateFormat).isSame(
moment(prevProps.selectedDate, this.props.dateFormat),
!moment(this.props.selectedDate, dateFormat).isSame(
moment(prevProps.selectedDate, dateFormat),
"seconds",
)
) {
@ -81,13 +83,13 @@ class DatePickerComponent extends React.Component<
render() {
const now = moment();
const year = now.get("year");
const dateFormat = this.props.dateFormat || ISO_DATE_FORMAT;
const minDate = this.props.minDate
? moment(this.props.minDate)
? moment(this.props.minDate, dateFormat)
: now.clone().set({ month: 0, date: 1, year: year - 100 });
const maxDate = this.props.maxDate
? moment(this.props.maxDate)
? moment(this.props.maxDate, dateFormat)
: now.clone().set({ month: 11, date: 31, year: year + 20 });
return (
<StyledControlGroup
fill
@ -142,11 +144,13 @@ class DatePickerComponent extends React.Component<
}
formatDate = (date: Date): string => {
return moment(date).format(this.props.dateFormat);
const dateFormat = this.props.dateFormat || ISO_DATE_FORMAT;
return moment(date).format(dateFormat);
};
parseDate = (dateStr: string): Date => {
return moment(dateStr, this.props.dateFormat).toDate();
const dateFormat = this.props.dateFormat || ISO_DATE_FORMAT;
return moment(dateStr, dateFormat).toDate();
};
/**
@ -156,16 +160,18 @@ class DatePickerComponent extends React.Component<
*
* @param selectedDate
*/
onDateSelected = (selectedDate: Date) => {
const { onDateSelected } = this.props;
onDateSelected = (selectedDate: Date, isUserChange: boolean) => {
if (isUserChange) {
const { onDateSelected } = this.props;
const date = selectedDate ? this.formatDate(selectedDate) : "";
this.setState({ selectedDate: date });
const date = selectedDate ? this.formatDate(selectedDate) : "";
this.setState({ selectedDate: date });
// if date is null ( if date is cleared ), don't call onDateSelected
if (!selectedDate) return false;
// if date is null ( if date is cleared ), don't call onDateSelected
if (!selectedDate) return false;
onDateSelected(date);
onDateSelected(date);
}
};
}

View File

@ -7,6 +7,7 @@ import { TimePrecision } from "@blueprintjs/datetime";
import { WidgetProps } from "widgets/BaseWidget";
import { Toaster } from "components/ads/Toast";
import { Variant } from "components/ads/common";
import { ISO_DATE_FORMAT } from "constants/WidgetValidation";
const DatePickerControlWrapper = styled.div<{ isValid: boolean }>`
display: flex;
@ -62,8 +63,17 @@ class DatePickerControl extends BaseControl<
}
render() {
const dateFormat =
this.props.widgetProperties.dateFormat || ISO_DATE_FORMAT;
const isValid = this.state.selectedDate
? this.validateDate(moment(this.state.selectedDate, dateFormat).toDate())
: true;
const maxDate =
this.props.widgetProperties?.evaluatedValues?.maxDate ?? this.maxDate;
const minDate =
this.props.widgetProperties?.evaluatedValues?.minDate ?? this.minDate;
return (
<DatePickerControlWrapper isValid={this.props.isValid}>
<DatePickerControlWrapper isValid={isValid}>
<StyledDatePicker
formatDate={this.formatDate}
parseDate={this.parseDate}
@ -72,8 +82,16 @@ class DatePickerControl extends BaseControl<
timePrecision={TimePrecision.MINUTE}
closeOnSelection
onChange={this.onDateSelected}
maxDate={this.maxDate}
minDate={this.minDate}
maxDate={
this.props.propertyName === "defaultDate"
? moment(maxDate, dateFormat).toDate()
: undefined
}
minDate={
this.props.propertyName === "defaultDate"
? moment(minDate, dateFormat).toDate()
: undefined
}
value={
this.props.propertyValue
? this.parseDate(this.props.propertyValue)
@ -91,15 +109,17 @@ class DatePickerControl extends BaseControl<
*
* @param date
*/
onDateSelected = (date: Date): void => {
const selectedDate = date ? this.formatDate(date) : undefined;
const isValid = this.validateDate(date);
onDateSelected = (date: Date, isUserChange: boolean): void => {
if (isUserChange) {
const selectedDate = date ? this.formatDate(date) : undefined;
const isValid = this.validateDate(date);
if (!isValid) return;
if (!isValid) return;
// if everything is ok, put date in state
this.setState({ selectedDate: selectedDate });
this.updateProperty(this.props.propertyName, selectedDate);
// if everything is ok, put date in state
this.setState({ selectedDate: selectedDate });
this.updateProperty(this.props.propertyName, selectedDate);
}
};
/**
@ -108,17 +128,37 @@ class DatePickerControl extends BaseControl<
* 2. if default date is in range of min and max date
*/
validateDate = (date: Date): boolean => {
const parsedSelectedDate = moment(
date,
this.props.widgetProperties.dateFormat,
);
const dateFormat =
this.props.widgetProperties.dateFormat || ISO_DATE_FORMAT;
const parsedSelectedDate = moment(date, dateFormat);
//validate defaultDate if both minDate and maxDate is already selected
if (this.props.propertyName === "defaultDate") {
if (
parsedSelectedDate.isValid() &&
this.props.widgetProperties?.evaluatedValues?.minDate &&
this.props.widgetProperties?.evaluatedValues?.maxDate
) {
const parsedMinDate = moment(
this.props.widgetProperties.evaluatedValues.minDate,
dateFormat,
);
const parsedMaxDate = moment(
this.props.widgetProperties.evaluatedValues.maxDate,
dateFormat,
);
if (
parsedSelectedDate.isBefore(parsedMinDate) ||
parsedSelectedDate.isAfter(parsedMaxDate)
) {
return false;
}
}
}
if (this.props.widgetProperties?.evaluatedValues?.value) {
const parsedWidgetDate = moment(
this.props.widgetProperties.evaluatedValues.value,
this.props.widgetProperties.dateFormat,
dateFormat,
);
// checking if widget date is after min date
if (this.props.propertyName === "minDate") {
if (
@ -149,21 +189,19 @@ class DatePickerControl extends BaseControl<
}
}
}
return true;
};
formatDate = (date: Date): string => {
return moment(date).format(
this.props.widgetProperties.dateFormat || "DD/MM/YYYY HH:mm",
);
const dateFormat =
this.props.widgetProperties.dateFormat || ISO_DATE_FORMAT;
return moment(date).format(dateFormat);
};
parseDate = (dateStr: string): Date => {
return moment(
dateStr,
this.props.widgetProperties.dateFormat || "DD/MM/YYYY HH:mm",
).toDate();
const dateFormat =
this.props.widgetProperties.dateFormat || ISO_DATE_FORMAT;
return moment(dateStr, dateFormat).toDate();
};
static getControlType() {

View File

@ -18,7 +18,7 @@ const FIELD_VALUES: Record<
isVisible: "boolean",
},
DATE_PICKER_WIDGET: {
defaultDate: "Date",
defaultDate: "string", //TODO:Vicky validate this property
isRequired: "boolean",
isVisible: "boolean",
isDisabled: "boolean",

View File

@ -14,6 +14,8 @@ export const VALIDATION_TYPES = {
OPTIONS_DATA: "OPTIONS_DATA",
DATE: "DATE",
DEFAULT_DATE: "DEFAULT_DATE",
MIN_DATE: "MIN_DATE",
MAX_DATE: "MAX_DATE",
TABS_DATA: "TABS_DATA",
CHART_DATA: "CHART_DATA",
MARKERS: "MARKERS",
@ -39,7 +41,7 @@ export type Validator = (
dataTree?: DataTree,
) => ValidationResponse;
export const ISO_DATE_FORMAT = "YYYY-MM-DDTHH:mm:ss.SSSZ";
export const ISO_DATE_FORMAT = "YYYY-MM-DDTHH:mm:ss.Z";
export const JAVASCRIPT_KEYWORDS = {
true: "true",

View File

@ -25,8 +25,8 @@ class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
dateFormat: VALIDATION_TYPES.TEXT,
label: VALIDATION_TYPES.TEXT,
datePickerType: VALIDATION_TYPES.TEXT,
maxDate: VALIDATION_TYPES.DATE,
minDate: VALIDATION_TYPES.DATE,
maxDate: VALIDATION_TYPES.MAX_DATE,
minDate: VALIDATION_TYPES.MIN_DATE,
isRequired: VALIDATION_TYPES.BOOLEAN,
// onDateSelected: VALIDATION_TYPES.ACTION_SELECTOR,
// onDateRangeSelected: VALIDATION_TYPES.ACTION_SELECTOR,

View File

@ -390,14 +390,8 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
dateString: string,
props: WidgetProps,
): ValidationResponse => {
const today = moment()
.hour(0)
.minute(0)
.second(0)
.millisecond(0);
const dateFormat = props.dateFormat ? props.dateFormat : ISO_DATE_FORMAT;
const todayDateString = today.format(dateFormat);
if (dateString === undefined) {
return {
isValid: false,
@ -409,10 +403,16 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
};
}
const isValid = moment(dateString, dateFormat).isValid();
const parsed = isValid ? dateString : todayDateString;
if (!isValid) {
return {
isValid: isValid,
parsed: "",
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Date`,
};
}
return {
isValid,
parsed,
parsed: dateString,
message: isValid ? "" : `${WIDGET_TYPE_VALIDATION_ERROR}: Date`,
};
},
@ -420,14 +420,7 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
dateString: string,
props: WidgetProps,
): ValidationResponse => {
const today = moment()
.hour(0)
.minute(0)
.second(0)
.millisecond(0);
const dateFormat = props.dateFormat ? props.dateFormat : ISO_DATE_FORMAT;
const todayDateString = today.format(dateFormat);
if (dateString === undefined) {
return {
isValid: false,
@ -460,13 +453,109 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
isValid = false;
}
}
const parsed = isValid ? dateString : todayDateString;
if (!isValid) {
return {
isValid: isValid,
parsed: "",
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Date R`,
};
}
return {
isValid,
parsed,
message: isValid ? "" : `${WIDGET_TYPE_VALIDATION_ERROR}: Date R`,
isValid: isValid,
parsed: dateString,
message: "",
};
},
[VALIDATION_TYPES.MIN_DATE]: (
dateString: string,
props: WidgetProps,
): ValidationResponse => {
const dateFormat = props.dateFormat ? props.dateFormat : ISO_DATE_FORMAT;
if (dateString === undefined) {
return {
isValid: false,
parsed: "",
message:
`${WIDGET_TYPE_VALIDATION_ERROR}: Date ` + props.dateFormat
? props.dateFormat
: "",
};
}
const parsedMinDate = moment(dateString, dateFormat);
let isValid = parsedMinDate.isValid();
if (!props.defaultDate) {
return {
isValid: isValid,
parsed: dateString,
message: "",
};
}
const parsedDefaultDate = moment(props.defaultDate, dateFormat);
if (
isValid &&
parsedDefaultDate.isValid() &&
parsedDefaultDate.isBefore(parsedMinDate)
) {
isValid = false;
}
if (!isValid) {
return {
isValid: isValid,
parsed: "",
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Date R`,
};
}
return {
isValid: isValid,
parsed: dateString,
message: "",
};
},
[VALIDATION_TYPES.MAX_DATE]: (
dateString: string,
props: WidgetProps,
): ValidationResponse => {
const dateFormat = props.dateFormat ? props.dateFormat : ISO_DATE_FORMAT;
if (dateString === undefined) {
return {
isValid: false,
parsed: "",
message:
`${WIDGET_TYPE_VALIDATION_ERROR}: Date ` + props.dateFormat
? props.dateFormat
: "",
};
}
const parsedMaxDate = moment(dateString, dateFormat);
let isValid = parsedMaxDate.isValid();
if (!props.defaultDate) {
return {
isValid: isValid,
parsed: dateString,
message: "",
};
}
const parsedDefaultDate = moment(props.defaultDate, dateFormat);
if (
isValid &&
parsedDefaultDate.isValid() &&
parsedDefaultDate.isAfter(parsedMaxDate)
) {
isValid = false;
}
if (!isValid) {
return {
isValid: isValid,
parsed: "",
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Date R`,
};
}
return {
isValid: isValid,
parsed: dateString,
message: "",
};
},
[VALIDATION_TYPES.ACTION_SELECTOR]: (value: any): ValidationResponse => {