Skip to content

Commit

Permalink
Consider maxItems value before rendering the add button (#599)
Browse files Browse the repository at this point in the history
* Consider maxItems value before rendering the add button

jsonschema refuses to validate an array of items when the number of
items is greater than the maxItems constraint. Therefore the UI should
consisder this constraint and not provide an "add" button when the
maximum number of elements is reached.

The "add" button is rendered in the following cases:

- ui:options.addable is not false and items.length < schema.maxItems
- schema.maxItems is unspecified and ui:options.addable is not false

As before, if ui:options.addable is not specified, it is considered as
true by default.

The "add" button is *not* rendered in the following cases:

- ui:options.addable is false,
- items.length >= schema.maxItems, even if ui.options:addable is true

* Consider maxItems value before rendering the add button on fixed arrays

The "add" button is rendered if "additionalItems" is defined *and*
if one of the following conditions is met:

- ui:options.addable is not false and items.length < schema.maxItems
- schema.maxItems is unspecified and ui:options.addable is not false

items length here takes all items into account, i.e. both fixed ones and
additional ones.

* use object spread instead of Object.assign()

… in order to match codebase convention

* extract "addable" logic in a dedicated method
  • Loading branch information
adimascio authored and glasserc committed Jun 12, 2017
1 parent 68077a7 commit 2a885bc
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 6 deletions.
23 changes: 17 additions & 6 deletions src/components/fields/ArrayField.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,21 @@ class ArrayField extends Component {
return itemSchema.type !== "null";
}

canAddItem(formItems) {
const { schema, uiSchema } = this.props;
let { addable } = getUiOptions(uiSchema);
if (addable !== false) {
// if ui:options.addable was not explicitly set to false, we can add
// another item if we have not exceeded maxItems yet
if (schema.maxItems !== undefined) {
addable = formItems.length < schema.maxItems;
} else {
addable = true;
}
}
return addable;
}

onAddClick = event => {
event.preventDefault();
const { schema, formData, registry = getDefaultRegistry() } = this.props;
Expand Down Expand Up @@ -298,10 +313,8 @@ class ArrayField extends Component {
const { ArrayFieldTemplate, definitions, fields } = registry;
const { TitleField, DescriptionField } = fields;
const itemsSchema = retrieveSchema(schema.items, definitions);
const { addable = true } = getUiOptions(uiSchema);

const arrayProps = {
canAdd: addable,
canAdd: this.canAddItem(formData),
items: formData.map((item, index) => {
const itemErrorSchema = errorSchema ? errorSchema[index] : undefined;
const itemIdPrefix = idSchema.$id + "_" + index;
Expand Down Expand Up @@ -434,8 +447,6 @@ class ArrayField extends Component {
const additionalSchema = allowAdditionalItems(schema)
? retrieveSchema(schema.additionalItems, definitions)
: null;
const { addable = true } = getUiOptions(uiSchema);
const canAdd = addable && additionalSchema;

if (!items || items.length < itemSchemas.length) {
// to make sure at least all fixed items are generated
Expand All @@ -445,7 +456,7 @@ class ArrayField extends Component {

// These are the props passed into the render function
const arrayProps = {
canAdd,
canAdd: this.canAddItem(items) && additionalSchema,
className: "field field-array field-array-fixed-items",
disabled,
idSchema,
Expand Down
96 changes: 96 additions & 0 deletions test/ArrayField_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,52 @@ describe("ArrayField", () => {
expect(node.querySelectorAll(".field-string")).to.have.length.of(1);
});

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 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 });

Expand Down Expand Up @@ -792,6 +838,56 @@ describe("ArrayField", () => {
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,
Expand Down

0 comments on commit 2a885bc

Please sign in to comment.