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

Issue:
Errors when parsing schema with a 'format' attribute
### Prerequisites

- [ ] I have read the [documentation](https://github.com/mozilla-services/react-jsonschema-form/blob/master/README.md#readme);
- [ ] In the case of a bug report, I understand that providing a [SSCCE](http://sscce.org/) example is tremendously useful to the maintainers.

### Description
I'm facing a problem generating a form from a JSON schema.
The API that I use to generate a JSON schema from C# types adds a **format** attribute to the properties.

Example:
```
{
  "title": "SomeClass",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "Number": {
      "type": "number",
      "format": "double"
    }
  }
}
```
Apparently `"format": "double"` doesn't allow to render a form and I'm curious if there is any way I can avoid it or let react-jsonschema ignore the attribute when parsing?

#### Expected behavior

A successfully generated react form and no errors in the output

#### Actual behavior

The following error in the output in case properties in the schema have a **format** attribute.
`
Uncaught Error: No widget "double" for type "number"
`


Pull request description:
Fix/warn unknown formats
### Reasons for making this change

Fixes #961. Allows for unknown formats to still work as per the JSON schema spec. Does the following:
- console warn on unknown format errors instead of showing an error
- default to default widget if the string format is not found (otherwise, providing an unknown format will look for a widget for that format, then fail)

If this is related to existing tickets, include links to them as well.

### Checklist

* [ ] **I'm updating documentation**
  - [ ] I've [checked the rendering](https://react-jsonschema-form.readthedocs.io/en/latest/#contributing) of the Markdown text I've added
* [x] **I'm adding or updating code**
  - [ ] I've added and/or updated tests
  - [ ] I've updated [docs](https://react-jsonschema-form.readthedocs.io/) 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/src/components/fields/StringField.js b/src/components/fields/StringField.js
index 31dc31580..6f583ba9b 100644
--- a/src/components/fields/StringField.js
+++ b/src/components/fields/StringField.js
@@ -7,6 +7,7 @@ import {
   isSelect,
   optionsList,
   getDefaultRegistry,
+  hasWidget,
 } from "../../utils";
 
 function StringField(props) {
@@ -29,7 +30,10 @@ function StringField(props) {
   const { title, format } = schema;
   const { widgets, formContext } = registry;
   const enumOptions = isSelect(schema) && optionsList(schema);
-  const defaultWidget = format || (enumOptions ? "select" : "text");
+  let defaultWidget = enumOptions ? "select" : "text";
+  if (format && hasWidget(schema, format, widgets)) {
+    defaultWidget = format;
+  }
   const { widget = defaultWidget, placeholder = "", ...options } = getUiOptions(
     uiSchema
   );
diff --git a/src/utils.js b/src/utils.js
index a4b2c8af1..8088bc5fa 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -123,6 +123,22 @@ export function getWidget(schema, widget, registeredWidgets = {}) {
   throw new Error(`No widget "${widget}" for type "${type}"`);
 }
 
+export function hasWidget(schema, widget, registeredWidgets = {}) {
+  try {
+    getWidget(schema, widget, registeredWidgets);
+    return true;
+  } catch (e) {
+    if (
+      e.message &&
+      (e.message.startsWith("No widget") ||
+        e.message.startsWith("Unsupported widget"))
+    ) {
+      return false;
+    }
+    throw e;
+  }
+}
+
 function computeDefaults(schema, parentDefaults, definitions = {}) {
   // Compute the defaults recursively: give highest priority to deepest nodes.
   let defaults = parentDefaults;
diff --git a/src/validate.js b/src/validate.js
index f20afc79e..526bceef9 100644
--- a/src/validate.js
+++ b/src/validate.js
@@ -14,6 +14,7 @@ function createAjvInstance() {
     allErrors: true,
     multipleOfPrecision: 8,
     schemaId: "auto",
+    unknownFormats: "ignore",
   });
 
   // add custom formats
@@ -215,13 +216,7 @@ export default function validateFormData(
     typeof validationError.message === "string" &&
     validationError.message.includes("no schema with key or ref ");
 
-  const unknownFormat =
-    validationError &&
-    validationError.message &&
-    typeof validationError.message === "string" &&
-    validationError.message.includes("unknown format");
-
-  if (noProperMetaSchema || unknownFormat) {
+  if (noProperMetaSchema) {
     errors = [
       ...errors,
       {
@@ -235,7 +230,7 @@ export default function validateFormData(
 
   let errorSchema = toErrorSchema(errors);
 
-  if (noProperMetaSchema || unknownFormat) {
+  if (noProperMetaSchema) {
     errorSchema = {
       ...errorSchema,
       ...{


Test patch:
diff --git a/test/Form_test.js b/test/Form_test.js
index 388ed1e66..45cc89e7e 100644
--- a/test/Form_test.js
+++ b/test/Form_test.js
@@ -1984,13 +1984,13 @@ describe("Form", () => {
       const formProps = {
         liveValidate: true,
         formData: {
-          areaCode: 123,
+          areaCode: "123455",
         },
         schema: {
           type: "object",
           properties: {
             areaCode: {
-              type: "number",
+              type: "string",
               format: "area-code",
             },
           },
@@ -2007,22 +2007,20 @@ describe("Form", () => {
 
       const { comp } = createFormComponent(formProps);
 
-      expect(comp.state.errorSchema).eql({
-        $schema: {
-          __errors: [
-            'unknown format "area-code" is used in schema at path "#/properties/areaCode"',
-          ],
-        },
-      });
+      expect(comp.state.errorSchema).eql({});
 
       setProps(comp, {
         ...formProps,
         customFormats: {
-          "area-code": /\d{3}/,
+          "area-code": /^\d{3}$/,
         },
       });
 
-      expect(comp.state.errorSchema).eql({});
+      expect(comp.state.errorSchema).eql({
+        areaCode: {
+          __errors: ['should match format "area-code"'],
+        },
+      });
     });
   });
 
diff --git a/test/validate_test.js b/test/validate_test.js
index 3ea9d40fe..696ed0631 100644
--- a/test/validate_test.js
+++ b/test/validate_test.js
@@ -174,15 +174,9 @@ describe("Validation", () => {
         },
       };
 
-      it("should return a validation error if unknown string format is used", () => {
+      it("should not return a validation error if unknown string format is used", () => {
         const result = validateFormData({ phone: "800.555.2368" }, schema);
-        const errMessage =
-          'unknown format "phone-us" is used in schema at path "#/properties/phone"';
-
-        expect(result.errors[0].stack).include(errMessage);
-        expect(result.errorSchema).to.eql({
-          $schema: { __errors: [errMessage] },
-        });
+        expect(result.errors.length).eql(0);
       });
 
       it("should return a validation error about formData", () => {