Repository: mozilla-services/react-jsonschema-form
Pull request #599 (2017-06-01 10:22:29)

Consider maxItems value before rendering the add button
jsonschema refuses to validate an array of items when the number of
items is greater than the maxItems constraint. Therefore the UI should
consisder this constraint and not provide an "add" button when the
maximum number of elements is reached.

The "add" button is rendered in the following cases:

- ui:options.addable is not false and items.length < schema.maxItems
- schema.maxItems is unspecified and ui:options.addable is not false

As before, if ui:options.addable is not specified, it is considered as
true by default.

The "add" button is *not* rendered in the following cases:

- ui:options.addable is false,
- items.length >= schema.maxItems, even if ui.options:addable is true

### Reasons for making this change

[Please describe them here]

If this is related to existing tickets, include links to them as well.

### Checklist

* [ ] **I'm updating documentation**
  - [ ] I've checked the rendering of the Markdown text I've added
  - [ ] If I'm adding a new section, I've updated the Table of Content
* [ ] **I'm adding or updating code**
  - [x] I've added and/or updated tests
  - [ ] I've updated docs if needed
  - [x] I've run `npm run cs-format` on my branch to conform my code to [prettier](https://github.com/prettier/prettier) coding style
* [ ] **I'm adding a new feature**
  - [ ] I've updated the playground with an example use of the feature



Patch:
diff --git a/README.md b/README.md
index 29dec165c..af632407d 100644
--- a/README.md
+++ b/README.md
@@ -821,7 +821,7 @@ function ArrayFieldTemplate(props) {
   return (
     <div>
       {props.items.map(element => element.children)}
-      {props.canAdd && <button onClick={props.onAddClick}></button>}
+      {props.canAdd && <button type="button" onClick={props.onAddClick}></button>}
     </div>
   );
 }
diff --git a/package.json b/package.json
index 5974f8489..b99e1ce1d 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,13 @@
 {
   "name": "react-jsonschema-form",
-  "version": "0.48.2",
+  "version": "0.49.0",
   "description": "A simple React component capable of building HTML forms out of a JSON schema.",
   "scripts": {
     "build:readme": "toctoc README.md -w",
     "build:lib": "rimraf lib && cross-env NODE_ENV=production babel -d lib/ src/",
     "build:dist": "rimraf dist && cross-env NODE_ENV=production webpack --config webpack.config.dist.js --optimize-minimize",
     "build:playground": "rimraf build && cross-env NODE_ENV=production webpack --config webpack.config.prod.js --optimize-minimize && cp playground/index.prod.html build/index.html",
-    "cs-check": "prettier-check $npm_package_prettierOptions '{playground,src,test}/**/*.js'",
+    "cs-check": "prettier -l $npm_package_prettierOptions '{playground,src,test}/**/*.js'",
     "cs-format": "prettier $npm_package_prettierOptions '{playground,src,test}/**/*.js' --write",
     "dist": "npm run build:lib && npm run build:dist",
     "lint": "eslint src test playground",
@@ -73,8 +73,7 @@
     "jsdom": "^8.3.0",
     "lint-staged": "^3.3.1",
     "mocha": "^2.5.3",
-    "prettier": "^1.2.2",
-    "prettier-check": "^1.0.0",
+    "prettier": "^1.4.2",
     "react": "^15.5.0",
     "react-addons-test-utils": "^15.3.2",
     "react-codemirror": "^0.2.3",
diff --git a/playground/app.js b/playground/app.js
index 364d3d442..4757b35fb 100644
--- a/playground/app.js
+++ b/playground/app.js
@@ -57,70 +57,89 @@ const cmOptions = {
 };
 const themes = {
   default: {
-    stylesheet: "//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css",
+    stylesheet:
+      "//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css",
   },
   cerulean: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/cerulean/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/cerulean/bootstrap.min.css",
   },
   cosmo: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/cosmo/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/cosmo/bootstrap.min.css",
   },
   cyborg: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/cyborg/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/cyborg/bootstrap.min.css",
     editor: "blackboard",
   },
   darkly: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/darkly/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/darkly/bootstrap.min.css",
     editor: "mbo",
   },
   flatly: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/flatly/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/flatly/bootstrap.min.css",
     editor: "ttcn",
   },
   journal: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/journal/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/journal/bootstrap.min.css",
   },
   lumen: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/lumen/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/lumen/bootstrap.min.css",
   },
   paper: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/paper/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/paper/bootstrap.min.css",
   },
   readable: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/readable/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/readable/bootstrap.min.css",
   },
   sandstone: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/sandstone/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/sandstone/bootstrap.min.css",
     editor: "solarized",
   },
   simplex: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/simplex/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/simplex/bootstrap.min.css",
     editor: "ttcn",
   },
   slate: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/slate/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/slate/bootstrap.min.css",
     editor: "monokai",
   },
   spacelab: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/spacelab/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/spacelab/bootstrap.min.css",
   },
   "solarized-dark": {
-    stylesheet: "//cdn.rawgit.com/aalpern/bootstrap-solarized/master/bootstrap-solarized-dark.css",
+    stylesheet:
+      "//cdn.rawgit.com/aalpern/bootstrap-solarized/master/bootstrap-solarized-dark.css",
     editor: "dracula",
   },
   "solarized-light": {
-    stylesheet: "//cdn.rawgit.com/aalpern/bootstrap-solarized/master/bootstrap-solarized-light.css",
+    stylesheet:
+      "//cdn.rawgit.com/aalpern/bootstrap-solarized/master/bootstrap-solarized-light.css",
     editor: "solarized",
   },
   superhero: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/superhero/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/superhero/bootstrap.min.css",
     editor: "dracula",
   },
   united: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/united/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/united/bootstrap.min.css",
   },
   yeti: {
-    stylesheet: "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/yeti/bootstrap.min.css",
+    stylesheet:
+      "//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/yeti/bootstrap.min.css",
     editor: "eclipse",
   },
 };
diff --git a/playground/samples/customArray.js b/playground/samples/customArray.js
index 638177595..771bcdb61 100644
--- a/playground/samples/customArray.js
+++ b/playground/samples/customArray.js
@@ -5,7 +5,7 @@ function ArrayFieldTemplate(props) {
     <div className={props.className}>
 
       {props.items &&
-        props.items.map(element => (
+        props.items.map(element =>
           <div key={element.index}>
             <div>{element.children}</div>
             {element.hasMoveDown &&
@@ -29,7 +29,7 @@ function ArrayFieldTemplate(props) {
             </button>
             <hr />
           </div>
-        ))}
+        )}
 
       {props.canAdd &&
         <div className="row">
diff --git a/playground/samples/date.js b/playground/samples/date.js
index 30886ea5c..206820d35 100644
--- a/playground/samples/date.js
+++ b/playground/samples/date.js
@@ -5,7 +5,8 @@ module.exports = {
     properties: {
       native: {
         title: "Native",
-        description: "May not work on some browsers, notably Firefox Desktop and IE.",
+        description:
+          "May not work on some browsers, notably Firefox Desktop and IE.",
         type: "object",
         properties: {
           datetime: {
diff --git a/playground/samples/nested.js b/playground/samples/nested.js
index fecf3f77d..adc5a6be5 100644
--- a/playground/samples/nested.js
+++ b/playground/samples/nested.js
@@ -49,12 +49,14 @@ module.exports = {
     tasks: [
       {
         title: "My first task",
-        details: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
+        details:
+          "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
         done: true,
       },
       {
         title: "My second task",
-        details: "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur",
+        details:
+          "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur",
         done: false,
       },
     ],
diff --git a/playground/samples/validation.js b/playground/samples/validation.js
index 492bfedb3..02434ad36 100644
--- a/playground/samples/validation.js
+++ b/playground/samples/validation.js
@@ -19,7 +19,8 @@ function transformErrors(errors) {
 module.exports = {
   schema: {
     title: "Custom validation",
-    description: "This form defines custom validation rules checking that the two passwords match.",
+    description:
+      "This form defines custom validation rules checking that the two passwords match.",
     type: "object",
     properties: {
       pass1: {
diff --git a/src/components/fields/ArrayField.js b/src/components/fields/ArrayField.js
index b7fd258ee..df43053e6 100644
--- a/src/components/fields/ArrayField.js
+++ b/src/components/fields/ArrayField.js
@@ -200,6 +200,21 @@ class ArrayField extends Component {
     return itemSchema.type !== "null";
   }
 
+  canAddItem(formItems) {
+    const { schema, uiSchema } = this.props;
+    let { addable } = getUiOptions(uiSchema);
+    if (addable !== false) {
+      // if ui:options.addable was not explicitly set to false, we can add
+      // another item if we have not exceeded maxItems yet
+      if (schema.maxItems !== undefined) {
+        addable = formItems.length < schema.maxItems;
+      } else {
+        addable = true;
+      }
+    }
+    return addable;
+  }
+
   onAddClick = event => {
     event.preventDefault();
     const { schema, formData, registry = getDefaultRegistry() } = this.props;
@@ -298,10 +313,8 @@ class ArrayField extends Component {
     const { ArrayFieldTemplate, definitions, fields } = registry;
     const { TitleField, DescriptionField } = fields;
     const itemsSchema = retrieveSchema(schema.items, definitions);
-    const { addable = true } = getUiOptions(uiSchema);
-
     const arrayProps = {
-      canAdd: addable,
+      canAdd: this.canAddItem(formData),
       items: formData.map((item, index) => {
         const itemErrorSchema = errorSchema ? errorSchema[index] : undefined;
         const itemIdPrefix = idSchema.$id + "_" + index;
@@ -434,8 +447,6 @@ class ArrayField extends Component {
     const additionalSchema = allowAdditionalItems(schema)
       ? retrieveSchema(schema.additionalItems, definitions)
       : null;
-    const { addable = true } = getUiOptions(uiSchema);
-    const canAdd = addable && additionalSchema;
 
     if (!items || items.length < itemSchemas.length) {
       // to make sure at least all fixed items are generated
@@ -445,7 +456,7 @@ class ArrayField extends Component {
 
     // These are the props passed into the render function
     const arrayProps = {
-      canAdd,
+      canAdd: this.canAddItem(items) && additionalSchema,
       className: "field field-array field-array-fixed-items",
       disabled,
       idSchema,
@@ -457,8 +468,8 @@ class ArrayField extends Component {
         const itemUiSchema = additional
           ? uiSchema.additionalItems || {}
           : Array.isArray(uiSchema.items)
-              ? uiSchema.items[index]
-              : uiSchema.items || {};
+            ? uiSchema.items[index]
+            : uiSchema.items || {};
         const itemErrorSchema = errorSchema ? errorSchema[index] : undefined;
 
         return this.renderArrayFieldItem({
diff --git a/src/components/widgets/AltDateWidget.js b/src/components/widgets/AltDateWidget.js
index b28f48e75..9618bd818 100644
--- a/src/components/widgets/AltDateWidget.js
+++ b/src/components/widgets/AltDateWidget.js
@@ -121,7 +121,7 @@ class AltDateWidget extends Component {
     const { id, disabled, readonly, autofocus, registry, onBlur } = this.props;
     return (
       <ul className="list-inline">
-        {this.dateElementProps.map((elemProps, i) => (
+        {this.dateElementProps.map((elemProps, i) =>
           <li key={i}>
             <DateElement
               rootId={id}
@@ -134,7 +134,7 @@ class AltDateWidget extends Component {
               autofocus={autofocus && i === 0}
             />
           </li>
-        ))}
+        )}
         <li>
           <a href="#" className="btn btn-info btn-now" onClick={this.setNow}>
             Now
diff --git a/src/components/widgets/SelectWidget.js b/src/components/widgets/SelectWidget.js
index 2a91455d5..2b822840a 100644
--- a/src/components/widgets/SelectWidget.js
+++ b/src/components/widgets/SelectWidget.js
@@ -63,10 +63,10 @@ function SelectWidget(props) {
       autoFocus={autofocus}
       onBlur={
         onBlur &&
-          (event => {
-            const newValue = getValue(event, multiple);
-            onBlur(id, processValue(schema, newValue));
-          })
+        (event => {
+          const newValue = getValue(event, multiple);
+          onBlur(id, processValue(schema, newValue));
+        })
       }
       onChange={event => {
         const newValue = getValue(event, multiple);
diff --git a/src/utils.js b/src/utils.js
index cf2e511a4..91245fbd5 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -67,11 +67,10 @@ export function getWidget(schema, widget, registeredWidgets = {}) {
   function mergeOptions(Widget) {
     // cache return value as property of widget for proper react reconciliation
     if (!Widget.MergedWidget) {
-      const defaultOptions = (Widget.defaultProps &&
-        Widget.defaultProps.options) || {};
-      Widget.MergedWidget = ({ options = {}, ...props }) => (
-        <Widget options={{ ...defaultOptions, ...options }} {...props} />
-      );
+      const defaultOptions =
+        (Widget.defaultProps && Widget.defaultProps.options) || {};
+      Widget.MergedWidget = ({ options = {}, ...props }) =>
+        <Widget options={{ ...defaultOptions, ...options }} {...props} />;
     }
     return Widget.MergedWidget;
   }
@@ -198,7 +197,8 @@ export function mergeObjects(obj1, obj2, concatArrays = false) {
   // Recursively merge deeply nested objects.
   var acc = Object.assign({}, obj1); // Prevent mutation of source object.
   return Object.keys(obj2).reduce((acc, key) => {
-    const left = obj1[key], right = obj2[key];
+    const left = obj1[key],
+      right = obj2[key];
     if (obj1.hasOwnProperty(key) && isObject(right)) {
       acc[key] = mergeObjects(left, right, concatArrays);
     } else if (concatArrays && Array.isArray(left) && Array.isArray(right)) {


Test patch:
diff --git a/test/ArrayField_test.js b/test/ArrayField_test.js
index 34d15be5a..db6e4fadd 100644
--- a/test/ArrayField_test.js
+++ b/test/ArrayField_test.js
@@ -62,9 +62,8 @@ describe("ArrayField", () => {
     });
 
     it("should render a customized description", () => {
-      const CustomDescriptionField = ({ description }) => (
-        <div id="custom">{description}</div>
-      );
+      const CustomDescriptionField = ({ description }) =>
+        <div id="custom">{description}</div>;
 
       const { node } = createFormComponent({
         schema,
@@ -117,6 +116,52 @@ describe("ArrayField", () => {
       expect(node.querySelectorAll(".field-string")).to.have.length.of(1);
     });
 
+    it("should not provide an add button if length equals maxItems", () => {
+      const { node } = createFormComponent({
+        schema: { maxItems: 2, ...schema },
+        formData: ["foo", "bar"],
+      });
+
+      expect(node.querySelector(".array-item-add button")).to.be.null;
+    });
+
+    it("should provide an add button if length is lesser than maxItems", () => {
+      const { node } = createFormComponent({
+        schema: { maxItems: 2, ...schema },
+        formData: ["foo"],
+      });
+
+      expect(node.querySelector(".array-item-add button")).not.eql(null);
+    });
+
+    it("should not provide an add button if addable is expliclty false regardless maxItems value", () => {
+      const { node } = createFormComponent({
+        schema: { maxItems: 2, ...schema },
+        formData: ["foo"],
+        uiSchema: {
+          "ui:options": {
+            addable: false,
+          },
+        },
+      });
+
+      expect(node.querySelector(".array-item-add button")).to.be.null;
+    });
+
+    it("should ignore addable value if maxItems constraint is not satisfied", () => {
+      const { node } = createFormComponent({
+        schema: { maxItems: 2, ...schema },
+        formData: ["foo", "bar"],
+        uiSchema: {
+          "ui:options": {
+            addable: true,
+          },
+        },
+      });
+
+      expect(node.querySelector(".array-item-add button")).to.be.null;
+    });
+
     it("should mark a non-null array item widget as required", () => {
       const { node } = createFormComponent({ schema });
 
@@ -793,6 +838,56 @@ describe("ArrayField", () => {
       expect(node.querySelector(".array-item-add button")).to.be.null;
     });
 
+    it("[fixed-noadditional] should not provide an add button regardless maxItems", () => {
+      const { node } = createFormComponent({
+        schema: { maxItems: 3, ...schema },
+      });
+
+      expect(node.querySelector(".array-item-add button")).to.be.null;
+    });
+
+    it("[fixed] should not provide an add button if length equals maxItems", () => {
+      const { node } = createFormComponent({
+        schema: { maxItems: 2, ...schemaAdditional },
+      });
+
+      expect(node.querySelector(".array-item-add button")).to.be.null;
+    });
+
+    it("[fixed] should provide an add button if length is lesser than maxItems", () => {
+      const { node } = createFormComponent({
+        schema: { maxItems: 3, ...schemaAdditional },
+      });
+
+      expect(node.querySelector(".array-item-add button")).not.to.be.null;
+    });
+
+    it("[fixed] should not provide an add button if addable is expliclty false regardless maxItems value", () => {
+      const { node } = createFormComponent({
+        schema: { maxItems: 2, ...schema },
+        uiSchema: {
+          "ui:options": {
+            addable: false,
+          },
+        },
+      });
+
+      expect(node.querySelector(".array-item-add button")).to.be.null;
+    });
+
+    it("[fixed] should ignore addable value if maxItems constraint is not satisfied", () => {
+      const { node } = createFormComponent({
+        schema: { maxItems: 2, ...schema },
+        uiSchema: {
+          "ui:options": {
+            addable: true,
+          },
+        },
+      });
+
+      expect(node.querySelector(".array-item-add button")).to.be.null;
+    });
+
     describe("operations for additional items", () => {
       const { comp, node } = createFormComponent({
         schema: schemaAdditional,
diff --git a/test/Form_test.js b/test/Form_test.js
index 6fcaea1a5..bf7005493 100644
--- a/test/Form_test.js
+++ b/test/Form_test.js
@@ -95,9 +95,9 @@ describe("Form", () => {
           </span>
           {rawErrors
             ? <ul>
-                {rawErrors.map((error, i) => (
+                {rawErrors.map((error, i) =>
                   <li key={i} className="raw-error">{error}</li>
-                ))}
+                )}
               </ul>
             : null}
         </div>
@@ -906,7 +906,7 @@ describe("Form", () => {
         schema: {
           type: "string",
           minLength: 8,
-          pattern: "\d+",
+          pattern: "d+",
         },
         formData: "short",
       };
@@ -916,7 +916,7 @@ describe("Form", () => {
         expect(comp.state.errorSchema).eql({
           __errors: [
             "does not meet minimum length of 8",
-            'does not match pattern "\d+"',
+            'does not match pattern "d+"',
           ],
         });
       });
@@ -929,7 +929,7 @@ describe("Form", () => {
 
         expect(errors).eql([
           "does not meet minimum length of 8",
-          'does not match pattern "\d+"',
+          'does not match pattern "d+"',
         ]);
       });
     });
diff --git a/test/ObjectField_test.js b/test/ObjectField_test.js
index 7b9b07e3b..182970b07 100644
--- a/test/ObjectField_test.js
+++ b/test/ObjectField_test.js
@@ -66,9 +66,8 @@ describe("ObjectField", () => {
     });
 
     it("should render a customized description", () => {
-      const CustomDescriptionField = ({ description }) => (
-        <div id="custom">{description}</div>
-      );
+      const CustomDescriptionField = ({ description }) =>
+        <div id="custom">{description}</div>;
 
       const { node } = createFormComponent({
         schema,
diff --git a/test/SchemaField_test.js b/test/SchemaField_test.js
index 153d748d6..2f3e79ea1 100644
--- a/test/SchemaField_test.js
+++ b/test/SchemaField_test.js
@@ -242,9 +242,8 @@ describe("SchemaField", () => {
     });
 
     it("should render a customized description field", () => {
-      const CustomDescriptionField = ({ description }) => (
-        <div id="custom">{description}</div>
-      );
+      const CustomDescriptionField = ({ description }) =>
+        <div id="custom">{description}</div>;
 
       const { node } = createFormComponent({
         schema,
diff --git a/test/uiSchema_test.js b/test/uiSchema_test.js
index 50d5a020d..8f7e41062 100644
--- a/test/uiSchema_test.js
+++ b/test/uiSchema_test.js
@@ -321,9 +321,9 @@ describe("uiSchema", () => {
         const { enumOptions, className } = options;
         return (
           <select className={className}>
-            {enumOptions.map(({ label, value }, i) => (
+            {enumOptions.map(({ label, value }, i) =>
               <option key={i}>{value}</option>
-            ))}
+            )}
           </select>
         );
       };
diff --git a/test/validate_test.js b/test/validate_test.js
index fa8733afa..6db74f780 100644
--- a/test/validate_test.js
+++ b/test/validate_test.js
@@ -515,9 +515,8 @@ describe("Validation", () => {
 
       const formData = 0;
 
-      const CustomErrorList = ({ errors }) => (
-        <div className="CustomErrorList">{errors.length} custom</div>
-      );
+      const CustomErrorList = ({ errors }) =>
+        <div className="CustomErrorList">{errors.length} custom</div>;
 
       it("should use CustomErrorList", () => {
         const { node } = createFormComponent({