Skip to content

Commit aaeb6d9

Browse files
committed
add error validation
1 parent 4167031 commit aaeb6d9

3 files changed

Lines changed: 104 additions & 38 deletions

File tree

src/components/fields/NumericOrDate.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import {UnconnectedDateTimePicker} from './DateTimePicker';
99

1010
export class UnconnectedNumericOrDate extends Component {
1111
render() {
12+
const date = typeof this.props.fullValue === 'string' && this.props.fullValue.split(' ')[0];
1213
const fullValueIsDate =
13-
typeof this.props.fullValue === 'string' &&
14-
(isDateTime(this.props.fullValue) || isJSDate(this.props.fullValue));
14+
typeof this.props.fullValue === 'string' && date && (isDateTime(date) || isJSDate(date));
1515

1616
return fullValueIsDate ? (
1717
<UnconnectedDateTimePicker {...this.props} placeholder={'yyyy-mm-dd hh:mm:ss.xxx'} />

src/components/widgets/DateTimePicker.js

Lines changed: 92 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,34 @@
11
import 'react-day-picker/lib/style.css';
22
import {CalendarMultiselectIcon} from 'plotly-icons';
3-
import {ms2DateTime, dateTime2ms} from 'plotly.js/src/lib/dates';
3+
import {ms2DateTime, dateTime2ms, isDateTime} from 'plotly.js/src/lib/dates';
44
import DayPicker from 'react-day-picker';
55
import PropTypes from 'prop-types';
66
import React, {Component} from 'react';
77
import TextInput from './TextInput';
88
import Dropdown from './Dropdown';
99

10-
function formatDigits(number, nbDigits) {
11-
const string = number.toString();
12-
if (string.length !== nbDigits) {
13-
return (
14-
new Array(nbDigits - string.length)
15-
.fill(0)
16-
.join()
17-
.replace(',', '') + number
18-
);
19-
}
20-
return number;
21-
}
10+
const testDate = '2000-01-01';
11+
const testTime = '00:00';
12+
const datePlaceholder = 'yyyy-mm-dd';
13+
const timePlaceholder = 'hh:mm:ss.xxx';
2214

2315
export default class DateTimePicker extends Component {
24-
constructor() {
25-
super();
16+
constructor(props) {
17+
super(props);
18+
const {time, date} = this.parseDateTime(props.value);
19+
const isValidTime = isDateTime(testDate + ' ' + time);
20+
const isValidDate = isDateTime(date + ' ' + testTime);
21+
2622
this.state = {
2723
calendarOpen: false,
24+
dateInputClassName: isValidDate
25+
? 'datetimepicker-container-date-input'
26+
: 'datetimepicker-container-date-input--error',
27+
timeInputClassName: isValidTime
28+
? 'datetimepicker-container-time-input'
29+
: 'datetimepicker-container-time-input--error',
30+
timeValue: time,
31+
dateValue: date,
2832
};
2933

3034
this.toPlotlyJSDate = this.toPlotlyJSDate.bind(this);
@@ -78,22 +82,33 @@ export default class DateTimePicker extends Component {
7882
onMonthChange(value) {
7983
const currentDateInJS = new Date(this.props.value);
8084
currentDateInJS.setMonth(value);
81-
this.props.onChange(this.toPlotlyJSDate(currentDateInJS));
85+
const plotlyJSDate = this.toPlotlyJSDate(currentDateInJS);
86+
87+
if (isDateTime(plotlyJSDate)) {
88+
this.props.onChange(plotlyJSDate);
89+
}
90+
91+
const {time, date} = this.parseDateTime(plotlyJSDate);
92+
this.setState({
93+
timeValue: time,
94+
dateValue: date,
95+
});
8296
}
8397

8498
onYearChange(value) {
8599
const currentDateInJS = new Date(this.props.value);
86100
currentDateInJS.setFullYear(value);
87-
this.props.onChange(this.toPlotlyJSDate(currentDateInJS));
88-
}
101+
const plotlyJSDate = this.toPlotlyJSDate(currentDateInJS);
89102

90-
getTime(JSDate) {
91-
return (
92-
`${formatDigits(JSDate.getHours(), 2)}:` +
93-
`${formatDigits(JSDate.getMinutes(), 2)}:` +
94-
`${formatDigits(JSDate.getSeconds(), 2)}.` +
95-
`${formatDigits(JSDate.getMilliseconds(), 3)}`
96-
);
103+
if (isDateTime(plotlyJSDate)) {
104+
this.props.onChange(plotlyJSDate);
105+
}
106+
107+
const {time, date} = this.parseDateTime(plotlyJSDate);
108+
this.setState({
109+
timeValue: time,
110+
dateValue: date,
111+
});
97112
}
98113

99114
parseDateTime(value) {
@@ -102,31 +117,72 @@ export default class DateTimePicker extends Component {
102117
}
103118

104119
updateTime(value) {
105-
const {date: currentDate, time: currentTime} = this.parseDateTime(this.props.value);
120+
const update = this.state.dateValue + ' ' + value;
121+
const isValidTime = isDateTime(testDate + ' ' + value);
122+
123+
if (value === '') {
124+
this.setState({
125+
timeInputClassName: 'datetimepicker-container-time-input',
126+
timeValue: timePlaceholder,
127+
});
128+
return;
129+
}
106130

107-
if (value !== currentTime) {
108-
this.props.onChange(currentDate + ' ' + value);
131+
if (isValidTime) {
132+
this.props.onChange(update);
133+
this.setState({
134+
timeValue: value,
135+
timeInputClassName: 'datetimepicker-container-time-input',
136+
});
137+
return;
138+
}
139+
140+
if (!isValidTime) {
141+
this.setState({
142+
timeInputClassName: 'datetimepicker-container-time-input--error',
143+
timeValue: value,
144+
});
109145
}
110146
}
111147

112148
updateDate(value) {
113-
const {date: currentDate, time: currentTime} = this.parseDateTime(this.props.value);
149+
const update = value + ' ' + this.state.timeValue;
150+
const isValidDate = isDateTime(value + ' ' + testTime);
151+
152+
if (isValidDate) {
153+
this.props.onChange(update);
154+
this.setState({
155+
dateValue: value,
156+
dateInputClassName: 'datetimepicker-container-date-input',
157+
});
158+
return;
159+
}
160+
161+
if (value === '') {
162+
this.setState({
163+
dateValue: datePlaceholder,
164+
dateInputClassName: 'datetimepicker-container-date-input',
165+
});
166+
return;
167+
}
114168

115-
if (value !== currentDate) {
116-
this.props.onChange(value + ' ' + currentTime);
169+
if (!isValidDate) {
170+
this.setState({
171+
dateValue: value,
172+
dateInputClassName: 'datetimepicker-container-date-input--error',
173+
});
117174
}
118175
}
119176

120177
render() {
121-
const {date: currentDate} = this.parseDateTime(this.props.value);
122178
const JSDate = new Date(this.props.value);
123179
const currentYear = JSDate.getFullYear();
124180

125181
return (
126182
<div className="datetimepicker-container">
127183
<TextInput
128-
value={currentDate}
129-
editableClassName={'datetimepicker-container-input'}
184+
value={this.state.dateValue}
185+
editableClassName={this.state.dateInputClassName}
130186
onUpdate={this.updateDate}
131187
/>
132188
<div className="datetimepicker-container-icons">
@@ -174,9 +230,10 @@ export default class DateTimePicker extends Component {
174230
) : null}
175231
<div className="datetimepicker-container-time">
176232
<TextInput
177-
value={this.getTime(JSDate)}
233+
value={this.state.timeValue}
178234
onUpdate={this.updateTime}
179235
placeHolder="hh:mm:ss.xxx"
236+
editableClassName={this.state.timeInputClassName}
180237
/>
181238
<span className="datetimepicker-date-units">
182239
{JSDate.toLocaleTimeString('en-US').split(' ')[1] === 'PM' ? 'PM' : 'AM'}

src/styles/components/widgets/_datetimepicker.scss

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,19 @@
2020
margin: 0 auto;
2121
}
2222

23-
.datetimepicker-container-input {
23+
.datetimepicker-container-date-input {
2424
margin-top: -20px;
2525
}
2626

27+
.datetimepicker-container-date-input--error {
28+
margin-top: -20px;
29+
border-color: var(--color-sienna);
30+
}
31+
32+
.datetimepicker-container-time-input--error {
33+
border-color: var(--color-sienna);
34+
}
35+
2736
.datetimepicker-container-icons {
2837
display: inline-block;
2938
margin-top: 2px;

0 commit comments

Comments
 (0)