diff --git a/src/components/fields/ObjectField.js b/src/components/fields/ObjectField.js index 4b7edb41f3..982aa3aaff 100644 --- a/src/components/fields/ObjectField.js +++ b/src/components/fields/ObjectField.js @@ -7,6 +7,7 @@ import { retrieveSchema, getDefaultRegistry, getUiOptions, + ADDITIONAL_PROPERTY_FLAG, } from "../../utils"; function DefaultObjectFieldTemplate(props) { @@ -79,9 +80,24 @@ class ObjectField extends Component { ); } - onPropertyChange = name => { + onPropertyChange = (name, addedByAdditionalProperties = false) => { return (value, errorSchema) => { - const newFormData = { ...this.props.formData, [name]: value }; + let newFormData; + //section below sets zero value of input field to empty string + //instead of undefined, so that value input in additionalProperties + //doesn't disappear when emptied + if (!value && addedByAdditionalProperties) { + newFormData = { + ...this.props.formData, + [name]: "", + }; + } else { + newFormData = { + ...this.props.formData, + [name]: value, + }; + } + this.props.onChange( newFormData, errorSchema && @@ -93,6 +109,16 @@ class ObjectField extends Component { }; }; + onDropIndexClick = key => { + return event => { + event.preventDefault(); + const { onChange, formData } = this.props; + const copiedFormData = { ...formData }; + delete copiedFormData[key]; + onChange(copiedFormData); + }; + }; + getAvailableKey = (preferredKey, formData) => { var index = 0; var newKey = preferredKey; @@ -176,7 +202,6 @@ class ObjectField extends Component { const title = schema.title === undefined ? name : schema.title; const description = uiSchema["ui:description"] || schema.description; let orderedProperties; - try { const properties = Object.keys(schema.properties); orderedProperties = orderProperties(properties, uiSchema["ui:order"]); @@ -200,6 +225,9 @@ class ObjectField extends Component { TitleField, DescriptionField, properties: orderedProperties.map(name => { + const addedByAdditionalProperties = schema.properties[ + name + ].hasOwnProperty(ADDITIONAL_PROPERTY_FLAG); return { content: ( ), name, diff --git a/src/components/fields/SchemaField.js b/src/components/fields/SchemaField.js index fd7ff9a795..1d10983c05 100644 --- a/src/components/fields/SchemaField.js +++ b/src/components/fields/SchemaField.js @@ -1,4 +1,5 @@ import { ADDITIONAL_PROPERTY_FLAG } from "../../utils"; +import IconButton from "../IconButton"; import React from "react"; import PropTypes from "prop-types"; @@ -107,7 +108,6 @@ function ErrorList(props) { ); } - function DefaultTemplate(props) { const { id, @@ -121,35 +121,57 @@ function DefaultTemplate(props) { required, displayLabel, onKeyChange, + onDropIndexClick, } = props; if (hidden) { return children; } + const additional = props.schema.hasOwnProperty(ADDITIONAL_PROPERTY_FLAG); const keyLabel = `${label} Key`; return (
- {additional && ( -
-
); } - if (process.env.NODE_ENV !== "production") { DefaultTemplate.propTypes = { id: PropTypes.string, @@ -186,6 +208,7 @@ function SchemaFieldRender(props) { idPrefix, name, onKeyChange, + onDropIndexClick, required, registry = getDefaultRegistry(), } = props; @@ -285,6 +308,7 @@ function SchemaFieldRender(props) { label, hidden, onKeyChange, + onDropIndexClick, required, disabled, readonly, diff --git a/src/components/fields/StringField.js b/src/components/fields/StringField.js index 91fcf6911e..6ef3f3cbff 100644 --- a/src/components/fields/StringField.js +++ b/src/components/fields/StringField.js @@ -34,7 +34,6 @@ function StringField(props) { uiSchema ); const Widget = getWidget(schema, widget, widgets); - return ( { ArrayFieldTemplate, }); - expect(node.querySelectorAll(".field-array div")).to.have.length.of(3); + expect(node.querySelectorAll(".field-array div")).to.have.length.of(6); }); }); diff --git a/test/ArrayField_test.js b/test/ArrayField_test.js index 633c57347c..40e7f90b3a 100644 --- a/test/ArrayField_test.js +++ b/test/ArrayField_test.js @@ -24,7 +24,8 @@ describe("ArrayField", () => { const { node } = createFormComponent({ schema: { type: "array" } }); expect( - node.querySelector(".field-array > .unsupported-field").textContent + node.querySelector(".field-array > div > div > .unsupported-field") + .textContent ).to.contain("Missing items definition"); }); }); diff --git a/test/ObjectField_test.js b/test/ObjectField_test.js index a0554e4b5f..b546ba55bf 100644 --- a/test/ObjectField_test.js +++ b/test/ObjectField_test.js @@ -197,7 +197,7 @@ describe("ObjectField", () => { }, }); const labels = [].map.call( - node.querySelectorAll(".field > label"), + node.querySelectorAll(".field > div > div > label"), l => l.textContent ); @@ -212,7 +212,7 @@ describe("ObjectField", () => { }, }); const labels = [].map.call( - node.querySelectorAll(".field > label"), + node.querySelectorAll(".field > div > div> label"), l => l.textContent ); @@ -277,7 +277,7 @@ describe("ObjectField", () => { }, }); const labels = [].map.call( - node.querySelectorAll(".field > label"), + node.querySelectorAll(".field > div > div > label"), l => l.textContent ); @@ -310,7 +310,7 @@ describe("ObjectField", () => { }, }); const labels = [].map.call( - node.querySelectorAll(".field > label"), + node.querySelectorAll(".field > div > div > label"), l => l.textContent ); @@ -678,5 +678,62 @@ describe("ObjectField", () => { expect(node.querySelector(".object-property-expand button")).to.be.null; }); + + it("should not have delete button if expand button has not been clicked", () => { + const { node } = createFormComponent({ schema }); + + expect(node.querySelector(".form-group > .btn-danger")).eql(null); + }); + + it("should have delete button if expand button has been clicked", () => { + const { node } = createFormComponent({ + schema, + }); + + expect( + node.querySelector(".form-group > .row > .col-xs-2 .btn-danger") + ).eql(null); + + Simulate.click(node.querySelector(".object-property-expand button")); + + expect(node.querySelector(".form-group > .row > .col-xs-2 > .btn-danger")) + .to.not.be.null; + }); + + it("delete button should delete key-value pair", () => { + const { node } = createFormComponent({ + schema, + formData: { first: 1 }, + }); + expect(node.querySelector("#root_first-key").value).to.eql("first"); + Simulate.click( + node.querySelector(".form-group > .row > .col-xs-2 > .btn-danger") + ); + expect(node.querySelector("#root_first-key")).to.not.exist; + }); + + it("delete button should delete correct pair", () => { + const { node } = createFormComponent({ + schema, + formData: { first: 1, second: 2, third: 3 }, + }); + const selector = ".form-group > .row > .col-xs-2 > .btn-danger"; + expect(node.querySelectorAll(selector).length).to.eql(3); + Simulate.click(node.querySelectorAll(selector)[1]); + expect(node.querySelector("#root_second-key")).to.not.exist; + expect(node.querySelectorAll(selector).length).to.eql(2); + }); + + it("deleting content of value input should not delete pair", () => { + const { comp, node } = createFormComponent({ + schema, + formData: { first: 1 }, + }); + + Simulate.change(node.querySelector("#root_first"), { + target: { value: "" }, + }); + expect(comp.state.formData["first"]).eql(""); + }); }); }); diff --git a/test/SchemaField_test.js b/test/SchemaField_test.js index fa503d767e..504e722ce8 100644 --- a/test/SchemaField_test.js +++ b/test/SchemaField_test.js @@ -313,7 +313,7 @@ describe("SchemaField", () => { submit(node); const matches = node.querySelectorAll( - "form > .form-group > div > .error-detail .text-danger" + "form > .form-group > div > div > div > .error-detail .text-danger" ); expect(matches).to.have.length.of(1); expect(matches[0].textContent).to.eql("container");