Repository: mozilla-services/react-jsonschema-form (js)

Issue:
Support enumNames for enums
We should probably offer support for `enumNames`, which is draft v5 atm:

```js
"obj": {
  "enum": [{a: 1}, {b: 1}, {c: 1}],
  "enumNames": ["a", "b", "c"]
}
```

Pull request description:
Support enumNames in schema (fixes #25)
Paired with @magopian and @n1k0.

Patch:
diff --git a/src/components/fields/ArrayField.js b/src/components/fields/ArrayField.js
index a179d1a93..837aeea7a 100644
--- a/src/components/fields/ArrayField.js
+++ b/src/components/fields/ArrayField.js
@@ -1,6 +1,6 @@
 import React, { Component, PropTypes } from "react";
 
-import { getDefaultFormState, isMultiSelect } from "../../utils";
+import { getDefaultFormState, isMultiSelect, optionsList } from "../../utils";
 import SelectWidget from "./../widgets/SelectWidget";
 
 
@@ -75,7 +75,7 @@ class ArrayField extends Component {
         <SelectWidget
           multiple
           onChange={this.onSelectChange.bind(this)}
-          options={schema.items.enum}
+          options={optionsList(schema.items)}
           schema={schema}
           title={title}
           defaultValue={schema.default}
diff --git a/src/components/fields/BooleanField.js b/src/components/fields/BooleanField.js
index b0dfaa050..10c8281fc 100644
--- a/src/components/fields/BooleanField.js
+++ b/src/components/fields/BooleanField.js
@@ -1,6 +1,6 @@
 import React, { PropTypes } from "react";
 
-import { defaultFieldValue, getAlternativeWidget } from "../../utils";
+import { defaultFieldValue, getAlternativeWidget, optionsList } from "../../utils";
 import CheckboxWidget from "./../widgets/CheckboxWidget";
 
 function BooleanField({schema, name, uiSchema, formData, required, onChange}) {
@@ -17,7 +17,7 @@ function BooleanField({schema, name, uiSchema, formData, required, onChange}) {
   };
   if (widget) {
     const Widget = getAlternativeWidget(schema.type, widget);
-    return <Widget options={[true, false]} {... commonProps} />;
+    return <Widget options={optionsList({enum: [true, false]})} {... commonProps} />;
   }
   return <CheckboxWidget {...commonProps} />;
 }
diff --git a/src/components/fields/StringField.js b/src/components/fields/StringField.js
index 9ce2ef2b0..d496a34a9 100644
--- a/src/components/fields/StringField.js
+++ b/src/components/fields/StringField.js
@@ -1,6 +1,6 @@
 import React, { PropTypes } from "react";
 
-import { defaultFieldValue, getAlternativeWidget } from "../../utils";
+import { defaultFieldValue, getAlternativeWidget, optionsList } from "../../utils";
 import TextWidget from "./../widgets/TextWidget";
 import SelectWidget from "./../widgets/SelectWidget";
 
@@ -20,9 +20,9 @@ function StringField({schema, name, uiSchema, formData, required, onChange}) {
   if (Array.isArray(schema.enum)) {
     if (widget) {
       const Widget = getAlternativeWidget(schema.type, widget);
-      return <Widget options={schema.enum} {...commonProps} />;
+      return <Widget options={optionsList(schema)} {...commonProps} />;
     }
-    return <SelectWidget options={schema.enum} {...commonProps} />;
+    return <SelectWidget options={optionsList(schema)} {...commonProps} />;
   }
   if (widget) {
     const Widget = getAlternativeWidget(schema.type, widget);
diff --git a/src/components/widgets/RadioWidget.js b/src/components/widgets/RadioWidget.js
index dd1f9788d..b3f396952 100644
--- a/src/components/widgets/RadioWidget.js
+++ b/src/components/widgets/RadioWidget.js
@@ -15,17 +15,17 @@ function RadioWidget({
   return (
     <div className="field-radio-group">{
       options.map((option, i) => {
-        const checked = value !== undefined ? option === value :
-                                              option === defaultValue;
+        const checked = value !== undefined ? option.value === value :
+                                              option.value === defaultValue;
         return (
           <label key={i}>
             <input type="radio"
               name={name}
-              value={option}
+              value={option.value}
               checked={checked}
               placeholder={placeholder}
-              onChange={_ => onChange(option)} />
-            {String(option)}
+              onChange={_ => onChange(option.value)} />
+            {option.label}
           </label>
         );
       })
diff --git a/src/components/widgets/SelectWidget.js b/src/components/widgets/SelectWidget.js
index 717ec4904..1560e268a 100644
--- a/src/components/widgets/SelectWidget.js
+++ b/src/components/widgets/SelectWidget.js
@@ -43,8 +43,8 @@ function SelectWidget({
 
         onChange(processValue(schema.type, newValue));
       }}>{
-      options.map((option, i) => {
-        return <option key={i} value={option}>{String(option)}</option>;
+      options.map(({value, label}, i) => {
+        return <option key={i} value={value}>{label}</option>;
       })
     }</select>
   );
diff --git a/src/utils.js b/src/utils.js
index 436b7538a..6ad084ce3 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -148,3 +148,10 @@ export function orderProperties(properties, order) {
 export function isMultiSelect(schema) {
   return Array.isArray(schema.items.enum) && schema.uniqueItems;
 }
+
+export function optionsList(schema) {
+  return schema.enum.map((value, i) => {
+    const label = schema.enumNames && schema.enumNames[i] || String(value);
+    return {label, value};
+  });
+}


Test patch:
diff --git a/test/index_test.js b/test/index_test.js
index 9c6d68608..fd31aae7a 100644
--- a/test/index_test.js
+++ b/test/index_test.js
@@ -269,6 +269,23 @@ describe("Form", () => {
 
         expect(comp.state.formData).eql("bar");
       });
+
+
+      describe("enumNames", () => {
+        it("should use enumNames as labels for option tags", () => {
+          const {node} = createComponent({schema: {
+            type: "string",
+            enum: ["1", "2"],
+            enumNames: ["foo", "bar"]
+          }});
+
+          const options = node.querySelectorAll(".field select option");
+          const labels = [].map.call(options, (n) => n.textContent);
+          expect(labels).eql(["foo", "bar"]);
+          const values = [].map.call(options, (n) => n.getAttribute("value"));
+          expect(values).eql(["1", "2"]);
+        });
+      });
     });
   });