Skip to content

Commit fc6164d

Browse files
committed
Fixes #10: Added support for integer fields.
1 parent 5ad7188 commit fc6164d

File tree

9 files changed

+210
-8
lines changed

9 files changed

+210
-8
lines changed

playground/samples/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import arrays from "./arrays";
22
import nested from "./nested";
3+
import numbers from "./numbers";
34
import simple from "./simple";
45
import widgets from "./widgets";
56

67
export const samples = {
78
Simple: simple,
89
Nested: nested,
910
Arrays: arrays,
11+
Numbers: numbers,
1012
Widgets: widgets,
1113
};

playground/samples/numbers.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module.exports = {
2+
schema: {
3+
type: "object",
4+
properties: {
5+
number: {
6+
title: "Number",
7+
type: "number"
8+
},
9+
integer: {
10+
title: "Integer",
11+
type: "integer"
12+
},
13+
numberEnum: {
14+
type: "number",
15+
title: "Number enum",
16+
enum: [1, 2, 3]
17+
}
18+
}
19+
},
20+
uiSchema: {},
21+
formData: {
22+
number: 3.14,
23+
integer: 42,
24+
numberEnum: 2
25+
}
26+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React, { PropTypes } from "react";
2+
3+
import { asNumber } from "../../utils";
4+
import StringField from "./StringField";
5+
6+
function NumberField(props) {
7+
return (
8+
<StringField {...props}
9+
onChange={(value) => props.onChange(asNumber(value))} />
10+
);
11+
}
12+
13+
if (process.env.NODE_ENV !== "production") {
14+
NumberField.propTypes = {
15+
schema: PropTypes.object.isRequired,
16+
onChange: PropTypes.func.isRequired,
17+
formData: PropTypes.number,
18+
required: PropTypes.bool,
19+
};
20+
}
21+
22+
NumberField.defaultProps = {
23+
uiSchema: {}
24+
};
25+
26+
export default NumberField;

src/components/fields/SchemaField.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import React, { PropTypes } from "react";
22

3-
import StringField from "./StringField";
43
import ArrayField from "./ArrayField";
54
import BooleanField from "./BooleanField";
5+
import NumberField from "./NumberField";
66
import ObjectField from "./ObjectField";
7+
import StringField from "./StringField";
78
import UnsupportedField from "./UnsupportedField";
89

910

1011
const COMPONENT_TYPES = {
11-
"string": StringField,
1212
"array": ArrayField,
1313
"boolean": BooleanField,
14-
"object": ObjectField,
1514
"date-time": StringField,
16-
"number": StringField,
15+
"integer": NumberField,
16+
"number": NumberField,
17+
"object": ObjectField,
18+
"string": StringField,
1719
};
1820

1921
function SchemaField(props) {

src/components/fields/StringField.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ if (process.env.NODE_ENV !== "production") {
3535
StringField.propTypes = {
3636
schema: PropTypes.object.isRequired,
3737
onChange: PropTypes.func.isRequired,
38-
formData: PropTypes.string,
38+
formData: PropTypes.oneOfType([
39+
React.PropTypes.string,
40+
React.PropTypes.number,
41+
]),
3942
required: PropTypes.bool,
4043
};
4144
}

src/components/widgets/SelectWidget.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { PropTypes } from "react";
22

3+
import { asNumber } from "../../utils";
34
import Wrapper from "./../widgets/Wrapper";
45

56

@@ -11,7 +12,7 @@ function processValue(type, value) {
1112
if (type === "boolean") {
1213
return value === "true";
1314
} else if (type === "number") {
14-
return value === Number(value);
15+
return asNumber(value);
1516
}
1617
return value;
1718
}

src/components/widgets/TextWidget.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,14 @@ if (process.env.NODE_ENV !== "production") {
2929
type: PropTypes.string.isRequired,
3030
label: PropTypes.string,
3131
placeholder: PropTypes.string,
32-
value: PropTypes.string,
33-
defaultValue: PropTypes.string,
32+
value: PropTypes.oneOfType([
33+
React.PropTypes.string,
34+
React.PropTypes.number,
35+
]),
36+
defaultValue: PropTypes.oneOfType([
37+
React.PropTypes.string,
38+
React.PropTypes.number,
39+
]),
3440
required: PropTypes.bool,
3541
onChange: PropTypes.func,
3642
};

src/utils.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,9 @@ export function getDefaultFormState(schema) {
4343
}
4444
return defaultTypeValue(schema.type);
4545
}
46+
47+
export function asNumber(value) {
48+
const n = Number(value);
49+
const valid = typeof n === "number" && !Number.isNaN(n);
50+
return valid ? n : value;
51+
}

test/index_test.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,136 @@ describe("Form", () => {
174174
});
175175
});
176176

177+
describe("NumberField", () => {
178+
describe("TextWidget", () => {
179+
it("should render a string field", () => {
180+
const {node} = createComponent({schema: {
181+
type: "number"
182+
}});
183+
184+
expect(node.querySelectorAll(".field input[type=text]"))
185+
.to.have.length.of(1);
186+
});
187+
188+
it("should render a string field with a label", () => {
189+
const {node} = createComponent({schema: {
190+
type: "number",
191+
title: "foo"
192+
}});
193+
194+
expect(node.querySelector(".field label > span").textContent)
195+
.eql("foo");
196+
});
197+
198+
it("should render a string field with a placeholder", () => {
199+
const {node} = createComponent({schema: {
200+
type: "number",
201+
description: "bar",
202+
}});
203+
204+
expect(node.querySelector(".field input").getAttribute("placeholder"))
205+
.eql("bar");
206+
});
207+
208+
it("should assign a default value", () => {
209+
const {node} = createComponent({schema: {
210+
type: "number",
211+
default: 2,
212+
}});
213+
214+
expect(node.querySelector(".field input").getAttribute("value"))
215+
.eql("2");
216+
});
217+
218+
it("should handle a change event", () => {
219+
const {comp, node} = createComponent({schema: {
220+
type: "number",
221+
}});
222+
223+
Simulate.change(node.querySelector("input"), {
224+
target: {value: "2"}
225+
});
226+
227+
expect(comp.state.formData).eql(2);
228+
});
229+
230+
it("should fill field with data", () => {
231+
const {node} = createComponent({schema: {
232+
type: "number",
233+
}, formData: 2});
234+
235+
expect(node.querySelector(".field input").getAttribute("value"))
236+
.eql("2");
237+
});
238+
});
239+
240+
describe("SelectWidget", () => {
241+
it("should render a number field", () => {
242+
const {node} = createComponent({schema: {
243+
type: "number",
244+
enum: [1, 2]
245+
}});
246+
247+
expect(node.querySelectorAll(".field select"))
248+
.to.have.length.of(1);
249+
});
250+
251+
it("should render a string field with a label", () => {
252+
const {node} = createComponent({schema: {
253+
type: "number",
254+
enum: [1, 2],
255+
title: "foo",
256+
}});
257+
258+
expect(node.querySelector(".field label > span").textContent)
259+
.eql("foo");
260+
});
261+
262+
it("should render a select field with a tooltip", () => {
263+
const {node} = createComponent({schema: {
264+
type: "number",
265+
enum: [1, 2],
266+
description: "baz",
267+
}});
268+
269+
expect(node.querySelector(".field select").getAttribute("title"))
270+
.eql("baz");
271+
});
272+
273+
it("should assign a default value", () => {
274+
const {comp} = createComponent({schema: {
275+
type: "number",
276+
enum: [1, 2],
277+
default: 1,
278+
}});
279+
280+
expect(comp.state.formData).eql(1);
281+
});
282+
283+
it("should handle a change event", () => {
284+
const {comp, node} = createComponent({schema: {
285+
type: "number",
286+
enum: [1, 2],
287+
}});
288+
289+
Simulate.change(node.querySelector("select"), {
290+
target: {value: "2"}
291+
});
292+
293+
expect(comp.state.formData).eql(2);
294+
});
295+
296+
it("should fill field with data", () => {
297+
const {comp} = createComponent({schema: {
298+
type: "number",
299+
enum: [1, 2],
300+
}, formData: 2});
301+
302+
expect(comp.state.formData).eql(2);
303+
});
304+
});
305+
});
306+
177307
describe("BooleanField", () => {
178308
it("should render a boolean field", () => {
179309
const {node} = createComponent({schema: {

0 commit comments

Comments
 (0)