Skip to content

Commit

Permalink
Merge branch 'master' into removable-and-addable
Browse files Browse the repository at this point in the history
# Conflicts:
#	README.md
  • Loading branch information
maartenth committed Nov 4, 2016
2 parents cda4362 + 84434be commit ca7e8d6
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 19 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ This allows you to programmatically trigger the browser's file selector which ca

### Object fields ordering

The `uiSchema` object spec also allows you to define in which order a given object field properties should be rendered using the `ui:order` property:
Since the order of object properties in Javascript and JSON is not guaranteed, the `uiSchema` object spec allows you to define the order in which properties are rendered using the `ui:order` property:

```jsx
const schema = {
Expand All @@ -412,6 +412,14 @@ render((
), document.getElementById("app"));
```

If a guarenteed fixed order is only important for some fields, you can insert a wildcard `"*"` item in your `ui:order` definition. All fields that are not referenced explicitly anywhere in the list will be rendered at that point:

```js
const uiSchema = {
"ui:order": ["bar", "*"]
};
```

### Array item options

#### `orderable` option
Expand Down
2 changes: 1 addition & 1 deletion playground/samples/ordering.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module.exports = {
}
},
uiSchema: {
"ui:order": ["firstName", "lastName", "age", "bio", "password"],
"ui:order": ["firstName", "lastName", "*", "password"],
age: {
"ui:widget": "updown"
},
Expand Down
35 changes: 27 additions & 8 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,16 +252,35 @@ export function orderProperties(properties, order) {
if (!Array.isArray(order)) {
return properties;
}
if (order.length !== properties.length) {
throw new Error(
"uiSchema order list length should match object properties length");

const arrayToHash = arr => arr.reduce((prev, curr) => {
prev[curr] = true;
return prev;
}, {});
const errorPropList = arr => arr.length > 1 ?
`properties '${arr.join("', '")}'` :
`property '${arr[0]}'`;
const propertyHash = arrayToHash(properties);
const orderHash = arrayToHash(order);
const extraneous = order.filter(prop => prop !== "*" && !propertyHash[prop]);
if (extraneous.length) {
throw new Error(`uiSchema order list contains extraneous ${errorPropList(extraneous)}`);
}
const rest = properties.filter(prop => !orderHash[prop]);
const restIndex = order.indexOf("*");
if (restIndex === -1) {
if (rest.length) {
throw new Error(`uiSchema order list does not contain ${errorPropList(rest)}`);
}
return order;
}
const fingerprint = (arr) => [].slice.call(arr).sort().toString();
if (fingerprint(order) !== fingerprint(properties)) {
throw new Error(
"uiSchema order list does not match object properties list");
if (restIndex !== order.lastIndexOf("*")) {
throw new Error("uiSchema order list contains more than one wildcard item");
}
return order;

const complete = [...order];
complete.splice(restIndex, 1, ...rest);
return complete;
}

export function isMultiSelect(schema) {
Expand Down
47 changes: 38 additions & 9 deletions test/ObjectField_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,36 +146,57 @@ describe("ObjectField", () => {
type: "object",
properties: {
foo: {type: "string"},
bar: {type: "string"}
bar: {type: "string"},
baz: {type: "string"},
qux: {type: "string"}
}
};

it("should use provided order", () => {
const {node} = createFormComponent({schema, uiSchema: {
"ui:order": ["bar", "foo"]
"ui:order": ["baz", "qux", "bar", "foo"]
}});
const labels = [].map.call(
node.querySelectorAll(".field > label"), l => l.textContent);

expect(labels).eql(["bar", "foo"]);
expect(labels).eql(["baz", "qux", "bar", "foo"]);
});

it("should throw when order list length mismatches", () => {
it("should insert unordered properties at wildcard position", () => {
const {node} = createFormComponent({schema, uiSchema: {
"ui:order": ["bar", "foo", "baz?"]
"ui:order": ["baz", "*", "foo"]
}});
const labels = [].map.call(
node.querySelectorAll(".field > label"), l => l.textContent);

expect(labels).eql(["baz", "bar", "qux", "foo"]);
});

it("should throw when order list contains an extraneous property", () => {
const {node} = createFormComponent({schema, uiSchema: {
"ui:order": ["baz", "qux", "bar", "wut?", "foo", "huh?"]
}});

expect(node.querySelector(".config-error").textContent)
.to.match(/should match object properties length/);
.to.match(/contains extraneous properties 'wut\?', 'huh\?'/);
});

it("should throw when order and properties lists differs", () => {
it("should throw when order list misses an existing property", () => {
const {node} = createFormComponent({schema, uiSchema: {
"ui:order": ["bar", "wut?"]
"ui:order": ["baz", "bar"]
}});

expect(node.querySelector(".config-error").textContent)
.to.match(/does not match object properties list/);
.to.match(/does not contain properties 'foo', 'qux'/);
});

it("should throw when more than one wildcard is present", () => {
const {node} = createFormComponent({schema, uiSchema: {
"ui:order": ["baz", "*", "bar", "*"]
}});

expect(node.querySelector(".config-error").textContent)
.to.match(/contains more than one wildcard/);
});

it("should order referenced schema definitions", () => {
Expand Down Expand Up @@ -228,6 +249,14 @@ describe("ObjectField", () => {
});

it("should render the widget with the expected id", () => {
const schema = {
type: "object",
properties: {
foo: {type: "string"},
bar: {type: "string"}
}
};

const {node} = createFormComponent({schema, uiSchema: {
"ui:order": ["bar", "foo"]
}});
Expand Down

0 comments on commit ca7e8d6

Please sign in to comment.