Skip to content

Commit 66e00da

Browse files
committed
Improved DateWidget and DateTimeWidget usability. (rjsf-team#148)
1 parent 0abe31c commit 66e00da

File tree

5 files changed

+206
-76
lines changed

5 files changed

+206
-76
lines changed

playground/samples/widgets.js

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,28 @@ module.exports = {
33
title: "Widgets",
44
type: "object",
55
properties: {
6+
stringFormats: {
7+
type: "object",
8+
title: "String formats",
9+
properties: {
10+
email: {
11+
type: "string",
12+
format: "email"
13+
},
14+
uri: {
15+
type: "string",
16+
format: "uri"
17+
},
18+
datetime: {
19+
type: "string",
20+
format: "date-time"
21+
},
22+
date: {
23+
type: "string",
24+
format: "date-time"
25+
}
26+
}
27+
},
628
boolean: {
729
type: "object",
830
title: "Boolean field",
@@ -35,28 +57,6 @@ module.exports = {
3557
}
3658
}
3759
},
38-
stringFormats: {
39-
type: "object",
40-
title: "String formats",
41-
properties: {
42-
email: {
43-
type: "string",
44-
format: "email"
45-
},
46-
uri: {
47-
type: "string",
48-
format: "uri"
49-
},
50-
datetime: {
51-
type: "string",
52-
format: "date-time"
53-
},
54-
date: {
55-
type: "string",
56-
format: "date-time"
57-
}
58-
}
59-
},
6060
secret: {
6161
type: "string",
6262
default: "I'm a hidden string."
@@ -87,6 +87,10 @@ module.exports = {
8787
}
8888
},
8989
formData: {
90+
stringFormats: {
91+
email: "chuck@norris.net",
92+
uri: "http://chucknorris.com/",
93+
},
9094
boolean: {
9195
default: true,
9296
radio: true,
@@ -96,10 +100,6 @@ module.exports = {
96100
default: "Hello...",
97101
textarea: "... World"
98102
},
99-
stringFormats: {
100-
email: "chuck@norris.net",
101-
uri: "http://chucknorris.com/",
102-
},
103103
secret: "I'm a hidden string."
104104
}
105105
};

src/components/widgets/DateWidget.js

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,43 @@ import { shouldRender, parseDateString, toDateString, pad } from "../../utils";
44
import SelectWidget from "../widgets/SelectWidget";
55

66

7-
function rangeOptions(start, stop) {
8-
let options = [];
7+
function rangeOptions(type, start, stop) {
8+
let options = [{value: -1, label: type}];
99
for (let i=start; i<= stop; i++) {
1010
options.push({value: i, label: pad(i, 2)});
1111
}
1212
return options;
1313
}
1414

15+
function valid(state) {
16+
return Object.keys(state).every(key => state[key] !== -1);
17+
}
18+
1519
function DateElement({type, range, value, select, rootId}) {
1620
const id = rootId + "_" + type;
1721
return (
18-
<span>
19-
<SelectWidget
20-
schema={{type: "integer"}}
21-
id={id}
22-
className="form-control"
23-
options={rangeOptions(range[0], range[1])}
24-
value={value}
25-
onChange={(value) => select(type, value)} />
26-
<p className="text-center help-block">
27-
<label htmlFor={id}><small>{type}</small></label>
28-
</p>
29-
</span>
22+
<SelectWidget
23+
schema={{type: "integer"}}
24+
id={id}
25+
className="form-control"
26+
options={rangeOptions(type, range[0], range[1])}
27+
value={value}
28+
onChange={(value) => select(type, value)} />
3029
);
3130
}
3231

3332
class DateWidget extends Component {
34-
defaultProps = {
33+
static defaultProps = {
3534
time: false
3635
};
3736

3837
constructor(props) {
3938
super(props);
40-
this.state = parseDateString(props.value, !!props.time);
39+
this.state = parseDateString(props.value, props.time);
4140
}
4241

4342
componentWillReceiveProps(nextProps) {
44-
this.setState(parseDateString(nextProps.value, !!nextProps.time));
43+
this.setState(parseDateString(nextProps.value, nextProps.time));
4544
}
4645

4746
shouldComponentUpdate(nextProps, nextState) {
@@ -50,12 +49,28 @@ class DateWidget extends Component {
5049

5150
onChange = (property, value) => {
5251
this.setState({[property]: value}, () => {
53-
this.props.onChange(toDateString(this.state));
52+
// Only propagate to parent state if we have a complete date{time}
53+
if (valid(this.state)) {
54+
this.props.onChange(toDateString(this.state));
55+
}
5456
});
5557
};
5658

57-
get dateElements() {
58-
const {id, time} = this.props;
59+
setNow = (event) => {
60+
event.preventDefault();
61+
const {time, onChange} = this.props;
62+
const nowDateObj = parseDateString(new Date().toJSON(), time);
63+
this.setState(nowDateObj, () => onChange(toDateString(this.state)));
64+
};
65+
66+
clear = (event) => {
67+
event.preventDefault();
68+
const {time, onChange} = this.props;
69+
this.setState(parseDateString("", time), () => onChange(undefined));
70+
};
71+
72+
get dateElementProps() {
73+
const {time} = this.props;
5974
const {year, month, day, hour, minute, second} = this.state;
6075
const data = [
6176
{type: "year", range: [1900, 2020], value: year},
@@ -69,16 +84,28 @@ class DateWidget extends Component {
6984
{type: "second", range: [0, 59], value: second}
7085
);
7186
}
72-
return data.map(props => {
73-
return <DateElement rootId={id} select={this.onChange} {...props} />;
74-
});
87+
return data;
7588
}
7689

7790
render() {
91+
const {id} = this.props;
7892
return (
7993
<ul className="list-inline">{
80-
this.dateElements.map((elem, i) => <li key={i}>{elem}</li>)
81-
}</ul>
94+
this.dateElementProps.map((props, i) => (
95+
<li key={i}>
96+
<DateElement rootId={id} select={this.onChange} {...props} />
97+
</li>
98+
))
99+
}
100+
<li>
101+
<a href="#" className="btn btn-info btn-now"
102+
onClick={this.setNow}>Now</a>
103+
</li>
104+
<li>
105+
<a href="#" className="btn btn-warning btn-clear"
106+
onClick={this.clear}>Clear</a>
107+
</li>
108+
</ul>
82109
);
83110
}
84111
}

src/utils.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,14 @@ export function toIdSchema(schema, id, definitions) {
316316

317317
export function parseDateString(dateString, includeTime = true) {
318318
if (!dateString) {
319-
dateString = new Date().toJSON();
319+
return {
320+
year: -1,
321+
month: -1,
322+
day: -1,
323+
hour: includeTime ? -1 : 0,
324+
minute: includeTime ? -1 : 0,
325+
second: includeTime ? -1 : 0,
326+
};
320327
}
321328
const date = new Date(dateString);
322329
if (Number.isNaN(date.getTime())) {
@@ -332,8 +339,7 @@ export function parseDateString(dateString, includeTime = true) {
332339
};
333340
}
334341

335-
export function toDateString(dateObj) {
336-
const {year, month, day, hour, minute, second} = dateObj;
342+
export function toDateString({year, month, day, hour=0, minute=0, second=0}) {
337343
const utcTime = Date.UTC(year, month - 1, day, hour, minute, second);
338344
return new Date(utcTime).toJSON();
339345
}

0 commit comments

Comments
 (0)