diff --git a/src/components/widgets/CheckboxWidget.js b/src/components/widgets/CheckboxWidget.js index f583f5e7a4..632ab4b426 100644 --- a/src/components/widgets/CheckboxWidget.js +++ b/src/components/widgets/CheckboxWidget.js @@ -2,12 +2,40 @@ import React from "react"; import PropTypes from "prop-types"; import DescriptionField from "../fields/DescriptionField.js"; +// Check to see if a schema specifies that a value must be true +function schemaRequiresTrueValue(schema) { + // Check if const is a truthy value + if (schema.const) { + return true; + } + + // Check if an enum has a single value of true + if (schema.enum && schema.enum.length === 1 && schema.enum[0] === true) { + return true; + } + + // If anyOf has a single value, evaluate the subschema + if (schema.anyOf && schema.anyOf.length === 1) { + return schemaRequiresTrueValue(schema.anyOf[0]); + } + + // If oneOf has a single value, evaluate the subschema + if (schema.oneOf && schema.oneOf.length === 1) { + return schemaRequiresTrueValue(schema.oneOf[0]); + } + + // Evaluate each subschema in allOf, to see if one of them requires a true + // value + if (schema.allOf) { + return schema.allOf.some(schemaRequiresTrueValue); + } +} + function CheckboxWidget(props) { const { schema, id, value, - required, disabled, readonly, label, @@ -16,6 +44,12 @@ function CheckboxWidget(props) { onFocus, onChange, } = props; + + // Because an unchecked checkbox will cause html5 validation to fail, only add + // the "required" attribute if the field value must be "true", due to the + // "const" or "enum" keywords + const required = schemaRequiresTrueValue(schema); + return (
{schema.description && ( diff --git a/test/BooleanField_test.js b/test/BooleanField_test.js index ea9d43b7a6..640dc46f5f 100644 --- a/test/BooleanField_test.js +++ b/test/BooleanField_test.js @@ -51,6 +51,116 @@ describe("BooleanField", () => { expect(node.querySelector(".field label span").textContent).eql("foo"); }); + describe("HTML5 required attribute", () => { + it("should not render a required attribute for simple required fields", () => { + const { node } = createFormComponent({ + schema: { + type: "object", + properties: { + foo: { + type: "boolean", + }, + }, + required: ["foo"], + }, + }); + + expect(node.querySelector("input[type=checkbox]").required).eql(false); + }); + + it("should add a required attribute if the schema uses const with a true value", () => { + const { node } = createFormComponent({ + schema: { + type: "object", + properties: { + foo: { + type: "boolean", + const: true, + }, + }, + }, + }); + + expect(node.querySelector("input[type=checkbox]").required).eql(true); + }); + + it("should add a required attribute if the schema uses an enum with a single value of true", () => { + const { node } = createFormComponent({ + schema: { + type: "object", + properties: { + foo: { + type: "boolean", + enum: [true], + }, + }, + }, + }); + + expect(node.querySelector("input[type=checkbox]").required).eql(true); + }); + + it("should add a required attribute if the schema uses an anyOf with a single value of true", () => { + const { node } = createFormComponent({ + schema: { + type: "object", + properties: { + foo: { + type: "boolean", + anyOf: [ + { + const: true, + }, + ], + }, + }, + }, + }); + + expect(node.querySelector("input[type=checkbox]").required).eql(true); + }); + + it("should add a required attribute if the schema uses a oneOf with a single value of true", () => { + const { node } = createFormComponent({ + schema: { + type: "object", + properties: { + foo: { + type: "boolean", + oneOf: [ + { + const: true, + }, + ], + }, + }, + }, + }); + + expect(node.querySelector("input[type=checkbox]").required).eql(true); + }); + + it("should add a required attribute if the schema uses an allOf with a value of true", () => { + const { node } = createFormComponent({ + schema: { + type: "object", + properties: { + foo: { + type: "boolean", + allOf: [ + { + const: true, + }, + ], + }, + }, + }, + }); + + expect(node.querySelector("input[type=checkbox]").required).eql(true); + }); + }); + it("should render a single label", () => { const { node } = createFormComponent({ schema: {