Skip to content

Commit

Permalink
Implement array reordering (rjsf-team#247)
Browse files Browse the repository at this point in the history
  • Loading branch information
dapetcu21 authored and n1k0 committed Jul 5, 2016
1 parent 37eb321 commit 5ffa9ea
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 14 deletions.
53 changes: 45 additions & 8 deletions src/components/fields/ArrayField.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ class ArrayField extends Component {
};
};

onReorderClick = (index, newIndex) => {
return (event) => {
event.preventDefault();
const items = this.state.items.slice(0);
const item = items.splice(index, 1)[0];
items.splice(newIndex, 0, item);
this.asyncSetState({ items }, { validate: true });
};
};

onChangeForIndex = (index) => {
return (value) => {
this.asyncSetState({
Expand Down Expand Up @@ -170,6 +180,8 @@ class ArrayField extends Component {
const itemIdSchema = toIdSchema(itemsSchema, itemIdPrefix, definitions);
return this.renderArrayFieldItem({
index,
canMoveUp: index > 0,
canMoveDown: index < items.length - 1,
itemSchema: itemsSchema,
itemIdSchema,
itemErrorSchema,
Expand Down Expand Up @@ -277,6 +289,8 @@ class ArrayField extends Component {
return this.renderArrayFieldItem({
index,
removable: additional,
canMoveUp: index >= itemSchemas.length + 1,
canMoveDown: additional && index < items.length - 1,
itemSchema,
itemData: item,
itemUiSchema,
Expand All @@ -297,6 +311,8 @@ class ArrayField extends Component {
renderArrayFieldItem({
index,
removable=true,
canMoveUp=true,
canMoveDown=true,
itemSchema,
itemData,
itemUiSchema,
Expand All @@ -305,9 +321,11 @@ class ArrayField extends Component {
}) {
const {SchemaField} = this.props.registry.fields;
const {disabled, readonly} = this.props;
const hasToolbar = removable || canMoveUp || canMoveDown;

return (
<div key={index} className="array-item">
<div className={removable ? "col-xs-10" : "col-xs-12"}>
<div className={hasToolbar ? "col-xs-10" : "col-xs-12"}>
<SchemaField
schema={itemSchema}
uiSchema={itemUiSchema}
Expand All @@ -321,12 +339,31 @@ class ArrayField extends Component {
readonly={this.props.readonly} />
</div>
{
removable ?
<div className="col-xs-2 array-item-remove text-right">
<button type="button" className="btn btn-danger col-xs-12"
tabIndex="-1"
disabled={disabled || readonly}
onClick={this.onDropIndexClick(index)}>Delete</button>
hasToolbar ?
<div className="col-xs-2 array-item-toolbox text-right">
<div className="btn-group" style={{ display: "flex" }}>
{ canMoveUp || canMoveDown ?
<button type="button" className="btn btn-default array-item-move-up"
style={{ flex: 1, paddingLeft: 6, paddingRight: 6 }}
tabIndex="-1"
disabled={disabled || readonly || !canMoveUp}
onClick={this.onReorderClick(index, index - 1)}></button>
: null}
{ canMoveUp || canMoveDown ?
<button type="button" className="btn btn-default array-item-move-down"
style={{ flex: 1, paddingLeft: 6, paddingRight: 6 }}
tabIndex="-1"
disabled={disabled || readonly || !canMoveDown}
onClick={this.onReorderClick(index, index + 1)}></button>
: null}
{removable ?
<button type="button" className="btn btn-danger array-item-remove"
style={{ flex: 1, paddingLeft: 6, paddingRight: 6 }}
tabIndex="-1"
disabled={disabled || readonly}
onClick={this.onDropIndexClick(index)}>✖︎</button>
: null}
</div>
</div>
: null
}
Expand All @@ -341,7 +378,7 @@ function AddButton({onClick, disabled}) {
<p className="col-xs-2 col-xs-offset-10 array-item-add text-right">
<button type="button" className="btn btn-info col-xs-12"
tabIndex="-1" onClick={onClick}
disabled={disabled}>Add</button>
disabled={disabled} style={{fontWeight: 'bold' }}></button>
</p>
</div>
);
Expand Down
63 changes: 59 additions & 4 deletions test/ArrayField_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,64 @@ describe("ArrayField", () => {
expect(inputs[1].value).eql("bar");
});

it("should'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 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 remove a field from the list", () => {
const {node} = createFormComponent({schema, formData: ["foo", "bar"]});
const dropBtns = node.querySelectorAll(".array-item-remove button");
const dropBtns = node.querySelectorAll(".array-item-remove");

Simulate.click(dropBtns[0]);

Expand Down Expand Up @@ -132,7 +187,7 @@ describe("ArrayField", () => {
expect(node.querySelectorAll(".has-error .error-detail"))
.to.have.length.of(1);

const dropBtns = node.querySelectorAll(".array-item-remove button");
const dropBtns = node.querySelectorAll(".array-item-remove");

Simulate.click(dropBtns[0]);

Expand Down Expand Up @@ -529,14 +584,14 @@ describe("ArrayField", () => {
});

it("should remove array items when clicking remove buttons", () => {
let dropBtns = node.querySelectorAll(".array-item-remove button");
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 button");
dropBtns = node.querySelectorAll(".array-item-remove");
Simulate.click(dropBtns[0]);

expect(node.querySelectorAll(".field-string")).to.be.empty;
Expand Down
1 change: 1 addition & 0 deletions test/mocha.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--timeout 5000
4 changes: 2 additions & 2 deletions test/uiSchema_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@ describe("uiSchema", () => {
});

it("should disable the Delete button", () => {
expect(node.querySelector(".array-item-remove button").disabled)
expect(node.querySelector(".array-item-remove").disabled)
.eql(true);
});
});
Expand Down Expand Up @@ -1152,7 +1152,7 @@ describe("uiSchema", () => {
});

it("should disable the Delete button", () => {
expect(node.querySelector(".array-item-remove button").disabled)
expect(node.querySelector(".array-item-remove").disabled)
.eql(true);
});
});
Expand Down

0 comments on commit 5ffa9ea

Please sign in to comment.