Repository: mozilla-services/react-jsonschema-form
Pull request #1101 (2018-12-07 14:40:55)

Key-value pair in additional properties retains order when edited
### Reasons for making this change

When you change key it shows on the bottom of the list, instead of keeping its place, keeping the order of key-value pairs.

If this is related to existing tickets, include links to them as well.

### Checklist

* [ ] **I'm updating documentation**
  - [ ] I've checked the rendering of the Markdown text I've added
  - [ ] If I'm adding a new section, I've updated the Table of Content
* [x] **I'm adding or updating code**
  - [x] I've added and/or updated tests
  - [ ] I've updated docs if needed
  - [x] I've run `npm run cs-format` on my branch to conform my code to [prettier](https://github.com/prettier/prettier) coding style
* [ ] **I'm adding a new feature**
  - [ ] I've updated the playground with an example use of the feature

Solves two things of #1099

aerfio: Hey guys, any comment on this?
edi9999: Can you add a test case in the same form as https://github.com/mozilla-services/react-jsonschema-form/blob/master/test/ObjectField_test.js#L582 that fails before your patch, and succeeds afterwards ?
aerfio: @edi9999 Done
edi9999: Great , merging !

Patch:
diff --git a/src/components/fields/ObjectField.js b/src/components/fields/ObjectField.js
index c4ab7cf80..4b7edb41f 100644
--- a/src/components/fields/ObjectField.js
+++ b/src/components/fields/ObjectField.js
@@ -96,7 +96,7 @@ class ObjectField extends Component {
   getAvailableKey = (preferredKey, formData) => {
     var index = 0;
     var newKey = preferredKey;
-    while (this.props.formData.hasOwnProperty(newKey)) {
+    while (formData.hasOwnProperty(newKey)) {
       newKey = `${preferredKey}-${++index}`;
     }
     return newKey;
@@ -104,13 +104,19 @@ class ObjectField extends Component {
 
   onKeyChange = oldValue => {
     return (value, errorSchema) => {
+      if (oldValue === value) {
+        return;
+      }
       value = this.getAvailableKey(value, this.props.formData);
       const newFormData = { ...this.props.formData };
-      const property = newFormData[oldValue];
-      delete newFormData[oldValue];
-      newFormData[value] = property;
+      const newKeys = { [oldValue]: value };
+      const keyValues = Object.keys(newFormData).map(key => {
+        const newKey = newKeys[key] || key;
+        return { [newKey]: newFormData[key] };
+      });
+      const renamedObj = Object.assign({}, ...keyValues);
       this.props.onChange(
-        newFormData,
+        renamedObj,
         errorSchema &&
           this.props.errorSchema && {
             ...this.props.errorSchema,


Test patch:
diff --git a/test/ObjectField_test.js b/test/ObjectField_test.js
index 04dab222c..a0554e4b5 100644
--- a/test/ObjectField_test.js
+++ b/test/ObjectField_test.js
@@ -520,6 +520,24 @@ describe("ObjectField", () => {
       expect(comp.state.formData.newFirst).eql(1);
     });
 
+    it("should keep order of renamed key-value pairs while renaming key", () => {
+      const { comp, node } = createFormComponent({
+        schema,
+        formData: { first: 1, second: 2, third: 3 },
+      });
+
+      const textNode = node.querySelector("#root_second-key");
+      Simulate.blur(textNode, {
+        target: { value: "newSecond" },
+      });
+
+      expect(Object.keys(comp.state.formData)).eql([
+        "first",
+        "newSecond",
+        "third",
+      ]);
+    });
+
     it("should attach suffix to formData key if new key already exists when key input is renamed", () => {
       const formData = {
         first: 1,
@@ -538,6 +556,21 @@ describe("ObjectField", () => {
       expect(comp.state.formData["second-1"]).eql(1);
     });
 
+    it("should not attach suffix when input is only clicked", () => {
+      const formData = {
+        first: 1,
+      };
+      const { comp, node } = createFormComponent({
+        schema,
+        formData,
+      });
+
+      const textNode = node.querySelector("#root_first-key");
+      Simulate.blur(textNode);
+
+      expect(comp.state.formData.hasOwnProperty("first")).to.be.true;
+    });
+
     it("should continue incrementing suffix to formData key until that key name is unique after a key input collision", () => {
       const formData = {
         first: 1,