diff --git a/client/app/assets/less/inc/visualizations/pivot-table.less b/client/app/assets/less/inc/visualizations/pivot-table.less
index 6fa85b4159..840c21b1c0 100644
--- a/client/app/assets/less/inc/visualizations/pivot-table.less
+++ b/client/app/assets/less/inc/visualizations/pivot-table.less
@@ -1,4 +1,4 @@
-.pivot-table-renderer > table,
+.pivot-table-visualization-container > table,
.visualization-renderer > .visualization-renderer-wrapper {
overflow: auto;
}
diff --git a/client/app/assets/less/redash/query.less b/client/app/assets/less/redash/query.less
index e14cec8ab5..4c73715ee9 100644
--- a/client/app/assets/less/redash/query.less
+++ b/client/app/assets/less/redash/query.less
@@ -343,7 +343,7 @@ a.label-tag {
border-bottom: 1px solid #efefef;
}
- .pivot-table-renderer > table,
+ .pivot-table-visualization-container > table,
.visualization-renderer > .visualization-renderer-wrapper {
overflow: visible;
}
diff --git a/client/app/components/Filters.jsx b/client/app/components/Filters.jsx
index c9c2b78df6..0c6307ea72 100644
--- a/client/app/components/Filters.jsx
+++ b/client/app/components/Filters.jsx
@@ -1,10 +1,10 @@
-import { isArray, indexOf, get, map, includes, every, some, toNumber, toLower } from 'lodash';
+import { isArray, indexOf, get, map, includes, every, some, toNumber } from 'lodash';
import moment from 'moment';
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import Select from 'antd/lib/select';
-import { formatDateTime, formatDate } from '@/filters/datetime';
+import { formatColumnValue } from '@/filters';
const ALL_VALUES = '###Redash::Filters::SelectAll###';
const NONE_VALUES = '###Redash::Filters::Clear###';
@@ -71,21 +71,6 @@ export function filterData(rows, filters = []) {
return result;
}
-function formatValue(value, columnType) {
- if (moment.isMoment(value)) {
- if (columnType === 'date') {
- return formatDate(value);
- }
- return formatDateTime(value);
- }
-
- if (typeof value === 'boolean') {
- return value.toString();
- }
-
- return value;
-}
-
export function Filters({ filters, onChange }) {
if (filters.length === 0) {
return null;
@@ -99,7 +84,7 @@ export function Filters({ filters, onChange }) {
{map(filters, (filter) => {
const options = map(filter.values, (value, index) => (
-
{formatValue(value, get(filter, 'column.type'))}
+
{formatColumnValue(value, get(filter, 'column.type'))}
));
return (
@@ -115,10 +100,10 @@ export function Filters({ filters, onChange }) {
mode={filter.multiple ? 'multiple' : 'default'}
value={isArray(filter.current) ?
map(filter.current,
- value => ({ key: `${indexOf(filter.values, value)}`, label: formatValue(value) })) :
- ({ key: `${indexOf(filter.values, filter.current)}`, label: formatValue(filter.current) })}
+ value => ({ key: `${indexOf(filter.values, value)}`, label: formatColumnValue(value) })) :
+ ({ key: `${indexOf(filter.values, filter.current)}`, label: formatColumnValue(filter.current) })}
allowClear={filter.multiple}
- filterOption={(searchText, option) => includes(toLower(option.props.children), toLower(searchText))}
+ optionFilterProp="children"
showSearch
onChange={values => onChange(filter, values)}
>
diff --git a/client/app/filters/index.js b/client/app/filters/index.js
index 623db500ad..a7231ce640 100644
--- a/client/app/filters/index.js
+++ b/client/app/filters/index.js
@@ -1,5 +1,6 @@
import moment from 'moment';
import { capitalize as _capitalize, isEmpty } from 'lodash';
+import { formatDate, formatDateTime } from './datetime';
export const IntervalEnum = {
NEVER: 'Never',
@@ -168,3 +169,18 @@ export function join(arr) {
return arr.join(' / ');
}
+
+export function formatColumnValue(value, columnType = null) {
+ if (moment.isMoment(value)) {
+ if (columnType === 'date') {
+ return formatDate(value);
+ }
+ return formatDateTime(value);
+ }
+
+ if (typeof value === 'boolean') {
+ return value.toString();
+ }
+
+ return value;
+}
diff --git a/client/app/pages/dashboards/dashboard.less b/client/app/pages/dashboards/dashboard.less
index 4ea608e0d6..47cd402aac 100644
--- a/client/app/pages/dashboards/dashboard.less
+++ b/client/app/pages/dashboards/dashboard.less
@@ -22,7 +22,7 @@
padding: 0;
}
- .pivot-table-renderer > table,
+ .pivot-table-visualization-container > table,
.visualization-renderer > .visualization-renderer-wrapper {
overflow: visible;
}
diff --git a/client/app/visualizations/pivot/Renderer.jsx b/client/app/visualizations/pivot/Renderer.jsx
new file mode 100644
index 0000000000..8209db956c
--- /dev/null
+++ b/client/app/visualizations/pivot/Renderer.jsx
@@ -0,0 +1,66 @@
+import React, { useState, useEffect } from 'react';
+import { get, find, pick, map, mapValues } from 'lodash';
+import PivotTableUI from 'react-pivottable/PivotTableUI';
+import { RendererPropTypes } from '@/visualizations';
+import { formatColumnValue } from '@/filters';
+
+import 'react-pivottable/pivottable.css';
+import './renderer.less';
+
+const VALID_OPTIONS = [
+ 'data',
+ 'rows',
+ 'cols',
+ 'vals',
+ 'aggregatorName',
+ 'valueFilter',
+ 'sorters',
+ 'rowOrder',
+ 'colOrder',
+ 'derivedAttributes',
+ 'rendererName',
+ 'hiddenAttributes',
+ 'hiddenFromAggregators',
+ 'hiddenFromDragDrop',
+ 'menuLimit',
+ 'unusedOrientationCutoff',
+ 'controls',
+ 'rendererOptions',
+];
+
+function formatRows({ rows, columns }) {
+ return map(rows, row => mapValues(row, (value, key) => formatColumnValue(value, find(columns, { name: key }).type)));
+}
+
+export default function Renderer({ data, options, onOptionsChange }) {
+ const [config, setConfig] = useState({ data: formatRows(data), ...options });
+
+ useEffect(() => {
+ setConfig({ data: formatRows(data), ...options });
+ }, [data, options]);
+
+ const onChange = (updatedOptions) => {
+ const validOptions = pick(updatedOptions, VALID_OPTIONS);
+ setConfig(validOptions);
+ onOptionsChange(validOptions);
+ };
+
+ // Legacy behavior: hideControls when controls.enabled is true
+ const hideControls = get(options, 'controls.enabled');
+ const hideRowTotals = !get(options, 'rendererOptions.table.rowTotals');
+ const hideColumnTotals = !get(options, 'rendererOptions.table.colTotals');
+ return (
+
+ );
+}
+
+Renderer.propTypes = RendererPropTypes;
+Renderer.defaultProps = { onOptionsChange: () => {} };
diff --git a/client/app/visualizations/pivot/index.js b/client/app/visualizations/pivot/index.js
index 398cb238e7..d6113d6de0 100644
--- a/client/app/visualizations/pivot/index.js
+++ b/client/app/visualizations/pivot/index.js
@@ -1,13 +1,7 @@
-import { merge, omit } from 'lodash';
-import angular from 'angular';
-import $ from 'jquery';
-import 'pivottable';
-import 'pivottable/dist/pivot.css';
-import { angular2react } from 'angular2react';
+import { merge } from 'lodash';
import { registerVisualization } from '@/visualizations';
-import './pivot.less';
-
+import Renderer from './Renderer';
import Editor from './Editor';
const DEFAULT_OPTIONS = {
@@ -22,63 +16,17 @@ const DEFAULT_OPTIONS = {
},
};
-const PivotTableRenderer = {
- template: `
-
- `,
- bindings: {
- data: '<',
- options: '<',
- onOptionsChange: '<',
- },
- controller($scope, $element) {
- const update = () => {
- // We need to give the pivot table its own copy of the data, because it changes
- // it which interferes with other visualizations.
- const data = angular.copy(this.data.rows);
- const options = {
- renderers: $.pivotUtilities.renderers,
- onRefresh: (config) => {
- if (this.onOptionsChange) {
- config = omit(config, [
- // delete some values which are functions
- 'aggregators',
- 'renderers',
- 'onRefresh',
- // delete some bulky de
- 'localeStrings',
- ]);
- this.onOptionsChange(config);
- }
- },
- ...this.options,
- };
-
- $('.pivot-table-renderer', $element).pivotUI(data, options, true);
- };
-
- $scope.$watch('$ctrl.data', update);
- // `options.controls.enabled` is not related to pivot renderer, it's handled by `ng-if`,
- // so re-render only if other options changed
- $scope.$watch(() => omit(this.options, 'controls'), update, true);
- },
-};
-
-export default function init(ngModule) {
- ngModule.component('pivotTableRenderer', PivotTableRenderer);
-
- ngModule.run(($injector) => {
- registerVisualization({
- type: 'PIVOT',
- name: 'Pivot Table',
- getOptions: options => merge({}, DEFAULT_OPTIONS, options),
- Renderer: angular2react('pivotTableRenderer', PivotTableRenderer, $injector),
- Editor,
-
- defaultRows: 10,
- defaultColumns: 3,
- minColumns: 2,
- });
+export default function init() {
+ registerVisualization({
+ type: 'PIVOT',
+ name: 'Pivot Table',
+ getOptions: options => merge({}, DEFAULT_OPTIONS, options),
+ Renderer,
+ Editor,
+
+ defaultRows: 10,
+ defaultColumns: 3,
+ minColumns: 2,
});
}
diff --git a/client/app/visualizations/pivot/pivottable-editor.html b/client/app/visualizations/pivot/pivottable-editor.html
deleted file mode 100644
index 51ae6d194b..0000000000
--- a/client/app/visualizations/pivot/pivottable-editor.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/client/app/visualizations/pivot/pivot.less b/client/app/visualizations/pivot/renderer.less
similarity index 74%
rename from client/app/visualizations/pivot/pivot.less
rename to client/app/visualizations/pivot/renderer.less
index efe0c5eff5..3702e06167 100644
--- a/client/app/visualizations/pivot/pivot.less
+++ b/client/app/visualizations/pivot/renderer.less
@@ -1,9 +1,25 @@
@redash-gray: rgba(102, 136, 153, 1);
-.pivot-table-renderer {
- &.hide-controls {
- .pvtAxisContainer, .pvtRenderer, .pvtVals {
- display: none !important;
+.pivot-table-visualization-container {
+ &[data-hide-controls] {
+ .pvtAxisContainer, .pvtRenderers, .pvtVals {
+ display: none;
+ }
+ }
+
+ &[data-hide-row-totals] {
+ td:last-child, th:last-child {
+ &.pvtTotalLabel:not(:empty), &.pvtTotal, &.pvtGrandTotal {
+ display: none;
+ }
+ }
+ }
+
+ &[data-hide-column-totals] {
+ tbody > tr:last-child {
+ & > .pvtTotalLabel, & > .pvtTotal, & > .pvtGrandTotal {
+ display: none;
+ }
}
}
}
@@ -13,16 +29,6 @@
background: #fff;
}
-.pvtUi {
- td, th {
- padding: 5px;
- }
-
- li.ui-sortable-handle {
- padding: 5px 5px 5px 0;
- }
-}
-
.pvtAxisContainer li span.pvtAttr {
background: fade(@redash-gray, 10%);
border: 1px solid fade(@redash-gray, 15%);
@@ -80,4 +86,3 @@ table.pvtTable tbody tr td {
border-radius: 3px;
}
}
-
diff --git a/client/cypress/integration/visualizations/pivot_spec.js b/client/cypress/integration/visualizations/pivot_spec.js
index d1003a86c7..3b8dc265bd 100644
--- a/client/cypress/integration/visualizations/pivot_spec.js
+++ b/client/cypress/integration/visualizations/pivot_spec.js
@@ -1,6 +1,7 @@
/* global cy, Cypress */
-import { createQuery } from '../../support/redash-api';
+import { createQuery, createVisualization, createDashboard, addWidget } from '../../support/redash-api';
+import { getWidgetTestId } from '../../support/dashboard';
const { get } = Cypress._;
@@ -15,19 +16,21 @@ const SQL = `
SELECT 'c' AS stage1, 'c1' AS stage2, 19 AS value UNION ALL
SELECT 'c' AS stage1, 'c2' AS stage2, 92 AS value UNION ALL
SELECT 'c' AS stage1, 'c3' AS stage2, 63 AS value UNION ALL
- SELECT 'c' AS stage1, 'c4' AS stage2, 44 AS v
+ SELECT 'c' AS stage1, 'c4' AS stage2, 44 AS value
`;
describe('Pivot', () => {
beforeEach(() => {
cy.login();
- createQuery({ query: SQL }).then(({ id }) => {
- cy.visit(`queries/${id}/source`);
- cy.getByTestId('ExecuteButton').click();
- });
+ createQuery({ name: 'Pivot Visualization', query: SQL })
+ .its('id')
+ .as('queryId');
});
- it('creates Pivot with controls', () => {
+ it('creates Pivot with controls', function () {
+ cy.visit(`queries/${this.queryId}/source`);
+ cy.getByTestId('ExecuteButton').click();
+
const visualizationName = 'Pivot';
cy.getByTestId('NewVisualization').click();
@@ -39,7 +42,10 @@ describe('Pivot', () => {
cy.getByTestId('QueryPageVisualizationTabs').contains('li', visualizationName).should('exist');
});
- it('creates Pivot without controls', () => {
+ it('creates Pivot without controls', function () {
+ cy.visit(`queries/${this.queryId}/source`);
+ cy.getByTestId('ExecuteButton').click();
+
const visualizationName = 'Pivot';
cy.server();
@@ -66,4 +72,46 @@ describe('Pivot', () => {
.should('be.not.visible');
});
});
+
+ it('takes a snapshot with different configured Pivots', function () {
+ const options = {
+ aggregatorName: 'Sum',
+ controls: { enabled: true },
+ cols: ['stage1'],
+ rows: ['stage2'],
+ vals: ['value'],
+ };
+
+ const pivotTables = [
+ { name: 'Pivot',
+ options,
+ position: { autoHeight: false, sizeY: 10, sizeX: 2 } },
+ { name: 'Pivot without Row Totals',
+ options: { ...options, rendererOptions: { table: { rowTotals: false } } },
+ position: { autoHeight: false, col: 2, sizeY: 10, sizeX: 2 } },
+ { name: 'Pivot without Col Totals',
+ options: { ...options, rendererOptions: { table: { colTotals: false } } },
+ position: { autoHeight: false, col: 4, sizeY: 10, sizeX: 2 } },
+ { name: 'Pivot with Controls',
+ options: { ...options, controls: { enabled: false } },
+ position: { autoHeight: false, row: 9, sizeY: 13 } },
+ ];
+
+ createDashboard('Pivot Visualization')
+ .then((dashboard) => {
+ this.dashboardUrl = `/dashboard/${dashboard.slug}`;
+ return cy.all(
+ pivotTables.map(pivot => () => createVisualization(this.queryId, 'PIVOT', pivot.name, pivot.options)
+ .then(visualization => addWidget(dashboard.id, visualization.id, { position: pivot.position }))),
+ );
+ })
+ .then((widgets) => {
+ cy.visit(this.dashboardUrl);
+ widgets.forEach((widget) => {
+ cy.getByTestId(getWidgetTestId(widget))
+ .within(() => cy.getByTestId('PivotTableVisualization').should('exist'));
+ });
+ cy.percySnapshot('Visualizations - Pivot Table');
+ });
+ });
});
diff --git a/client/cypress/support/commands.js b/client/cypress/support/commands.js
index 67fd12244d..a401a804af 100644
--- a/client/cypress/support/commands.js
+++ b/client/cypress/support/commands.js
@@ -65,3 +65,19 @@ Cypress.Commands.add('dragBy', { prevSubject: true }, (subject, offsetLeft, offs
.trigger('mousemove', offsetLeft, offsetTop, { force })
.trigger('mouseup', { force });
});
+
+Cypress.Commands.add('all', (...functions) => {
+ if (Cypress._.isEmpty(functions)) {
+ return [];
+ }
+
+ const fns = Cypress._.isArray(functions[0]) ? functions[0] : functions;
+ const results = [];
+
+ fns.reduce((prev, fn) => {
+ fn().then(result => results.push(result));
+ return results;
+ }, results);
+
+ return cy.wrap(results);
+});
diff --git a/package-lock.json b/package-lock.json
index ec83da854f..02ff4e93e4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1945,11 +1945,6 @@
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
"dev": true
},
- "async-validator": {
- "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",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -2531,7 +2526,7 @@
},
"bl": {
"version": "1.2.2",
- "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
"requires": {
"readable-stream": "^2.3.5",
@@ -2545,7 +2540,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
@@ -2763,7 +2758,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
@@ -6378,7 +6373,7 @@
},
"finalhandler": {
"version": "1.1.1",
- "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"dev": true,
"requires": {
@@ -8884,7 +8879,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
@@ -9216,7 +9211,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -9310,7 +9305,7 @@
},
"html-webpack-plugin": {
"version": "3.2.0",
- "resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
"integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
"dev": true,
"requires": {
@@ -9565,6 +9560,14 @@
"dev": true,
"optional": true
},
+ "immutability-helper": {
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-2.9.1.tgz",
+ "integrity": "sha512-r/RmRG8xO06s/k+PIaif2r5rGc3j4Yhc01jSBfwPCXDLYZwp/yxralI37Df1mwmuzcCsen/E/ITKcTEvc1PQmQ==",
+ "requires": {
+ "invariant": "^2.2.0"
+ }
+ },
"immutable": {
"version": "3.7.6",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
@@ -9957,7 +9960,7 @@
},
"is-obj": {
"version": "1.0.1",
- "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
},
"is-observable": {
@@ -11515,7 +11518,7 @@
},
"magic-string": {
"version": "0.22.5",
- "resolved": "http://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
"integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==",
"requires": {
"vlq": "^0.2.2"
@@ -11627,12 +11630,12 @@
},
"minimist": {
"version": "0.0.8",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
@@ -11831,7 +11834,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -12027,7 +12030,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -12528,7 +12531,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -12930,7 +12933,7 @@
"dependencies": {
"minimist": {
"version": "0.0.10",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
"dev": true
},
@@ -13351,14 +13354,6 @@
"node-modules-regexp": "^1.0.0"
}
},
- "pivottable": {
- "version": "2.23.0",
- "resolved": "https://registry.npmjs.org/pivottable/-/pivottable-2.23.0.tgz",
- "integrity": "sha512-6WRaiiI0mU5JxzNMWbtf3vfrBvBhBPIUbwu2Q7Nv7fVCxIvlmFqXSldMwmHAsiEFwdZdUrpQHqIu+N3jZUezyg==",
- "requires": {
- "jquery": ">=1.9.0"
- }
- },
"pkg-dir": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
@@ -14535,6 +14530,16 @@
"hoist-non-react-statics": "^3.3.0",
"lodash": "^4.17.4",
"warning": "^4.0.3"
+ },
+ "dependencies": {
+ "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"
+ }
+ }
}
},
"rc-hammerjs": {
@@ -14965,6 +14970,18 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
+ "react-pivottable": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/react-pivottable/-/react-pivottable-0.9.0.tgz",
+ "integrity": "sha512-fs1pGV5z4BvOXL4iLu79kKCLgR5XINW2ZredJHoPqXEMbJaIv50Eoec1XowWf3i3Dvdb8EYgvNqlF2ggC4GJOw==",
+ "requires": {
+ "immutability-helper": "^2.3.1",
+ "prop-types": "^15.5.10",
+ "react-draggable": "^3.0.3",
+ "react-sortablejs": "^1.3.4",
+ "sortablejs": "^1.6.1"
+ }
+ },
"react-resizable": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-1.7.5.tgz",
@@ -15011,6 +15028,14 @@
}
}
},
+ "react-sortablejs": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/react-sortablejs/-/react-sortablejs-1.5.1.tgz",
+ "integrity": "sha512-bKIc1UVhjZt55Nb6WZFxZ8Jwyngg8CTt+w+iG1pA5k9LQsg1J0X6nLppHatSSDZDECtRZKp2z47tmmhPRJNj4g==",
+ "requires": {
+ "prop-types": ">=15.0.0"
+ }
+ },
"react-test-renderer": {
"version": "16.8.3",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.3.tgz",
@@ -16141,7 +16166,7 @@
"dependencies": {
"minimist": {
"version": "0.0.5",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz",
"integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY="
}
}
@@ -16481,6 +16506,11 @@
"sort-desc": "^0.1.1"
}
},
+ "sortablejs": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.9.0.tgz",
+ "integrity": "sha512-Ot6bYJ6PoqPmpsqQYXjn1+RKrY2NWQvQt/o4jfd/UYwVWndyO5EPO8YHbnm5HIykf8ENsm4JUrdAvolPT86yYA=="
+ },
"source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
@@ -16629,7 +16659,7 @@
},
"split": {
"version": "0.2.10",
- "resolved": "http://registry.npmjs.org/split/-/split-0.2.10.tgz",
+ "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz",
"integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=",
"requires": {
"through": "2"
@@ -16987,7 +17017,7 @@
"dependencies": {
"readable-stream": {
"version": "1.1.14",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "~1.0.0",
@@ -17121,7 +17151,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
diff --git a/package.json b/package.json
index eb13ac48ce..b12b504ae1 100644
--- a/package.json
+++ b/package.json
@@ -74,7 +74,6 @@
"mustache": "^2.3.0",
"numeral": "^2.0.6",
"pace-progress": "git+https://github.com/getredash/pace.git",
- "pivottable": "^2.15.0",
"plotly.js": "1.41.3",
"prop-types": "^15.6.1",
"qs": "^6.7.0",
@@ -82,6 +81,7 @@
"react-ace": "^6.1.0",
"react-dom": "^16.8.3",
"react-grid-layout": "git+https://github.com/getredash/react-grid-layout.git",
+ "react-pivottable": "^0.9.0",
"react-sortable-hoc": "^1.9.1",
"react2angular": "^3.2.1",
"tinycolor2": "^1.4.1",