Repository: mozilla-services/react-jsonschema-form
Pull request #81 (2016-03-22 08:20:09)

[Experiment] Ref #80: Add support for enumNames to boolean widgets.
WiP, experimental, do not merge. Refs #80.

Feedback=? @chmac

chmac: Can't really comment on the code, looks good to me...

The docs might be clearer with an example. Something like this?

> To set the labels for a boolean field, instead of using `true` and `false` you can set `enumNames` in your schema. Note that `enumNames` belongs in your `schema`, not the `ui:schema` and the order is `[true, false]`.

One minor thought, will the `enumNames` keyword be valid on fields of type `boolean`? My JSON schema fu is not strong enough to answer that definitively, and I couldn't find anything in the `enumNames` proposal to suggest either way...
n1k0: > One minor thought, will the enumNames keyword be valid on fields of type boolean?

It's quite surprisingly "valid" when tested on http://jsonschemalint.com/draft4/, though I think we're probably bending the spec a little here.

I may be spend a little while figuring out how hard would be to add support for proper uiSchema widget options, which would sound definitely much cleaner to me.
chmac: @n1k0 Could it be that arbitrary keys are allowed, but once v5 gets finalised the key might be only allowed in some situations? Anyway, if it works now, great, no harm, no foul!
n1k0: Yeah, and my main concern is that uiSchema introduces a nice way of separating the data model from its presentation, and moving the labels definition there seems most definitely the right thing to do.

I'll probably implement the uiSchema widget option object thing. 
n1k0: Okay, I just had a look at the required changes to properly support widget options, it's too much work for what we're trying to achieve here. I think I'm gonna land this for now, and we'll revisit the way we handle widget options when we'll need them.

Patch:
diff --git a/README.md b/README.md
index 2cb2af3a8..0d27e1af8 100644
--- a/README.md
+++ b/README.md
@@ -165,6 +165,8 @@ Here's a list of supported alternative widgets for different JSONSchema data typ
   * `select`: a select box with `true` and `false` as options;
   * by default, a checkbox is used
 
+> Note: To set the labels for a boolean field, instead of using `true` and `false` you can set `enumNames` in your schema. Note that `enumNames` belongs in your `schema`, not the `uiSchema`, and the order is always `[true, false]`.
+
 #### For `string` fields
 
   * `textarea`: a `textarea` element;
diff --git a/src/components/fields/BooleanField.js b/src/components/fields/BooleanField.js
index 4e0f43bdf..3f6715f8a 100644
--- a/src/components/fields/BooleanField.js
+++ b/src/components/fields/BooleanField.js
@@ -3,6 +3,14 @@ import React, { PropTypes } from "react";
 import { defaultFieldValue, getAlternativeWidget, optionsList } from "../../utils";
 import CheckboxWidget from "./../widgets/CheckboxWidget";
 
+
+function buildOptions(schema) {
+  return optionsList(Object.assign({
+    enumNames: ["true", "false"],
+    enum: [true, false]
+  }, {enumNames: schema.enumNames}));
+}
+
 function BooleanField(props) {
   const {schema, name, uiSchema, formData, widgets, required, onChange} = props;
   const {title, description} = schema;
@@ -18,7 +26,7 @@ function BooleanField(props) {
   };
   if (widget) {
     const Widget = getAlternativeWidget(schema.type, widget, widgets);
-    return <Widget options={optionsList({enum: [true, false]})} {... commonProps} />;
+    return <Widget options={buildOptions(schema)} {... commonProps} />;
   }
   return <CheckboxWidget {...commonProps} />;
 }


Test patch:
diff --git a/test/BooleanField_test.js b/test/BooleanField_test.js
index 7b82a8b95..6aa319ac6 100644
--- a/test/BooleanField_test.js
+++ b/test/BooleanField_test.js
@@ -54,4 +54,26 @@ describe("BooleanField", () => {
     expect(node.querySelector(".field input").checked)
       .eql(true);
   });
+
+  it("should support enumNames for radio widgets", () => {
+    const {node} = createFormComponent({schema: {
+      type: "boolean",
+      enumNames: ["Yes", "No"],
+    }, formData: true, uiSchema: {"ui:widget": "radio"}});
+
+    const labels = [].map.call(node.querySelectorAll(".field-radio-group label"),
+                               label => label.textContent);
+    expect(labels).eql(["Yes", "No"]);
+  });
+
+  it("should support enumNames for select", () => {
+    const {node} = createFormComponent({schema: {
+      type: "boolean",
+      enumNames: ["Yes", "No"],
+    }, formData: true, uiSchema: {"ui:widget": "select"}});
+
+    const labels = [].map.call(node.querySelectorAll(".field option"),
+                               label => label.textContent);
+    expect(labels).eql(["Yes", "No"]);
+  });
 });