Skip to content

Commit

Permalink
Limit cases where "required" attribute is used on checkboxes (#1194)
Browse files Browse the repository at this point in the history
Fixes #338

If the "required" attribute is applied to a checkbox, then HTML5
validation will only pass if the checkbox is ticked. This is at odds
with the JSON schema definition of "required" that simply requires that
the key exists.

This PR will make it so that the "required" HTML5 attribute will only be
applied if the schema uses `{ const: true }` or `{ enum: [ true ] }`.

Signed-off-by: Lucian <lucian.buzzo@gmail.com>
  • Loading branch information
LucianBuzzo authored and epicfaace committed Mar 6, 2019
1 parent 1651066 commit 2a6b830
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 1 deletion.
36 changes: 35 additions & 1 deletion src/components/widgets/CheckboxWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 (
<div className={`checkbox ${disabled || readonly ? "disabled" : ""}`}>
{schema.description && (
Expand Down
110 changes: 110 additions & 0 deletions test/BooleanField_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down

0 comments on commit 2a6b830

Please sign in to comment.