Preserve property order in environment editor (#1497)

* Add json-order package

* Add propertyMap and sort properties boolean to model

* Keep property order

* Remove extra check

* Allow env editor in request group overrides to maintain order

* Add checkbox

* Order control added

* Use up to date json-order

* Rename

* Remove extra field

* Update separator

* Prevent unnecessary parse

* Move to regular dependency

* add to packedDeps

* Renames

* Remove extra code

* Removed extra state

* More fixes...

* add flowtypes for json-order

* typo

* Updated flowtypes

* Fixed?

* linting to remove unused import
This commit is contained in:
Opender Singh
2019-05-30 08:10:48 +12:00
committed by Gregory Schier
parent 5175515e99
commit 24a2e75fa3
10 changed files with 242 additions and 79 deletions

92
package-lock.json generated
View File

@@ -74,6 +74,7 @@
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
@@ -111,7 +112,8 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@@ -132,12 +134,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -152,17 +156,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@@ -279,7 +286,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@@ -291,6 +299,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -305,6 +314,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -312,12 +322,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -336,6 +348,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -416,7 +429,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@@ -428,6 +442,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -513,7 +528,8 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -549,6 +565,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -568,6 +585,7 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -611,12 +629,14 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
}
}
},
@@ -647,7 +667,8 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true
"dev": true,
"optional": true
},
"is-glob": {
"version": "4.0.1",
@@ -8846,7 +8867,8 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@@ -8867,12 +8889,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -8887,17 +8911,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@@ -9014,7 +9041,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@@ -9026,6 +9054,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -9040,6 +9069,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -9047,12 +9077,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -9071,6 +9103,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -9151,7 +9184,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@@ -9163,6 +9197,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -9248,7 +9283,8 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -9284,6 +9320,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -9303,6 +9340,7 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -9346,12 +9384,14 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
}
}
},
@@ -14933,7 +14973,8 @@
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true
"dev": true,
"optional": true
},
"braces": {
"version": "2.3.2",
@@ -15196,7 +15237,8 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
"dev": true,
"optional": true
},
"micromatch": {
"version": "3.1.10",

View File

@@ -13,6 +13,7 @@ export const canSync = true;
type BaseEnvironment = {
name: string,
data: Object,
dataPropertyOrder: Object | null,
color: string | null,
metaSortKey: number,
@@ -26,6 +27,7 @@ export function init() {
return {
name: 'New Environment',
data: {},
dataPropertyOrder: null,
color: null,
isPrivate: false,
metaSortKey: Date.now(),

View File

@@ -12,6 +12,7 @@ type BaseRequestGroup = {
name: string,
description: string,
environment: Object,
environmentPropertyOrder: Object | null,
metaSortKey: number,
};
@@ -22,6 +23,7 @@ export function init() {
name: 'New Folder',
description: '',
environment: {},
environmentPropertyOrder: null,
metaSortKey: -1 * Date.now(),
};
}

View File

@@ -2,9 +2,15 @@
import * as React from 'react';
import autobind from 'autobind-decorator';
import CodeEditor from '../codemirror/code-editor';
import orderedJSON from 'json-order';
export type EnvironmentInfo = {
object: Object,
propertyOrder: Object | null,
};
type Props = {
environment: Object,
environmentInfo: EnvironmentInfo,
didChange: Function,
editorFontSize: number,
editorIndentSize: number,
@@ -69,11 +75,16 @@ class EnvironmentEditor extends React.PureComponent<Props, State> {
this._editor = n;
}
getValue() {
getValue(): EnvironmentInfo | null {
if (this._editor) {
return JSON.parse(this._editor.getValue());
const data = orderedJSON.parse(this._editor.getValue(), '&', `~|`);
return {
object: data.object,
propertyOrder: data.map || null,
};
} else {
return '';
return null;
}
}
@@ -83,7 +94,7 @@ class EnvironmentEditor extends React.PureComponent<Props, State> {
render() {
const {
environment,
environmentInfo,
editorFontSize,
editorIndentSize,
editorKeyMap,
@@ -107,7 +118,10 @@ class EnvironmentEditor extends React.PureComponent<Props, State> {
lineWrapping={lineWrapping}
keyMap={editorKeyMap}
onChange={this._handleChange}
defaultValue={JSON.stringify(environment)}
defaultValue={orderedJSON.stringify(
environmentInfo.object,
environmentInfo.propertyOrder || null,
)}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
render={render}

View File

@@ -30,10 +30,20 @@ class EnvironmentEditModal extends PureComponent {
return;
}
const environment = this._envEditor.getValue();
let patch;
try {
const data = this._envEditor.getValue();
patch = {
environment: data && data.object,
environmentPropertyOrder: data && data.propertyOrder,
};
} catch (err) {
// Invalid JSON probably
return;
}
const { requestGroup } = this.state;
this.props.onChange(Object.assign({}, requestGroup, { environment }));
this.props.onChange(Object.assign({}, requestGroup, patch));
}
_didChange() {
@@ -70,6 +80,11 @@ class EnvironmentEditModal extends PureComponent {
const { requestGroup, isValid } = this.state;
const environmentInfo = {
object: requestGroup ? requestGroup.environment : {},
propertyOrder: requestGroup && requestGroup.environmentPropertyOrder,
};
return (
<Modal ref={this._setModalRef} tall {...extraProps}>
<ModalHeader>Environment Overrides (JSON Format)</ModalHeader>
@@ -81,7 +96,7 @@ class EnvironmentEditModal extends PureComponent {
ref={this._setEditorRef}
key={requestGroup ? requestGroup._id : 'n/a'}
lineWrapping={lineWrapping}
environment={requestGroup ? requestGroup.environment : {}}
environmentInfo={environmentInfo}
didChange={this._didChange}
render={render}
getRenderContext={getRenderContext}

View File

@@ -342,9 +342,13 @@ class WorkspaceEnvironmentsEditModal extends React.PureComponent<Props, State> {
return;
}
let data;
let patch;
try {
data = this.environmentEditorRef.getValue();
const data = this.environmentEditorRef.getValue();
patch = {
data: data && data.object,
dataPropertyOrder: data && data.propertyOrder,
};
} catch (err) {
// Invalid JSON probably
return;
@@ -355,7 +359,7 @@ class WorkspaceEnvironmentsEditModal extends React.PureComponent<Props, State> {
if (activeEnvironment) {
clearTimeout(this.saveTimeout);
this.saveTimeout = setTimeout(() => {
models.environment.update(activeEnvironment, { data });
models.environment.update(activeEnvironment, patch);
}, DEBOUNCE_MILLIS * 4);
}
}
@@ -376,6 +380,11 @@ class WorkspaceEnvironmentsEditModal extends React.PureComponent<Props, State> {
const activeEnvironment = this._getActiveEnvironment();
const environmentInfo = {
object: activeEnvironment ? activeEnvironment.data : {},
propertyOrder: activeEnvironment && activeEnvironment.dataPropertyOrder,
};
return (
<Modal ref={this._setModalRef} wide tall {...this.props}>
<ModalHeader>Manage Environments</ModalHeader>
@@ -441,39 +450,39 @@ class WorkspaceEnvironmentsEditModal extends React.PureComponent<Props, State> {
</h1>
{activeEnvironment && rootEnvironment !== activeEnvironment ? (
<Dropdown className="space-right" right>
<DropdownButton className="btn btn--clicky">
{activeEnvironment.color && (
<i
className="fa fa-circle space-right"
style={{ color: activeEnvironment.color }}
/>
)}
Color <i className="fa fa-caret-down" />
</DropdownButton>
<React.Fragment>
<Dropdown className="space-right" right>
<DropdownButton className="btn btn--clicky">
{activeEnvironment.color && (
<i
className="fa fa-circle space-right"
style={{ color: activeEnvironment.color }}
/>
)}
Color <i className="fa fa-caret-down" />
</DropdownButton>
<DropdownItem value={activeEnvironment} onClick={this._handleClickColorChange}>
<i className="fa fa-circle" style={{ color: activeEnvironment.color }} />
{activeEnvironment.color ? 'Change Color' : 'Assign Color'}
</DropdownItem>
<DropdownItem value={activeEnvironment} onClick={this._handleClickColorChange}>
<i className="fa fa-circle" style={{ color: activeEnvironment.color }} />
{activeEnvironment.color ? 'Change Color' : 'Assign Color'}
</DropdownItem>
<DropdownItem
<DropdownItem
value={activeEnvironment}
onClick={this._handleUnsetColor}
disabled={!activeEnvironment.color}>
<i className="fa fa-minus-circle" />
Unset Color
</DropdownItem>
</Dropdown>
<PromptButton
value={activeEnvironment}
onClick={this._handleUnsetColor}
disabled={!activeEnvironment.color}>
<i className="fa fa-minus-circle" />
Unset Color
</DropdownItem>
</Dropdown>
) : null}
{activeEnvironment && rootEnvironment !== activeEnvironment ? (
<PromptButton
value={activeEnvironment}
onClick={this._handleDeleteEnvironment}
className="btn btn--clicky">
<i className="fa fa-trash-o" />
</PromptButton>
onClick={this._handleDeleteEnvironment}
className="btn btn--clicky">
<i className="fa fa-trash-o" />
</PromptButton>
</React.Fragment>
) : null}
</div>
<div className="env-modal__editor">
@@ -484,7 +493,7 @@ class WorkspaceEnvironmentsEditModal extends React.PureComponent<Props, State> {
lineWrapping={lineWrapping}
ref={this._setEditorRef}
key={`${this.editorKey}::${activeEnvironment ? activeEnvironment._id : 'n/a'}`}
environment={activeEnvironment ? activeEnvironment.data : {}}
environmentInfo={environmentInfo}
didChange={this._didChange}
render={render}
getRenderContext={getRenderContext}

View File

@@ -1,3 +1,5 @@
@import '../constants/dimensions.less';
.environment-editor {
display: grid;
grid-template-rows: 1fr auto;

View File

@@ -0,0 +1,20 @@
// @flow
declare module 'json-order' {
declare type OrderedParseResult = {
object: Object,
map: { [key: string]: Array<string> },
};
declare type orderedJSON = {
parse: (jsonString: string, prefix?: string, separator?: string) => OrderedParseResult,
stringify: (
sourceObject: Object,
map: { [key: string]: Array<string> } | null,
separator?: string,
space?: number | void,
) => string,
};
declare module.exports: orderedJSON;
}

View File

@@ -2647,6 +2647,11 @@
"buffer-indexof": "^1.0.0"
}
},
"docopt": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/docopt/-/docopt-0.6.2.tgz",
"integrity": "sha1-so6eIiDaXsSffqW7JKR3h0Be6xE="
},
"doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -2703,6 +2708,15 @@
"domelementtype": "1"
}
},
"dot-json": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/dot-json/-/dot-json-1.1.0.tgz",
"integrity": "sha512-PiQZW9/C8xILPYK2bOye/cbPZrakNEkt28jFb8RlPCwsoMAHYYw9T8JoACxgttHL9Y2AmdqVvibbZJHtLgeqTQ==",
"requires": {
"docopt": "~0.6.2",
"underscore-keypath": "~0.0.22"
}
},
"dot-prop": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
@@ -4868,7 +4882,8 @@
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true
"bundled": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@@ -4888,11 +4903,13 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true
"bundled": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -4906,15 +4923,18 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true
"bundled": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@@ -5030,7 +5050,8 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true
"bundled": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@@ -5041,6 +5062,7 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -5054,17 +5076,20 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true
"bundled": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -5082,6 +5107,7 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -5161,7 +5187,8 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true
"bundled": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@@ -5172,6 +5199,7 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -5256,7 +5284,8 @@
},
"safe-buffer": {
"version": "5.1.2",
"bundled": true
"bundled": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -5291,6 +5320,7 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -5309,6 +5339,7 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -5351,11 +5382,13 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true
"bundled": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
"bundled": true
"bundled": true,
"optional": true
}
}
},
@@ -7260,6 +7293,15 @@
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
},
"json-order": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/json-order/-/json-order-1.0.9.tgz",
"integrity": "sha512-MiULsoJSkszrEdyWyaecNvJ7m0EJvYDqJ0NjVUoU8NRKuTo9OcFDCDOLyGA/qOQ3+FTJpF2vdjggiPKP+6Aa/A==",
"requires": {
"dot-json": "^1.1.0",
"lodash.clonedeep": "^4.5.0"
}
},
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
@@ -7719,6 +7761,11 @@
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
"dev": true
},
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
},
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
@@ -11711,6 +11758,14 @@
}
}
},
"underscore-keypath": {
"version": "0.0.22",
"resolved": "https://registry.npmjs.org/underscore-keypath/-/underscore-keypath-0.0.22.tgz",
"integrity": "sha1-SKUoOSu278QkvhyqVtpLX6zPJk0=",
"requires": {
"underscore": "*"
}
},
"union-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",

View File

@@ -77,6 +77,7 @@
"insomnia-url",
"insomnia-xpath",
"highlight.js",
"json-order",
"jwt-authentication",
"moment",
"nunjucks",
@@ -133,6 +134,7 @@
"insomnia-prettify": "^0.1.11",
"insomnia-url": "^0.1.10",
"insomnia-xpath": "^1.0.13",
"json-order": "^1.0.9",
"jsonlint": "^1.6.3",
"jsonpath": "^0.2.12",
"jwt-authentication": "^0.4.0",