Skip to content

Commit 6563fd2

Browse files
committed
Added object properties ordering to uiSchema.
1 parent cdd1307 commit 6563fd2

File tree

6 files changed

+130
-3
lines changed

6 files changed

+130
-3
lines changed

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ That should give something like this (if you use the default stylesheet):
7575

7676
### Alternative widgets
7777

78-
JSONSchema is limited for describing how a given data type should be rendered as an input component, that's why this lib introduces the concept of *UI schema*. A UI schema is basically an object literal describing which UI widget should be used to render a certain field
78+
JSONSchema is limited for describing how a given data type should be rendered as an input component, that's why this lib introduces the concept of *UI schema*. A UI schema is basically an object literal describing which UI widget should be used to render a certain field.
7979

8080
Example:
8181

@@ -115,6 +115,29 @@ Here's a list of supported alternative widgets for different JSONSchema data typ
115115

116116
> Note: for numbers, `min`, `max` and `step` input attributes values will be handled according to JSONSchema's `minimum`, `maximium` and `multipleOf` values when they're defined.
117117
118+
## Object fields ordering
119+
120+
The `uiSchema` object spec also allows you to define in which order a given object field properties should be rendered using the `order` property:
121+
122+
```jsx
123+
const schema = {
124+
type: "object",
125+
properties: {
126+
foo: {type: "string"},
127+
bar: {type: "string"}
128+
}
129+
};
130+
131+
const uiSchema = {
132+
order: ["bar", "foo"]
133+
};
134+
135+
render((
136+
<Form schema={schema}
137+
uiSchema={uiSchema} />
138+
), document.getElementById("app"));
139+
```
140+
118141
## Custom styles
119142

120143
The UISchema object accepts a `classNames` property for each field of the schema:

playground/samples/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import nested from "./nested";
33
import numbers from "./numbers";
44
import simple from "./simple";
55
import widgets from "./widgets";
6+
import ordering from "./ordering";
67

78
export const samples = {
89
Simple: simple,
910
Nested: nested,
1011
Arrays: arrays,
1112
Numbers: numbers,
1213
Widgets: widgets,
14+
Ordering: ordering,
1315
};

playground/samples/ordering.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
module.exports = {
2+
schema: {
3+
title: "A registration form",
4+
type: "object",
5+
required: ["firstName", "lastName"],
6+
properties: {
7+
password: {
8+
type: "string",
9+
title: "Password"
10+
},
11+
lastName: {
12+
type: "string",
13+
title: "Last name",
14+
},
15+
bio: {
16+
type: "string",
17+
title: "Bio",
18+
},
19+
firstName: {
20+
type: "string",
21+
title: "First name",
22+
},
23+
age: {
24+
type: "integer",
25+
title: "Age"
26+
},
27+
}
28+
},
29+
uiSchema: {
30+
order: ["firstName", "lastName", "age", "bio", "password"],
31+
age: {
32+
widget: "updown"
33+
},
34+
bio: {
35+
widget: "textarea"
36+
},
37+
password: {
38+
widget: "password"
39+
}
40+
},
41+
formData: {
42+
firstName: "Chuck",
43+
lastName: "Norris",
44+
age: 75,
45+
bio: "Roundhouse kicking asses since 1940",
46+
password: "noneed",
47+
}
48+
};

src/components/fields/ObjectField.js

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

3-
import { getDefaultFormState } from "../../utils";
3+
import { getDefaultFormState, orderProperties } from "../../utils";
44

55

66
class ObjectField extends Component {
@@ -40,13 +40,15 @@ class ObjectField extends Component {
4040
const {schema, uiSchema, name} = this.props;
4141
const title = name || schema.title;
4242
const SchemaField = this.props.SchemaField;
43+
const orderedProperties = orderProperties(
44+
Object.keys(schema.properties), uiSchema.order);
4345
return (
4446
<fieldset>
4547
{title ? <legend>{title}</legend> : null}
4648
{schema.description ?
4749
<div className="field-description">{schema.description}</div> : null}
4850
{
49-
Object.keys(schema.properties).map((name, index) => {
51+
orderedProperties.map((name, index) => {
5052
return (
5153
<SchemaField key={index}
5254
name={name}

src/utils.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,19 @@ export function asNumber(value) {
8080
const valid = typeof n === "number" && !Number.isNaN(n);
8181
return valid ? n : value;
8282
}
83+
84+
export function orderProperties(properties, order) {
85+
if (!Array.isArray(order)) {
86+
return properties;
87+
}
88+
if (order.length !== properties.length) {
89+
throw new Error(
90+
"uiSchema order list length should match object properties length");
91+
}
92+
if ([].slice.call(order).sort().toString() !==
93+
[].slice.call(properties).sort().toString()) {
94+
throw new Error(
95+
"uiSchema order list does not match object properties list");
96+
}
97+
return order;
98+
}

test/index_test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,42 @@ describe("Form", () => {
6161
});
6262
});
6363

64+
describe("Object fields ordering", () => {
65+
const schema = {
66+
type: "object",
67+
properties: {
68+
foo: {type: "string"},
69+
bar: {type: "string"}
70+
}
71+
};
72+
73+
it("should use provided order", () => {
74+
const {node} = createComponent({schema, uiSchema: {
75+
order: ["bar", "foo"]
76+
}});
77+
const labels = [].map.call(
78+
node.querySelectorAll(".field > label"), l => l.textContent);
79+
80+
expect(labels).eql(["bar", "foo"]);
81+
});
82+
83+
it("should throw when order list length mismatches", () => {
84+
expect(() => {
85+
createComponent({schema, uiSchema: {
86+
order: ["bar", "foo", "baz?"]
87+
}});
88+
}).to.Throw(Error, /should match object properties length/);
89+
});
90+
91+
it("should throw when order and properties lists differs", () => {
92+
expect(() => {
93+
createComponent({schema, uiSchema: {
94+
order: ["bar", "wut?"]
95+
}});
96+
}).to.Throw(Error, /does not match object properties list/);
97+
});
98+
});
99+
64100
describe("StringField", () => {
65101
describe("TextWidget", () => {
66102
it("should render a string field", () => {

0 commit comments

Comments
 (0)