Skip to content

Commit 2a6b830

Browse files
LucianBuzzoepicfaace
authored andcommittedMar 6, 2019
Limit cases where "required" attribute is used on checkboxes (#1194)
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>
1 parent 1651066 commit 2a6b830

File tree

2 files changed

+145
-1
lines changed

2 files changed

+145
-1
lines changed
 

‎src/components/widgets/CheckboxWidget.js

+35-1
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,40 @@ import React from "react";
22
import PropTypes from "prop-types";
33
import DescriptionField from "../fields/DescriptionField.js";
44

5+
// Check to see if a schema specifies that a value must be true
6+
function schemaRequiresTrueValue(schema) {
7+
// Check if const is a truthy value
8+
if (schema.const) {
9+
return true;
10+
}
11+
12+
// Check if an enum has a single value of true
13+
if (schema.enum && schema.enum.length === 1 && schema.enum[0] === true) {
14+
return true;
15+
}
16+
17+
// If anyOf has a single value, evaluate the subschema
18+
if (schema.anyOf && schema.anyOf.length === 1) {
19+
return schemaRequiresTrueValue(schema.anyOf[0]);
20+
}
21+
22+
// If oneOf has a single value, evaluate the subschema
23+
if (schema.oneOf && schema.oneOf.length === 1) {
24+
return schemaRequiresTrueValue(schema.oneOf[0]);
25+
}
26+
27+
// Evaluate each subschema in allOf, to see if one of them requires a true
28+
// value
29+
if (schema.allOf) {
30+
return schema.allOf.some(schemaRequiresTrueValue);
31+
}
32+
}
33+
534
function CheckboxWidget(props) {
635
const {
736
schema,
837
id,
938
value,
10-
required,
1139
disabled,
1240
readonly,
1341
label,
@@ -16,6 +44,12 @@ function CheckboxWidget(props) {
1644
onFocus,
1745
onChange,
1846
} = props;
47+
48+
// Because an unchecked checkbox will cause html5 validation to fail, only add
49+
// the "required" attribute if the field value must be "true", due to the
50+
// "const" or "enum" keywords
51+
const required = schemaRequiresTrueValue(schema);
52+
1953
return (
2054
<div className={`checkbox ${disabled || readonly ? "disabled" : ""}`}>
2155
{schema.description && (

‎test/BooleanField_test.js

+110
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,116 @@ describe("BooleanField", () => {
5151
expect(node.querySelector(".field label span").textContent).eql("foo");
5252
});
5353

54+
describe("HTML5 required attribute", () => {
55+
it("should not render a required attribute for simple required fields", () => {
56+
const { node } = createFormComponent({
57+
schema: {
58+
type: "object",
59+
properties: {
60+
foo: {
61+
type: "boolean",
62+
},
63+
},
64+
required: ["foo"],
65+
},
66+
});
67+
68+
expect(node.querySelector("input[type=checkbox]").required).eql(false);
69+
});
70+
71+
it("should add a required attribute if the schema uses const with a true value", () => {
72+
const { node } = createFormComponent({
73+
schema: {
74+
type: "object",
75+
properties: {
76+
foo: {
77+
type: "boolean",
78+
const: true,
79+
},
80+
},
81+
},
82+
});
83+
84+
expect(node.querySelector("input[type=checkbox]").required).eql(true);
85+
});
86+
87+
it("should add a required attribute if the schema uses an enum with a single value of true", () => {
88+
const { node } = createFormComponent({
89+
schema: {
90+
type: "object",
91+
properties: {
92+
foo: {
93+
type: "boolean",
94+
enum: [true],
95+
},
96+
},
97+
},
98+
});
99+
100+
expect(node.querySelector("input[type=checkbox]").required).eql(true);
101+
});
102+
103+
it("should add a required attribute if the schema uses an anyOf with a single value of true", () => {
104+
const { node } = createFormComponent({
105+
schema: {
106+
type: "object",
107+
properties: {
108+
foo: {
109+
type: "boolean",
110+
anyOf: [
111+
{
112+
const: true,
113+
},
114+
],
115+
},
116+
},
117+
},
118+
});
119+
120+
expect(node.querySelector("input[type=checkbox]").required).eql(true);
121+
});
122+
123+
it("should add a required attribute if the schema uses a oneOf with a single value of true", () => {
124+
const { node } = createFormComponent({
125+
schema: {
126+
type: "object",
127+
properties: {
128+
foo: {
129+
type: "boolean",
130+
oneOf: [
131+
{
132+
const: true,
133+
},
134+
],
135+
},
136+
},
137+
},
138+
});
139+
140+
expect(node.querySelector("input[type=checkbox]").required).eql(true);
141+
});
142+
143+
it("should add a required attribute if the schema uses an allOf with a value of true", () => {
144+
const { node } = createFormComponent({
145+
schema: {
146+
type: "object",
147+
properties: {
148+
foo: {
149+
type: "boolean",
150+
allOf: [
151+
{
152+
const: true,
153+
},
154+
],
155+
},
156+
},
157+
},
158+
});
159+
160+
expect(node.querySelector("input[type=checkbox]").required).eql(true);
161+
});
162+
});
163+
54164
it("should render a single label", () => {
55165
const { node } = createFormComponent({
56166
schema: {

0 commit comments

Comments
 (0)
Please sign in to comment.