Skip to content

Commit

Permalink
Fixes #137: Optional fields shouldn't always enforce a value. (#139)
Browse files Browse the repository at this point in the history
r=@leplatrem

- Dropped obsolete defaultTypeValue helper.
- Avoid having uncontrolled widgets components.
- Ensure no React warning can pass through the test suites.
  • Loading branch information
n1k0 committed Apr 15, 2016
1 parent dde39ce commit d3e1078
Show file tree
Hide file tree
Showing 20 changed files with 151 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"react/react-in-jsx-scope": 2,

"curly": [2],
"indent": [2, 2],
"indent": [2, 2, {SwitchCase: 1}],
"quotes": [1, "double"],
"linebreak-style": [2, "unix"],
"semi": [2, "always"],
Expand Down
2 changes: 1 addition & 1 deletion devServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ app.listen(port, host, function(err) {
return;
}

console.log(`Listening at ${server}`);
console.log(`Listening at http://${server}`);
});
2 changes: 1 addition & 1 deletion src/components/widgets/CheckboxWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function CheckboxWidget({
<input type="checkbox"
id={id}
title={placeholder}
checked={value}
checked={typeof value === "undefined" ? false : value}
required={required}
onChange={(event) => onChange(event.target.checked)} />
<strong>{label}</strong>
Expand Down
2 changes: 1 addition & 1 deletion src/components/widgets/EmailWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function EmailWidget({
<input type="email"
id={id}
className="form-control"
value={value}
value={typeof value === "undefined" ? "" : value}
placeholder={placeholder}
required={required}
onChange={(event) => onChange(event.target.value)} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/widgets/PasswordWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function PasswordWidget({
<input type="password"
id={id}
className="form-control"
value={value}
value={typeof value === "undefined" ? "" : value}
placeholder={placeholder}
required={required}
onChange={(event) => onChange(event.target.value)} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/widgets/TextWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function TextWidget({
<input type="text"
id={id}
className="form-control"
value={value}
value={typeof value === "undefined" ? "" : value}
placeholder={placeholder}
required={required}
onChange={(event) => onChange(event.target.value)} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/widgets/TextareaWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function TextWidget({
<textarea
id={id}
className="form-control"
value={value}
value={typeof value === "undefined" ? "" : value}
placeholder={placeholder}
required={required}
onChange={(event) => onChange(event.target.value)} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/widgets/URLWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function URLWidget({
<input type="url"
id={id}
className="form-control"
value={value}
value={typeof value === "undefined" ? "" : value}
placeholder={placeholder}
required={required}
onChange={(event) => onChange(event.target.value)} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/widgets/UpDownWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function UpDownWidget({
<input type="number"
id={id}
className="form-control"
value={value}
value={typeof value === "undefined" ? "" : value}
placeholder={placeholder}
required={required}
onChange={(event) => onChange(event.target.value)}
Expand Down
26 changes: 3 additions & 23 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,28 +66,8 @@ export function getDefaultRegistry() {
};
}

export function defaultTypeValue(schema) {
if (typeof schema.default !== "undefined") {
return schema.default;
}
const {type} = schema;
switch (type) {
case "array": return [];
case "boolean": return false;
case "number": return 0;
case "object": return {};
case "string": {
if (schema.format === "date-time") {
return new Date().toJSON();
}
return "";
}
default: return undefined;
}
}

export function defaultFieldValue(formData, schema) {
return typeof formData === "undefined" ? defaultTypeValue(schema) : formData;
return typeof formData === "undefined" ? schema.default : formData;
}

export function getAlternativeWidget(schema, widget, registeredWidgets={}) {
Expand Down Expand Up @@ -134,15 +114,15 @@ function computeDefaults(schema, parentDefaults, definitions={}) {
}
// Not defaults defined for this node, fallback to generic typed ones.
if (typeof(defaults) === "undefined") {
defaults = defaultTypeValue(schema);
defaults = schema.default;
}
// We need to recur for object schema inner default values.
if (schema.type === "object") {
return Object.keys(schema.properties).reduce((acc, key) => {
// Compute the defaults for this node, with the parent defaults we might
// have from a previous run: defaults[key].
acc[key] = computeDefaults(
schema.properties[key], defaults[key], definitions);
schema.properties[key], (defaults || {})[key], definitions);
return acc;
}, {});
}
Expand Down
13 changes: 12 additions & 1 deletion test/ArrayField_test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { expect } from "chai";
import { Simulate } from "react-addons-test-utils";

import { createFormComponent } from "./test_utils";
import { createFormComponent, createSandbox } from "./test_utils";


describe("ArrayField", () => {
let sandbox;

beforeEach(() => {
sandbox = createSandbox();
});

afterEach(() => {
sandbox.restore();
});

describe("List of inputs", () => {
const schema = {
type: "array",
Expand Down
19 changes: 18 additions & 1 deletion test/BooleanField_test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { expect } from "chai";
import { Simulate } from "react-addons-test-utils";

import { createFormComponent } from "./test_utils";
import { createFormComponent, createSandbox } from "./test_utils";


describe("BooleanField", () => {
let sandbox;

beforeEach(() => {
sandbox = createSandbox();
});

afterEach(() => {
sandbox.restore();
});

it("should render a boolean field", () => {
const {node} = createFormComponent({schema: {
type: "boolean"
Expand Down Expand Up @@ -43,6 +54,12 @@ describe("BooleanField", () => {
.eql(true);
});

it("should default state value to undefined", () => {
const {comp} = createFormComponent({schema: {type: "boolean"}});

expect(comp.state.formData).eql(undefined);
});

it("should handle a change event", () => {
const {comp, node} = createFormComponent({schema: {
type: "boolean",
Expand Down
25 changes: 21 additions & 4 deletions test/Form_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { Simulate, renderIntoDocument } from "react-addons-test-utils";
import { findDOMNode } from "react-dom";

import Form from "../src";
import { createFormComponent } from "./test_utils";
import { createFormComponent, createSandbox } from "./test_utils";


describe("Form", () => {
var sandbox;
let sandbox;

beforeEach(() => {
sandbox = sinon.sandbox.create();
sandbox = createSandbox();
});

afterEach(() => {
Expand Down Expand Up @@ -561,7 +562,7 @@ describe("Form", () => {
};

it("should update the errorSchema on form submission", () => {
const {comp, node} = createFormComponent({schema});
const {comp, node} = createFormComponent({schema, onError: () => {}});

Simulate.change(node.querySelector("input[type=text]"), {
target: {value: "short"}
Expand All @@ -573,6 +574,22 @@ describe("Form", () => {
errors: ["does not meet minimum length of 8"]
});
});

it("should call the onError handler", () => {
const onError = sandbox.spy();
const {node} = createFormComponent({schema, onError});

Simulate.change(node.querySelector("input[type=text]"), {
target: {value: "short"}
});

Simulate.submit(node);

sinon.assert.calledWithMatch(onError, sinon.match(value => {
return value.length === 1 &&
value[0].message === "does not meet minimum length of 8";
}));
});
});

describe("root level", () => {
Expand Down
19 changes: 18 additions & 1 deletion test/NumberField_test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { expect } from "chai";
import { Simulate} from "react-addons-test-utils";

import { createFormComponent } from "./test_utils";
import { createFormComponent, createSandbox } from "./test_utils";


describe("NumberField", () => {
let sandbox;

beforeEach(() => {
sandbox = createSandbox();
});

afterEach(() => {
sandbox.restore();
});

describe("TextWidget", () => {
it("should render a string field", () => {
const {node} = createFormComponent({schema: {
Expand Down Expand Up @@ -34,6 +45,12 @@ describe("NumberField", () => {
.eql("bar");
});

it("should default state value to undefined", () => {
const {comp} = createFormComponent({schema: {type: "number"}});

expect(comp.state.formData).eql(undefined);
});

it("should assign a default value", () => {
const {node} = createFormComponent({schema: {
type: "number",
Expand Down
13 changes: 12 additions & 1 deletion test/ObjectField_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@ import React from "react";
import { expect } from "chai";
import { Simulate } from "react-addons-test-utils";

import { createFormComponent } from "./test_utils";
import { createFormComponent, createSandbox } from "./test_utils";


describe("ObjectField", () => {
let sandbox;

beforeEach(() => {
sandbox = createSandbox();
});

afterEach(() => {
sandbox.restore();
});

describe("schema", () => {
const schema = {
type: "object",
Expand Down
13 changes: 12 additions & 1 deletion test/SchemaField_test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
import React from "react";
import { expect } from "chai";

import SchemaField from "../src/components/fields/SchemaField";
import TitleField from "../src/components/fields/TitleField";
import { createFormComponent, createSandbox } from "./test_utils";

import { createFormComponent } from "./test_utils";

describe("SchemaField", () => {
let sandbox;

beforeEach(() => {
sandbox = createSandbox();
});

afterEach(() => {
sandbox.restore();
});

describe("Custom SchemaField component", () => {
const CustomSchemaField = function(props) {
return (<div id="custom"><SchemaField {...props} /></div>);
Expand Down
19 changes: 18 additions & 1 deletion test/StringField_test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { expect } from "chai";
import { Simulate } from "react-addons-test-utils";

import { createFormComponent } from "./test_utils";
import { createFormComponent, createSandbox } from "./test_utils";


describe("StringField", () => {
let sandbox;

beforeEach(() => {
sandbox = createSandbox();
});

afterEach(() => {
sandbox.restore();
});

describe("TextWidget", () => {
it("should render a string field", () => {
const {node} = createFormComponent({schema: {
Expand Down Expand Up @@ -44,6 +55,12 @@ describe("StringField", () => {
.eql("plop");
});

it("should default state value to undefined", () => {
const {comp} = createFormComponent({schema: {type: "string"}});

expect(comp.state.formData).eql(undefined);
});

it("should handle a change event", () => {
const {comp, node} = createFormComponent({schema: {
type: "string",
Expand Down
8 changes: 6 additions & 2 deletions test/performance_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import React from "react";
import { getDefaultRegistry } from "../src/utils";
import ArrayField from "../src/components/fields/ArrayField";
import ObjectField from "../src/components/fields/ObjectField";
import { createComponent, createFormComponent } from "./test_utils";
import {
createComponent,
createFormComponent,
createSandbox
} from "./test_utils";


describe("Rendering performance optimizations", () => {
var sandbox;

beforeEach(() => {
sandbox = sinon.sandbox.create();
sandbox = createSandbox();
});

afterEach(() => {
Expand Down
10 changes: 10 additions & 0 deletions test/test_utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* Utils for tests. */

import React from "react";
import sinon from "sinon";
import { renderIntoDocument } from "react-addons-test-utils";
import { findDOMNode } from "react-dom";

Expand All @@ -15,3 +16,12 @@ export function createComponent(Component, props) {
export function createFormComponent(props) {
return createComponent(Form, props);
}

export function createSandbox() {
const sandbox = sinon.sandbox.create();
// Ensure we catch any React warning and mark them as test failures.
sandbox.stub(console, "error", (error) => {
throw new Error(error);
});
return sandbox;
}
Loading

0 comments on commit d3e1078

Please sign in to comment.