From 0408f285af1a11409737ea355e3900ad4eb1b2cf Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Mon, 22 Jul 2019 18:46:42 -0300 Subject: [PATCH 01/32] Start Parameters Migration --- .../app/components/ParameterApplyButton.jsx | 2 +- client/app/components/Parameters.jsx | 126 ++++++++++++++++++ client/app/components/Parameters.less | 13 ++ client/app/components/parameters.js | 2 +- package-lock.json | 26 +++- package.json | 1 + 6 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 client/app/components/Parameters.jsx create mode 100644 client/app/components/Parameters.less diff --git a/client/app/components/ParameterApplyButton.jsx b/client/app/components/ParameterApplyButton.jsx index 1808ac0542..f9bee75f4f 100644 --- a/client/app/components/ParameterApplyButton.jsx +++ b/client/app/components/ParameterApplyButton.jsx @@ -6,7 +6,7 @@ import Badge from 'antd/lib/badge'; import Tooltip from 'antd/lib/tooltip'; import { KeyboardShortcuts } from '@/services/keyboard-shortcuts'; -function ParameterApplyButton({ paramCount, onClick, isApplying }) { +export function ParameterApplyButton({ paramCount, onClick, isApplying }) { // show spinner when applying (also when count is empty so the fade out is consistent) const icon = isApplying || !paramCount ? 'spinner fa-pulse' : 'check'; diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx new file mode 100644 index 0000000000..e3f272687a --- /dev/null +++ b/client/app/components/Parameters.jsx @@ -0,0 +1,126 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { isEqual } from 'lodash'; +import { react2angular } from 'react2angular'; +import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'; +import { Parameter } from '@/services/query'; +import { ParameterApplyButton } from '@/components/ParameterApplyButton'; +import { ParameterValueInput } from '@/components/ParameterValueInput'; + +import './Parameters.less'; + +const DragHandle = sortableHandle(() =>
); + +const SortableItem = sortableElement(({ children }) => ( +
+ + {children} +
+)); +const SortableContainer = sortableContainer(({ children }) => children); + +export class Parameters extends React.Component { + static propTypes = { + parameters: PropTypes.arrayOf(PropTypes.instanceOf(Parameter)), + onSortUpdate: PropTypes.func, + editable: PropTypes.bool, + }; + + static defaultProps = { + parameters: [], + onSortUpdate: () => {}, + editable: true, + } + + constructor(props) { + super(props); + this.state = { parameters: props.parameters }; + } + + componentDidUpdate = (prevProps) => { + const { parameters } = this.props; + if (!isEqual(prevProps.parameters, parameters)) { + this.setState({ parameters }); + } + } + + onSortEnd = ({ oldIndex, newIndex }) => { + const { onSortUpdate } = this.props; + this.setState(({ parameters }) => { + parameters.splice(newIndex, 0, parameters.splice(oldIndex, 1)[0]); + onSortUpdate(parameters); + return { parameters }; + }); + } + + onSelect = () => { + console.log('Selected value!'); + }; + + renderParameter(param) { + return ( +
+ + +
+ ); + } + + render() { + const { parameters } = this.state; + const { editable } = this.props; + return ( + +
+ {parameters.map((param, index) => (editable ? ( + + {this.renderParameter(param)} + + ) : this.renderParameter(param)))} + + +
+
+ ); + } +} + +export default function init(ngModule) { + ngModule.component('parameters', { + template: ` + + `, + bindings: { + parameters: '=', + }, + controller($scope) { + this.onSortUpdate = (parameters) => { + this.parameters = parameters; + }; + this.setValue = (value, isDirty) => { + if (isDirty) { + this.param.setPendingValue(value); + } else { + this.param.clearPendingValue(); + } + $scope.$apply(); + }; + }, + }); + ngModule.component('parametersImpl', react2angular(Parameters)); +} + +init.init = true; diff --git a/client/app/components/Parameters.less b/client/app/components/Parameters.less new file mode 100644 index 0000000000..41ba8aa784 --- /dev/null +++ b/client/app/components/Parameters.less @@ -0,0 +1,13 @@ +.drag-handle { + background: linear-gradient(90deg, transparent 0px, white 1px, white 2px) + center, + linear-gradient(transparent 0px, white 1px, white 2px) center, #111111; + background-size: 2px 2px; + display: inline-block; + width: 6px; + height: 34px; + vertical-align: bottom; + margin-right: 5px; + margin-bottom: 2px; + cursor: move; +} diff --git a/client/app/components/parameters.js b/client/app/components/parameters.js index 0dc8a87e99..5acc0d9c28 100644 --- a/client/app/components/parameters.js +++ b/client/app/components/parameters.js @@ -96,4 +96,4 @@ export default function init(ngModule) { ngModule.directive('parameters', ParametersDirective); } -init.init = true; +// init.init = true; diff --git a/package-lock.json b/package-lock.json index 9a0ba92089..88842280a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9690,7 +9690,6 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, "requires": { "loose-envify": "^1.0.0" } @@ -14955,6 +14954,31 @@ "resize-observer-polyfill": "^1.5.0" } }, + "react-sortable-hoc": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-1.9.1.tgz", + "integrity": "sha512-2VeofjRav8+eZeE5Nm/+b8mrA94rQ+gBsqhXi8pRBSjOWNqslU3ZEm+0XhSlfoXJY2lkgHipfYAUuJbDtCixRg==", + "requires": { + "@babel/runtime": "^7.2.0", + "invariant": "^2.2.4", + "prop-types": "^15.5.7" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz", + "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + } + } + }, "react-test-renderer": { "version": "16.8.3", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.3.tgz", diff --git a/package.json b/package.json index 15e4995921..6aaf31780e 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "react-ace": "^6.1.0", "react-dom": "^16.8.3", "react-grid-layout": "git+https://github.com/getredash/react-grid-layout.git", + "react-sortable-hoc": "^1.9.1", "react2angular": "^3.2.1", "ui-select": "^0.19.8", "underscore.string": "^3.3.4" From 15419f6b57159e702d4cdb1ba666c877ef6ca1a0 Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Tue, 23 Jul 2019 23:23:17 -0300 Subject: [PATCH 02/32] Add dirtyCount --- client/app/components/Parameters.jsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index e3f272687a..2a188e8914 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { isEqual } from 'lodash'; +import { isEqual, size, filter } from 'lodash'; import { react2angular } from 'react2angular'; import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'; import { Parameter } from '@/services/query'; @@ -34,13 +34,15 @@ export class Parameters extends React.Component { constructor(props) { super(props); - this.state = { parameters: props.parameters }; + const { parameters } = props; + this.state = { parameters, dirtyParamCount: size(filter(parameters, 'hasPendingValue')) }; } componentDidUpdate = (prevProps) => { const { parameters } = this.props; if (!isEqual(prevProps.parameters, parameters)) { - this.setState({ parameters }); + console.log(parameters); + this.setState({ parameters, dirtyParamCount: size(filter(parameters, 'hasPendingValue')) }); } } @@ -53,8 +55,8 @@ export class Parameters extends React.Component { }); } - onSelect = () => { - console.log('Selected value!'); + onSelect = (param, value) => { + param.setPendingValue(value); }; renderParameter(param) { @@ -63,21 +65,21 @@ export class Parameters extends React.Component { className="di-block m-r-10" data-test={`ParameterName-${param.name}`} > - + this.onSelect(param, value)} />
); } render() { - const { parameters } = this.state; + const { parameters, dirtyParamCount } = this.state; const { editable } = this.props; return ( @@ -88,7 +90,7 @@ export class Parameters extends React.Component { ) : this.renderParameter(param)))} - + ); From 61541d411f86c68491256d3f2215a62a46f731ed Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Wed, 24 Jul 2019 18:50:48 -0300 Subject: [PATCH 03/32] Use workaround with setState --- client/app/components/Parameters.jsx | 47 +++++++++++++++++++--------- client/app/pages/queries/query.html | 4 +-- client/app/pages/queries/view.js | 6 ++++ 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index 2a188e8914..c84487ecfc 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { isEqual, size, filter } from 'lodash'; +import { isEqual, size, filter, forEach } from 'lodash'; import { react2angular } from 'react2angular'; import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'; import { Parameter } from '@/services/query'; @@ -22,46 +22,62 @@ const SortableContainer = sortableContainer(({ children }) => children); export class Parameters extends React.Component { static propTypes = { parameters: PropTypes.arrayOf(PropTypes.instanceOf(Parameter)), - onSortUpdate: PropTypes.func, + onUpdate: PropTypes.func, editable: PropTypes.bool, }; static defaultProps = { parameters: [], - onSortUpdate: () => {}, + onUpdate: () => {}, editable: true, } constructor(props) { super(props); const { parameters } = props; - this.state = { parameters, dirtyParamCount: size(filter(parameters, 'hasPendingValue')) }; + this.state = { parameters }; } componentDidUpdate = (prevProps) => { const { parameters } = this.props; if (!isEqual(prevProps.parameters, parameters)) { + console.log(prevProps.parameters === parameters); console.log(parameters); - this.setState({ parameters, dirtyParamCount: size(filter(parameters, 'hasPendingValue')) }); + this.setState({ parameters }); } } onSortEnd = ({ oldIndex, newIndex }) => { - const { onSortUpdate } = this.props; + const { onUpdate } = this.props; this.setState(({ parameters }) => { parameters.splice(newIndex, 0, parameters.splice(oldIndex, 1)[0]); - onSortUpdate(parameters); + onUpdate(parameters); return { parameters }; }); } onSelect = (param, value) => { - param.setPendingValue(value); + const { onUpdate } = this.props; + this.setState(({ parameters }) => { + param.setPendingValue(value); + onUpdate(parameters); + return { parameters }; + }); }; + onApply = () => { + const { onUpdate } = this.props; + this.setState(({ parameters }) => { + forEach(parameters, p => p.applyPendingValue()); + onUpdate(parameters); + return { parameters }; + }); + } + renderParameter(param) { return (
@@ -79,8 +95,9 @@ export class Parameters extends React.Component { } render() { - const { parameters, dirtyParamCount } = this.state; + const { parameters } = this.state; const { editable } = this.props; + const dirtyParamCount = size(filter(parameters, 'hasPendingValue')); return (
@@ -90,7 +107,7 @@ export class Parameters extends React.Component { ) : this.renderParameter(param)))} - +
); @@ -98,19 +115,19 @@ export class Parameters extends React.Component { } export default function init(ngModule) { - ngModule.component('parameters', { + ngModule.component('parametersOld', { template: ` `, bindings: { parameters: '=', }, controller($scope) { - this.onSortUpdate = (parameters) => { - this.parameters = parameters; + this.onUpdate = () => { + // this.parameters = parameters; }; this.setValue = (value, isDirty) => { if (isDirty) { @@ -122,7 +139,7 @@ export default function init(ngModule) { }; }, }); - ngModule.component('parametersImpl', react2angular(Parameters)); + ngModule.component('parameters', react2angular(Parameters)); } init.init = true; diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html index 9adf1fdacf..a83ac02c38 100644 --- a/client/app/pages/queries/query.html +++ b/client/app/pages/queries/query.html @@ -191,8 +191,8 @@

- +
diff --git a/client/app/pages/queries/view.js b/client/app/pages/queries/view.js index 2605d62b79..3c2fc5ebdb 100644 --- a/client/app/pages/queries/view.js +++ b/client/app/pages/queries/view.js @@ -321,6 +321,12 @@ function QueryViewCtrl( Events.record('cancel_execute', 'query', $scope.query.id); }; + $scope.onUpdateParameters = (parameters) => { + // $scope.$apply(() => { $scope.query.options.parameters = parameters; }); + console.log(parameters === $scope.query.getParametersDefs()); + console.log($scope.query.getParametersDefs()); + }; + $scope.archiveQuery = () => { function archive() { Query.delete( From d9dbef52a508c57af961e3f031cf31648c8c411a Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Thu, 25 Jul 2019 13:04:44 -0300 Subject: [PATCH 04/32] Apply Changes --- client/app/components/Parameters.jsx | 35 +++++----------------------- client/app/pages/queries/view.js | 20 ++++++++++++---- 2 files changed, 22 insertions(+), 33 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index c84487ecfc..c72b3c3c75 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -22,14 +22,16 @@ const SortableContainer = sortableContainer(({ children }) => children); export class Parameters extends React.Component { static propTypes = { parameters: PropTypes.arrayOf(PropTypes.instanceOf(Parameter)), - onUpdate: PropTypes.func, editable: PropTypes.bool, + onUpdate: PropTypes.func, + onValuesChange: PropTypes.func, }; static defaultProps = { parameters: [], + editable: false, onUpdate: () => {}, - editable: true, + onValuesChange: () => {}, } constructor(props) { @@ -41,8 +43,6 @@ export class Parameters extends React.Component { componentDidUpdate = (prevProps) => { const { parameters } = this.props; if (!isEqual(prevProps.parameters, parameters)) { - console.log(prevProps.parameters === parameters); - console.log(parameters); this.setState({ parameters }); } } @@ -66,10 +66,11 @@ export class Parameters extends React.Component { }; onApply = () => { - const { onUpdate } = this.props; + const { onUpdate, onValuesChange } = this.props; this.setState(({ parameters }) => { forEach(parameters, p => p.applyPendingValue()); onUpdate(parameters); + onValuesChange(); return { parameters }; }); } @@ -115,30 +116,6 @@ export class Parameters extends React.Component { } export default function init(ngModule) { - ngModule.component('parametersOld', { - template: ` - - `, - bindings: { - parameters: '=', - }, - controller($scope) { - this.onUpdate = () => { - // this.parameters = parameters; - }; - this.setValue = (value, isDirty) => { - if (isDirty) { - this.param.setPendingValue(value); - } else { - this.param.clearPendingValue(); - } - $scope.$apply(); - }; - }, - }); ngModule.component('parameters', react2angular(Parameters)); } diff --git a/client/app/pages/queries/view.js b/client/app/pages/queries/view.js index 3c2fc5ebdb..60954ac6eb 100644 --- a/client/app/pages/queries/view.js +++ b/client/app/pages/queries/view.js @@ -1,4 +1,4 @@ -import { pick, some, find, minBy, map, intersection, isArray } from 'lodash'; +import { pick, some, find, minBy, map, intersection, isArray, extend } from 'lodash'; import { SCHEMA_NOT_SUPPORTED, SCHEMA_LOAD_ERROR } from '@/services/data-source'; import getTags from '@/services/getTags'; import { policy } from '@/services/policy'; @@ -322,9 +322,7 @@ function QueryViewCtrl( }; $scope.onUpdateParameters = (parameters) => { - // $scope.$apply(() => { $scope.query.options.parameters = parameters; }); - console.log(parameters === $scope.query.getParametersDefs()); - console.log($scope.query.getParametersDefs()); + $scope.$apply(() => { $scope.query.options.parameters = parameters; }); }; $scope.archiveQuery = () => { @@ -521,6 +519,20 @@ function QueryViewCtrl( }, ); + if (!$scope.query.isNew()) { + $scope.$watch( + 'query.options && query.options.parameters', + (parameters) => { + const params = extend({}, $location.search()); + parameters.forEach((param) => { + extend(params, param.toUrlParams()); + }); + $location.search(params); + }, + true, + ); + } + $scope.showManagePermissionsModal = () => { $uibModal.open({ component: 'permissionsEditor', From 2fe9a16e540c71aa746433995bfcd59565b066ac Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Thu, 25 Jul 2019 17:49:51 -0300 Subject: [PATCH 05/32] Add EditSettingsDialog --- client/app/components/Parameters.jsx | 60 +++- client/app/components/Parameters.less | 10 + client/app/pages/queries/query.html | 2 +- package-lock.json | 487 ++++++++++++++------------ package.json | 2 +- 5 files changed, 320 insertions(+), 241 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index c72b3c3c75..f24996b443 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -1,19 +1,21 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { isEqual, size, filter, forEach } from 'lodash'; +import { isEqual, size, filter, forEach, extend } from 'lodash'; import { react2angular } from 'react2angular'; import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'; +import Button from 'antd/lib/button'; import { Parameter } from '@/services/query'; import { ParameterApplyButton } from '@/components/ParameterApplyButton'; import { ParameterValueInput } from '@/components/ParameterValueInput'; +import EditParameterSettingsDialog from './EditParameterSettingsDialog'; import './Parameters.less'; const DragHandle = sortableHandle(() =>
); -const SortableItem = sortableElement(({ children }) => ( -
- +const SortableItem = sortableElement(({ disabled, children }) => ( +
+ {!disabled && } {children}
)); @@ -25,6 +27,7 @@ export class Parameters extends React.Component { editable: PropTypes.bool, onUpdate: PropTypes.func, onValuesChange: PropTypes.func, + onParameterEdited: PropTypes.func, }; static defaultProps = { @@ -32,6 +35,7 @@ export class Parameters extends React.Component { editable: false, onUpdate: () => {}, onValuesChange: () => {}, + onParameterEdited: () => {}, } constructor(props) { @@ -45,7 +49,7 @@ export class Parameters extends React.Component { if (!isEqual(prevProps.parameters, parameters)) { this.setState({ parameters }); } - } + }; onSortEnd = ({ oldIndex, newIndex }) => { const { onUpdate } = this.props; @@ -54,7 +58,7 @@ export class Parameters extends React.Component { onUpdate(parameters); return { parameters }; }); - } + }; onSelect = (param, value) => { const { onUpdate } = this.props; @@ -73,16 +77,42 @@ export class Parameters extends React.Component { onValuesChange(); return { parameters }; }); - } + }; + + showParameterSettings = (parameter, index) => { + const { onParameterEdited } = this.props; + EditParameterSettingsDialog + .showModal({ parameter }) + .result.then((updated) => { + this.setState(({ parameters }) => { + parameters[index] = extend(parameter, updated); + onParameterEdited(parameters[index]); + return { parameters }; + }); + }); + }; - renderParameter(param) { + renderParameter(param, index) { + const { editable } = this.props; return (
- +
+ + {editable && ( + + )} +
-
- {parameters.map((param, index) => (editable ? ( - - {this.renderParameter(param)} +
+ {parameters.map((param, index) => ( + + {this.renderParameter(param, index)} - ) : this.renderParameter(param)))} + ))}
diff --git a/client/app/components/Parameters.less b/client/app/components/Parameters.less index 41ba8aa784..a94c820267 100644 --- a/client/app/components/Parameters.less +++ b/client/app/components/Parameters.less @@ -1,3 +1,5 @@ +@import '../assets/less/ant'; + .drag-handle { background: linear-gradient(90deg, transparent 0px, white 1px, white 2px) center, @@ -11,3 +13,11 @@ margin-bottom: 2px; cursor: move; } + +.parameter-settings-button { + color: @text-color !important; + + &:hover { + color: @primary-color !important; + } +} diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html index a83ac02c38..1c04d2cb23 100644 --- a/client/app/pages/queries/query.html +++ b/client/app/pages/queries/query.html @@ -192,7 +192,7 @@

+ on-update="onUpdateParameters" on-values-change="executeQuery" on-parameter-edited="onParametersUpdated">
diff --git a/package-lock.json b/package-lock.json index 88842280a1..746a6a1e78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,17 +27,34 @@ "right-now": "^1.0.0" } }, + "@ant-design/colors": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-3.1.0.tgz", + "integrity": "sha512-Td7g1P53sNFyT4Gya6836e70TrhoVZ+HjZs6mpWIHrxl4/VqsjjOyzj/8ktOuw0lCx+BfYu9UO1CiJ0MoYYfhg==", + "requires": { + "tinycolor2": "^1.4.1" + } + }, + "@ant-design/create-react-context": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@ant-design/create-react-context/-/create-react-context-0.2.4.tgz", + "integrity": "sha512-8sw+/w6r+aEbd+OJ62ojoSE4zDt/3yfQydmbWFznoftjr8v/opOswGjM+/MU0rSaREbluqzOmZ6xdecHpSaS2w==", + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + } + }, "@ant-design/icons": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-1.1.16.tgz", - "integrity": "sha512-0zNVP5JYBJkfMi9HotN6QBQjF3SFmUlumJNJXZIH+pZWp/5EbrCczzlG3YTmBWoyRHAsuOGIjSFIy8v/76DTPg==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz", + "integrity": "sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w==" }, "@ant-design/icons-react": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ant-design/icons-react/-/icons-react-1.1.2.tgz", - "integrity": "sha512-7Fgt9d8ABgxrhZxsFjHk/VpPcxodQJJhbJO8Lsh7u58pGN4NoxxW++92naeGTXCyqZsbDPBReP+SC0bdBtbsGQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons-react/-/icons-react-2.0.1.tgz", + "integrity": "sha512-r1QfoltMuruJZqdiKcbPim3d8LNsVPB733U0gZEUSxBLuqilwsW28K2rCTWSMTjmFX7Mfpf+v/wdiFe/XCqThw==", "requires": { - "ant-design-palettes": "^1.1.3", + "@ant-design/colors": "^3.1.0", "babel-runtime": "^6.26.0" } }, @@ -1116,6 +1133,14 @@ "@types/react": "*" } }, + "@types/react-slick": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.4.tgz", + "integrity": "sha512-vXoIy4GUfB7/YgqubR4H7RALo+pRdMYCeLgWwV3MPwl5pggTlEkFBTF19R7u+LJc85uMqC7RfsbkqPLMQ4ab+A==", + "requires": { + "@types/react": "*" + } + }, "@webassemblyjs/ast": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", @@ -1570,69 +1595,70 @@ "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz", "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=" }, - "ant-design-palettes": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/ant-design-palettes/-/ant-design-palettes-1.1.3.tgz", - "integrity": "sha512-UpkkTp8egEN21KZNvY7sTcabLlkHvLvS71EVPk4CYi77Z9AaGGCaVn7i72tbOgWDrQp2wjIg8WgMbKBdK7GtWA==", - "requires": { - "tinycolor2": "^1.4.1" - } - }, "antd": { - "version": "3.13.6", - "resolved": "https://registry.npmjs.org/antd/-/antd-3.13.6.tgz", - "integrity": "sha512-V98EecoaKhHAxulhj07Uy9K4Hxzi+JkmdgYtimZIlaVnAGG3rIfIVgKVkWRK8uHTvvUZJtRIB621BNkhuHW1wQ==", - "requires": { - "@ant-design/icons": "~1.1.16", - "@ant-design/icons-react": "~1.1.2", + "version": "3.20.7", + "resolved": "https://registry.npmjs.org/antd/-/antd-3.20.7.tgz", + "integrity": "sha512-tVovzxwVD1vVx43SDiHKOusQN11JNxGSsyOKDjcXUdlnZegSAXic/YmX8G7Aw0l/E6qhsbBYrB0PI6kCvp5arw==", + "requires": { + "@ant-design/create-react-context": "^0.2.4", + "@ant-design/icons": "~2.1.1", + "@ant-design/icons-react": "~2.0.1", + "@types/react-slick": "^0.23.4", "array-tree-filter": "^2.1.0", "babel-runtime": "6.x", "classnames": "~2.2.6", - "create-react-class": "^15.6.3", - "create-react-context": "0.2.2", + "copy-to-clipboard": "^3.2.0", "css-animation": "^1.5.0", "dom-closest": "^0.2.0", "enquire.js": "^2.1.6", - "lodash": "^4.17.11", + "lodash": "^4.17.13", "moment": "^2.24.0", - "omit.js": "^1.0.0", - "prop-types": "^15.6.2", - "raf": "^3.4.0", - "rc-animate": "^2.5.4", - "rc-calendar": "~9.10.3", - "rc-cascader": "~0.17.0", - "rc-checkbox": "~2.1.5", - "rc-collapse": "~1.10.2", - "rc-dialog": "~7.3.0", - "rc-drawer": "~1.7.6", + "omit.js": "^1.0.2", + "prop-types": "^15.7.2", + "raf": "^3.4.1", + "rc-animate": "^2.8.3", + "rc-calendar": "~9.15.0", + "rc-cascader": "~0.17.4", + "rc-checkbox": "~2.1.6", + "rc-collapse": "~1.11.3", + "rc-dialog": "~7.5.2", + "rc-drawer": "~2.0.1", "rc-dropdown": "~2.4.1", - "rc-editor-mention": "^1.1.7", - "rc-form": "^2.4.0", - "rc-input-number": "~4.3.7", - "rc-menu": "~7.4.12", - "rc-notification": "~3.3.0", - "rc-pagination": "~1.17.7", - "rc-progress": "~2.3.0", + "rc-editor-mention": "^1.1.13", + "rc-form": "^2.4.5", + "rc-input-number": "~4.4.5", + "rc-mentions": "~0.3.1", + "rc-menu": "~7.4.23", + "rc-notification": "~3.3.1", + "rc-pagination": "~1.20.1", + "rc-progress": "~2.5.0", "rc-rate": "~2.5.0", - "rc-select": "^8.6.7", - "rc-slider": "~8.6.5", - "rc-steps": "~3.3.0", + "rc-select": "~9.2.0", + "rc-slider": "~8.6.11", + "rc-steps": "~3.4.1", "rc-switch": "~1.9.0", - "rc-table": "~6.4.0", - "rc-tabs": "~9.6.0", - "rc-time-picker": "~3.6.1", + "rc-table": "~6.6.0", + "rc-tabs": "~9.6.4", + "rc-time-picker": "~3.7.1", "rc-tooltip": "~3.7.3", - "rc-tree": "~1.14.6", - "rc-tree-select": "~2.5.0", + "rc-tree": "~2.1.0", + "rc-tree-select": "~2.9.1", "rc-trigger": "^2.6.2", - "rc-upload": "~2.6.0", - "rc-util": "^4.5.1", + "rc-upload": "~2.6.7", + "rc-util": "^4.6.0", "react-lazy-load": "^3.0.13", "react-lifecycles-compat": "^3.0.4", - "react-slick": "~0.23.2", - "resize-observer-polyfill": "^1.5.0", + "react-slick": "~0.24.0", + "resize-observer-polyfill": "^1.5.1", "shallowequal": "^1.1.0", - "warning": "~4.0.2" + "warning": "~4.0.3" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + } } }, "any-observable": { @@ -1911,12 +1937,9 @@ "dev": true }, "async-validator": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.8.5.tgz", - "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==", - "requires": { - "babel-runtime": "6.x" - } + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.11.5.tgz", + "integrity": "sha512-XNtCsMAeAH1pdLMEg1z8/Bb3a8cdCbui9QbJATRFHHHW5kT6+NPI3zSVQUXgikTFITzsg+kYY5NTWhM2Orwt9w==" }, "asynckit": { "version": "0.4.0", @@ -3880,6 +3903,14 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "copy-to-clipboard": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz", + "integrity": "sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, "copy-webpack-plugin": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", @@ -4778,9 +4809,9 @@ } }, "dom-align": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.8.1.tgz", - "integrity": "sha512-0EPUDSr/Cha3IlS60bFTVj1eq7weXdzDNpdQDSfjsqAeftNPIW27wxmXpglboh10C0qV7/FdOvhp278XHkiM6Q==" + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.9.0.tgz", + "integrity": "sha512-HvPfXISxoU7dKrbqS4vIFa1hx88wD7VdKaZ7sHWeow8y76tuzsxXkiPGbeilemLXrTd9cWbPqR4MOl4y3dkcXA==" }, "dom-closest": { "version": "0.2.0", @@ -4872,13 +4903,6 @@ "fbjs": "^0.8.15", "immutable": "~3.7.4", "object-assign": "^4.1.0" - }, - "dependencies": { - "immutable": { - "version": "3.7.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", - "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=" - } } }, "draw-svg-path": { @@ -9533,9 +9557,9 @@ "optional": true }, "immutable": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", - "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=" + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=" }, "import-fresh": { "version": "3.1.0", @@ -9907,11 +9931,6 @@ "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-2.0.0.tgz", "integrity": "sha512-k2+p7BBCzhqHMdYJwGUNNo+6zegGiMIVbM6bEPzxWXpQV6BUzV892UW0oDFgqxT6DygO7LdxRbwC0xmOhJdbew==" }, - "is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" - }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -10091,9 +10110,9 @@ "dev": true }, "ismobilejs": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-0.5.1.tgz", - "integrity": "sha512-QX4STsOcBYqlTjVGuAdP1MiRVxtiUbRHOKH0v7Gn1EvfUVIQnrSdgCM4zB4VCZuIejnb2NUMUx0Bwd3EIG6yyA==" + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-0.5.2.tgz", + "integrity": "sha512-ta9UdV60xVZk/ZafFtSFslQaE76SvNkcs1r73d2PVR21zVzx9xuYv9tNe4MxA1NN7WoeCc2RjGot3Bz1eHDx3Q==" }, "isobject": { "version": "3.0.1", @@ -12831,9 +12850,9 @@ "dev": true }, "omit.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/omit.js/-/omit.js-1.0.0.tgz", - "integrity": "sha512-O1rwbvEfAdhtonTv+v6IQeMOKTi/wlHcXpI3hehyPDlujkjSBQC6Vtzg0mdy+v2KVDmuPf7hAbHlTBM6q1bUHQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/omit.js/-/omit.js-1.0.2.tgz", + "integrity": "sha512-/QPc6G2NS+8d4L/cQhbk6Yit1WTB6Us2g84A7A/1+w9d/eRGHyEqC5kkQtHVoHZ5NFWGG7tUGgrhVZwgZanKrQ==", "requires": { "babel-runtime": "^6.23.0" } @@ -13991,11 +14010,6 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, - "prettier": { - "version": "1.16.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz", - "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==" - }, "pretty-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", @@ -14374,22 +14388,23 @@ } }, "rc-animate": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-2.6.0.tgz", - "integrity": "sha512-JXDycchgbOI+7T/VKmFWnAIn042LLScK1fNkmNunb0jz5q5aPGCAybx2bTo7X5t31Jkj9OsxKNb/vZPDPWufCg==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-2.9.2.tgz", + "integrity": "sha512-rkJjeJgfbDqVjVX1/QTRfS7PiCq3AnmeYo840cVcuC4pXq4k4yAQMsC2v5BPXXdawC04vnyO4/qHQdbx9ANaiw==", "requires": { "babel-runtime": "6.x", "classnames": "^2.2.6", "css-animation": "^1.3.2", "prop-types": "15.x", "raf": "^3.4.0", + "rc-util": "^4.8.0", "react-lifecycles-compat": "^3.0.4" } }, "rc-calendar": { - "version": "9.10.10", - "resolved": "https://registry.npmjs.org/rc-calendar/-/rc-calendar-9.10.10.tgz", - "integrity": "sha512-WFnxpXGzIt2cPCJjFmrju/w2jZHAO9jW3JSDZovaJuBtVciu1p8brL6PSjWCo4flD3jVurL9LO8tJwgajELj2w==", + "version": "9.15.4", + "resolved": "https://registry.npmjs.org/rc-calendar/-/rc-calendar-9.15.4.tgz", + "integrity": "sha512-+3gS00OO6OfNL7R9vI/YOY86oZ1NKRiK74CzVinJ6qm1PhQobr8IfSdli3cdj6vib9i1MKVn7xRSgUpgJ0rKhA==", "requires": { "babel-runtime": "6.x", "classnames": "2.x", @@ -14401,9 +14416,9 @@ } }, "rc-cascader": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-0.17.1.tgz", - "integrity": "sha512-JED1iOLpj1+uob+0Asd4zwhhMRp3gLs2iYOY2/0OsdEsPc8Qj6TUwj8+isVtqyXiwGWG3vo8XgO6KCM/i7ZFqQ==", + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-0.17.4.tgz", + "integrity": "sha512-CeFQJIMzY7x++uPqlx4Xl/cH8iTs8nRoW522+DLb21kdL5kWqKlK+3iHXExoxcAymjwo5ScIiXi+NY4m8Pgq9w==", "requires": { "array-tree-filter": "^2.1.0", "prop-types": "^15.5.8", @@ -14415,20 +14430,20 @@ } }, "rc-checkbox": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-2.1.6.tgz", - "integrity": "sha512-+VxQbt2Cwe1PxCvwosrAYXT6EQeGwrbLJB2K+IPGCSRPCKnk9zcub/0eW8A4kxjyyfh60PkwsAUZ7qmB31OmRA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-2.1.7.tgz", + "integrity": "sha512-8L+0XuucUOMUM6F/7qH+hnQpEHPZfW1Um02lUHEVdpZNor5mC0Fj4x8GvTtwcM1pAl5tD3I6lHYD8cE1W8RZJw==", "requires": { "babel-runtime": "^6.23.0", "classnames": "2.x", "prop-types": "15.x", - "rc-util": "^4.0.4" + "react-lifecycles-compat": "^3.0.4" } }, "rc-collapse": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-1.10.3.tgz", - "integrity": "sha512-uGZpSwTnKw9lZ4ODTqU3u4d6OcdAG0uB6lwSRNDISVdsDX8oMXULULqvFyM8UrgOkQFqU6klch78hyVE8+gFfA==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-1.11.3.tgz", + "integrity": "sha512-yECQX2iDPWnKcVi3Wz5bomZuJ2u+wv+kGxuKo2GIRz7Brh9jkGQz5ElghCV1jqDGnzy8GIRxxHHSwlSgdxdUog==", "requires": { "classnames": "2.x", "css-animation": "1.x", @@ -14439,24 +14454,24 @@ } }, "rc-dialog": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-7.3.0.tgz", - "integrity": "sha512-YLQHqZuU0cO02LUwhCsCCtvSw24SKLrT4DkNHCNGGcH9YpZP/IOFaH4zVUmXGEQiwyt0D1f3volHthMCKzLzMg==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-7.5.2.tgz", + "integrity": "sha512-5Bvfhwv9qAo9bmQIGFLrh/jaEUQaalLBXEb2DpoC0m+CRgAyDbB1arP9v5zmypXki2Ffje4exZSei6kll1lxJw==", "requires": { "babel-runtime": "6.x", "rc-animate": "2.x", - "rc-util": "^4.4.0" + "rc-util": "^4.8.1" } }, "rc-drawer": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-1.7.7.tgz", - "integrity": "sha512-7dESNkClYdWGSdBdwcfeOz6DUCqzrW44QT013fsTBJIiWNLSLgDV5KoHKXG8VTJWU4mBn7M5Lqgyr94CRZcxGA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-2.0.3.tgz", + "integrity": "sha512-ubjc8fgPl7ZEsr1+jTnRt4ei8f6FHvw8UvZo+6g4MjJG6V1mNZBAl6eWXOo4DTeALK+7dySlitBmuI1hoCP4nQ==", "requires": { "babel-runtime": "6.x", "classnames": "^2.2.5", - "prop-types": "^15.5.0", - "rc-util": "^4.5.1" + "rc-util": "^4.7.0", + "react-lifecycles-compat": "^3.0.4" } }, "rc-dropdown": { @@ -14472,9 +14487,9 @@ } }, "rc-editor-core": { - "version": "0.8.9", - "resolved": "https://registry.npmjs.org/rc-editor-core/-/rc-editor-core-0.8.9.tgz", - "integrity": "sha512-fGTkTm96Kil/i9n5a3JwAzJcl2TkfjO1r1WBWf6NIOxXiJXpC3Lajkf3j6E5K7iz5AW0QRaSGnNQFBrwvXKKWA==", + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/rc-editor-core/-/rc-editor-core-0.8.10.tgz", + "integrity": "sha512-T3aHpeMCIYA1sdAI7ynHHjXy5fqp83uPlD68ovZ0oClTSc3tbHmyCxXlA+Ti4YgmcpCYv7avF6a+TIbAka53kw==", "requires": { "babel-runtime": "^6.26.0", "classnames": "^2.2.5", @@ -14486,26 +14501,26 @@ } }, "rc-editor-mention": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/rc-editor-mention/-/rc-editor-mention-1.1.12.tgz", - "integrity": "sha512-cPm2rQ7P+hXaKMsO0ajVv08QlTDcSPVtw8/lVr9D+QzQKRPChCqLw9rVGOa4YGYTeS3gVe8lBfLr8a9JKFk3gA==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/rc-editor-mention/-/rc-editor-mention-1.1.13.tgz", + "integrity": "sha512-3AOmGir91Fi2ogfRRaXLtqlNuIwQpvla7oUnGHS1+3eo7b+fUp5IlKcagqtwUBB5oDNofoySXkLBxzWvSYNp/Q==", "requires": { "babel-runtime": "^6.23.0", "classnames": "^2.2.5", "dom-scroll-into-view": "^1.2.0", "draft-js": "~0.10.0", - "immutable": "^3.7.4", + "immutable": "~3.7.4", "prop-types": "^15.5.8", "rc-animate": "^2.3.0", "rc-editor-core": "~0.8.3" } }, "rc-form": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/rc-form/-/rc-form-2.4.2.tgz", - "integrity": "sha512-1UeBuKBH9V5W9KxE48ecGAj5//kSt6F+AHB2vBxGRy1cZL+A5L9cKfI3HwsFpRa/ty6vaUAlGfq2xytQqN8enA==", + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/rc-form/-/rc-form-2.4.8.tgz", + "integrity": "sha512-hlHajcYg51pFQf+B6neAbhy2ZA+8DmxnDxiOYZRAXCLhPN788ZnrtZq5/iADDWcZqjHFnXiThoZE/Fu8syciDQ==", "requires": { - "async-validator": "~1.8.5", + "async-validator": "~1.11.3", "babel-runtime": "6.x", "create-react-class": "^15.5.3", "dom-scroll-into-view": "1.x", @@ -14525,22 +14540,35 @@ } }, "rc-input-number": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-4.3.9.tgz", - "integrity": "sha512-u2ioYqAwHIbDpp6sUEIOv8kqQw8e7VfTR6Kn+wJYff7TBA3Zw2Ql9I4DQniuLXglCJzDiHqG1OR5v3kN3GrD4w==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-4.4.5.tgz", + "integrity": "sha512-Dt20e8Ylc/N/6oXiPUlwDVdx3fz7W5umUOa4z5pBuWFG7NPlBVXRWkq7+nbnTyaK24UxN67PVpmD3+Omo+QRZQ==", "requires": { "babel-runtime": "6.x", "classnames": "^2.2.0", - "is-negative-zero": "^2.0.0", "prop-types": "^15.5.7", "rc-util": "^4.5.1", "rmc-feedback": "^2.0.0" } }, + "rc-mentions": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-0.3.1.tgz", + "integrity": "sha512-fa5dN3IMTahJfAga1nmma9OymK/ZBV/MZfV11h4kjDmCAVETv5EbAlV0mn6Y+JajvXS6n/XFoPUSF+nwK/AeWw==", + "requires": { + "@ant-design/create-react-context": "^0.2.4", + "babel-runtime": "^6.23.0", + "classnames": "^2.2.6", + "rc-menu": "^7.4.22", + "rc-trigger": "^2.6.2", + "rc-util": "^4.6.0", + "react-lifecycles-compat": "^3.0.4" + } + }, "rc-menu": { - "version": "7.4.21", - "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-7.4.21.tgz", - "integrity": "sha512-TfcwybKLuw2WhEkplYH7iFMGlDbH6KhPcd+gv5J2oLQcgiGeUECzyOWSVaFRRlkpB7g2eNzXbha/AXN/Xyzvnw==", + "version": "7.4.23", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-7.4.23.tgz", + "integrity": "sha512-d0pUMN0Zr3GCFxNpas8p7AUTeX8viItUOQXku4AsyX82ZzUz79HgGul2Nk17BIFTtLzqdB7/NT6WVb5PAOOILw==", "requires": { "babel-runtime": "6.x", "classnames": "2.x", @@ -14568,19 +14596,20 @@ } }, "rc-pagination": { - "version": "1.17.8", - "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-1.17.8.tgz", - "integrity": "sha512-duEV+K/b/nZNGr943+TMCEcY4xWkjAkpKW0Vr7fSR8wQk0DY7aTJC+k+vjl4X2EzEmPXqy85hibzpsO9vydKAw==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-1.20.4.tgz", + "integrity": "sha512-L5d965Rata4TN2lCX9517r90wrspMk1tmLSw4fj4u3xNhyEv9/LDbAzjhtb6iG7RZl+TzCSovcB10xsDcq25Yw==", "requires": { "babel-runtime": "6.x", + "classnames": "^2.2.6", "prop-types": "^15.5.7", "react-lifecycles-compat": "^3.0.4" } }, "rc-progress": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-2.3.0.tgz", - "integrity": "sha512-hYBKFSsNgD7jsF8j+ZC1J8y5UIC2X/ktCYI/OQhQNSX6mGV1IXnUCjAd9gbLmzmpChPvKyymRNfckScUNiTpFQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-2.5.1.tgz", + "integrity": "sha512-jIoCYAktSG1SvA7gu2jM7hYwb2JiBrqOexY80TBiIIznhMDHh1Mb7rvtfIoEF3WS5evoqoKa3A+szGMyv3J9Cw==", "requires": { "babel-runtime": "6.x", "prop-types": "^15.5.8" @@ -14598,9 +14627,9 @@ } }, "rc-select": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-8.8.4.tgz", - "integrity": "sha512-3ylsjhiUtEN4Tg6/ddg1wG0FXSmT/67LT2/KF8azaG6x/1sMNmSxoyi1nxVoFdmkCO5luDA5F1xCHCOxGuEj3A==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-9.2.1.tgz", + "integrity": "sha512-nW/Zr2OCgxN26OX8ff3xcO1wK0e1l5ixnEfyN15Rbdk7TNI/rIPJIjPCQAoihRpk9A2C/GH8pahjlvKV1Vj++g==", "requires": { "babel-runtime": "^6.23.0", "classnames": "2.x", @@ -14617,9 +14646,9 @@ } }, "rc-slider": { - "version": "8.6.6", - "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-8.6.6.tgz", - "integrity": "sha512-byfnq1LbBFyZ0HURWo22sjeiKIxLyzSnIiNUsUf6SWu1ZhQe/Qt24JnE/ZJsqKoUirXxlX+d577ptfAybZHm+Q==", + "version": "8.6.13", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-8.6.13.tgz", + "integrity": "sha512-fCUe8pPn8n9pq1ARX44nN2nzJoATtna4x/PdskUrxIvZXN8ja7HuceN/hq6kokZjo3FBD2B1yMZvZh6oi68l6Q==", "requires": { "babel-runtime": "6.x", "classnames": "^2.2.5", @@ -14631,9 +14660,9 @@ } }, "rc-steps": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-3.3.1.tgz", - "integrity": "sha512-LGzmPYS9ETePo+6YbHlFukCdcKppeBZXO49ZxewaC7Cba00q0zrMXlexquZ4fm+9iz0IkpzwgmenvjsVWCmGOw==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-3.4.1.tgz", + "integrity": "sha512-zdeOFmFqiXlXCQyHet1qrDDbGKZ7OQTrlzn8DP5N6M/WqN7HaYoUDy1fZ+NY2htL5WzzVFQpDRKzjiOiHaSqgw==", "requires": { "babel-runtime": "^6.23.0", "classnames": "^2.2.3", @@ -14652,9 +14681,9 @@ } }, "rc-table": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-6.4.3.tgz", - "integrity": "sha512-4/f7mS87EtNxM2vhIaA7I1J8hPZ5OiOQwmjac7RJTmGOFVA8PJDGwEipeyU/eC9RM7f3v4Lc+a05KCfIbRU4tg==", + "version": "6.6.8", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-6.6.8.tgz", + "integrity": "sha512-G/ICx/jVgWEpnXwVD+WWTm8BjATN0DAWtNFx1O1lxCVs9IY50fZy/GumBpwf91hnWFqlo96Dd/SN50P2O3EM0w==", "requires": { "babel-runtime": "6.x", "classnames": "^2.2.5", @@ -14679,9 +14708,9 @@ } }, "rc-tabs": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-9.6.1.tgz", - "integrity": "sha512-3/Ip9yCEERFFvCjU0ZoQqvn6unMo0XOQESygNLq1DyOAYRcukpq8Q28awpXWqh8l8NBcyw1sVfrs6SZN/zmAKg==", + "version": "9.6.4", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-9.6.4.tgz", + "integrity": "sha512-l4PoDSShNJ6pWGuR1UcUgvee48b3Qu1jgMEaD1hH3Rc+mqysoO7hA9AQ1YywkIy34afGTTejAWDSIFZ0lmg08g==", "requires": { "babel-runtime": "6.x", "classnames": "2.x", @@ -14691,6 +14720,7 @@ "raf": "^3.4.1", "rc-hammerjs": "~0.6.0", "rc-util": "^4.0.4", + "resize-observer-polyfill": "^1.5.1", "warning": "^3.0.0" }, "dependencies": { @@ -14705,13 +14735,14 @@ } }, "rc-time-picker": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/rc-time-picker/-/rc-time-picker-3.6.2.tgz", - "integrity": "sha512-SyGEVXO0ImeG2mz+7fkVmDoVM0+OrX6uYGpKYijNr/lAah7c5p310ZR6fVrblXOl4TpqVnfWR67RMJ3twAyM7w==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/rc-time-picker/-/rc-time-picker-3.7.1.tgz", + "integrity": "sha512-ULiLnal/0erk9LrPLcDMroPnqL/LBDT4gz9MzQgtc2QN6KBAOgGihHXZempSQTYCg575oAl+BNX5e1teKWOrjw==", "requires": { "classnames": "2.x", "moment": "2.x", "prop-types": "^15.5.8", + "raf": "^3.4.1", "rc-trigger": "^2.2.0" } }, @@ -14726,54 +14757,30 @@ } }, "rc-tree": { - "version": "1.14.10", - "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-1.14.10.tgz", - "integrity": "sha512-iOn7+SpWzM4OQoF/7wJeFiuRpBGJ3ndTe6YVGnfIhsWqDd7S6a7z0anDQcBpPsW/PvisjNDXr4zKchZvx+0iCA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-2.1.0.tgz", + "integrity": "sha512-DyHG/W9rW8cYfBrqVrZUep5yt30scyBuYvFnGrU32bh1DUj8GKqOcdoRBaIiOBYurmIiJ02rq6BeBbvVtVp0mw==", "requires": { "babel-runtime": "^6.23.0", "classnames": "2.x", "prop-types": "^15.5.8", - "rc-animate": "^3.0.0-rc.5", + "rc-animate": "^2.6.0", "rc-util": "^4.5.1", "react-lifecycles-compat": "^3.0.4", - "warning": "^3.0.0" - }, - "dependencies": { - "rc-animate": { - "version": "3.0.0-rc.6", - "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-3.0.0-rc.6.tgz", - "integrity": "sha512-oBLPpiT6Q4t6YvD/pkLcmofBP1p01TX0Otse8Q4+Mxt8J+VSDflLZGIgf62EwkvRwsQUkLPjZVFBsldnPKLzjg==", - "requires": { - "babel-runtime": "6.x", - "classnames": "^2.2.5", - "component-classes": "^1.2.6", - "fbjs": "^0.8.16", - "prop-types": "15.x", - "raf": "^3.4.0", - "rc-util": "^4.5.0", - "react-lifecycles-compat": "^3.0.4" - } - }, - "warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", - "requires": { - "loose-envify": "^1.0.0" - } - } + "warning": "^4.0.3" } }, "rc-tree-select": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-2.5.4.tgz", - "integrity": "sha512-7PVC3SV8X02cTZ9+VtNLM5dpYdIbB/xe6N2MVKFnc2YNgMx76sm+Fk2OrgbgicIK/lWAPkKTxnwvNk5PKR4a1g==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-2.9.1.tgz", + "integrity": "sha512-AfJQC1ZzaeH+Onmx84TtVLUL2guBZe7exA8XSfj1RRB1doDbYGTtybzpP3CEw/tuSftSRnz+iPt+iaxRTrgXRw==", "requires": { "classnames": "^2.2.1", + "dom-scroll-into-view": "^1.2.1", "prop-types": "^15.5.8", "raf": "^3.4.0", - "rc-animate": "^3.0.0-rc.4", - "rc-tree": "~1.14.3", + "rc-animate": "^2.8.2", + "rc-tree": "~2.0.0", "rc-trigger": "^3.0.0-rc.2", "rc-util": "^4.5.0", "react-lifecycles-compat": "^3.0.4", @@ -14781,19 +14788,28 @@ "warning": "^4.0.1" }, "dependencies": { - "rc-animate": { - "version": "3.0.0-rc.6", - "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-3.0.0-rc.6.tgz", - "integrity": "sha512-oBLPpiT6Q4t6YvD/pkLcmofBP1p01TX0Otse8Q4+Mxt8J+VSDflLZGIgf62EwkvRwsQUkLPjZVFBsldnPKLzjg==", - "requires": { - "babel-runtime": "6.x", - "classnames": "^2.2.5", - "component-classes": "^1.2.6", - "fbjs": "^0.8.16", - "prop-types": "15.x", - "raf": "^3.4.0", - "rc-util": "^4.5.0", - "react-lifecycles-compat": "^3.0.4" + "rc-tree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-2.0.0.tgz", + "integrity": "sha512-DAT/jsbnFbHqG9Df9OaVG93CAVtTsJVnJiwKX+wqsG8TChpty3s6QX3zJZ+gBgjkq4ikLbu1kuFJtX63EKhSAA==", + "requires": { + "babel-runtime": "^6.23.0", + "classnames": "2.x", + "prop-types": "^15.5.8", + "rc-animate": "^2.6.0", + "rc-util": "^4.5.1", + "react-lifecycles-compat": "^3.0.4", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } } }, "rc-trigger": { @@ -14808,27 +14824,45 @@ "rc-align": "^2.4.1", "rc-animate": "^3.0.0-rc.1", "rc-util": "^4.4.0" + }, + "dependencies": { + "rc-animate": { + "version": "3.0.0-rc.6", + "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-3.0.0-rc.6.tgz", + "integrity": "sha512-oBLPpiT6Q4t6YvD/pkLcmofBP1p01TX0Otse8Q4+Mxt8J+VSDflLZGIgf62EwkvRwsQUkLPjZVFBsldnPKLzjg==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.5", + "component-classes": "^1.2.6", + "fbjs": "^0.8.16", + "prop-types": "15.x", + "raf": "^3.4.0", + "rc-util": "^4.5.0", + "react-lifecycles-compat": "^3.0.4" + } + } } } } }, "rc-trigger": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-2.6.2.tgz", - "integrity": "sha512-op4xCu95/gdHVaysyxxiYxbY+Z+UcIBSUY9nQfLqm1FlitdtnAN+owD5iMPfnnsRXntgcQ5+RdYKNUFQT5DjzA==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-2.6.5.tgz", + "integrity": "sha512-m6Cts9hLeZWsTvWnuMm7oElhf+03GOjOLfTuU0QmdB9ZrW7jR2IpI5rpNM7i9MvAAlMAmTx5Zr7g3uu/aMvZAw==", "requires": { "babel-runtime": "6.x", "classnames": "^2.2.6", "prop-types": "15.x", "rc-align": "^2.4.0", "rc-animate": "2.x", - "rc-util": "^4.4.0" + "rc-util": "^4.4.0", + "react-lifecycles-compat": "^3.0.4" } }, "rc-upload": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-2.6.3.tgz", - "integrity": "sha512-wM57UH/EEqW2/EcWz5nwnU07d4LHDHjBgxRin2Q56TW9JcFVnaQVq/JHycVFumsgSFV5CZfNW8PBROsKT9VFMw==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-2.6.8.tgz", + "integrity": "sha512-Uz7hys+FdIfS8qIm+VnCUZ21sY+/VaCjyMKw6cANmgBIbemFNOxEPEfgEBZH1YHt89HCGNPSpIK976ndsuc2YQ==", "requires": { "babel-runtime": "6.x", "classnames": "^2.2.5", @@ -14837,13 +14871,14 @@ } }, "rc-util": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.6.0.tgz", - "integrity": "sha512-rbgrzm1/i8mgfwOI4t1CwWK7wGe+OwX+dNa7PVMgxZYPBADGh86eD4OcJO1UKGeajIMDUUKMluaZxvgraQIOmw==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.8.4.tgz", + "integrity": "sha512-1B2h0/pMXfSUBRAgPdoDIKK5XBuzLBuLI9rLwUEW163SPoDvfb9jmg3ymBPtzne2jWgwtdNw4j0vIq/8Yo849A==", "requires": { "add-dom-event-listener": "^1.1.0", "babel-runtime": "6.x", "prop-types": "^15.5.10", + "react-lifecycles-compat": "^3.0.4", "shallowequal": "^0.2.2" }, "dependencies": { @@ -14942,15 +14977,14 @@ } }, "react-slick": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.23.2.tgz", - "integrity": "sha512-fM6DXX7+22eOcYE9cgaXUfioZL/Zw6fwS6aPMDBt0kLHl4H4fFNEbp4JsJQdEWMLUNFtUytNcvd9KRml22Tp5w==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.24.0.tgz", + "integrity": "sha512-Pvo0B74ohumQdYOf0qP+pdQpj9iUbAav7+2qiF3uTc5XeQp/Y/cnIeDBM2tB3txthfSe05jKIqLMJTS6qVvt5g==", "requires": { "classnames": "^2.2.5", "enquire.js": "^2.1.6", "json2mq": "^0.2.0", "lodash.debounce": "^4.0.8", - "prettier": "^1.14.3", "resize-observer-polyfill": "^1.5.0" } }, @@ -16085,9 +16119,9 @@ "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=" }, "shallow-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.1.0.tgz", - "integrity": "sha512-0SW1nWo1hnabO62SEeHsl8nmTVVEzguVWZCj5gaQrgWAxz/BaCja4OWdJBWLVPDxdtE/WU7c98uUCCXyPHSCvw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.0.tgz", + "integrity": "sha512-Z21pVxR4cXsfwpMKMhCEIO1PCi5sp7KEp+CmOpBQ+E8GpHwKOw2sEzk7sgblM3d/j4z4gakoWEoPcjK0VJQogA==" }, "shallowequal": { "version": "1.1.0", @@ -17669,6 +17703,11 @@ "to-array-buffer": "^3.0.0" } }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, "topojson-client": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-2.1.0.tgz", @@ -17817,9 +17856,9 @@ } }, "ua-parser-js": { - "version": "0.7.19", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", - "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==" + "version": "0.7.20", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz", + "integrity": "sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw==" }, "uglify-js": { "version": "2.8.29", diff --git a/package.json b/package.json index 6aaf31780e..8b42a12c4f 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "angular-ui-bootstrap": "^2.5.0", "angular-vs-repeat": "^1.1.7", "angular2react": "^3.0.2", - "antd": "^3.12.3", + "antd": "^3.20.7", "beautifymarker": "^1.0.7", "bootstrap": "^3.3.7", "brace": "^0.11.0", From 1d51a66f69e48f6629064fd0f7895bede85cb23f Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Fri, 26 Jul 2019 00:30:19 -0300 Subject: [PATCH 06/32] Add Cmd/Ctrl + Enter behavior --- client/app/components/Parameters.jsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index f24996b443..1ea581222e 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -69,7 +69,7 @@ export class Parameters extends React.Component { }); }; - onApply = () => { + applyChanges = () => { const { onUpdate, onValuesChange } = this.props; this.setState(({ parameters }) => { forEach(parameters, p => p.applyPendingValue()); @@ -92,6 +92,17 @@ export class Parameters extends React.Component { }); }; + handleKeyDown = (e) => { + const { parameters } = this.props; + const dirtyParamCount = size(filter(parameters, 'hasPendingValue')); + + // Cmd/Ctrl + Enter + if (dirtyParamCount > 0 && e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { + e.stopPropagation(); + this.applyChanges(); + } + }; + renderParameter(param, index) { const { editable } = this.props; return ( @@ -131,14 +142,14 @@ export class Parameters extends React.Component { const dirtyParamCount = size(filter(parameters, 'hasPendingValue')); return ( -
+
{parameters.map((param, index) => ( {this.renderParameter(param, index)} ))} - +
); From 0ede83ac9354fdece1cec04a6e1e1341e4bae598 Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Fri, 26 Jul 2019 00:36:51 -0300 Subject: [PATCH 07/32] Remove isApplying --- client/app/components/ParameterApplyButton.jsx | 7 +++---- client/app/components/Parameters.jsx | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/client/app/components/ParameterApplyButton.jsx b/client/app/components/ParameterApplyButton.jsx index f9bee75f4f..b0d097b660 100644 --- a/client/app/components/ParameterApplyButton.jsx +++ b/client/app/components/ParameterApplyButton.jsx @@ -6,9 +6,9 @@ import Badge from 'antd/lib/badge'; import Tooltip from 'antd/lib/tooltip'; import { KeyboardShortcuts } from '@/services/keyboard-shortcuts'; -export function ParameterApplyButton({ paramCount, onClick, isApplying }) { - // show spinner when applying (also when count is empty so the fade out is consistent) - const icon = isApplying || !paramCount ? 'spinner fa-pulse' : 'check'; +export function ParameterApplyButton({ paramCount, onClick }) { + // show spinner when count is empty so the fade out is consistent + const icon = !paramCount ? 'spinner fa-pulse' : 'check'; return (
@@ -28,7 +28,6 @@ export function ParameterApplyButton({ paramCount, onClick, isApplying }) { ParameterApplyButton.propTypes = { onClick: PropTypes.func.isRequired, paramCount: PropTypes.number.isRequired, - isApplying: PropTypes.bool.isRequired, }; export default function init(ngModule) { diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index 1ea581222e..f6c2228eb5 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -149,7 +149,7 @@ export class Parameters extends React.Component { ))} - +
); From cf4995291b3f5e22f28ca62ce24d541f84d42b6e Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Fri, 26 Jul 2019 00:37:54 -0300 Subject: [PATCH 08/32] Delete Angular version of parameters --- client/app/components/parameters.html | 24 ------- client/app/components/parameters.js | 97 --------------------------- 2 files changed, 121 deletions(-) delete mode 100644 client/app/components/parameters.html delete mode 100644 client/app/components/parameters.js diff --git a/client/app/components/parameters.html b/client/app/components/parameters.html deleted file mode 100644 index 3e493ea188..0000000000 --- a/client/app/components/parameters.html +++ /dev/null @@ -1,24 +0,0 @@ -
-
- - - -
- -
diff --git a/client/app/components/parameters.js b/client/app/components/parameters.js deleted file mode 100644 index 46640c3828..0000000000 --- a/client/app/components/parameters.js +++ /dev/null @@ -1,97 +0,0 @@ -import { extend, filter, forEach, size } from 'lodash'; -import template from './parameters.html'; -import EditParameterSettingsDialog from './EditParameterSettingsDialog'; - -function ParametersDirective($location, KeyboardShortcuts) { - return { - restrict: 'E', - transclude: true, - scope: { - parameters: '=', - syncValues: '=?', - editable: '=?', - changed: '&onChange', - onUpdated: '=', - onValuesChange: '=', - applyOnKeyboardShortcut: ' scope.onApply(), - 'alt+enter': () => scope.onApply(), - }; - - const onFocus = () => { KeyboardShortcuts.bind(shortcuts); }; - const onBlur = () => { KeyboardShortcuts.unbind(shortcuts); }; - - el.addEventListener('focus', onFocus, true); - el.addEventListener('blur', onBlur, true); - - scope.$on('$destroy', () => { - KeyboardShortcuts.unbind(shortcuts); - el.removeEventListener('focus', onFocus); - el.removeEventListener('blur', onBlur); - }); - - // is this the correct location for this logic? - if (scope.syncValues !== false) { - scope.$watch( - 'parameters', - () => { - if (scope.changed) { - scope.changed({}); - } - const params = extend({}, $location.search()); - scope.parameters.forEach((param) => { - extend(params, param.toUrlParams()); - }); - $location.search(params); - }, - true, - ); - } - - scope.showParameterSettings = (parameter, index) => { - EditParameterSettingsDialog - .showModal({ parameter }) - .result.then((updated) => { - scope.parameters[index] = extend(parameter, updated); - scope.onUpdated(); - }); - }; - - scope.dirtyParamCount = 0; - scope.$watch( - 'parameters', - () => { - scope.dirtyParamCount = size(filter(scope.parameters, 'hasPendingValue')); - }, - true, - ); - - scope.isApplying = false; - scope.applyChanges = () => { - scope.isApplying = true; - forEach(scope.parameters, p => p.applyPendingValue()); - scope.isApplying = false; - }; - - scope.onApply = () => { - if (!scope.dirtyParamCount) { - return false; // so keyboard shortcut doesn't run needlessly - } - - scope.$apply(scope.applyChanges); - scope.onValuesChange(); - }; - }, - }; -} - -export default function init(ngModule) { - ngModule.directive('parameters', ParametersDirective); -} - -// init.init = true; From f2ab142c63301e1c246cab6e0fe30c3dcfb76c3e Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Fri, 26 Jul 2019 16:07:23 -0300 Subject: [PATCH 09/32] Update tests --- client/app/components/Parameters.jsx | 13 ++-- .../__snapshots__/ScheduleDialog.test.js.snap | 71 +++++-------------- 2 files changed, 25 insertions(+), 59 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index f6c2228eb5..1c6a76883c 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -60,10 +60,14 @@ export class Parameters extends React.Component { }); }; - onSelect = (param, value) => { + onSelect = (param, value, isDirty) => { const { onUpdate } = this.props; this.setState(({ parameters }) => { - param.setPendingValue(value); + if (isDirty) { + param.setPendingValue(value); + } else { + param.clearPendingValue(); + } onUpdate(parameters); return { parameters }; }); @@ -97,7 +101,7 @@ export class Parameters extends React.Component { const dirtyParamCount = size(filter(parameters, 'hasPendingValue')); // Cmd/Ctrl + Enter - if (dirtyParamCount > 0 && e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { + if (dirtyParamCount > 0 && e.keyCode === 13 && (e.ctrlKey || e.metaKey || e.altKey)) { e.stopPropagation(); this.applyChanges(); } @@ -119,6 +123,7 @@ export class Parameters extends React.Component { type="link" size="small" onClick={() => this.showParameterSettings(param, index)} + data-test={`ParameterSettings-${param.name}`} > @@ -130,7 +135,7 @@ export class Parameters extends React.Component { parameter={param} enumOptions={param.enumOptions} queryI={param.queryId} - onSelect={value => this.onSelect(param, value)} + onSelect={(value, isDirty) => this.onSelect(param, value, isDirty)} />
); diff --git a/client/app/components/queries/__snapshots__/ScheduleDialog.test.js.snap b/client/app/components/queries/__snapshots__/ScheduleDialog.test.js.snap index ffdf31b3af..35e2464828 100644 --- a/client/app/components/queries/__snapshots__/ScheduleDialog.test.js.snap +++ b/client/app/components/queries/__snapshots__/ScheduleDialog.test.js.snap @@ -74,7 +74,6 @@ exports[`ScheduleDialog Sets correct schedule settings Sets to "1 Day 22:15" Set "click", ] } - showArrow={true} showSearch={false} tabIndex={0} tags={false} @@ -312,6 +311,7 @@ exports[`ScheduleDialog Sets correct schedule settings Sets to "1 Day 22:15" Set className="" data-icon="down" fill="currentColor" + focusable="false" height="1em" key="svg-down" viewBox="64 64 896 896" @@ -354,7 +354,6 @@ exports[`ScheduleDialog Sets correct schedule settings Sets to "1 Day 22:15" Set } } allowClear={false} - disabled={false} focusOnOpen={true} format="HH:mm" hideDisabledOptions={false} @@ -394,7 +393,6 @@ exports[`ScheduleDialog Sets correct schedule settings Sets to "1 Day 22:15" Set clearText="clear" defaultOpen={false} defaultOpenValue={"2000-01-01T00:00:00.000Z"} - disabled={false} disabledHours={[Function]} disabledMinutes={[Function]} disabledSeconds={[Function]} @@ -533,7 +531,6 @@ exports[`ScheduleDialog Sets correct schedule settings Sets to "1 Day 22:15" Set popup={ @@ -1678,7 +1671,6 @@ exports[`ScheduleDialog Sets correct schedule settings Sets to "2 Weeks 22:15 Tu Date: Fri, 26 Jul 2019 16:11:01 -0300 Subject: [PATCH 10/32] Remove angular stuff --- .../app/components/ParameterApplyButton.jsx | 9 ++--- .../app/components/ParameterMappingInput.jsx | 2 +- client/app/components/ParameterValueInput.jsx | 34 ++----------------- client/app/components/Parameters.jsx | 4 +-- 4 files changed, 7 insertions(+), 42 deletions(-) diff --git a/client/app/components/ParameterApplyButton.jsx b/client/app/components/ParameterApplyButton.jsx index b0d097b660..c41ec3898d 100644 --- a/client/app/components/ParameterApplyButton.jsx +++ b/client/app/components/ParameterApplyButton.jsx @@ -1,12 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { react2angular } from 'react2angular'; import Button from 'antd/lib/button'; import Badge from 'antd/lib/badge'; import Tooltip from 'antd/lib/tooltip'; import { KeyboardShortcuts } from '@/services/keyboard-shortcuts'; -export function ParameterApplyButton({ paramCount, onClick }) { +function ParameterApplyButton({ paramCount, onClick }) { // show spinner when count is empty so the fade out is consistent const icon = !paramCount ? 'spinner fa-pulse' : 'check'; @@ -30,8 +29,4 @@ ParameterApplyButton.propTypes = { paramCount: PropTypes.number.isRequired, }; -export default function init(ngModule) { - ngModule.component('parameterApplyButton', react2angular(ParameterApplyButton)); -} - -init.init = true; +export default ParameterApplyButton; diff --git a/client/app/components/ParameterMappingInput.jsx b/client/app/components/ParameterMappingInput.jsx index ad455b8a6b..0af383c6d2 100644 --- a/client/app/components/ParameterMappingInput.jsx +++ b/client/app/components/ParameterMappingInput.jsx @@ -14,7 +14,7 @@ import Input from 'antd/lib/input'; import Radio from 'antd/lib/radio'; import Form from 'antd/lib/form'; import Tooltip from 'antd/lib/tooltip'; -import { ParameterValueInput } from '@/components/ParameterValueInput'; +import ParameterValueInput from '@/components/ParameterValueInput'; import { ParameterMappingType } from '@/services/widget'; import { Parameter } from '@/services/query'; import { HelpTrigger } from '@/components/HelpTrigger'; diff --git a/client/app/components/ParameterValueInput.jsx b/client/app/components/ParameterValueInput.jsx index 8ea1c76a8d..a59c8bef67 100644 --- a/client/app/components/ParameterValueInput.jsx +++ b/client/app/components/ParameterValueInput.jsx @@ -1,6 +1,5 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { react2angular } from 'react2angular'; import Select from 'antd/lib/select'; import Input from 'antd/lib/input'; import InputNumber from 'antd/lib/input-number'; @@ -15,7 +14,7 @@ import './ParameterValueInput.less'; const { Option } = Select; -export class ParameterValueInput extends React.Component { +class ParameterValueInput extends React.Component { static propTypes = { type: PropTypes.string, value: PropTypes.any, // eslint-disable-line react/forbid-prop-types @@ -217,33 +216,4 @@ export class ParameterValueInput extends React.Component { } } -export default function init(ngModule) { - ngModule.component('parameterValueInput', { - template: ` - - `, - bindings: { - param: '<', - }, - controller($scope) { - this.setValue = (value, isDirty) => { - if (isDirty) { - this.param.setPendingValue(value); - } else { - this.param.clearPendingValue(); - } - $scope.$apply(); - }; - }, - }); - ngModule.component('parameterValueInputImpl', react2angular(ParameterValueInput)); -} - -init.init = true; +export default ParameterValueInput; diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index 1c6a76883c..2fddd4af44 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -5,8 +5,8 @@ import { react2angular } from 'react2angular'; import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'; import Button from 'antd/lib/button'; import { Parameter } from '@/services/query'; -import { ParameterApplyButton } from '@/components/ParameterApplyButton'; -import { ParameterValueInput } from '@/components/ParameterValueInput'; +import ParameterApplyButton from '@/components/ParameterApplyButton'; +import ParameterValueInput from '@/components/ParameterValueInput'; import EditParameterSettingsDialog from './EditParameterSettingsDialog'; import './Parameters.less'; From 3fb3e3fc30d1ebbf7e88fb1fbad4f99250961380 Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Fri, 26 Jul 2019 17:14:57 -0300 Subject: [PATCH 11/32] Update jest --- .../__snapshots__/ScheduleDialog.test.js.snap | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/client/app/components/queries/__snapshots__/ScheduleDialog.test.js.snap b/client/app/components/queries/__snapshots__/ScheduleDialog.test.js.snap index 35e2464828..2108b9fccb 100644 --- a/client/app/components/queries/__snapshots__/ScheduleDialog.test.js.snap +++ b/client/app/components/queries/__snapshots__/ScheduleDialog.test.js.snap @@ -354,6 +354,7 @@ exports[`ScheduleDialog Sets correct schedule settings Sets to "1 Day 22:15" Set } } allowClear={false} + disabled={false} focusOnOpen={true} format="HH:mm" hideDisabledOptions={false} @@ -393,6 +394,7 @@ exports[`ScheduleDialog Sets correct schedule settings Sets to "1 Day 22:15" Set clearText="clear" defaultOpen={false} defaultOpenValue={"2000-01-01T00:00:00.000Z"} + disabled={false} disabledHours={[Function]} disabledMinutes={[Function]} disabledSeconds={[Function]} @@ -587,6 +589,7 @@ exports[`ScheduleDialog Sets correct schedule settings Sets to "1 Day 22:15" Set > @@ -1671,6 +1678,7 @@ exports[`ScheduleDialog Sets correct schedule settings Sets to "2 Weeks 22:15 Tu Date: Mon, 29 Jul 2019 13:14:40 -0300 Subject: [PATCH 12/32] Drag placeholder --- client/app/components/Parameters.jsx | 16 +++++++++------- client/app/components/Parameters.less | 12 ++++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index 2fddd4af44..3d6d964f91 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -11,11 +11,13 @@ import EditParameterSettingsDialog from './EditParameterSettingsDialog'; import './Parameters.less'; -const DragHandle = sortableHandle(() =>
); +const DragHandle = sortableHandle(({ parameterName }) => ( +
+)); -const SortableItem = sortableElement(({ disabled, children }) => ( -
- {!disabled && } +const SortableItem = sortableElement(({ className, parameterName, disabled, children }) => ( +
+ {!disabled && } {children}
)); @@ -146,10 +148,10 @@ export class Parameters extends React.Component { const { editable } = this.props; const dirtyParamCount = size(filter(parameters, 'hasPendingValue')); return ( - -
+ +
{parameters.map((param, index) => ( - + {this.renderParameter(param, index)} ))} diff --git a/client/app/components/Parameters.less b/client/app/components/Parameters.less index a94c820267..8713e58cfb 100644 --- a/client/app/components/Parameters.less +++ b/client/app/components/Parameters.less @@ -14,6 +14,18 @@ cursor: move; } +.parameter-container { + background-color: #f6f8f9; + display: inline; + padding: 35px 0 11px; +} + +.parameter { + display: inline-block; + background: white; + padding: 6px; +} + .parameter-settings-button { color: @text-color !important; From 2c737265d2fc865be35c687925f088d884f3b93a Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Mon, 29 Jul 2019 13:28:14 -0300 Subject: [PATCH 13/32] Update events --- client/app/components/Parameters.jsx | 29 +++++++++++++--------------- client/app/pages/queries/query.html | 2 +- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index 3d6d964f91..b2eaded9dc 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -27,17 +27,15 @@ export class Parameters extends React.Component { static propTypes = { parameters: PropTypes.arrayOf(PropTypes.instanceOf(Parameter)), editable: PropTypes.bool, - onUpdate: PropTypes.func, onValuesChange: PropTypes.func, - onParameterEdited: PropTypes.func, + onParametersEdit: PropTypes.func, }; static defaultProps = { parameters: [], editable: false, - onUpdate: () => {}, onValuesChange: () => {}, - onParameterEdited: () => {}, + onParametersEdit: () => {}, } constructor(props) { @@ -54,45 +52,44 @@ export class Parameters extends React.Component { }; onSortEnd = ({ oldIndex, newIndex }) => { - const { onUpdate } = this.props; - this.setState(({ parameters }) => { - parameters.splice(newIndex, 0, parameters.splice(oldIndex, 1)[0]); - onUpdate(parameters); - return { parameters }; - }); + const { onParametersEdit } = this.props; + if (oldIndex !== newIndex) { + this.setState(({ parameters }) => { + parameters.splice(newIndex, 0, parameters.splice(oldIndex, 1)[0]); + onParametersEdit(); + return { parameters }; + }); + } }; onSelect = (param, value, isDirty) => { - const { onUpdate } = this.props; this.setState(({ parameters }) => { if (isDirty) { param.setPendingValue(value); } else { param.clearPendingValue(); } - onUpdate(parameters); return { parameters }; }); }; applyChanges = () => { - const { onUpdate, onValuesChange } = this.props; + const { onValuesChange } = this.props; this.setState(({ parameters }) => { forEach(parameters, p => p.applyPendingValue()); - onUpdate(parameters); onValuesChange(); return { parameters }; }); }; showParameterSettings = (parameter, index) => { - const { onParameterEdited } = this.props; + const { onParametersEdit } = this.props; EditParameterSettingsDialog .showModal({ parameter }) .result.then((updated) => { this.setState(({ parameters }) => { parameters[index] = extend(parameter, updated); - onParameterEdited(parameters[index]); + onParametersEdit(); return { parameters }; }); }); diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html index 1c04d2cb23..99c35871e6 100644 --- a/client/app/pages/queries/query.html +++ b/client/app/pages/queries/query.html @@ -192,7 +192,7 @@

+ on-values-change="executeQuery" on-parameters-edit="onParametersUpdated">
From 9f0d1ab8bf165c0d022840c1bba9a1831f468af4 Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Mon, 29 Jul 2019 13:52:47 -0300 Subject: [PATCH 14/32] Use old button styling and move css --- .../app/components/ParameterValueInput.less | 62 --------------- client/app/components/Parameters.jsx | 14 ++-- client/app/components/Parameters.less | 76 ++++++++++++++++--- 3 files changed, 72 insertions(+), 80 deletions(-) diff --git a/client/app/components/ParameterValueInput.less b/client/app/components/ParameterValueInput.less index fb99a9542f..968134a083 100644 --- a/client/app/components/ParameterValueInput.less +++ b/client/app/components/ParameterValueInput.less @@ -18,65 +18,3 @@ } } } - -.parameter-container { - position: relative; - - .parameter-apply-button { - display: none; // default for mobile - - // "floating" on desktop - @media (min-width: 768px) { - position: absolute; - bottom: -42px; - left: -15px; - border-radius: 2px; - z-index: 1; - transition: opacity 150ms ease-out; - box-shadow: 0 4px 9px -3px rgba(102, 136, 153, 0.15); - background-color: #ffffff; - padding: 4px; - padding-left: 16px; - opacity: 0; - display: block; - pointer-events: none; // so tooltip doesn't remain after button hides - } - - &[data-show="true"] { - opacity: 1; - display: block; - pointer-events: auto; - } - - button { - padding: 0 8px 0 6px; - color: #2096f3; - border-color: #50acf6; - - // smaller on desktop - @media (min-width: 768px) { - font-size: 12px; - height: 27px; - } - - &:hover, &:focus, &:active { - background-color: #eef7fe; - } - - i { - margin-right: 3px; - } - } - - .ant-badge-count { - min-width: 15px; - height: 15px; - padding: 0 5px; - font-size: 10px; - line-height: 15px; - background: #f77b74; - border-radius: 7px; - box-shadow: 0px 0px 0 1px white, -1px 1px 0 1px #5d6f7d85; - } - } -} diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index b2eaded9dc..7ce9dfda63 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import { isEqual, size, filter, forEach, extend } from 'lodash'; import { react2angular } from 'react2angular'; import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'; -import Button from 'antd/lib/button'; import { Parameter } from '@/services/query'; import ParameterApplyButton from '@/components/ParameterApplyButton'; import ParameterValueInput from '@/components/ParameterValueInput'; @@ -114,18 +113,17 @@ export class Parameters extends React.Component { className="di-block" data-test={`ParameterName-${param.name}`} > -
+
{editable && ( - + )}
{parameters.map((param, index) => ( - + {this.renderParameter(param, index)} ))} diff --git a/client/app/components/Parameters.less b/client/app/components/Parameters.less index 8713e58cfb..146417c064 100644 --- a/client/app/components/Parameters.less +++ b/client/app/components/Parameters.less @@ -14,22 +14,78 @@ cursor: move; } +.parameter-block { + display: inline-block; + background: white; + padding: 6px; +} + +.parameter-heading { + display: flex; + margin-bottom: 2px; +} + .parameter-container { + position: relative; background-color: #f6f8f9; display: inline; padding: 35px 0 11px; -} -.parameter { - display: inline-block; - background: white; - padding: 6px; -} + .parameter-apply-button { + display: none; // default for mobile + + // "floating" on desktop + @media (min-width: 768px) { + position: absolute; + bottom: -42px; + left: -15px; + border-radius: 2px; + z-index: 1; + transition: opacity 150ms ease-out; + box-shadow: 0 4px 9px -3px rgba(102, 136, 153, 0.15); + background-color: #ffffff; + padding: 4px; + padding-left: 16px; + opacity: 0; + display: block; + pointer-events: none; // so tooltip doesn't remain after button hides + } + + &[data-show="true"] { + opacity: 1; + display: block; + pointer-events: auto; + } + + button { + padding: 0 8px 0 6px; + color: #2096f3; + border-color: #50acf6; + + // smaller on desktop + @media (min-width: 768px) { + font-size: 12px; + height: 27px; + } + + &:hover, &:focus, &:active { + background-color: #eef7fe; + } -.parameter-settings-button { - color: @text-color !important; + i { + margin-right: 3px; + } + } - &:hover { - color: @primary-color !important; + .ant-badge-count { + min-width: 15px; + height: 15px; + padding: 0 5px; + font-size: 10px; + line-height: 15px; + background: #f77b74; + border-radius: 7px; + box-shadow: 0px 0px 0 1px white, -1px 1px 0 1px #5d6f7d85; + } } } From 0b928c061af0c805fe6746218d406bdf136a3165 Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Mon, 29 Jul 2019 16:01:48 -0300 Subject: [PATCH 15/32] Reviewing code --- client/app/components/Parameters.jsx | 55 ++++++++++++++-------------- client/app/pages/queries/view.js | 4 -- client/app/services/query.js | 13 +++++-- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index 7ce9dfda63..7351223f01 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { isEqual, size, filter, forEach, extend } from 'lodash'; +import { size, filter, forEach, extend } from 'lodash'; import { react2angular } from 'react2angular'; import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'; import { Parameter } from '@/services/query'; @@ -45,23 +45,23 @@ export class Parameters extends React.Component { componentDidUpdate = (prevProps) => { const { parameters } = this.props; - if (!isEqual(prevProps.parameters, parameters)) { + if (prevProps.parameters !== parameters) { this.setState({ parameters }); } }; - onSortEnd = ({ oldIndex, newIndex }) => { - const { onParametersEdit } = this.props; - if (oldIndex !== newIndex) { - this.setState(({ parameters }) => { - parameters.splice(newIndex, 0, parameters.splice(oldIndex, 1)[0]); - onParametersEdit(); - return { parameters }; - }); + handleKeyDown = (e) => { + const { parameters } = this.props; + const dirtyParamCount = size(filter(parameters, 'hasPendingValue')); + + // Cmd/Ctrl/Alt + Enter + if (dirtyParamCount > 0 && e.keyCode === 13 && (e.ctrlKey || e.metaKey || e.altKey)) { + e.stopPropagation(); + this.applyChanges(); } }; - onSelect = (param, value, isDirty) => { + setPendingValue = (param, value, isDirty) => { this.setState(({ parameters }) => { if (isDirty) { param.setPendingValue(value); @@ -72,6 +72,17 @@ export class Parameters extends React.Component { }); }; + moveParameter = ({ oldIndex, newIndex }) => { + const { onParametersEdit } = this.props; + if (oldIndex !== newIndex) { + this.setState(({ parameters }) => { + parameters.splice(newIndex, 0, parameters.splice(oldIndex, 1)[0]); + onParametersEdit(); + return { parameters }; + }); + } + }; + applyChanges = () => { const { onValuesChange } = this.props; this.setState(({ parameters }) => { @@ -87,24 +98,14 @@ export class Parameters extends React.Component { .showModal({ parameter }) .result.then((updated) => { this.setState(({ parameters }) => { - parameters[index] = extend(parameter, updated); + const updatedParameter = extend(parameter, updated); + parameters[index] = new Parameter(updatedParameter, updatedParameter.parentQueryId); onParametersEdit(); return { parameters }; }); }); }; - handleKeyDown = (e) => { - const { parameters } = this.props; - const dirtyParamCount = size(filter(parameters, 'hasPendingValue')); - - // Cmd/Ctrl + Enter - if (dirtyParamCount > 0 && e.keyCode === 13 && (e.ctrlKey || e.metaKey || e.altKey)) { - e.stopPropagation(); - this.applyChanges(); - } - }; - renderParameter(param, index) { const { editable } = this.props; return ( @@ -117,7 +118,7 @@ export class Parameters extends React.Component { {editable && (
); @@ -143,7 +144,7 @@ export class Parameters extends React.Component { const { editable } = this.props; const dirtyParamCount = size(filter(parameters, 'hasPendingValue')); return ( - +
{parameters.map((param, index) => ( diff --git a/client/app/pages/queries/view.js b/client/app/pages/queries/view.js index 0ffd584399..b0cd1a0ac1 100644 --- a/client/app/pages/queries/view.js +++ b/client/app/pages/queries/view.js @@ -329,10 +329,6 @@ function QueryViewCtrl( Events.record('cancel_execute', 'query', $scope.query.id); }; - $scope.onUpdateParameters = (parameters) => { - $scope.$apply(() => { $scope.query.options.parameters = parameters; }); - }; - $scope.archiveQuery = () => { function archive() { Query.delete( diff --git a/client/app/services/query.js b/client/app/services/query.js index 6669106da6..127875ffeb 100644 --- a/client/app/services/query.js +++ b/client/app/services/query.js @@ -94,6 +94,10 @@ function collectParams(parts) { return parameters; } +function isEmptyValue(value) { + return isNull(value) || isUndefined(value) || (value === ''); +} + function isDateParameter(paramType) { return includes(['date', 'datetime-local', 'datetime-with-seconds'], paramType); } @@ -190,7 +194,6 @@ export class Parameter { static getValue(param) { const { value, type, useCurrentDateTime } = param; - const isEmptyValue = isNull(value) || isUndefined(value) || (value === ''); if (isDateRangeParameter(type) && param.hasDynamicValue) { const { dynamicValue } = param; if (dynamicValue) { @@ -211,7 +214,7 @@ export class Parameter { return null; } - if (isEmptyValue) { + if (isEmptyValue(value)) { // keep support for existing useCurentDateTime (not available in UI) if ( includes(['date', 'datetime-local', 'datetime-with-seconds'], type) && @@ -303,7 +306,11 @@ export class Parameter { } get hasPendingValue() { - return this.pendingValue !== undefined && this.pendingValue !== this.value; + // normalize empty values + const pendingValue = isEmptyValue(this.pendingValue) ? null : this.pendingValue; + const value = isEmptyValue(this.value) ? null : this.value; + + return this.pendingValue !== undefined && pendingValue !== value; } get normalizedValue() { From 00d633d53a4efdcb9d49b373628f56c102186510 Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Mon, 29 Jul 2019 17:06:08 -0300 Subject: [PATCH 16/32] Add parameter rearrange test --- .../dashboard/grid_compliant_widgets_spec.js | 14 +++++--- .../integration/query/parameter_spec.js | 36 +++++++++++++++++++ client/cypress/support/commands.js | 15 ++++++++ client/cypress/support/dashboard/index.js | 17 +-------- 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/client/cypress/integration/dashboard/grid_compliant_widgets_spec.js b/client/cypress/integration/dashboard/grid_compliant_widgets_spec.js index b30041e155..85541d8ebe 100644 --- a/client/cypress/integration/dashboard/grid_compliant_widgets_spec.js +++ b/client/cypress/integration/dashboard/grid_compliant_widgets_spec.js @@ -1,7 +1,7 @@ /* global cy */ import { createDashboard, addTextbox } from '../../support/redash-api'; -import { getWidgetTestId, editDashboard, dragBy, resizeBy } from '../../support/dashboard'; +import { getWidgetTestId, editDashboard, resizeBy } from '../../support/dashboard'; describe('Grid compliant widgets', () => { @@ -26,19 +26,22 @@ describe('Grid compliant widgets', () => { }); it('stays put when dragged under snap threshold', () => { - dragBy(cy.get('@textboxEl'), 90) + cy.get('@textboxEl') + .dragBy(90) .invoke('offset') .should('have.property', 'left', 15); // no change, 15 -> 15 }); it('moves one column when dragged over snap threshold', () => { - dragBy(cy.get('@textboxEl'), 110) + cy.get('@textboxEl') + .dragBy(110) .invoke('offset') .should('have.property', 'left', 215); // moved by 200, 15 -> 215 }); it('moves two columns when dragged over snap threshold', () => { - dragBy(cy.get('@textboxEl'), 330) + cy.get('@textboxEl') + .dragBy(330) .invoke('offset') .should('have.property', 'left', 415); // moved by 400, 15 -> 415 }); @@ -49,7 +52,8 @@ describe('Grid compliant widgets', () => { cy.route('POST', 'api/widgets/*').as('WidgetSave'); editDashboard(); - dragBy(cy.get('@textboxEl'), 330); + cy.get('@textboxEl') + .dragBy(330); cy.wait('@WidgetSave'); }); }); diff --git a/client/cypress/integration/query/parameter_spec.js b/client/cypress/integration/query/parameter_spec.js index 86362f01f3..b4e61853b4 100644 --- a/client/cypress/integration/query/parameter_spec.js +++ b/client/cypress/integration/query/parameter_spec.js @@ -499,4 +499,40 @@ describe('Parameter', () => { cy.getByTestId('ExecuteButton').should('not.be.disabled'); }); }); + + describe('Draggable', () => { + beforeEach(() => { + const queryData = { + name: 'Draggable', + query: "SELECT '{{param1}}', '{{param2}}', '{{param3}}', '{{param4}}' AS parameter", + options: { + parameters: [ + { name: 'param1', title: 'Parameter 1', type: 'text' }, + { name: 'param2', title: 'Parameter 2', type: 'text' }, + { name: 'param3', title: 'Parameter 3', type: 'text' }, + { name: 'param4', title: 'Parameter 4', type: 'text' }, + ], + }, + }; + + createQuery(queryData, false) + .then(({ id }) => cy.visit(`/queries/${id}/source`)); + + cy.get('.parameter-block') + .first() + .invoke('width') + .as('paramWidth'); + }); + + it('is possible to rearrange parameters', function () { + cy.getByTestId('DragHandle-param1').dragBy(this.paramWidth, 0, true); // swap param1 and param2 + cy.getByTestId('DragHandle-param4').dragBy(-this.paramWidth, 0, true); // swap param4 and param3 + + cy.reload(); + + const expectedOrder = ['Parameter 2', 'Parameter 1', 'Parameter 4', 'Parameter 3']; + cy.get('.parameter-container label') + .each(($label, index) => expect($label).to.have.text(expectedOrder[index])); + }); + }); }); diff --git a/client/cypress/support/commands.js b/client/cypress/support/commands.js index 37654fccdf..67fd12244d 100644 --- a/client/cypress/support/commands.js +++ b/client/cypress/support/commands.js @@ -50,3 +50,18 @@ Cypress.Commands.add('fillInputs', (elements) => { cy.getByTestId(testId).clear().type(value); }); }); + +Cypress.Commands.add('dragBy', { prevSubject: true }, (subject, offsetLeft, offsetTop, force = false) => { + if (!offsetLeft) { + offsetLeft = 1; + } + if (!offsetTop) { + offsetTop = 1; + } + return cy.wrap(subject) + .trigger('mouseover', { force }) + .trigger('mousedown', 'topLeft', { force }) + .trigger('mousemove', 1, 1, { force }) // must have at least 2 mousemove events for react-grid-layout to trigger onLayoutChange + .trigger('mousemove', offsetLeft, offsetTop, { force }) + .trigger('mouseup', { force }); +}); diff --git a/client/cypress/support/dashboard/index.js b/client/cypress/support/dashboard/index.js index b2a2224a69..cd7b38a0c1 100644 --- a/client/cypress/support/dashboard/index.js +++ b/client/cypress/support/dashboard/index.js @@ -37,24 +37,9 @@ export function shareDashboard() { return cy.getByTestId('SecretAddress').invoke('val'); } -export function dragBy(wrapper, offsetLeft, offsetTop, force = false) { - if (!offsetLeft) { - offsetLeft = 1; - } - if (!offsetTop) { - offsetTop = 1; - } - return wrapper - .trigger('mouseover', { force }) - .trigger('mousedown', 'topLeft', { force }) - .trigger('mousemove', 1, 1, { force }) // must have at least 2 mousemove events for react-grid-layout to trigger onLayoutChange - .trigger('mousemove', offsetLeft, offsetTop, { force }) - .trigger('mouseup', { force }); -} - export function resizeBy(wrapper, offsetLeft = 0, offsetTop = 0) { return wrapper .within(() => { - dragBy(cy.get(RESIZE_HANDLE_SELECTOR), offsetLeft, offsetTop, true); + cy.get(RESIZE_HANDLE_SELECTOR).dragBy(offsetLeft, offsetTop, true); }); } From 95f1a42cf0a960b19909abc085201df0f3fd153b Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Mon, 29 Jul 2019 18:28:37 -0300 Subject: [PATCH 17/32] Add Parameter Settings title change test --- .../EditParameterSettingsDialog.jsx | 1 + .../integration/query/parameter_spec.js | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/client/app/components/EditParameterSettingsDialog.jsx b/client/app/components/EditParameterSettingsDialog.jsx index 503e5ae477..2693a9cf73 100644 --- a/client/app/components/EditParameterSettingsDialog.jsx +++ b/client/app/components/EditParameterSettingsDialog.jsx @@ -145,6 +145,7 @@ function EditParameterSettingsDialog(props) { setParam({ ...param, title: e.target.value })} + data-test="ParameterTitleInput" /> diff --git a/client/cypress/integration/query/parameter_spec.js b/client/cypress/integration/query/parameter_spec.js index b4e61853b4..c67d2a49cd 100644 --- a/client/cypress/integration/query/parameter_spec.js +++ b/client/cypress/integration/query/parameter_spec.js @@ -535,4 +535,36 @@ describe('Parameter', () => { .each(($label, index) => expect($label).to.have.text(expectedOrder[index])); }); }); + + describe('Parameter Settings', () => { + beforeEach(() => { + const queryData = { + name: 'Draggable', + query: "SELECT '{{parameter}}' AS parameter", + options: { + parameters: [ + { name: 'parameter', title: 'Parameter', type: 'text' }, + ], + }, + }; + + createQuery(queryData, false) + .then(({ id }) => cy.visit(`/queries/${id}/source`)); + + cy.getByTestId('ParameterSettings-parameter').click(); + }); + + it('changes the parameter title', () => { + cy.getByTestId('ParameterTitleInput') + .type('{selectall}New Parameter Name'); + cy.getByTestId('SaveParameterSettings') + .click(); + + cy.contains('Query saved'); + cy.reload(); + + cy.getByTestId('ParameterName-parameter') + .contains('label', 'New Parameter Name'); + }); + }); }); From 9598d9527a5c1261cba9f6fa2c5f28943fa76194 Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Mon, 29 Jul 2019 20:24:58 -0300 Subject: [PATCH 18/32] Update Parameter Settings button styling --- client/app/components/Parameters.jsx | 2 +- client/app/components/Parameters.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index 7351223f01..442d099350 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -123,7 +123,7 @@ export class Parameters extends React.Component { data-test={`ParameterSettings-${param.name}`} type="button" > - + )}
diff --git a/client/app/components/Parameters.less b/client/app/components/Parameters.less index 146417c064..0260b01dd1 100644 --- a/client/app/components/Parameters.less +++ b/client/app/components/Parameters.less @@ -22,7 +22,7 @@ .parameter-heading { display: flex; - margin-bottom: 2px; + align-items: center; } .parameter-container { From dab94d5ba315831901d49d4e5a1eb07f304cb0b2 Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Mon, 29 Jul 2019 22:45:38 -0300 Subject: [PATCH 19/32] Move parameter url logic back to Parameters --- client/app/components/Parameters.jsx | 13 +++++++++++++ client/app/pages/queries/view.js | 17 +---------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index 442d099350..bd8f3dc3c8 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { size, filter, forEach, extend } from 'lodash'; import { react2angular } from 'react2angular'; import { sortableContainer, sortableElement, sortableHandle } from 'react-sortable-hoc'; +import { $location } from '@/services/ng'; import { Parameter } from '@/services/query'; import ParameterApplyButton from '@/components/ParameterApplyButton'; import ParameterValueInput from '@/components/ParameterValueInput'; @@ -22,6 +23,15 @@ const SortableItem = sortableElement(({ className, parameterName, disabled, chil )); const SortableContainer = sortableContainer(({ children }) => children); +function updateUrl(parameters) { + const params = extend({}, $location.search()); + parameters.forEach((param) => { + extend(params, param.toUrlParams()); + }); + Object.keys(params).forEach(key => params[key] == null && delete params[key]); + $location.search(params); +} + export class Parameters extends React.Component { static propTypes = { parameters: PropTypes.arrayOf(PropTypes.instanceOf(Parameter)), @@ -41,12 +51,14 @@ export class Parameters extends React.Component { super(props); const { parameters } = props; this.state = { parameters }; + updateUrl(parameters); } componentDidUpdate = (prevProps) => { const { parameters } = this.props; if (prevProps.parameters !== parameters) { this.setState({ parameters }); + updateUrl(parameters); } }; @@ -88,6 +100,7 @@ export class Parameters extends React.Component { this.setState(({ parameters }) => { forEach(parameters, p => p.applyPendingValue()); onValuesChange(); + updateUrl(parameters); return { parameters }; }); }; diff --git a/client/app/pages/queries/view.js b/client/app/pages/queries/view.js index b0cd1a0ac1..aef1b66a39 100644 --- a/client/app/pages/queries/view.js +++ b/client/app/pages/queries/view.js @@ -1,4 +1,4 @@ -import { pick, some, find, minBy, map, intersection, isArray, omit, extend } from 'lodash'; +import { pick, some, find, minBy, map, intersection, isArray, omit } from 'lodash'; import { SCHEMA_NOT_SUPPORTED, SCHEMA_LOAD_ERROR } from '@/services/data-source'; import getTags from '@/services/getTags'; import { policy } from '@/services/policy'; @@ -523,21 +523,6 @@ function QueryViewCtrl( }, ); - if (!$scope.query.isNew()) { - $scope.$watch( - 'query.options && query.options.parameters', - (parameters) => { - const params = extend({}, $location.search()); - parameters.forEach((param) => { - extend(params, param.toUrlParams()); - }); - Object.keys(params).forEach(key => params[key] == null && delete params[key]); - $location.search(params); - }, - true, - ); - } - $scope.showManagePermissionsModal = () => { $uibModal.open({ component: 'permissionsEditor', From dfa60b6cd40c61a7b0e0f0451a412fa7a616a0ae Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Mon, 29 Jul 2019 22:54:16 -0300 Subject: [PATCH 20/32] Disable url update when query is new --- client/app/components/Parameters.jsx | 18 +++++++++++++----- client/app/pages/queries/query.html | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index bd8f3dc3c8..b0b6545808 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -36,6 +36,7 @@ export class Parameters extends React.Component { static propTypes = { parameters: PropTypes.arrayOf(PropTypes.instanceOf(Parameter)), editable: PropTypes.bool, + disableUrlUpdate: PropTypes.bool, onValuesChange: PropTypes.func, onParametersEdit: PropTypes.func, }; @@ -43,6 +44,7 @@ export class Parameters extends React.Component { static defaultProps = { parameters: [], editable: false, + disableUrlUpdate: false, onValuesChange: () => {}, onParametersEdit: () => {}, } @@ -51,14 +53,18 @@ export class Parameters extends React.Component { super(props); const { parameters } = props; this.state = { parameters }; - updateUrl(parameters); + if (!props.disableUrlUpdate) { + updateUrl(parameters); + } } componentDidUpdate = (prevProps) => { - const { parameters } = this.props; + const { parameters, disableUrlUpdate } = this.props; if (prevProps.parameters !== parameters) { this.setState({ parameters }); - updateUrl(parameters); + if (!disableUrlUpdate) { + updateUrl(parameters); + } } }; @@ -96,11 +102,13 @@ export class Parameters extends React.Component { }; applyChanges = () => { - const { onValuesChange } = this.props; + const { onValuesChange, disableUrlUpdate } = this.props; this.setState(({ parameters }) => { forEach(parameters, p => p.applyPendingValue()); onValuesChange(); - updateUrl(parameters); + if (!disableUrlUpdate) { + updateUrl(parameters); + } return { parameters }; }); }; diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html index 99c35871e6..1b9fb44e6a 100644 --- a/client/app/pages/queries/query.html +++ b/client/app/pages/queries/query.html @@ -191,7 +191,7 @@

-
From 0c0bf6ec686eddd82ac445c7155e8f8fcde029cf Mon Sep 17 00:00:00 2001 From: Ran Byron Date: Wed, 31 Jul 2019 08:09:37 +0300 Subject: [PATCH 21/32] Styling changes (#4019) --- client/app/components/Parameters.jsx | 20 +++++++++++++---- client/app/components/Parameters.less | 32 ++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index b0b6545808..1dc9a92fef 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -52,7 +52,7 @@ export class Parameters extends React.Component { constructor(props) { super(props); const { parameters } = props; - this.state = { parameters }; + this.state = { parameters, dragging: false }; if (!props.disableUrlUpdate) { updateUrl(parameters); } @@ -99,6 +99,11 @@ export class Parameters extends React.Component { return { parameters }; }); } + this.setState({ dragging: false }); + }; + + onBeforeSortStart = () => { + this.setState({ dragging: true }); }; applyChanges = () => { @@ -161,12 +166,19 @@ export class Parameters extends React.Component { } render() { - const { parameters } = this.state; + const { parameters, dragging } = this.state; const { editable } = this.props; const dirtyParamCount = size(filter(parameters, 'hasPendingValue')); return ( - -
+ +
{parameters.map((param, index) => ( {this.renderParameter(param, index)} diff --git a/client/app/components/Parameters.less b/client/app/components/Parameters.less index 0260b01dd1..9af02643e8 100644 --- a/client/app/components/Parameters.less +++ b/client/app/components/Parameters.less @@ -7,29 +7,49 @@ background-size: 2px 2px; display: inline-block; width: 6px; - height: 34px; + height: 36px; vertical-align: bottom; margin-right: 5px; - margin-bottom: 2px; cursor: move; } .parameter-block { display: inline-block; background: white; - padding: 6px; + padding: 3px 6px 6px; + + .parameter-container[data-draggable] & { + margin: 4px 0 0 4px; + } + + &.parameter-dragged { + box-shadow: 0 4px 9px -3px rgba(102, 136, 153, 0.15); + } } .parameter-heading { display: flex; align-items: center; + padding-bottom: 4px; + + label { + margin-bottom: 1px; + } } .parameter-container { position: relative; - background-color: #f6f8f9; - display: inline; - padding: 35px 0 11px; + + &[data-draggable] { + padding: 0 4px 4px 0; + transition: background-color 200ms ease-out; + transition-delay: 300ms; // short pause before returning to original bgcolor + } + + &[data-dragging] { + transition-delay: 0s; + background-color: #f6f8f9; + } .parameter-apply-button { display: none; // default for mobile From ed848abec8d70d8abdfdeb227d9d501e5e5689b9 Mon Sep 17 00:00:00 2001 From: Gabriel Dutra Date: Wed, 31 Jul 2019 13:40:09 -0300 Subject: [PATCH 22/32] Ran's title width styling --- client/app/components/ParameterValueInput.less | 7 +++++-- client/app/components/Parameters.jsx | 4 ++-- client/app/components/Parameters.less | 10 ++++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/client/app/components/ParameterValueInput.less b/client/app/components/ParameterValueInput.less index 968134a083..36ef855a80 100644 --- a/client/app/components/ParameterValueInput.less +++ b/client/app/components/ParameterValueInput.less @@ -5,9 +5,12 @@ .parameter-input { display: inline-block; position: relative; + width: 100%; - .@{ant-prefix}-input[type="text"] { - width: 195px; + .@{ant-prefix}-input, + .@{ant-prefix}-input-number, + .@{ant-prefix}-select { + width: 100%; } &[data-dirty] { diff --git a/client/app/components/Parameters.jsx b/client/app/components/Parameters.jsx index 1dc9a92fef..3fed748185 100644 --- a/client/app/components/Parameters.jsx +++ b/client/app/components/Parameters.jsx @@ -16,7 +16,7 @@ const DragHandle = sortableHandle(({ parameterName }) => ( )); const SortableItem = sortableElement(({ className, parameterName, disabled, children }) => ( -
+
{!disabled && } {children}
@@ -141,7 +141,7 @@ export class Parameters extends React.Component { data-test={`ParameterName-${param.name}`} >
- + {editable && (