mirror of
https://github.com/zhigang1992/react-jsonschema-form.git
synced 2026-04-28 20:25:31 +08:00
Use Lerna to maintain core and themes as a monorepo. This should streamline creation of new themes and maintenance of existing theme(s).
1674 lines
51 KiB
JavaScript
1674 lines
51 KiB
JavaScript
import React from "react";
|
|
|
|
import { expect } from "chai";
|
|
import { Simulate } from "react-dom/test-utils";
|
|
|
|
import { createFormComponent, createSandbox } from "./test_utils";
|
|
|
|
const ArrayKeyDataAttr = "data-rjsf-itemkey";
|
|
const ExposedArrayKeyTemplate = function(props) {
|
|
return (
|
|
<div className="array">
|
|
{props.items &&
|
|
props.items.map(element => (
|
|
<div
|
|
key={element.key}
|
|
className="array-item"
|
|
data-rjsf-itemkey={element.key}>
|
|
<div>{element.children}</div>
|
|
{(element.hasMoveUp || element.hasMoveDown) && (
|
|
<button
|
|
className="array-item-move-down"
|
|
onClick={element.onReorderClick(
|
|
element.index,
|
|
element.index + 1
|
|
)}>
|
|
Down
|
|
</button>
|
|
)}
|
|
{(element.hasMoveUp || element.hasMoveDown) && (
|
|
<button
|
|
className="array-item-move-up"
|
|
onClick={element.onReorderClick(
|
|
element.index,
|
|
element.index - 1
|
|
)}>
|
|
Up
|
|
</button>
|
|
)}
|
|
{element.hasRemove && (
|
|
<button
|
|
className="array-item-remove"
|
|
onClick={element.onDropIndexClick(element.index)}>
|
|
Remove
|
|
</button>
|
|
)}
|
|
<button onClick={element.onDropIndexClick(element.index)}>
|
|
Delete
|
|
</button>
|
|
<hr />
|
|
</div>
|
|
))}
|
|
|
|
{props.canAdd && (
|
|
<div className="array-item-add">
|
|
<button onClick={props.onAddClick} type="button">
|
|
Add New
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
describe("ArrayField", () => {
|
|
let sandbox;
|
|
const CustomComponent = props => {
|
|
return <div id="custom">{props.rawErrors}</div>;
|
|
};
|
|
|
|
beforeEach(() => {
|
|
sandbox = createSandbox();
|
|
});
|
|
|
|
afterEach(() => {
|
|
sandbox.restore();
|
|
});
|
|
|
|
describe("Unsupported array schema", () => {
|
|
it("should warn on missing items descriptor", () => {
|
|
const { node } = createFormComponent({ schema: { type: "array" } });
|
|
|
|
expect(
|
|
node.querySelector(".field-array > .unsupported-field").textContent
|
|
).to.contain("Missing items definition");
|
|
});
|
|
});
|
|
|
|
describe("List of inputs", () => {
|
|
const schema = {
|
|
type: "array",
|
|
title: "my list",
|
|
description: "my description",
|
|
items: { type: "string" },
|
|
};
|
|
|
|
it("should render a fieldset", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
const fieldset = node.querySelectorAll("fieldset");
|
|
expect(fieldset).to.have.length.of(1);
|
|
expect(fieldset[0].id).eql("root");
|
|
});
|
|
|
|
it("should render a fieldset legend", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
const legend = node.querySelector("fieldset > legend");
|
|
|
|
expect(legend.textContent).eql("my list");
|
|
expect(legend.id).eql("root__title");
|
|
});
|
|
|
|
it("should render a description", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
const description = node.querySelector("fieldset > .field-description");
|
|
|
|
expect(description.textContent).eql("my description");
|
|
expect(description.id).eql("root__description");
|
|
});
|
|
|
|
it("should render a hidden list", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
uiSchema: {
|
|
"ui:widget": "hidden",
|
|
},
|
|
});
|
|
expect(node.querySelector("div.hidden > fieldset")).to.exist;
|
|
});
|
|
|
|
it("should render a customized title", () => {
|
|
const CustomTitleField = ({ title }) => <div id="custom">{title}</div>;
|
|
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
fields: { TitleField: CustomTitleField },
|
|
});
|
|
expect(node.querySelector("fieldset > #custom").textContent).to.eql(
|
|
"my list"
|
|
);
|
|
});
|
|
|
|
it("should render a customized description", () => {
|
|
const CustomDescriptionField = ({ description }) => (
|
|
<div id="custom">{description}</div>
|
|
);
|
|
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
fields: {
|
|
DescriptionField: CustomDescriptionField,
|
|
},
|
|
});
|
|
expect(node.querySelector("fieldset > #custom").textContent).to.eql(
|
|
"my description"
|
|
);
|
|
});
|
|
|
|
it("should render a customized file widget", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
uiSchema: {
|
|
"ui:widget": "files",
|
|
},
|
|
widgets: { FileWidget: CustomComponent },
|
|
});
|
|
expect(node.querySelector("#custom")).to.exist;
|
|
});
|
|
|
|
it("should pass rawErrors down to custom array field templates", () => {
|
|
const schema = {
|
|
type: "array",
|
|
title: "my list",
|
|
description: "my description",
|
|
items: { type: "string" },
|
|
minItems: 2,
|
|
};
|
|
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
ArrayFieldTemplate: CustomComponent,
|
|
formData: [1],
|
|
liveValidate: true,
|
|
});
|
|
|
|
const matches = node.querySelectorAll("#custom");
|
|
expect(matches).to.have.length.of(1);
|
|
expect(matches[0].textContent).to.eql(
|
|
"should NOT have fewer than 2 items"
|
|
);
|
|
});
|
|
|
|
it("should contain no field in the list by default", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelectorAll(".field-string")).to.have.length.of(0);
|
|
});
|
|
|
|
it("should have an add button", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelector(".array-item-add button")).not.eql(null);
|
|
});
|
|
|
|
it("should not have an add button if addable is false", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
uiSchema: { "ui:options": { addable: false } },
|
|
});
|
|
|
|
expect(node.querySelector(".array-item-add button")).to.be.null;
|
|
});
|
|
|
|
it("should add a new field when clicking the add button", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
Simulate.click(node.querySelector(".array-item-add button"));
|
|
|
|
expect(node.querySelectorAll(".field-string")).to.have.length.of(1);
|
|
});
|
|
|
|
it("should assign new keys/ids when clicking the add button", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
ArrayFieldTemplate: ExposedArrayKeyTemplate,
|
|
});
|
|
|
|
Simulate.click(node.querySelector(".array-item-add button"));
|
|
|
|
expect(node.querySelector(".array-item").hasAttribute(ArrayKeyDataAttr))
|
|
.to.be.true;
|
|
});
|
|
|
|
it("should not provide an add button if length equals maxItems", () => {
|
|
const { node } = createFormComponent({
|
|
schema: { maxItems: 2, ...schema },
|
|
formData: ["foo", "bar"],
|
|
});
|
|
|
|
expect(node.querySelector(".array-item-add button")).to.be.null;
|
|
});
|
|
|
|
it("should provide an add button if length is lesser than maxItems", () => {
|
|
const { node } = createFormComponent({
|
|
schema: { maxItems: 2, ...schema },
|
|
formData: ["foo"],
|
|
});
|
|
|
|
expect(node.querySelector(".array-item-add button")).not.eql(null);
|
|
});
|
|
|
|
it("should retain existing row keys/ids when adding new row", () => {
|
|
const { node } = createFormComponent({
|
|
schema: { maxItems: 2, ...schema },
|
|
formData: ["foo"],
|
|
ArrayFieldTemplate: ExposedArrayKeyTemplate,
|
|
});
|
|
|
|
const startRows = node.querySelectorAll(".array-item");
|
|
const startRow1_key = startRows[0].getAttribute(ArrayKeyDataAttr);
|
|
const startRow2_key = startRows[1]
|
|
? startRows[1].getAttribute(ArrayKeyDataAttr)
|
|
: undefined;
|
|
|
|
Simulate.click(node.querySelector(".array-item-add button"));
|
|
|
|
const endRows = node.querySelectorAll(".array-item");
|
|
const endRow1_key = endRows[0].getAttribute(ArrayKeyDataAttr);
|
|
const endRow2_key = endRows[1].getAttribute(ArrayKeyDataAttr);
|
|
|
|
expect(startRow1_key).to.equal(endRow1_key);
|
|
expect(startRow2_key).to.not.equal(endRow2_key);
|
|
|
|
expect(startRow2_key).to.be.undefined;
|
|
expect(endRows[0].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[1].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
});
|
|
|
|
it("should allow inserting anywhere in list", () => {
|
|
function addItemAboveOrBelow(item) {
|
|
const beforeIndex = item.index;
|
|
const addBeforeButton = (
|
|
<button
|
|
key={`array-item-add-before-${item.key}`}
|
|
className={
|
|
"array-item-move-before array-item-move-before-to-" + beforeIndex
|
|
}
|
|
onClick={item.onAddIndexClick(beforeIndex)}>
|
|
{"Add Item Above"}
|
|
</button>
|
|
);
|
|
|
|
const afterIndex = item.index + 1;
|
|
const addAfterButton = (
|
|
<button
|
|
key={`array-item-add-after-${item.key}`}
|
|
className={
|
|
"array-item-move-after array-item-move-after-to-" + afterIndex
|
|
}
|
|
onClick={item.onAddIndexClick(afterIndex)}>
|
|
{"Add Item Below"}
|
|
</button>
|
|
);
|
|
|
|
return (
|
|
<div
|
|
key={item.key}
|
|
data-rjsf-itemkey={item.key}
|
|
className={`array-item item-${item.index}`}>
|
|
<div>{addBeforeButton}</div>
|
|
{item.children}
|
|
<div>{addAfterButton}</div>
|
|
<hr />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function addAboveOrBelowArrayFieldTemplate(props) {
|
|
return (
|
|
<div className="array">{props.items.map(addItemAboveOrBelow)}</div>
|
|
);
|
|
}
|
|
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar", "baz"],
|
|
ArrayFieldTemplate: addAboveOrBelowArrayFieldTemplate,
|
|
});
|
|
|
|
const addBeforeButtons = node.querySelectorAll(".array-item-move-before");
|
|
const addAfterButtons = node.querySelectorAll(".array-item-move-after");
|
|
|
|
const startRows = node.querySelectorAll(".array-item");
|
|
const startRow1_key = startRows[0].getAttribute(ArrayKeyDataAttr);
|
|
const startRow2_key = startRows[1].getAttribute(ArrayKeyDataAttr);
|
|
const startRow3_key = startRows[2].getAttribute(ArrayKeyDataAttr);
|
|
|
|
Simulate.click(addBeforeButtons[0]);
|
|
Simulate.click(addAfterButtons[0]);
|
|
|
|
const endRows = node.querySelectorAll(".array-item");
|
|
const endRow2_key = endRows[1].getAttribute(ArrayKeyDataAttr);
|
|
const endRow4_key = endRows[3].getAttribute(ArrayKeyDataAttr);
|
|
const endRow5_key = endRows[4].getAttribute(ArrayKeyDataAttr);
|
|
|
|
expect(startRow1_key).to.equal(endRow2_key);
|
|
expect(startRow2_key).to.equal(endRow4_key);
|
|
expect(startRow3_key).to.equal(endRow5_key);
|
|
|
|
expect(endRows[0].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[1].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[2].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[3].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[4].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
});
|
|
|
|
it("should not provide an add button if addable is expliclty false regardless maxItems value", () => {
|
|
const { node } = createFormComponent({
|
|
schema: { maxItems: 2, ...schema },
|
|
formData: ["foo"],
|
|
uiSchema: {
|
|
"ui:options": {
|
|
addable: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(node.querySelector(".array-item-add button")).to.be.null;
|
|
});
|
|
|
|
it("should ignore addable value if maxItems constraint is not satisfied", () => {
|
|
const { node } = createFormComponent({
|
|
schema: { maxItems: 2, ...schema },
|
|
formData: ["foo", "bar"],
|
|
uiSchema: {
|
|
"ui:options": {
|
|
addable: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(node.querySelector(".array-item-add button")).to.be.null;
|
|
});
|
|
|
|
it("should mark a non-null array item widget as required", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
Simulate.click(node.querySelector(".array-item-add button"));
|
|
|
|
expect(node.querySelector(".field-string input[type=text]").required).eql(
|
|
true
|
|
);
|
|
});
|
|
|
|
it("should fill an array field with data", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar"],
|
|
});
|
|
const inputs = node.querySelectorAll(".field-string input[type=text]");
|
|
|
|
expect(inputs).to.have.length.of(2);
|
|
expect(inputs[0].value).eql("foo");
|
|
expect(inputs[1].value).eql("bar");
|
|
});
|
|
|
|
it("shouldn't have reorder buttons when list length <= 1", () => {
|
|
const { node } = createFormComponent({ schema, formData: ["foo"] });
|
|
|
|
expect(node.querySelector(".array-item-move-up")).eql(null);
|
|
expect(node.querySelector(".array-item-move-down")).eql(null);
|
|
});
|
|
|
|
it("should have reorder buttons when list length >= 2", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar"],
|
|
});
|
|
|
|
expect(node.querySelector(".array-item-move-up")).not.eql(null);
|
|
expect(node.querySelector(".array-item-move-down")).not.eql(null);
|
|
});
|
|
|
|
it("should move down a field from the list", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar", "baz"],
|
|
});
|
|
const moveDownBtns = node.querySelectorAll(".array-item-move-down");
|
|
|
|
Simulate.click(moveDownBtns[0]);
|
|
|
|
const inputs = node.querySelectorAll(".field-string input[type=text]");
|
|
expect(inputs).to.have.length.of(3);
|
|
expect(inputs[0].value).eql("bar");
|
|
expect(inputs[1].value).eql("foo");
|
|
expect(inputs[2].value).eql("baz");
|
|
});
|
|
|
|
it("should move up a field from the list", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar", "baz"],
|
|
});
|
|
const moveUpBtns = node.querySelectorAll(".array-item-move-up");
|
|
|
|
Simulate.click(moveUpBtns[2]);
|
|
|
|
const inputs = node.querySelectorAll(".field-string input[type=text]");
|
|
expect(inputs).to.have.length.of(3);
|
|
expect(inputs[0].value).eql("foo");
|
|
expect(inputs[1].value).eql("baz");
|
|
expect(inputs[2].value).eql("bar");
|
|
});
|
|
|
|
it("should retain row keys/ids when moving down", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar", "baz"],
|
|
ArrayFieldTemplate: ExposedArrayKeyTemplate,
|
|
});
|
|
const moveDownBtns = node.querySelectorAll(".array-item-move-down");
|
|
const startRows = node.querySelectorAll(".array-item");
|
|
const startRow1_key = startRows[0].getAttribute(ArrayKeyDataAttr);
|
|
const startRow2_key = startRows[1].getAttribute(ArrayKeyDataAttr);
|
|
const startRow3_key = startRows[2].getAttribute(ArrayKeyDataAttr);
|
|
|
|
Simulate.click(moveDownBtns[0]);
|
|
|
|
const endRows = node.querySelectorAll(".array-item");
|
|
const endRow1_key = endRows[0].getAttribute(ArrayKeyDataAttr);
|
|
const endRow2_key = endRows[1].getAttribute(ArrayKeyDataAttr);
|
|
const endRow3_key = endRows[2].getAttribute(ArrayKeyDataAttr);
|
|
|
|
expect(startRow1_key).to.equal(endRow2_key);
|
|
expect(startRow2_key).to.equal(endRow1_key);
|
|
expect(startRow3_key).to.equal(endRow3_key);
|
|
|
|
expect(endRows[0].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[1].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[2].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
});
|
|
|
|
it("should retain row keys/ids when moving up", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar", "baz"],
|
|
ArrayFieldTemplate: ExposedArrayKeyTemplate,
|
|
});
|
|
const moveUpBtns = node.querySelectorAll(".array-item-move-up");
|
|
const startRows = node.querySelectorAll(".array-item");
|
|
const startRow1_key = startRows[0].getAttribute(ArrayKeyDataAttr);
|
|
const startRow2_key = startRows[1].getAttribute(ArrayKeyDataAttr);
|
|
const startRow3_key = startRows[2].getAttribute(ArrayKeyDataAttr);
|
|
|
|
Simulate.click(moveUpBtns[2]);
|
|
|
|
const endRows = node.querySelectorAll(".array-item");
|
|
const endRow1_key = endRows[0].getAttribute(ArrayKeyDataAttr);
|
|
const endRow2_key = endRows[1].getAttribute(ArrayKeyDataAttr);
|
|
const endRow3_key = endRows[2].getAttribute(ArrayKeyDataAttr);
|
|
|
|
expect(startRow1_key).to.equal(endRow1_key);
|
|
expect(startRow2_key).to.equal(endRow3_key);
|
|
expect(startRow3_key).to.equal(endRow2_key);
|
|
|
|
expect(endRows[0].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[1].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[2].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
});
|
|
|
|
it("should move from first to last in the list", () => {
|
|
function moveAnywhereArrayItemTemplate(props) {
|
|
const buttons = [];
|
|
for (let i = 0; i < 3; i++) {
|
|
buttons.push(
|
|
<button
|
|
key={i}
|
|
className={"array-item-move-to-" + i}
|
|
onClick={props.onReorderClick(props.index, i)}>
|
|
{"Move item to index " + i}
|
|
</button>
|
|
);
|
|
}
|
|
return (
|
|
<div
|
|
key={props.key}
|
|
data-rjsf-itemkey={props.key}
|
|
className={`array-item item-${props.index}`}>
|
|
{props.children}
|
|
{buttons}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function moveAnywhereArrayFieldTemplate(props) {
|
|
return (
|
|
<div className="array">
|
|
{props.items.map(moveAnywhereArrayItemTemplate)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar", "baz"],
|
|
ArrayFieldTemplate: moveAnywhereArrayFieldTemplate,
|
|
});
|
|
|
|
const startRows = node.querySelectorAll(".array-item");
|
|
const startRow1_key = startRows[0].getAttribute(ArrayKeyDataAttr);
|
|
const startRow2_key = startRows[1].getAttribute(ArrayKeyDataAttr);
|
|
const startRow3_key = startRows[2].getAttribute(ArrayKeyDataAttr);
|
|
|
|
const button = node.querySelector(".item-0 .array-item-move-to-2");
|
|
Simulate.click(button);
|
|
|
|
const inputs = node.querySelectorAll(".field-string input[type=text]");
|
|
expect(inputs[0].value).eql("bar");
|
|
expect(inputs[1].value).eql("baz");
|
|
expect(inputs[2].value).eql("foo");
|
|
|
|
const endRows = node.querySelectorAll(".array-item");
|
|
const endRow1_key = endRows[0].getAttribute(ArrayKeyDataAttr);
|
|
const endRow2_key = endRows[1].getAttribute(ArrayKeyDataAttr);
|
|
const endRow3_key = endRows[2].getAttribute(ArrayKeyDataAttr);
|
|
|
|
expect(startRow1_key).to.equal(endRow3_key);
|
|
expect(startRow2_key).to.equal(endRow1_key);
|
|
expect(startRow3_key).to.equal(endRow2_key);
|
|
|
|
expect(endRows[0].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[1].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[2].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
});
|
|
|
|
it("should disable move buttons on the ends of the list", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar"],
|
|
});
|
|
const moveUpBtns = node.querySelectorAll(".array-item-move-up");
|
|
const moveDownBtns = node.querySelectorAll(".array-item-move-down");
|
|
|
|
expect(moveUpBtns[0].disabled).eql(true);
|
|
expect(moveDownBtns[0].disabled).eql(false);
|
|
expect(moveUpBtns[1].disabled).eql(false);
|
|
expect(moveDownBtns[1].disabled).eql(true);
|
|
});
|
|
|
|
it("should not show move up/down buttons if orderable is false", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar"],
|
|
uiSchema: { "ui:options": { orderable: false } },
|
|
});
|
|
const moveUpBtns = node.querySelector(".array-item-move-up");
|
|
const moveDownBtns = node.querySelector(".array-item-move-down");
|
|
|
|
expect(moveUpBtns).to.be.null;
|
|
expect(moveDownBtns).to.be.null;
|
|
});
|
|
|
|
it("should remove a field from the list", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar"],
|
|
});
|
|
const dropBtns = node.querySelectorAll(".array-item-remove");
|
|
|
|
Simulate.click(dropBtns[0]);
|
|
|
|
const inputs = node.querySelectorAll(".field-string input[type=text]");
|
|
expect(inputs).to.have.length.of(1);
|
|
expect(inputs[0].value).eql("bar");
|
|
});
|
|
|
|
it("should delete item from list and correct indices", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar", "baz"],
|
|
});
|
|
const deleteBtns = node.querySelectorAll(".array-item-remove");
|
|
|
|
Simulate.click(deleteBtns[0]);
|
|
|
|
const inputs = node.querySelectorAll(".field-string input[type=text]");
|
|
|
|
Simulate.change(inputs[0], { target: { value: "fuzz" } });
|
|
expect(inputs).to.have.length.of(2);
|
|
expect(inputs[0].value).eql("fuzz");
|
|
expect(inputs[1].value).eql("baz");
|
|
});
|
|
|
|
it("should retain row keys/ids of remaining rows when a row is removed", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar"],
|
|
ArrayFieldTemplate: ExposedArrayKeyTemplate,
|
|
});
|
|
|
|
const startRows = node.querySelectorAll(".array-item");
|
|
const startRow2_key = startRows[1].getAttribute(ArrayKeyDataAttr);
|
|
|
|
const dropBtns = node.querySelectorAll(".array-item-remove");
|
|
Simulate.click(dropBtns[0]);
|
|
|
|
const endRows = node.querySelectorAll(".array-item");
|
|
const endRow1_key = endRows[0].getAttribute(ArrayKeyDataAttr);
|
|
|
|
expect(startRow2_key).to.equal(endRow1_key);
|
|
expect(endRows[0].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
});
|
|
|
|
it("should not show remove button if removable is false", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar"],
|
|
uiSchema: { "ui:options": { removable: false } },
|
|
});
|
|
const dropBtn = node.querySelector(".array-item-remove");
|
|
|
|
expect(dropBtn).to.be.null;
|
|
});
|
|
|
|
it("should force revalidation when a field is removed", () => {
|
|
// refs #195
|
|
const { node } = createFormComponent({
|
|
schema: {
|
|
...schema,
|
|
items: { ...schema.items, minLength: 4 },
|
|
},
|
|
formData: ["foo", "bar!"],
|
|
});
|
|
|
|
try {
|
|
Simulate.submit(node);
|
|
} catch (e) {
|
|
// Silencing error thrown as failure is expected here
|
|
}
|
|
|
|
expect(
|
|
node.querySelectorAll(".has-error .error-detail")
|
|
).to.have.length.of(1);
|
|
|
|
const dropBtns = node.querySelectorAll(".array-item-remove");
|
|
|
|
Simulate.click(dropBtns[0]);
|
|
|
|
expect(
|
|
node.querySelectorAll(".has-error .error-detail")
|
|
).to.have.length.of(0);
|
|
});
|
|
|
|
it("should handle cleared field values in the array", () => {
|
|
const schema = {
|
|
type: "array",
|
|
items: { type: "integer" },
|
|
};
|
|
const formData = [1, 2, 3];
|
|
const { comp, node } = createFormComponent({
|
|
liveValidate: true,
|
|
schema,
|
|
formData,
|
|
});
|
|
|
|
Simulate.change(node.querySelector("#root_1"), {
|
|
target: { value: "" },
|
|
});
|
|
|
|
expect(comp.state.formData).eql([1, null, 3]);
|
|
expect(comp.state.errors).to.have.length.of(1);
|
|
});
|
|
|
|
it("should render the input widgets with the expected ids", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar"],
|
|
});
|
|
|
|
const inputs = node.querySelectorAll("input[type=text]");
|
|
expect(inputs[0].id).eql("root_0");
|
|
expect(inputs[1].id).eql("root_1");
|
|
});
|
|
|
|
it("should render nested input widgets with the expected ids", () => {
|
|
const complexSchema = {
|
|
type: "object",
|
|
properties: {
|
|
foo: {
|
|
type: "array",
|
|
items: {
|
|
type: "object",
|
|
properties: {
|
|
bar: { type: "string" },
|
|
baz: { type: "string" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
const { node } = createFormComponent({
|
|
schema: complexSchema,
|
|
formData: {
|
|
foo: [{ bar: "bar1", baz: "baz1" }, { bar: "bar2", baz: "baz2" }],
|
|
},
|
|
});
|
|
|
|
const inputs = node.querySelectorAll("input[type=text]");
|
|
expect(inputs[0].id).eql("root_foo_0_bar");
|
|
expect(inputs[1].id).eql("root_foo_0_baz");
|
|
expect(inputs[2].id).eql("root_foo_1_bar");
|
|
expect(inputs[3].id).eql("root_foo_1_baz");
|
|
});
|
|
|
|
it("should render enough inputs with proper defaults to match minItems in schema when no formData is set", () => {
|
|
const complexSchema = {
|
|
type: "object",
|
|
definitions: {
|
|
Thing: {
|
|
type: "object",
|
|
properties: {
|
|
name: {
|
|
type: "string",
|
|
default: "Default name",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
properties: {
|
|
foo: {
|
|
type: "array",
|
|
minItems: 2,
|
|
items: {
|
|
$ref: "#/definitions/Thing",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
let form = createFormComponent({
|
|
schema: complexSchema,
|
|
formData: {},
|
|
});
|
|
let inputs = form.node.querySelectorAll("input[type=text]");
|
|
expect(inputs[0].value).eql("Default name");
|
|
expect(inputs[1].value).eql("Default name");
|
|
});
|
|
|
|
it("should render an input for each default value, even when this is greater than minItems", () => {
|
|
const schema = {
|
|
type: "object",
|
|
properties: {
|
|
turtles: {
|
|
type: "array",
|
|
minItems: 2,
|
|
default: ["Raphael", "Michaelangelo", "Donatello", "Leonardo"],
|
|
items: {
|
|
type: "string",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
const { node } = createFormComponent({ schema: schema });
|
|
const inputs = node.querySelectorAll("input[type=text]");
|
|
expect(inputs.length).to.eql(4);
|
|
expect(inputs[0].value).to.eql("Raphael");
|
|
expect(inputs[1].value).to.eql("Michaelangelo");
|
|
expect(inputs[2].value).to.eql("Donatello");
|
|
expect(inputs[3].value).to.eql("Leonardo");
|
|
});
|
|
|
|
it("should render enough input to match minItems, populating the first with default values, and the rest empty", () => {
|
|
const schema = {
|
|
type: "object",
|
|
properties: {
|
|
turtles: {
|
|
type: "array",
|
|
minItems: 4,
|
|
default: ["Raphael", "Michaelangelo"],
|
|
items: {
|
|
type: "string",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
const { node } = createFormComponent({ schema });
|
|
const inputs = node.querySelectorAll("input[type=text]");
|
|
expect(inputs.length).to.eql(4);
|
|
expect(inputs[0].value).to.eql("Raphael");
|
|
expect(inputs[1].value).to.eql("Michaelangelo");
|
|
expect(inputs[2].value).to.eql("");
|
|
expect(inputs[3].value).to.eql("");
|
|
});
|
|
|
|
it("should render enough input to match minItems, populating the first with default values, and the rest with the item default", () => {
|
|
const schema = {
|
|
type: "object",
|
|
properties: {
|
|
turtles: {
|
|
type: "array",
|
|
minItems: 4,
|
|
default: ["Raphael", "Michaelangelo"],
|
|
items: {
|
|
type: "string",
|
|
default: "Unknown",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
const { node } = createFormComponent({ schema });
|
|
const inputs = node.querySelectorAll("input[type=text]");
|
|
expect(inputs.length).to.eql(4);
|
|
expect(inputs[0].value).to.eql("Raphael");
|
|
expect(inputs[1].value).to.eql("Michaelangelo");
|
|
expect(inputs[2].value).to.eql("Unknown");
|
|
expect(inputs[3].value).to.eql("Unknown");
|
|
});
|
|
|
|
it("should not add minItems extra formData entries when schema item is a multiselect", () => {
|
|
const schema = {
|
|
type: "object",
|
|
properties: {
|
|
multipleChoicesList: {
|
|
type: "array",
|
|
minItems: 3,
|
|
uniqueItems: true,
|
|
items: {
|
|
type: "string",
|
|
enum: ["Aramis", "Athos", "Porthos", "d'Artagnan"],
|
|
},
|
|
},
|
|
},
|
|
};
|
|
const uiSchema = {
|
|
multipleChoicesList: {
|
|
"ui:widget": "checkboxes",
|
|
},
|
|
};
|
|
const form = createFormComponent({
|
|
schema: schema,
|
|
uiSchema: uiSchema,
|
|
formData: {},
|
|
liveValidate: true,
|
|
}).comp;
|
|
|
|
expect(form.state.formData).to.have.property("multipleChoicesList");
|
|
expect(form.state.formData.multipleChoicesList).to.be.empty;
|
|
expect(form.state.errors.length).to.equal(1);
|
|
expect(form.state.errors[0].name).to.equal("minItems");
|
|
expect(form.state.errors[0].params.limit).to.equal(3);
|
|
});
|
|
|
|
it("should honor given formData, even when it does not meet ths minItems-requirement", () => {
|
|
const complexSchema = {
|
|
type: "object",
|
|
definitions: {
|
|
Thing: {
|
|
type: "object",
|
|
properties: {
|
|
name: {
|
|
type: "string",
|
|
default: "Default name",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
properties: {
|
|
foo: {
|
|
type: "array",
|
|
minItems: 2,
|
|
items: {
|
|
$ref: "#/definitions/Thing",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
const form = createFormComponent({
|
|
schema: complexSchema,
|
|
formData: { foo: [] },
|
|
});
|
|
const inputs = form.node.querySelectorAll("input[type=text]");
|
|
expect(inputs.length).eql(0);
|
|
});
|
|
});
|
|
|
|
describe("Multiple choices list", () => {
|
|
const schema = {
|
|
type: "array",
|
|
title: "My field",
|
|
items: {
|
|
enum: ["foo", "bar", "fuzz"],
|
|
type: "string",
|
|
},
|
|
uniqueItems: true,
|
|
};
|
|
|
|
describe("Select multiple widget", () => {
|
|
it("should render a select widget", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelectorAll("select")).to.have.length.of(1);
|
|
});
|
|
|
|
it("should render a select widget with a label", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelector(".field label").textContent).eql("My field");
|
|
});
|
|
|
|
it("should render a select widget with multiple attribute", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelector(".field select").getAttribute("multiple")).not
|
|
.to.be.null;
|
|
});
|
|
|
|
it("should render options", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelectorAll("select option")).to.have.length.of(3);
|
|
});
|
|
|
|
it("should handle a change event", () => {
|
|
const { comp, node } = createFormComponent({ schema });
|
|
|
|
Simulate.change(node.querySelector(".field select"), {
|
|
target: {
|
|
options: [
|
|
{ selected: true, value: "foo" },
|
|
{ selected: true, value: "bar" },
|
|
{ selected: false, value: "fuzz" },
|
|
],
|
|
},
|
|
});
|
|
|
|
expect(comp.state.formData).eql(["foo", "bar"]);
|
|
});
|
|
|
|
it("should handle a blur event", () => {
|
|
const onBlur = sandbox.spy();
|
|
const { node } = createFormComponent({ schema, onBlur });
|
|
|
|
const select = node.querySelector(".field select");
|
|
Simulate.blur(select, {
|
|
target: {
|
|
options: [
|
|
{ selected: true, value: "foo" },
|
|
{ selected: true, value: "bar" },
|
|
{ selected: false, value: "fuzz" },
|
|
],
|
|
},
|
|
});
|
|
|
|
expect(onBlur.calledWith(select.id, ["foo", "bar"])).to.be.true;
|
|
});
|
|
|
|
it("should handle a focus event", () => {
|
|
const onFocus = sandbox.spy();
|
|
const { node } = createFormComponent({ schema, onFocus });
|
|
|
|
const select = node.querySelector(".field select");
|
|
Simulate.focus(select, {
|
|
target: {
|
|
options: [
|
|
{ selected: true, value: "foo" },
|
|
{ selected: true, value: "bar" },
|
|
{ selected: false, value: "fuzz" },
|
|
],
|
|
},
|
|
});
|
|
|
|
expect(onFocus.calledWith(select.id, ["foo", "bar"])).to.be.true;
|
|
});
|
|
|
|
it("should fill field with data", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", "bar"],
|
|
});
|
|
|
|
const options = node.querySelectorAll(".field select option");
|
|
expect(options).to.have.length.of(3);
|
|
expect(options[0].selected).eql(true); // foo
|
|
expect(options[1].selected).eql(true); // bar
|
|
expect(options[2].selected).eql(false); // fuzz
|
|
});
|
|
|
|
it("should render the select widget with the expected id", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelector("select").id).eql("root");
|
|
});
|
|
|
|
it("should pass rawErrors down to custom widgets", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
widgets: {
|
|
SelectWidget: CustomComponent,
|
|
},
|
|
formData: ["foo", "foo"],
|
|
liveValidate: true,
|
|
});
|
|
|
|
const matches = node.querySelectorAll("#custom");
|
|
expect(matches).to.have.length.of(1);
|
|
expect(matches[0].textContent).to.eql(
|
|
"should NOT have duplicate items (items ## 1 and 0 are identical)"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("CheckboxesWidget", () => {
|
|
const uiSchema = {
|
|
"ui:widget": "checkboxes",
|
|
};
|
|
|
|
it("should render the expected number of checkboxes", () => {
|
|
const { node } = createFormComponent({ schema, uiSchema });
|
|
|
|
expect(node.querySelectorAll("[type=checkbox]")).to.have.length.of(3);
|
|
});
|
|
|
|
it("should render the expected labels", () => {
|
|
const { node } = createFormComponent({ schema, uiSchema });
|
|
|
|
const labels = [].map.call(
|
|
node.querySelectorAll(".checkbox label"),
|
|
node => node.textContent
|
|
);
|
|
expect(labels).eql(["foo", "bar", "fuzz"]);
|
|
});
|
|
|
|
it("should handle a change event", () => {
|
|
const { comp, node } = createFormComponent({
|
|
schema,
|
|
uiSchema,
|
|
});
|
|
|
|
Simulate.change(node.querySelectorAll("[type=checkbox]")[0], {
|
|
target: { checked: true },
|
|
});
|
|
Simulate.change(node.querySelectorAll("[type=checkbox]")[2], {
|
|
target: { checked: true },
|
|
});
|
|
|
|
expect(comp.state.formData).eql(["foo", "fuzz"]);
|
|
});
|
|
|
|
it("should fill field with data", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
uiSchema,
|
|
formData: ["foo", "fuzz"],
|
|
});
|
|
|
|
const labels = [].map.call(
|
|
node.querySelectorAll("[type=checkbox]"),
|
|
node => node.checked
|
|
);
|
|
expect(labels).eql([true, false, true]);
|
|
});
|
|
|
|
it("should render the widget with the expected id", () => {
|
|
const { node } = createFormComponent({ schema, uiSchema });
|
|
|
|
expect(node.querySelector(".checkboxes").id).eql("root");
|
|
});
|
|
|
|
it("should support inline checkboxes", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
uiSchema: {
|
|
"ui:widget": "checkboxes",
|
|
"ui:options": {
|
|
inline: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(node.querySelectorAll(".checkbox-inline")).to.have.length.of(3);
|
|
});
|
|
|
|
it("should pass rawErrors down to custom widgets", () => {
|
|
const schema = {
|
|
type: "array",
|
|
title: "My field",
|
|
items: {
|
|
enum: ["foo", "bar", "fuzz"],
|
|
type: "string",
|
|
},
|
|
minItems: 3,
|
|
uniqueItems: true,
|
|
};
|
|
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
widgets: {
|
|
CheckboxesWidget: CustomComponent,
|
|
},
|
|
uiSchema,
|
|
formData: [],
|
|
liveValidate: true,
|
|
});
|
|
|
|
const matches = node.querySelectorAll("#custom");
|
|
expect(matches).to.have.length.of(1);
|
|
expect(matches[0].textContent).to.eql(
|
|
"should NOT have fewer than 3 items"
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Multiple files field", () => {
|
|
const schema = {
|
|
type: "array",
|
|
title: "My field",
|
|
items: {
|
|
type: "string",
|
|
format: "data-url",
|
|
},
|
|
};
|
|
|
|
it("should render an input[type=file] widget", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelectorAll("input[type=file]")).to.have.length.of(1);
|
|
});
|
|
|
|
it("should render a select widget with a label", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelector(".field label").textContent).eql("My field");
|
|
});
|
|
|
|
it("should render a file widget with multiple attribute", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelector(".field [type=file]").getAttribute("multiple"))
|
|
.not.to.be.null;
|
|
});
|
|
|
|
it("should handle a change event", () => {
|
|
sandbox.stub(window, "FileReader").returns({
|
|
set onload(fn) {
|
|
fn({ target: { result: "data:text/plain;base64,x=" } });
|
|
},
|
|
readAsDataUrl() {},
|
|
});
|
|
|
|
const { comp, node } = createFormComponent({ schema });
|
|
|
|
Simulate.change(node.querySelector(".field input[type=file]"), {
|
|
target: {
|
|
files: [
|
|
{ name: "file1.txt", size: 1, type: "type" },
|
|
{ name: "file2.txt", size: 2, type: "type" },
|
|
],
|
|
},
|
|
});
|
|
|
|
return new Promise(setImmediate).then(() =>
|
|
expect(comp.state.formData).eql([
|
|
"data:text/plain;name=file1.txt;base64,x=",
|
|
"data:text/plain;name=file2.txt;base64,x=",
|
|
])
|
|
);
|
|
});
|
|
|
|
it("should fill field with data", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: [
|
|
"data:text/plain;name=file1.txt;base64,dGVzdDE=",
|
|
"data:image/png;name=file2.png;base64,ZmFrZXBuZw==",
|
|
],
|
|
});
|
|
|
|
const li = node.querySelectorAll(".file-info li");
|
|
|
|
expect(li).to.have.length.of(2);
|
|
expect(li[0].textContent).eql("file1.txt (text/plain, 5 bytes)");
|
|
expect(li[1].textContent).eql("file2.png (image/png, 7 bytes)");
|
|
});
|
|
|
|
it("should render the file widget with the expected id", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelector("input[type=file]").id).eql("root");
|
|
});
|
|
|
|
it("should pass rawErrors down to custom widgets", () => {
|
|
const schema = {
|
|
type: "array",
|
|
title: "My field",
|
|
items: {
|
|
type: "string",
|
|
format: "data-url",
|
|
},
|
|
minItems: 5,
|
|
};
|
|
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
widgets: {
|
|
FileWidget: CustomComponent,
|
|
},
|
|
formData: [],
|
|
liveValidate: true,
|
|
});
|
|
|
|
const matches = node.querySelectorAll("#custom");
|
|
expect(matches).to.have.length.of(1);
|
|
expect(matches[0].textContent).to.eql(
|
|
"should NOT have fewer than 5 items"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Nested lists", () => {
|
|
const schema = {
|
|
type: "array",
|
|
title: "A list of arrays",
|
|
items: {
|
|
type: "array",
|
|
title: "A list of numbers",
|
|
items: {
|
|
type: "number",
|
|
},
|
|
},
|
|
};
|
|
|
|
it("should render two lists of inputs inside of a list", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: [[1, 2], [3, 4]],
|
|
});
|
|
expect(node.querySelectorAll("fieldset fieldset")).to.have.length.of(2);
|
|
});
|
|
|
|
it("should add an inner list when clicking the add button", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
expect(node.querySelectorAll("fieldset fieldset")).to.be.empty;
|
|
|
|
Simulate.click(node.querySelector(".array-item-add button"));
|
|
|
|
expect(node.querySelectorAll("fieldset fieldset")).to.have.length.of(1);
|
|
});
|
|
|
|
it("should pass rawErrors down to every level of custom widgets", () => {
|
|
const CustomItem = props => <div id="custom-item">{props.children}</div>;
|
|
const CustomTemplate = props => {
|
|
return (
|
|
<div id="custom">
|
|
{props.items &&
|
|
props.items.map((p, i) => <CustomItem key={i} {...p} />)}
|
|
<div id="custom-error">
|
|
{props.rawErrors && props.rawErrors.join(", ")}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const schema = {
|
|
type: "array",
|
|
title: "A list of arrays",
|
|
items: {
|
|
type: "array",
|
|
title: "A list of numbers",
|
|
items: {
|
|
type: "number",
|
|
},
|
|
minItems: 3,
|
|
},
|
|
minItems: 2,
|
|
};
|
|
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
ArrayFieldTemplate: CustomTemplate,
|
|
formData: [[]],
|
|
liveValidate: true,
|
|
});
|
|
|
|
const matches = node.querySelectorAll("#custom-error");
|
|
expect(matches).to.have.length.of(2);
|
|
expect(matches[0].textContent).to.eql(
|
|
"should NOT have fewer than 3 items"
|
|
);
|
|
expect(matches[1].textContent).to.eql(
|
|
"should NOT have fewer than 2 items"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Fixed items lists", () => {
|
|
const schema = {
|
|
type: "array",
|
|
title: "List of fixed items",
|
|
items: [
|
|
{
|
|
type: "string",
|
|
title: "Some text",
|
|
},
|
|
{
|
|
type: "number",
|
|
title: "A number",
|
|
},
|
|
],
|
|
};
|
|
|
|
const schemaAdditional = {
|
|
type: "array",
|
|
title: "List of fixed items",
|
|
items: [
|
|
{
|
|
type: "number",
|
|
title: "A number",
|
|
},
|
|
{
|
|
type: "number",
|
|
title: "Another number",
|
|
},
|
|
],
|
|
additionalItems: {
|
|
type: "string",
|
|
title: "Additional item",
|
|
},
|
|
};
|
|
|
|
it("should render a fieldset", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
|
|
expect(node.querySelectorAll("fieldset")).to.have.length.of(1);
|
|
});
|
|
|
|
it("should render a fieldset legend", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
const legend = node.querySelector("fieldset > legend");
|
|
expect(legend.textContent).eql("List of fixed items");
|
|
expect(legend.id).eql("root__title");
|
|
});
|
|
|
|
it("should render field widgets", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
const strInput = node.querySelector(
|
|
"fieldset .field-string input[type=text]"
|
|
);
|
|
const numInput = node.querySelector(
|
|
"fieldset .field-number input[type=number]"
|
|
);
|
|
expect(strInput.id).eql("root_0");
|
|
expect(numInput.id).eql("root_1");
|
|
});
|
|
|
|
it("should mark non-null item widgets as required", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
const strInput = node.querySelector(
|
|
"fieldset .field-string input[type=text]"
|
|
);
|
|
const numInput = node.querySelector(
|
|
"fieldset .field-number input[type=number]"
|
|
);
|
|
expect(strInput.required).eql(true);
|
|
expect(numInput.required).eql(true);
|
|
});
|
|
|
|
it("should fill fields with data", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
formData: ["foo", 42],
|
|
});
|
|
const strInput = node.querySelector(
|
|
"fieldset .field-string input[type=text]"
|
|
);
|
|
const numInput = node.querySelector(
|
|
"fieldset .field-number input[type=number]"
|
|
);
|
|
expect(strInput.value).eql("foo");
|
|
expect(numInput.value).eql("42");
|
|
});
|
|
|
|
it("should handle change events", () => {
|
|
const { comp, node } = createFormComponent({ schema });
|
|
const strInput = node.querySelector(
|
|
"fieldset .field-string input[type=text]"
|
|
);
|
|
const numInput = node.querySelector(
|
|
"fieldset .field-number input[type=number]"
|
|
);
|
|
|
|
Simulate.change(strInput, { target: { value: "bar" } });
|
|
Simulate.change(numInput, { target: { value: "101" } });
|
|
|
|
expect(comp.state.formData).eql(["bar", 101]);
|
|
});
|
|
|
|
it("should generate additional fields and fill data", () => {
|
|
const { node } = createFormComponent({
|
|
schema: schemaAdditional,
|
|
formData: [1, 2, "bar"],
|
|
});
|
|
const addInput = node.querySelector(
|
|
"fieldset .field-string input[type=text]"
|
|
);
|
|
expect(addInput.id).eql("root_2");
|
|
expect(addInput.value).eql("bar");
|
|
});
|
|
|
|
it("should apply uiSchema to additionalItems", () => {
|
|
const { node } = createFormComponent({
|
|
schema: schemaAdditional,
|
|
uiSchema: {
|
|
additionalItems: {
|
|
"ui:title": "Custom title",
|
|
},
|
|
},
|
|
formData: [1, 2, "bar"],
|
|
});
|
|
const label = node.querySelector(
|
|
"fieldset .field-string label.control-label"
|
|
);
|
|
expect(label.textContent).eql("Custom title*");
|
|
});
|
|
|
|
it("should have an add button if additionalItems is an object", () => {
|
|
const { node } = createFormComponent({ schema: schemaAdditional });
|
|
expect(node.querySelector(".array-item-add button")).not.to.be.null;
|
|
});
|
|
|
|
it("should not have an add button if additionalItems is not set", () => {
|
|
const { node } = createFormComponent({ schema });
|
|
expect(node.querySelector(".array-item-add button")).to.be.null;
|
|
});
|
|
|
|
it("should not have an add button if addable is false", () => {
|
|
const { node } = createFormComponent({
|
|
schema,
|
|
uiSchema: { "ui:options": { addable: false } },
|
|
});
|
|
expect(node.querySelector(".array-item-add button")).to.be.null;
|
|
});
|
|
|
|
it("[fixed-noadditional] should not provide an add button regardless maxItems", () => {
|
|
const { node } = createFormComponent({
|
|
schema: { maxItems: 3, ...schema },
|
|
});
|
|
|
|
expect(node.querySelector(".array-item-add button")).to.be.null;
|
|
});
|
|
|
|
it("[fixed] should not provide an add button if length equals maxItems", () => {
|
|
const { node } = createFormComponent({
|
|
schema: { maxItems: 2, ...schemaAdditional },
|
|
});
|
|
|
|
expect(node.querySelector(".array-item-add button")).to.be.null;
|
|
});
|
|
|
|
it("[fixed] should provide an add button if length is lesser than maxItems", () => {
|
|
const { node } = createFormComponent({
|
|
schema: { maxItems: 3, ...schemaAdditional },
|
|
});
|
|
|
|
expect(node.querySelector(".array-item-add button")).not.to.be.null;
|
|
});
|
|
|
|
it("[fixed] should not provide an add button if addable is expliclty false regardless maxItems value", () => {
|
|
const { node } = createFormComponent({
|
|
schema: { maxItems: 2, ...schema },
|
|
uiSchema: {
|
|
"ui:options": {
|
|
addable: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(node.querySelector(".array-item-add button")).to.be.null;
|
|
});
|
|
|
|
it("[fixed] should ignore addable value if maxItems constraint is not satisfied", () => {
|
|
const { node } = createFormComponent({
|
|
schema: { maxItems: 2, ...schema },
|
|
uiSchema: {
|
|
"ui:options": {
|
|
addable: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(node.querySelector(".array-item-add button")).to.be.null;
|
|
});
|
|
|
|
describe("operations for additional items", () => {
|
|
const { comp, node } = createFormComponent({
|
|
schema: schemaAdditional,
|
|
formData: [1, 2, "foo"],
|
|
ArrayFieldTemplate: ExposedArrayKeyTemplate,
|
|
});
|
|
|
|
const startRows = node.querySelectorAll(".array-item");
|
|
const startRow1_key = startRows[0].getAttribute(ArrayKeyDataAttr);
|
|
const startRow2_key = startRows[1].getAttribute(ArrayKeyDataAttr);
|
|
const startRow3_key = startRows[2].getAttribute(ArrayKeyDataAttr);
|
|
const startRow4_key = startRows[3]
|
|
? startRows[3].getAttribute(ArrayKeyDataAttr)
|
|
: undefined;
|
|
|
|
it("should add a field when clicking add button", () => {
|
|
const addBtn = node.querySelector(".array-item-add button");
|
|
|
|
Simulate.click(addBtn);
|
|
|
|
expect(node.querySelectorAll(".field-string")).to.have.length.of(2);
|
|
expect(comp.state.formData).eql([1, 2, "foo", undefined]);
|
|
});
|
|
|
|
it("should retain existing row keys/ids when adding additional items", () => {
|
|
const endRows = node.querySelectorAll(".array-item");
|
|
const endRow1_key = endRows[0].getAttribute(ArrayKeyDataAttr);
|
|
const endRow2_key = endRows[1].getAttribute(ArrayKeyDataAttr);
|
|
const endRow3_key = endRows[2].getAttribute(ArrayKeyDataAttr);
|
|
const endRow4_key = endRows[3].getAttribute(ArrayKeyDataAttr);
|
|
|
|
expect(startRow1_key).to.equal(endRow1_key);
|
|
expect(startRow2_key).to.equal(endRow2_key);
|
|
expect(startRow3_key).to.equal(endRow3_key);
|
|
|
|
expect(startRow4_key).to.not.equal(endRow4_key);
|
|
expect(startRow4_key).to.be.undefined;
|
|
expect(endRows[0].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[1].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[2].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
expect(endRows[3].hasAttribute(ArrayKeyDataAttr)).to.be.true;
|
|
});
|
|
|
|
it("should change the state when changing input value", () => {
|
|
const inputs = node.querySelectorAll(".field-string input[type=text]");
|
|
|
|
Simulate.change(inputs[0], { target: { value: "bar" } });
|
|
Simulate.change(inputs[1], { target: { value: "baz" } });
|
|
|
|
expect(comp.state.formData).eql([1, 2, "bar", "baz"]);
|
|
});
|
|
|
|
it("should remove array items when clicking remove buttons", () => {
|
|
let dropBtns = node.querySelectorAll(".array-item-remove");
|
|
|
|
Simulate.click(dropBtns[0]);
|
|
|
|
expect(node.querySelectorAll(".field-string")).to.have.length.of(1);
|
|
expect(comp.state.formData).eql([1, 2, "baz"]);
|
|
|
|
dropBtns = node.querySelectorAll(".array-item-remove");
|
|
Simulate.click(dropBtns[0]);
|
|
|
|
expect(node.querySelectorAll(".field-string")).to.be.empty;
|
|
expect(comp.state.formData).eql([1, 2]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Multiple number choices list", () => {
|
|
const schema = {
|
|
type: "array",
|
|
title: "My field",
|
|
items: {
|
|
enum: [1, 2, 3],
|
|
type: "integer",
|
|
},
|
|
uniqueItems: true,
|
|
};
|
|
|
|
it("should convert array of strings to numbers if type of items is 'number'", () => {
|
|
const { comp, node } = createFormComponent({ schema });
|
|
|
|
Simulate.change(node.querySelector(".field select"), {
|
|
target: {
|
|
options: [
|
|
{ selected: true, value: "1" },
|
|
{ selected: true, value: "2" },
|
|
{ selected: false, value: "3" },
|
|
],
|
|
},
|
|
});
|
|
|
|
expect(comp.state.formData).eql([1, 2]);
|
|
});
|
|
});
|
|
|
|
describe("Title", () => {
|
|
const TitleField = props => <div id={`title-${props.title}`} />;
|
|
|
|
const fields = { TitleField };
|
|
|
|
it("should pass field name to TitleField if there is no title", () => {
|
|
const schema = {
|
|
type: "object",
|
|
properties: {
|
|
array: {
|
|
type: "array",
|
|
items: {},
|
|
},
|
|
},
|
|
};
|
|
|
|
const { node } = createFormComponent({ schema, fields });
|
|
expect(node.querySelector("#title-array")).to.not.be.null;
|
|
});
|
|
|
|
it("should pass schema title to TitleField", () => {
|
|
const schema = {
|
|
type: "array",
|
|
title: "test",
|
|
items: {},
|
|
};
|
|
|
|
const { node } = createFormComponent({ schema, fields });
|
|
expect(node.querySelector("#title-test")).to.not.be.null;
|
|
});
|
|
|
|
it("should pass empty schema title to TitleField", () => {
|
|
const schema = {
|
|
type: "array",
|
|
title: "",
|
|
items: {},
|
|
};
|
|
const { node } = createFormComponent({ schema, fields });
|
|
expect(node.querySelector("#title-")).to.be.null;
|
|
});
|
|
});
|
|
});
|