Bumping the JS libs to fix the build (#2616)

* bumping the js libs

* New linting rules

* More linting

* More

* Done linting

* npm >=4.5.0

* Bumping node

* Tweaking the build

* Fixing the damn build

* Fixing the apps
This commit is contained in:
Maxime Beauchemin 2017-04-13 15:04:09 -07:00 committed by GitHub
parent a2b30f35fc
commit 366ecefbaa
132 changed files with 611 additions and 553 deletions

View File

@ -65,7 +65,7 @@ confidence=
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=standarderror-builtin,long-builtin,dict-view-method,intern-builtin,suppressed-message,no-absolute-import,unpacking-in-except,apply-builtin,delslice-method,indexing-exception,old-raise-syntax,print-statement,cmp-builtin,reduce-builtin,useless-suppression,coerce-method,input-builtin,cmp-method,raw_input-builtin,nonzero-method,backtick,basestring-builtin,setslice-method,reload-builtin,oct-method,map-builtin-not-iterating,execfile-builtin,old-octal-literal,zip-builtin-not-iterating,buffer-builtin,getslice-method,metaclass-assignment,xrange-builtin,long-suffix,round-builtin,range-builtin-not-iterating,next-method-called,dict-iter-method,parameter-unpacking,unicode-builtin,unichr-builtin,import-star-module-level,raising-string,filter-builtin-not-iterating,old-ne-operator,using-cmp-argument,coerce-builtin,file-builtin,old-division,hex-method
disable=standarderror-builtin,long-builtin,dict-view-method,intern-builtin,suppressed-message,no-absolute-import,unpacking-in-except,apply-builtin,delslice-method,indexing-exception,old-raise-syntax,print-statement,cmp-builtin,reduce-builtin,useless-suppression,coerce-method,input-builtin,cmp-method,raw_input-builtin,nonzero-method,backtick,basestring-builtin,setslice-method,reload-builtin,oct-method,map-builtin-not-iterating,execfile-builtin,old-octal-literal,zip-builtin-not-iterating,buffer-builtin,getslice-method,metaclass-assignment,xrange-builtin,long-suffix,round-builtin,range-builtin-not-iterating,next-method-called,dict-iter-method,parameter-unpacking,unicode-builtin,unichr-builtin,import-star-module-level,raising-string,filter-builtin-not-iterating,old-ne-operator,using-cmp-argument,coerce-builtin,file-builtin,old-division,hex-method,invalid-unary-operand-type
[REPORTS]

View File

@ -14,7 +14,7 @@ cache:
env:
global:
- TRAVIS_CACHE=$HOME/.travis_cache/
- TRAVIS_NODE_VERSION="5.11"
- TRAVIS_NODE_VERSION="6.10.2"
matrix:
- TOX_ENV=javascript
- TOX_ENV=pylint
@ -23,7 +23,7 @@ env:
- TOX_ENV=py27-mysql
- TOX_ENV=py27-sqlite
before_install:
- npm install -g npm@'>=3.9.5'
- npm install -g npm@'>=4.5.0'
before_script:
- mysql -e 'drop database if exists superset; create database superset DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci' -u root
- mysql -u root -e "CREATE USER 'mysqluser'@'localhost' IDENTIFIED BY 'mysqluserpassword';"

View File

@ -17,5 +17,26 @@
"func-names": 0,
"react/jsx-no-bind": 0,
"no-confusing-arrow": 0,
"jsx-a11y/no-static-element-interactions": 0,
"jsx-a11y/anchor-has-content": 0,
"react/require-default-props": 0,
"no-plusplus": 0,
"no-mixed-operators": 0,
"no-continue": 0,
"no-bitwise": 0,
"no-undef": 0,
"no-multi-assign": 0,
"react/no-array-index-key": 0,
"no-restricted-properties": 0,
"no-prototype-builtins": 0,
"jsx-a11y/href-no-hash": 0,
"react/forbid-prop-types": 0,
"class-methods-use-this": 0,
"import/no-named-as-default": 0,
"import/prefer-default-export": 0,
"react/no-unescaped-entities": 0,
"react/no-unused-prop-types": 0,
"react/no-string-refs": 0,
}
}

View File

@ -1,6 +1,7 @@
/* global notify */
import shortid from 'shortid';
import { now } from '../modules/dates';
const $ = require('jquery');
export const RESET_STATE = 'RESET_STATE';
@ -262,7 +263,7 @@ export function addTable(query, tableName, schemaName) {
queryEditorId: query.id,
schema: schemaName,
expanded: true,
}), dataPreviewQuery)
}), dataPreviewQuery),
);
// Run query to get preview data for table
dispatch(runQuery(dataPreviewQuery));
@ -272,7 +273,7 @@ export function addTable(query, tableName, schemaName) {
addAlert({
msg: 'Error occurred while fetching metadata',
bsStyle: 'danger',
})
}),
);
});

View File

@ -59,15 +59,9 @@ class AceEditorWrapper extends React.PureComponent {
this.setState({ sql: nextProps.sql });
}
}
textChange(text) {
this.setState({ sql: text });
}
onBlur() {
this.props.onBlur(this.state.sql);
}
getCompletions(aceEditor, session, pos, prefix, callback) {
callback(null, this.state.words);
}
onEditorLoad(editor) {
editor.commands.addCommand({
name: 'runQuery',
@ -87,10 +81,10 @@ class AceEditorWrapper extends React.PureComponent {
let words = [];
const columns = {};
const tables = props.tables || [];
tables.forEach(t => {
tables.forEach((t) => {
words.push({ name: t.name, value: t.name, score: 55, meta: 'table' });
const cols = t.columns || [];
cols.forEach(col => {
cols.forEach((col) => {
columns[col.name] = null; // using an object as a unique set
});
});
@ -107,6 +101,12 @@ class AceEditorWrapper extends React.PureComponent {
}
});
}
getCompletions(aceEditor, session, pos, prefix, callback) {
callback(null, this.state.words);
}
textChange(text) {
this.setState({ sql: text });
}
render() {
return (
<AceEditor

View File

@ -1,14 +1,14 @@
const $ = window.$ = require('jquery');
import * as Actions from '../actions';
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import TabbedSqlEditors from './TabbedSqlEditors';
import QueryAutoRefresh from './QueryAutoRefresh';
import QuerySearch from './QuerySearch';
import AlertsWrapper from '../../components/AlertsWrapper';
import * as Actions from '../actions';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
const $ = window.$ = require('jquery');
class App extends React.PureComponent {
constructor(props) {
@ -28,6 +28,9 @@ class App extends React.PureComponent {
window.removeEventListener('hashchange', this.onHashChanged.bind(this));
window.removeEventListener('resize', this.handleResize.bind(this));
}
onHashChanged() {
this.setState({ hash: window.location.hash });
}
getHeight() {
const navHeight = 90;
const headerHeight = $('.nav-tabs').outerHeight() ?
@ -39,9 +42,6 @@ class App extends React.PureComponent {
handleResize() {
this.setState({ contentHeight: this.getHeight() });
}
onHashChanged() {
this.setState({ hash: window.location.hash });
}
render() {
let content;
if (this.state.hash) {

View File

@ -17,43 +17,39 @@ const tooltipTitleMap = {
index: 'Index',
};
class ColumnElement extends React.PureComponent {
render() {
const col = this.props.column;
let name = col.name;
let icons;
if (col.keys && col.keys.length > 0) {
name = <strong>{col.name}</strong>;
icons = col.keys.map((key, i) => (
<span key={i} className="ColumnElement">
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="idx-json" bsSize="lg">
<strong>{tooltipTitleMap[key.type]}</strong>
<hr />
<pre className="text-small">
{JSON.stringify(key, null, ' ')}
</pre>
</Tooltip>
}
>
<i className={`fa text-muted m-l-2 ${iconMap[key.type]}`} />
</OverlayTrigger>
</span>
));
}
return (
<div className="clearfix table-column">
<div className="pull-left m-l-10 col-name">
{name}{icons}
</div>
<div className="pull-right text-muted">
<small> {col.type}</small>
</div>
</div>);
export default function ColumnElement(props) {
const col = props.column;
let name = col.name;
let icons;
if (col.keys && col.keys.length > 0) {
name = <strong>{col.name}</strong>;
icons = col.keys.map((key, i) => (
<span key={i} className="ColumnElement">
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="idx-json" bsSize="lg">
<strong>{tooltipTitleMap[key.type]}</strong>
<hr />
<pre className="text-small">
{JSON.stringify(key, null, ' ')}
</pre>
</Tooltip>
}
>
<i className={`fa text-muted m-l-2 ${iconMap[key.type]}`} />
</OverlayTrigger>
</span>
));
}
return (
<div className="clearfix table-column">
<div className="pull-left m-l-10 col-name">
{name}{icons}
</div>
<div className="pull-right text-muted">
<small> {col.type}</small>
</div>
</div>);
}
ColumnElement.propTypes = propTypes;
export default ColumnElement;

View File

@ -1,9 +1,9 @@
import * as Actions from '../actions';
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Modal } from 'react-bootstrap';
import * as Actions from '../actions';
import ResultSet from './ResultSet';
const propTypes = {

View File

@ -25,38 +25,35 @@ class HighlightedSql extends React.Component {
};
}
shrinkSql() {
const props = this.props;
const sql = props.sql || '';
const sql = this.props.sql || '';
let lines = sql.split('\n');
if (lines.length >= props.maxLines) {
lines = lines.slice(0, props.maxLines);
if (lines.length >= this.props.maxLines) {
lines = lines.slice(0, this.props.maxLines);
lines.push('{...}');
}
return lines.map((line) => {
if (line.length > props.maxWidth) {
return line.slice(0, props.maxWidth) + '{...}';
if (line.length > this.props.maxWidth) {
return line.slice(0, this.props.maxWidth) + '{...}';
}
return line;
})
.join('\n');
}
triggerNode() {
const props = this.props;
let shownSql = props.shrink ? this.shrinkSql(props.sql) : props.sql;
const shownSql = this.props.shrink ? this.shrinkSql(this.props.sql) : this.props.sql;
return (
<SyntaxHighlighter language="sql" style={github}>
{shownSql}
</SyntaxHighlighter>);
}
generateModal() {
const props = this.props;
let rawSql;
if (props.rawSql && props.rawSql !== this.props.sql) {
if (this.props.rawSql && this.props.rawSql !== this.props.sql) {
rawSql = (
<div>
<h4>Raw SQL</h4>
<SyntaxHighlighter language="sql" style={github}>
{props.rawSql}
{this.props.rawSql}
</SyntaxHighlighter>
</div>
);

View File

@ -22,7 +22,7 @@ const defaultProps = {
class Link extends React.PureComponent {
render() {
let tooltip = (
const tooltip = (
<Tooltip id="tooltip">
{this.props.tooltip}
</Tooltip>
@ -34,7 +34,7 @@ class Link extends React.PureComponent {
style={this.props.style}
className={'Link ' + this.props.className}
>
{this.props.children}
{this.props.children}
</a>
);
if (this.props.tooltip) {

View File

@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import * as Actions from '../actions';
const $ = require('jquery');
const QUERY_UPDATE_FREQ = 2000;
const QUERY_UPDATE_BUFFER_MS = 5000;

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Alert } from 'react-bootstrap';
import QueryTable from './QueryTable';
import { Alert } from 'react-bootstrap';
const propTypes = {
queries: React.PropTypes.array.isRequired,

View File

@ -1,4 +1,3 @@
const $ = window.$ = require('jquery');
import React from 'react';
import { Button } from 'react-bootstrap';
import Select from 'react-select';
@ -8,8 +7,11 @@ import { now, epochTimeXHoursAgo,
import { STATUS_OPTIONS, TIME_OPTIONS } from '../constants';
import AsyncSelect from '../../components/AsyncSelect';
const $ = window.$ = require('jquery');
const propTypes = {
actions: React.PropTypes.object.isRequired,
height: React.PropTypes.integer,
};
class QuerySearch extends React.PureComponent {
@ -75,7 +77,7 @@ class QuerySearch extends React.PureComponent {
}
insertParams(baseUrl, params) {
const validParams = params.filter(
function (p) { return p !== ''; }
function (p) { return p !== ''; },
);
return baseUrl + '?' + validParams.join('&');
}
@ -94,7 +96,7 @@ class QuerySearch extends React.PureComponent {
return options;
}
dbMutator(data) {
const options = data.result.map((db) => ({ value: db.id, label: db.database_name }));
const options = data.result.map(db => ({ value: db.id, label: db.database_name }));
this.props.actions.setDatabases(data.result);
if (data.result.length === 0) {
this.props.actions.addAlert({
@ -154,8 +156,8 @@ class QuerySearch extends React.PureComponent {
<Select
name="select-from"
placeholder="[From]-"
options={TIME_OPTIONS.
slice(1, TIME_OPTIONS.length).map((t) => ({ value: t, label: t }))}
options={TIME_OPTIONS
.slice(1, TIME_OPTIONS.length).map(t => ({ value: t, label: t }))}
value={this.state.from}
autosize={false}
onChange={this.changeFrom.bind(this)}
@ -165,7 +167,7 @@ class QuerySearch extends React.PureComponent {
<Select
name="select-to"
placeholder="[To]-"
options={TIME_OPTIONS.map((t) => ({ value: t, label: t }))}
options={TIME_OPTIONS.map(t => ({ value: t, label: t }))}
value={this.state.to}
autosize={false}
onChange={this.changeTo.bind(this)}
@ -175,7 +177,7 @@ class QuerySearch extends React.PureComponent {
<Select
name="select-status"
placeholder="[Query Status]"
options={STATUS_OPTIONS.map((s) => ({ value: s, label: s }))}
options={STATUS_OPTIONS.map(s => ({ value: s, label: s }))}
value={this.state.status}
isLoading={false}
autosize={false}
@ -190,23 +192,23 @@ class QuerySearch extends React.PureComponent {
(<img className="loading" alt="Loading..." src="/static/assets/images/loading.gif" />)
:
(
<div
style={{ height: this.props.height }}
className="scrollbar-container"
>
<div className="scrollbar-content">
<QueryTable
columns={[
'state', 'db', 'user', 'time',
'progress', 'rows', 'sql', 'querylink',
]}
onUserClicked={this.onUserClicked.bind(this)}
onDbClicked={this.onDbClicked.bind(this)}
queries={this.state.queriesArray}
actions={this.props.actions}
/>
<div
style={{ height: this.props.height }}
className="scrollbar-container"
>
<div className="scrollbar-content">
<QueryTable
columns={[
'state', 'db', 'user', 'time',
'progress', 'rows', 'sql', 'querylink',
]}
onUserClicked={this.onUserClicked.bind(this)}
onDbClicked={this.onDbClicked.bind(this)}
queries={this.state.queriesArray}
actions={this.props.actions}
/>
</div>
</div>
</div>
)
}
</div>

View File

@ -141,7 +141,7 @@ class QueryTable extends React.PureComponent {
// if query was run using ctas and force_ctas_schema was set
// tempTable will have the schema
const schemaUsed = q.ctas && q.tempTable && q.tempTable.includes('.') ? '' : q.schema;
q.output = [schemaUsed, q.tempTable].filter((v) => (v)).join('.');
q.output = [schemaUsed, q.tempTable].filter(v => (v)).join('.');
}
q.progress = (
<ProgressBar

View File

@ -17,6 +17,7 @@ const propTypes = {
showSql: React.PropTypes.bool,
visualize: React.PropTypes.bool,
cache: React.PropTypes.bool,
resultSetHeight: React.PropTypes.number,
};
const defaultProps = {
search: true,
@ -36,7 +37,6 @@ class ResultSet extends React.PureComponent {
searchText: '',
showModal: false,
data: [],
resultSetHeight: '0',
};
}
componentWillReceiveProps(nextProps) {
@ -46,7 +46,7 @@ class ResultSet extends React.PureComponent {
&& nextProps.query.results.data.length > 0) {
this.setState(
{ data: nextProps.query.results.data },
this.clearQueryResults(nextProps.query)
this.clearQueryResults(nextProps.query),
);
}
if (nextProps.query.resultsKey
@ -204,7 +204,7 @@ class ResultSet extends React.PureComponent {
const newRow = {};
for (const k in row) {
const val = row[k];
if (typeof(val) === 'string') {
if (typeof (val) === 'string') {
newRow[k] = val;
} else {
newRow[k] = JSON.stringify(val);
@ -212,11 +212,11 @@ class ResultSet extends React.PureComponent {
}
return newRow;
})}
columns={results.columns.map((col) => col.name)}
columns={results.columns.map(col => col.name)}
sortable
className="table table-condensed table-bordered"
filterBy={this.state.searchText}
filterable={results.columns.map((c) => c.name)}
filterable={results.columns.map(c => c.name)}
hideFilterInput
/>
</div>

View File

@ -51,6 +51,9 @@ class SaveQuery extends React.PureComponent {
onDescriptionChange(e) {
this.setState({ description: e.target.value });
}
toggleSave(e) {
this.setState({ target: e.target, showSave: !this.state.showSave });
}
renderPopover() {
return (
<Popover id="embed-code-popover">
@ -103,9 +106,6 @@ class SaveQuery extends React.PureComponent {
</Popover>
);
}
toggleSave(e) {
this.setState({ target: e.target, showSave: !this.state.showSave });
}
render() {
return (
<span className="SaveQuery">

View File

@ -1,12 +1,12 @@
import React from 'react';
import shortid from 'shortid';
import { Alert, Tab, Tabs } from 'react-bootstrap';
import QueryHistory from './QueryHistory';
import ResultSet from './ResultSet';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import React from 'react';
import shortid from 'shortid';
import * as Actions from '../actions';
import QueryHistory from './QueryHistory';
import ResultSet from './ResultSet';
/*
editorQueries are queries executed by users passed from SqlEditor component
@ -78,7 +78,7 @@ class SouthPane extends React.PureComponent {
results = <Alert bsStyle="info">Run a query to display results here</Alert>;
}
const dataPreviewTabs = props.dataPreviewQueries.map((query) => (
const dataPreviewTabs = props.dataPreviewQueries.map(query => (
<Tab
title={`Preview for ${query.tableName}`}
eventKey={query.id}

View File

@ -19,7 +19,7 @@ import SaveQuery from './SaveQuery';
import Timer from '../../components/Timer';
import SqlEditorLeftBar from './SqlEditorLeftBar';
import AceEditorWrapper from './AceEditorWrapper';
import { STATE_BSSTYLE_MAP } from '../constants.js';
import { STATE_BSSTYLE_MAP } from '../constants';
import RunQueryActionButton from './RunQueryActionButton';
const propTypes = {
@ -59,6 +59,9 @@ class SqlEditor extends React.PureComponent {
this.startQuery();
}
}
setQueryEditorSql(sql) {
this.props.actions.queryEditorSetSql(this.props.queryEditor, sql);
}
runQuery(runAsync = false) {
let effectiveRunAsync = runAsync;
if (!this.props.database.allow_run_sync) {
@ -87,9 +90,6 @@ class SqlEditor extends React.PureComponent {
createTableAs() {
this.startQuery(true, true);
}
setQueryEditorSql(sql) {
this.props.actions.queryEditorSetSql(this.props.queryEditor, sql);
}
ctasChanged(event) {
this.setState({ ctas: event.target.value });
}

View File

@ -1,11 +1,13 @@
const $ = window.$ = require('jquery');
import React from 'react';
import { Button } from 'react-bootstrap';
import TableElement from './TableElement';
import AsyncSelect from '../../components/AsyncSelect';
import Select from 'react-virtualized-select';
import createFilterOptions from 'react-select-fast-filter-options';
import TableElement from './TableElement';
import AsyncSelect from '../../components/AsyncSelect';
const $ = window.$ = require('jquery');
const propTypes = {
queryEditor: React.PropTypes.object.isRequired,
tables: React.PropTypes.array,
@ -43,8 +45,16 @@ class SqlEditorLeftBar extends React.PureComponent {
this.fetchSchemas(val);
}
}
getTableNamesBySubStr(input) {
if (!this.props.queryEditor.dbId || !input) {
return Promise.resolve({ options: [] });
}
const url = `/superset/tables/${this.props.queryEditor.dbId}/` +
`${this.props.queryEditor.schema}/${input}`;
return $.get(url).then(data => ({ options: data.options }));
}
dbMutator(data) {
const options = data.result.map((db) => ({ value: db.id, label: db.database_name }));
const options = data.result.map(db => ({ value: db.id, label: db.database_name }));
this.props.actions.setDatabases(data.result);
if (data.result.length === 0) {
this.props.actions.addAlert({
@ -57,14 +67,6 @@ class SqlEditorLeftBar extends React.PureComponent {
resetState() {
this.props.actions.resetState();
}
getTableNamesBySubStr(input) {
if (!this.props.queryEditor.dbId || !input) {
return Promise.resolve({ options: [] });
}
const url = `/superset/tables/${this.props.queryEditor.dbId}/` +
`${this.props.queryEditor.schema}/${input}`;
return $.get(url).then((data) => ({ options: data.options }));
}
fetchTables(dbId, schema, substr) {
// This can be large so it shouldn't be put in the Redux store
if (dbId && schema) {
@ -117,7 +119,7 @@ class SqlEditorLeftBar extends React.PureComponent {
this.setState({ schemaLoading: true });
const url = `/superset/schemas/${actualDbId}/`;
$.get(url, (data) => {
const schemaOptions = data.schemas.map((s) => ({ value: s, label: s }));
const schemaOptions = data.schemas.map(s => ({ value: s, label: s }));
this.setState({ schemaOptions });
this.setState({ schemaLoading: false });
});
@ -138,7 +140,7 @@ class SqlEditorLeftBar extends React.PureComponent {
value={this.props.queryEditor.dbId}
databaseId={this.props.queryEditor.dbId}
actions={this.props.actions}
valueRenderer={(o) => (
valueRenderer={o => (
<div>
<span className="text-muted">Database:</span> {o.label}
</div>
@ -153,7 +155,7 @@ class SqlEditorLeftBar extends React.PureComponent {
placeholder={`Select a schema (${this.state.schemaOptions.length})`}
options={this.state.schemaOptions}
value={this.props.queryEditor.schema}
valueRenderer={(o) => (
valueRenderer={o => (
<div>
<span className="text-muted">Schema:</span> {o.label}
</div>
@ -183,7 +185,7 @@ class SqlEditorLeftBar extends React.PureComponent {
name="async-select-table"
ref="selectTable"
value={this.state.tableName}
placeholder={"Type to search ..."}
placeholder={'Type to search ...'}
autosize={false}
onChange={this.changeTable.bind(this)}
loadOptions={this.getTableNamesBySubStr.bind(this)}
@ -192,7 +194,7 @@ class SqlEditorLeftBar extends React.PureComponent {
</div>
<hr />
<div className="m-t-5">
{this.props.tables.map((table) => (
{this.props.tables.map(table => (
<TableElement
table={table}
key={table.id}

View File

@ -2,14 +2,16 @@ import React from 'react';
import { DropdownButton, MenuItem, Tab, Tabs } from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import URI from 'urijs';
import * as Actions from '../actions';
import SqlEditor from './SqlEditor';
import CopyQueryTabUrl from './CopyQueryTabUrl';
import { areArraysShallowEqual } from '../../reduxUtils';
import URI from 'urijs';
const propTypes = {
actions: React.PropTypes.object.isRequired,
defaultDbId: React.PropTypes.number,
databases: React.PropTypes.object.isRequired,
queries: React.PropTypes.object.isRequired,
queryEditors: React.PropTypes.array,
@ -68,11 +70,6 @@ class TabbedSqlEditors extends React.PureComponent {
this.popNewTab();
}
}
popNewTab() {
queryCount++;
// Clean the url in browser history
window.history.replaceState({}, document.title, this.state.sqlLabUrl);
}
componentWillReceiveProps(nextProps) {
const nextActiveQeId = nextProps.tabHistory[nextProps.tabHistory.length - 1];
const queriesArray = [];
@ -96,6 +93,11 @@ class TabbedSqlEditors extends React.PureComponent {
this.setState({ dataPreviewQueries });
}
}
popNewTab() {
queryCount++;
// Clean the url in browser history
window.history.replaceState({}, document.title, this.state.sqlLabUrl);
}
renameTab(qe) {
/* eslint no-alert: 0 */
const newTitle = prompt('Enter a new title for the tab');
@ -190,7 +192,7 @@ class TabbedSqlEditors extends React.PureComponent {
{isSelected &&
<SqlEditor
height={this.props.editorHeight}
tables={this.props.tables.filter((t) => (t.queryEditorId === qe.id))}
tables={this.props.tables.filter(t => (t.queryEditorId === qe.id))}
queryEditor={qe}
editorQueries={this.state.queriesArray}
dataPreviewQueries={this.state.dataPreviewQueries}

View File

@ -57,6 +57,10 @@ class TableElement extends React.PureComponent {
this.setState({ sortColumns: !this.state.sortColumns });
}
removeFromStore() {
this.props.actions.removeTable(this.props.table);
}
renderHeader() {
const table = this.props.table;
let header;
@ -119,9 +123,6 @@ class TableElement extends React.PureComponent {
);
return metadata;
}
removeFromStore() {
this.props.actions.removeTable(this.props.table);
}
render() {
const table = this.props.table;

View File

@ -129,7 +129,7 @@ class VisualizeModal extends React.PureComponent {
data: JSON.stringify(vizOptions),
},
dataType: 'json',
success: resp => {
success: (resp) => {
const columns = Object.keys(this.state.columns).map(k => this.state.columns[k]);
const data = JSON.parse(resp);
const mainMetric = columns.filter(d => d.agg)[0];
@ -181,7 +181,7 @@ class VisualizeModal extends React.PureComponent {
</div>
);
}
const tableData = this.props.query.results.columns.map((col) => ({
const tableData = this.props.query.results.columns.map(col => ({
column: col.name,
is_dimension: (
<input

View File

@ -1,27 +1,24 @@
const $ = window.$ = require('jquery');
const jQuery = window.jQuery = $; // eslint-disable-line
require('bootstrap');
import React from 'react';
import { render } from 'react-dom';
import { getInitialState, sqlLabReducer } from './reducers';
import { initEnhancer } from '../reduxUtils';
import { initJQueryAjaxCSRF } from '../modules/utils';
import { createStore, compose, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunkMiddleware from 'redux-thunk';
import { getInitialState, sqlLabReducer } from './reducers';
import { initEnhancer } from '../reduxUtils';
import { initJQueryAjaxCSRF } from '../modules/utils';
import App from './components/App';
import { appSetup } from '../common';
import './main.css';
require('./main.css');
appSetup();
initJQueryAjaxCSRF();
const appContainer = document.getElementById('app');
const bootstrapData = JSON.parse(appContainer.getAttribute('data-bootstrap'));
const state = Object.assign({}, getInitialState(bootstrapData.defaultDbId), bootstrapData);
let store = createStore(
const store = createStore(
sqlLabReducer, state, compose(applyMiddleware(thunkMiddleware), initEnhancer()));
// jquery hack to highlight the navbar menu
@ -31,5 +28,5 @@ render(
<Provider store={store}>
<App />
</Provider>,
appContainer
appContainer,
);

View File

@ -2,7 +2,7 @@ import shortid from 'shortid';
import * as actions from './actions';
import { now } from '../modules/dates';
import { addToObject, alterInObject, alterInArr, removeFromArr, getFromArr, addToArr }
from '../reduxUtils.js';
from '../reduxUtils';
export function getInitialState(defaultDbId) {
const defaultQueryEditor = {
@ -36,7 +36,7 @@ export const sqlLabReducer = function (state, action) {
return addToArr(newState, 'queryEditors', action.queryEditor);
},
[actions.CLONE_QUERY_TO_NEW_TAB]() {
const progenitor = state.queryEditors.find((qe) =>
const progenitor = state.queryEditors.find(qe =>
qe.id === state.tabHistory[state.tabHistory.length - 1]);
const qe = {
id: shortid.generate(),
@ -52,7 +52,7 @@ export const sqlLabReducer = function (state, action) {
[actions.REMOVE_QUERY_EDITOR]() {
let newState = removeFromArr(state, 'queryEditors', action.queryEditor);
// List of remaining queryEditor ids
const qeIds = newState.queryEditors.map((qe) => qe.id);
const qeIds = newState.queryEditors.map(qe => qe.id);
const queries = {};
Object.keys(state.queries).forEach((k) => {
const query = state.queries[k];
@ -61,7 +61,7 @@ export const sqlLabReducer = function (state, action) {
}
});
let tabHistory = state.tabHistory.slice();
tabHistory = tabHistory.filter((id) => qeIds.indexOf(id) > -1);
tabHistory = tabHistory.filter(id => qeIds.indexOf(id) > -1);
newState = Object.assign({}, newState, { tabHistory, queries });
return newState;
},
@ -187,7 +187,7 @@ export const sqlLabReducer = function (state, action) {
return alterInObject(state, 'queries', action.query, alts);
},
[actions.SET_ACTIVE_QUERY_EDITOR]() {
const qeIds = state.queryEditors.map((qe) => qe.id);
const qeIds = state.queryEditors.map(qe => qe.id);
if (qeIds.indexOf(action.queryEditor.id) > -1) {
const tabHistory = state.tabHistory.slice();
tabHistory.push(action.queryEditor.id);

View File

@ -1,5 +1,8 @@
const $ = require('jquery');
/* eslint-disable global-require */
import $ from 'jquery';
const utils = require('./modules/utils');
$(document).ready(function () {
$(':checkbox[data-checkbox-api-prefix]').change(function () {
const $this = $(this);
@ -8,3 +11,11 @@ $(document).ready(function () {
utils.toggleCheckbox(prefix, '#' + id);
});
});
export function appSetup() {
// A set of hacks to allow apps to run within a FAB template
// this allows for the server side generated menus to function
window.$ = $;
window.jQuery = $;
require('bootstrap');
}

View File

@ -5,7 +5,7 @@ export default class AlertsWrapper extends React.PureComponent {
render() {
return (
<AlertContainer
ref={ref => {
ref={(ref) => {
global.notify = ref;
}}
offset={14}

View File

@ -1,7 +1,8 @@
const $ = window.$ = require('jquery');
import React from 'react';
import Select from 'react-select';
const $ = window.$ = require('jquery');
const propTypes = {
dataEndpoint: React.PropTypes.string.isRequired,
onChange: React.PropTypes.func.isRequired,
@ -13,7 +14,7 @@ const propTypes = {
const defaultProps = {
placeholder: 'Select ...',
valueRenderer: (o) => (<div>{o.label}</div>),
valueRenderer: o => (<div>{o.label}</div>),
};
class AsyncSelect extends React.PureComponent {
@ -27,6 +28,9 @@ class AsyncSelect extends React.PureComponent {
componentDidMount() {
this.fetchOptions();
}
onChange(opt) {
this.props.onChange(opt);
}
fetchOptions() {
this.setState({ isLoading: true });
const mutator = this.props.mutator;
@ -34,9 +38,6 @@ class AsyncSelect extends React.PureComponent {
this.setState({ options: mutator ? mutator(data) : data, isLoading: false });
});
}
onChange(opt) {
this.props.onChange(opt);
}
render() {
return (
<div>

View File

@ -97,7 +97,7 @@ export default class CopyToClipboard extends React.Component {
onClick={this.onClick.bind(this)}
onMouseOut={this.onMouseOut}
>
{this.props.copyNode}
{this.props.copyNode}
</OverlayTrigger>
</span>
);

View File

@ -1,7 +1,7 @@
import React, { PropTypes } from 'react';
import { Modal } from 'react-bootstrap';
import Button from './Button';
import cx from 'classnames';
import Button from './Button';
const propTypes = {
triggerNode: PropTypes.node.isRequired,

View File

@ -1,2 +1,2 @@
require('../stylesheets/less/index.less');
require('../stylesheets/react-select/select.less');
import '../stylesheets/less/index.less';
import '../stylesheets/react-select/select.less';

View File

@ -1,18 +1,19 @@
const $ = window.$ = require('jquery');
const jQuery = window.jQuery = require('jquery'); // eslint-disable-line
const px = require('../modules/superset');
const d3 = require('d3');
const urlLib = require('url');
const utils = require('../modules/utils');
const { Alert } = require('react-bootstrap');
import React from 'react';
import { render } from 'react-dom';
import d3 from 'd3';
import { Alert } from 'react-bootstrap';
import GridLayout from './components/GridLayout';
import Header from './components/Header';
import { appSetup } from '../common';
require('bootstrap');
require('../../stylesheets/dashboard.css');
import '../../stylesheets/dashboard.css';
const px = require('../modules/superset');
const urlLib = require('url');
const utils = require('../modules/utils');
appSetup();
export function getInitialState(boostrapData) {
const dashboard = Object.assign({}, utils.controllerInterface, boostrapData.dashboard_data);
@ -20,7 +21,7 @@ export function getInitialState(boostrapData) {
dashboard.posDict = {};
if (dashboard.position_json) {
dashboard.position_json.forEach(position => {
dashboard.position_json.forEach((position) => {
dashboard.posDict[position.slice_id] = position;
});
}
@ -52,19 +53,19 @@ function renderAlert() {
button on the top right to save your changes.
</Alert>
</div>,
document.getElementById('alert-container')
document.getElementById('alert-container'),
);
}
function initDashboardView(dashboard) {
render(
<Header dashboard={dashboard} />,
document.getElementById('dashboard-header')
document.getElementById('dashboard-header'),
);
// eslint-disable-next-line no-param-reassign
dashboard.reactGridLayout = render(
<GridLayout dashboard={dashboard} />,
document.getElementById('grid-container')
document.getElementById('grid-container'),
);
// Displaying widget controls on hover
@ -74,7 +75,7 @@ function initDashboardView(dashboard) {
},
function () {
$(this).find('.chart-controls').fadeOut(300);
}
},
);
$('div.grid-container').css('visibility', 'visible');
@ -100,7 +101,7 @@ export function dashboardContainer(dashboard, datasources) {
filters: {},
init() {
this.sliceObjects = [];
dashboard.slices.forEach(data => {
dashboard.slices.forEach((data) => {
if (data.error) {
const html = `<div class="alert alert-danger">${data.error}</div>`;
$(`#slice_${data.slice_id}`).find('.token').html(html);
@ -235,7 +236,7 @@ export function dashboardContainer(dashboard, datasources) {
const dash = this;
const maxRandomDelay = Math.max(interval * 0.2, 5000);
const refreshAll = () => {
dash.sliceObjects.forEach(slice => {
dash.sliceObjects.forEach((slice) => {
const force = !dash.firstLoad;
setTimeout(() => {
slice.render(force);
@ -258,7 +259,7 @@ export function dashboardContainer(dashboard, datasources) {
},
refreshExcept(sliceId) {
const immune = this.metadata.filter_immune_slices || [];
this.sliceObjects.forEach(slice => {
this.sliceObjects.forEach((slice) => {
if (slice.data.slice_id !== sliceId && immune.indexOf(slice.data.slice_id) === -1) {
slice.render();
const sliceSeletor = $(`#${slice.data.slice_id}-cell`);

View File

@ -9,14 +9,13 @@ const propTypes = {
};
const defaultProps = {
codeCallback: () => {},
};
export default class CodeModal extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
refreshFrequency: props.initialRefreshFrequency,
};
this.state = { code: props.code };
}
beforeOpen() {
let code = this.props.code;

View File

@ -1,8 +1,6 @@
const $ = window.$ = require('jquery');
import React from 'react';
import { ButtonGroup } from 'react-bootstrap';
import Button from '../../components/Button';
import CssEditor from './CssEditor';
import RefreshIntervalModal from './RefreshIntervalModal';
@ -10,6 +8,8 @@ import SaveModal from './SaveModal';
import CodeModal from './CodeModal';
import SliceAdder from './SliceAdder';
const $ = window.$ = require('jquery');
const propTypes = {
dashboard: React.PropTypes.object.isRequired,
};
@ -22,14 +22,9 @@ class Controls extends React.PureComponent {
cssTemplates: [],
};
}
refresh() {
this.props.dashboard.sliceObjects.forEach((slice) => {
slice.render(true);
});
}
componentWillMount() {
$.get('/csstemplateasyncmodelview/api/read', (data) => {
const cssTemplates = data.result.map((row) => ({
const cssTemplates = data.result.map(row => ({
value: row.template_name,
css: row.css,
label: row.template_name,
@ -37,6 +32,11 @@ class Controls extends React.PureComponent {
this.setState({ cssTemplates });
});
}
refresh() {
this.props.dashboard.sliceObjects.forEach((slice) => {
slice.render(true);
});
}
changeCss(css) {
this.setState({ css });
this.props.dashboard.onChange();
@ -99,7 +99,7 @@ class Controls extends React.PureComponent {
<Button
onClick={() => { window.location = emailLink; }}
>
<i className="fa fa-envelope"></i>
<i className="fa fa-envelope" />
</Button>
</ButtonGroup>
);

View File

@ -1,12 +1,11 @@
import React from 'react';
import ModalTrigger from '../../components/ModalTrigger';
import Select from 'react-select';
import AceEditor from 'react-ace';
import 'brace/mode/css';
import 'brace/theme/github';
import ModalTrigger from '../../components/ModalTrigger';
const propTypes = {
initialCss: React.PropTypes.string,

View File

@ -1,10 +1,12 @@
import $ from 'jquery';
import React, { PropTypes } from 'react';
import { Responsive, WidthProvider } from 'react-grid-layout';
import $ from 'jquery';
import SliceCell from './SliceCell';
require('react-grid-layout/css/styles.css');
require('react-resizable/css/styles.css');
const ResponsiveReactGridLayout = WidthProvider(Responsive);
const propTypes = {

View File

@ -25,7 +25,7 @@ class Header extends React.PureComponent {
</h1>
</div>
<div className="pull-right" style={{ marginTop: '35px' }}>
{!this.props.dashboard.standalone_mode &&
{!this.props.dashboard.standalone_mode &&
<Controls dashboard={dashboard} />
}
</div>

View File

@ -1,7 +1,6 @@
import React from 'react';
import ModalTrigger from '../../components/ModalTrigger';
import Select from 'react-select';
import ModalTrigger from '../../components/ModalTrigger';
const propTypes = {
triggerNode: React.PropTypes.node.isRequired,

View File

@ -1,11 +1,10 @@
const $ = window.$ = require('jquery');
import React from 'react';
import { Button, FormControl, FormGroup, Radio } from 'react-bootstrap';
import { getAjaxErrorMsg, showModal } from '../../modules/utils';
import ModalTrigger from '../../components/ModalTrigger';
const $ = window.$ = require('jquery');
const propTypes = {
css: React.PropTypes.string,
dashboard: React.PropTypes.object.isRequired,

View File

@ -1,7 +1,9 @@
import $ from 'jquery';
import React, { PropTypes } from 'react';
import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';
import ModalTrigger from '../../components/ModalTrigger';
require('react-bootstrap-table/css/react-bootstrap-table.css');
const propTypes = {
@ -39,7 +41,7 @@ class SliceAdder extends React.Component {
this.slicesRequest = $.ajax({
url: uri,
type: 'GET',
success: response => {
success: (response) => {
// Prepare slice data for table
const slices = response.result.map(slice => ({
id: slice.id,
@ -54,7 +56,7 @@ class SliceAdder extends React.Component {
slicesLoaded: true,
});
},
error: error => {
error: (error) => {
this.errored = true;
this.setState({
errorMsg: this.props.dashboard.getAjaxErrorMsg(error),

View File

@ -1,3 +1,4 @@
/* eslint-disable react/no-danger */
import React, { PropTypes } from 'react';
const propTypes = {
@ -62,9 +63,7 @@ function SliceCell({ expandedSlices, removeSlice, slice }) {
expandedSlices[String(slice.slice_id)] ? {} : { display: 'none' }
}
dangerouslySetInnerHTML={{ __html: slice.description_markeddown }}
>
</div>
/>
<div className="row chart-container">
<input type="hidden" value="false" />
<div id={'token_' + slice.slice_id} className="token col-md-12">
@ -76,8 +75,7 @@ function SliceCell({ expandedSlices, removeSlice, slice }) {
<div
id={'con_' + slice.slice_id}
className={`slice_container ${slice.form_data.viz_type}`}
>
</div>
/>
</div>
</div>
</div>

View File

@ -1,8 +1,10 @@
/* eslint camelcase: 0 */
const $ = window.$ = require('jquery');
const FAVESTAR_BASE_URL = '/superset/favstar/slice';
import { getExploreUrl } from '../exploreUtils';
const $ = window.$ = require('jquery');
const FAVESTAR_BASE_URL = '/superset/favstar/slice';
export const SET_DATASOURCE_TYPE = 'SET_DATASOURCE_TYPE';
export function setDatasourceType(datasourceType) {
return { type: SET_DATASOURCE_TYPE, datasourceType };

View File

@ -34,6 +34,9 @@ const propTypes = {
viz_type: PropTypes.string.isRequired,
formData: PropTypes.object,
latestQueryFormData: PropTypes.object,
queryResponse: PropTypes.object,
triggerRender: PropTypes.bool,
standalone: PropTypes.bool,
};
class ChartContainer extends React.PureComponent {
@ -45,17 +48,6 @@ class ChartContainer extends React.PureComponent {
};
}
renderViz() {
this.props.actions.renderTriggered();
const mockSlice = this.getMockedSliceObject();
this.setState({ mockSlice });
try {
visMap[this.props.viz_type](mockSlice, this.props.queryResponse);
} catch (e) {
this.props.actions.chartRenderingFailed(e);
}
}
componentDidUpdate(prevProps) {
if (
this.props.queryResponse &&
@ -95,8 +87,8 @@ class ChartContainer extends React.PureComponent {
},
height: getHeight,
show: () => { },
get: (n) => ($(this.state.selector).get(n)),
find: (classname) => ($(this.state.selector).find(classname)),
get: n => ($(this.state.selector).get(n)),
find: classname => ($(this.state.selector).find(classname)),
},
width: () => this.chartContainerRef.getBoundingClientRect().width,
@ -149,6 +141,10 @@ class ChartContainer extends React.PureComponent {
this.props.actions.removeChartAlert();
}
runQuery() {
this.props.actions.runQuery(this.props.formData, true);
}
renderChartTitle() {
let title;
if (this.props.slice) {
@ -159,6 +155,17 @@ class ChartContainer extends React.PureComponent {
return title;
}
renderViz() {
this.props.actions.renderTriggered();
const mockSlice = this.getMockedSliceObject();
this.setState({ mockSlice });
try {
visMap[this.props.viz_type](mockSlice, this.props.queryResponse);
} catch (e) {
this.props.actions.chartRenderingFailed(e);
}
}
renderAlert() {
const msg = (
<div>
@ -204,7 +211,7 @@ class ChartContainer extends React.PureComponent {
}
<div
id={this.props.containerId}
ref={ref => { this.chartContainerRef = ref; }}
ref={(ref) => { this.chartContainerRef = ref; }}
className={this.props.viz_type}
style={{
opacity: loading ? '0.25' : '1',
@ -213,9 +220,6 @@ class ChartContainer extends React.PureComponent {
</div>
);
}
runQuery() {
this.props.actions.runQuery(this.props.formData, true);
}
render() {
if (this.props.standalone) {
@ -262,17 +266,17 @@ class ChartContainer extends React.PureComponent {
{this.props.chartStatus === 'success' &&
this.props.queryResponse &&
this.props.queryResponse.is_cached &&
<TooltipWrapper
tooltip="Loaded from cache. Click to force refresh"
label="cache-desc"
>
<Label
style={{ fontSize: '10px', marginRight: '5px', cursor: 'pointer' }}
onClick={this.runQuery.bind(this)}
>
<TooltipWrapper
tooltip="Loaded from cache. Click to force refresh"
label="cache-desc"
>
<Label
style={{ fontSize: '10px', marginRight: '5px', cursor: 'pointer' }}
onClick={this.runQuery.bind(this)}
>
cached
</Label>
</TooltipWrapper>
</TooltipWrapper>
}
<Timer
startTime={this.props.chartUpdateStartTime}

View File

@ -22,6 +22,7 @@ const propTypes = {
actions: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
type: PropTypes.oneOf(controlTypes).isRequired,
hidden: PropTypes.bool,
label: PropTypes.string.isRequired,
choices: PropTypes.arrayOf(PropTypes.array),
description: PropTypes.string,
@ -40,6 +41,7 @@ const propTypes = {
const defaultProps = {
renderTrigger: false,
validators: [],
hidden: false,
validationErrors: [],
};
@ -64,7 +66,7 @@ export default class Control extends React.PureComponent {
const validators = this.props.validators;
const validationErrors = [];
if (validators && validators.length > 0) {
validators.forEach(f => {
validators.forEach((f) => {
const v = f(value);
if (v) {
validationErrors.push(v);

View File

@ -1,7 +1,6 @@
/* eslint camelcase: 0 */
import React, { PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import * as actions from '../actions/exploreActions';
import { connect } from 'react-redux';
import { Panel, Alert } from 'react-bootstrap';
import { sectionsToRender } from '../stores/visTypes';
@ -9,6 +8,7 @@ import ControlPanelSection from './ControlPanelSection';
import ControlRow from './ControlRow';
import Control from './Control';
import controls from '../stores/controls';
import * as actions from '../actions/exploreActions';
const propTypes = {
actions: PropTypes.object.isRequired,
@ -54,7 +54,7 @@ class ControlPanelsContainer extends React.Component {
/>
</Alert>
}
{this.sectionsToRender().map((section) => (
{this.sectionsToRender().map(section => (
<ControlPanelSection
key={section.label}
label={section.label}

View File

@ -1,8 +1,9 @@
import React, { PropTypes } from 'react';
import ModalTrigger from './../../components/ModalTrigger';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { github } from 'react-syntax-highlighter/dist/styles';
import ModalTrigger from './../../components/ModalTrigger';
const $ = window.$ = require('jquery');
const propTypes = {
@ -37,9 +38,9 @@ export default class DisplayQueryButton extends React.PureComponent {
url: this.props.queryEndpoint,
success: (data) => {
const modalBody = data.language ?
<SyntaxHighlighter language={data.language} style={github}>
(<SyntaxHighlighter language={data.language} style={github}>
{data.query}
</SyntaxHighlighter>
</SyntaxHighlighter>)
:
<pre>{data.query}</pre>;
this.setState({ modalBody });

View File

@ -1,6 +1,6 @@
import React, { PropTypes } from 'react';
import CopyToClipboard from './../../components/CopyToClipboard';
import { Popover, OverlayTrigger } from 'react-bootstrap';
import CopyToClipboard from './../../components/CopyToClipboard';
const propTypes = {
slice: PropTypes.object.isRequired,
@ -56,14 +56,13 @@ export default class EmbedCodeButton extends React.Component {
rows="4"
readOnly
className="form-control input-sm"
>
</textarea>
/>
</div>
<div className="col-sm-2">
<CopyToClipboard
shouldShowText={false}
text={html}
copyNode={<i className="fa fa-clipboard" title="Copy to clipboard"></i>}
copyNode={<i className="fa fa-clipboard" title="Copy to clipboard" />}
/>
</div>
</div>
@ -112,7 +111,7 @@ export default class EmbedCodeButton extends React.Component {
overlay={this.renderPopover()}
>
<span className="btn btn-default btn-sm">
<i className="fa fa-code"></i>&nbsp;
<i className="fa fa-code" />&nbsp;
</span>
</OverlayTrigger>
);

View File

@ -27,8 +27,9 @@ export default function ExploreActionButtons({ canDownload, slice, query, queryE
className="btn btn-default btn-sm"
title="Export to .json"
target="_blank"
rel="noopener noreferrer"
>
<i className="fa fa-file-code-o"></i> .json
<i className="fa fa-file-code-o" /> .json
</a>
<a
@ -36,8 +37,9 @@ export default function ExploreActionButtons({ canDownload, slice, query, queryE
className={exportToCSVClasses}
title="Export to .csv format"
target="_blank"
rel="noopener noreferrer"
>
<i className="fa fa-file-text-o"></i> .csv
<i className="fa fa-file-text-o" /> .csv
</a>
<DisplayQueryButton

View File

@ -1,19 +1,19 @@
/* eslint camelcase: 0 */
import React, { PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import * as actions from '../actions/exploreActions';
import { connect } from 'react-redux';
import ChartContainer from './ChartContainer';
import ControlPanelsContainer from './ControlPanelsContainer';
import SaveModal from './SaveModal';
import QueryAndSaveBtns from './QueryAndSaveBtns';
import { getExploreUrl } from '../exploreUtils';
import * as actions from '../actions/exploreActions';
import { getFormDataFromControls } from '../stores/store';
const propTypes = {
actions: PropTypes.object.isRequired,
datasource_type: PropTypes.string.isRequired,
chartStatus: PropTypes.string.isRequired,
chartStatus: PropTypes.string,
controls: PropTypes.object.isRequired,
forcedHeight: PropTypes.string,
form_data: PropTypes.object.isRequired,
@ -110,7 +110,7 @@ class ExploreViewContainer extends React.Component {
<div key={controlName}>
<strong>{`[ ${control.label} ] `}</strong>
{control.validationErrors.join('. ')}
</div>
</div>,
);
}
}
@ -143,7 +143,7 @@ class ExploreViewContainer extends React.Component {
overflow: 'hidden',
}}
>
{this.state.showModal &&
{this.state.showModal &&
<SaveModal
onHide={this.toggleModal.bind(this)}
actions={this.props.actions}

View File

@ -1,8 +1,9 @@
import React, { PropTypes } from 'react';
import { ButtonGroup, OverlayTrigger, Tooltip } from 'react-bootstrap';
import Button from '../../components/Button';
import classnames from 'classnames';
import Button from '../../components/Button';
const propTypes = {
canAdd: PropTypes.string.isRequired,
onQuery: PropTypes.func.isRequired,
@ -54,7 +55,7 @@ export default function QueryAndSaveBtns(
disabled={saveButtonDisabled}
onClick={onSave}
>
<i className="fa fa-plus-circle"></i> Save as
<i className="fa fa-plus-circle" /> Save as
</Button>
</ButtonGroup>
{errorMessage &&

View File

@ -146,7 +146,7 @@ class SaveModal extends React.Component {
checked={this.state.action === 'overwrite'}
onChange={this.changeAction.bind(this, 'overwrite')}
>
{`Overwrite slice ${this.props.slice.slice_name}`}
{`Overwrite slice ${this.props.slice.slice_name}`}
</Radio>
}

View File

@ -32,11 +32,11 @@ export default class URLShortLinkButton extends React.Component {
<Popover id="shorturl-popover">
<CopyToClipboard
text={this.state.shortUrl}
copyNode={<i className="fa fa-clipboard" title="Copy to clipboard"></i>}
copyNode={<i className="fa fa-clipboard" title="Copy to clipboard" />}
/>
&nbsp;&nbsp;
<a href={`mailto:?Subject=Superset%20Slice%20&Body=${emailBody}`}>
<i className="fa fa-envelope"></i>
<i className="fa fa-envelope" />
</a>
</Popover>
);
@ -52,7 +52,7 @@ export default class URLShortLinkButton extends React.Component {
overlay={this.renderPopover()}
>
<span className="btn btn-default btn-sm">
<i className="fa fa-link"></i>&nbsp;
<i className="fa fa-link" />&nbsp;
</span>
</OverlayTrigger>
);

View File

@ -1,9 +1,10 @@
const $ = window.$ = require('jquery');
import React, { PropTypes } from 'react';
import Select from 'react-select';
import { Button, Row, Col } from 'react-bootstrap';
import SelectControl from './SelectControl';
const $ = window.$ = require('jquery');
const operatorsArr = [
{ val: 'in', type: 'array', useSelect: true, multi: true },
{ val: 'not in', type: 'array', useSelect: true, multi: true },
@ -17,7 +18,7 @@ const operatorsArr = [
{ val: 'LIKE', type: 'string', datasourceTypes: ['table'] },
];
const operators = {};
operatorsArr.forEach(op => {
operatorsArr.forEach((op) => {
operators[op.val] = op;
});

View File

@ -13,11 +13,9 @@ const defaultProps = {
onChange: () => {},
};
export default class HiddenControl extends React.PureComponent {
render() {
// This wouldn't be necessary but might as well
return <FormControl type="hidden" value={this.props.value} />;
}
export default function HiddenControl(props) {
// This wouldn't be necessary but might as well
return <FormControl type="hidden" value={props.value} />;
}
HiddenControl.propTypes = propTypes;

View File

@ -42,13 +42,13 @@ export default class SelectControl extends React.PureComponent {
let optionValue = opt ? opt.value : null;
// if multi, return options values as an array
if (this.props.multi) {
optionValue = opt ? opt.map((o) => o.value) : null;
optionValue = opt ? opt.map(o => o.value) : null;
}
this.props.onChange(optionValue);
}
getOptions(props) {
// Accepts different formats of input
const options = props.choices.map(c => {
const options = props.choices.map((c) => {
let option;
if (Array.isArray(c)) {
const label = c.length > 1 ? c[1] : c[0];
@ -75,7 +75,7 @@ export default class SelectControl extends React.PureComponent {
if (!Array.isArray(valuesToAdd)) {
valuesToAdd = [valuesToAdd];
}
valuesToAdd.forEach(v => {
valuesToAdd.forEach((v) => {
if (values.indexOf(v) < 0) {
options.push({ value: v, label: v });
}

View File

@ -1,22 +1,20 @@
/* eslint camelcase: 0 */
import React from 'react';
import ReactDOM from 'react-dom';
import ExploreViewContainer from './components/ExploreViewContainer';
import { createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { now } from '../modules/dates';
import { initEnhancer } from '../reduxUtils';
import AlertsWrapper from '../components/AlertsWrapper';
import { getControlsState, getFormDataFromControls } from './stores/store';
import { initJQueryAjaxCSRF } from '../modules/utils';
import ExploreViewContainer from './components/ExploreViewContainer';
import { exploreReducer } from './reducers/exploreReducer';
import { appSetup } from '../common';
// jquery and bootstrap required to make bootstrap dropdown menu's work
const $ = window.$ = require('jquery'); // eslint-disable-line
const jQuery = window.jQuery = require('jquery'); // eslint-disable-line
require('bootstrap');
require('./main.css');
appSetup();
initJQueryAjaxCSRF();
const exploreViewContainer = document.getElementById('js-explore-view-container');
@ -24,7 +22,6 @@ const bootstrapData = JSON.parse(exploreViewContainer.getAttribute('data-bootstr
const controls = getControlsState(bootstrapData, bootstrapData.form_data);
delete bootstrapData.form_data;
import { exploreReducer } from './reducers/exploreReducer';
// Initial state
const bootstrappedState = Object.assign(
@ -41,11 +38,11 @@ const bootstrappedState = Object.assign(
queryResponse: null,
triggerQuery: true,
triggerRender: false,
}
},
);
const store = createStore(exploreReducer, bootstrappedState,
compose(applyMiddleware(thunk), initEnhancer(false))
compose(applyMiddleware(thunk), initEnhancer(false)),
);
ReactDOM.render(
@ -55,5 +52,5 @@ ReactDOM.render(
<AlertsWrapper />
</div>
</Provider>,
exploreViewContainer
exploreViewContainer,
);

View File

@ -77,7 +77,7 @@ export const exploreReducer = function (state, action) {
{
chartStatus: 'success',
queryResponse: action.queryResponse,
}
},
);
},
[actions.CHART_UPDATE_STARTED]() {

View File

@ -1,5 +1,5 @@
import { formatSelectOptionsForRange, formatSelectOptions } from '../../modules/utils';
import React from 'react';
import { formatSelectOptionsForRange, formatSelectOptions } from '../../modules/utils';
import visTypes from './visTypes';
import * as v from '../validators';
@ -67,7 +67,7 @@ export const controls = {
validators: [v.nonEmpty],
default: control =>
control.choices && control.choices.length > 0 ? [control.choices[0][0]] : null,
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.metrics_combo : [],
}),
description: 'One or many metrics to display',
@ -79,7 +79,7 @@ export const controls = {
label: 'Ordering',
default: [],
description: 'One or many metrics to display',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.order_by_choices : [],
}),
},
@ -91,7 +91,7 @@ export const controls = {
description: 'Choose the metric',
default: control =>
control.choices && control.choices.length > 0 ? control.choices[0][0] : null,
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.metrics_combo : null,
}),
},
@ -102,7 +102,7 @@ export const controls = {
choices: [],
default: [],
description: 'Choose a metric for right axis',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.metrics_combo : [],
}),
},
@ -261,7 +261,7 @@ export const controls = {
label: 'Color Metric',
default: null,
description: 'A metric to use for color',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.metrics_combo : [],
}),
},
@ -286,7 +286,7 @@ export const controls = {
label: 'Group by',
default: [],
description: 'One or many controls to group by',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.gb_cols : [],
}),
},
@ -295,7 +295,7 @@ export const controls = {
type: 'SelectControl',
multi: true,
label: 'Columns',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.gb_cols : [],
}),
default: [],
@ -308,7 +308,7 @@ export const controls = {
label: 'Columns',
default: [],
description: 'Columns to display',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.all_cols : [],
}),
},
@ -318,7 +318,7 @@ export const controls = {
label: 'X',
default: null,
description: 'Columns to display',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.all_cols : [],
}),
},
@ -328,7 +328,7 @@ export const controls = {
label: 'Y',
default: null,
description: 'Columns to display',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.all_cols : [],
}),
},
@ -436,7 +436,7 @@ export const controls = {
'column in the table or. Also note that the ' +
'filter below is applied against this column or ' +
'expression',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.granularity_sqla : [],
}),
},
@ -450,7 +450,7 @@ export const controls = {
'your time column and defines a new time granularity. ' +
'The options here are defined on a per database ' +
'engine basis in the Superset source code.',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.time_grain_sqla : null,
}),
},
@ -577,7 +577,7 @@ export const controls = {
label: 'Sort By',
default: null,
description: 'Metric used to define the top series',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.metrics_combo : [],
}),
},
@ -606,7 +606,7 @@ export const controls = {
description: 'Defines the grouping of entities. ' +
'Each series is shown as a specific color on the chart and ' +
'has a legend toggle',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.gb_cols : [],
}),
},
@ -616,7 +616,7 @@ export const controls = {
label: 'Entity',
default: null,
description: 'This define the element to be plotted on the chart',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.gb_cols : [],
}),
},
@ -626,7 +626,7 @@ export const controls = {
label: 'X Axis',
default: null,
description: 'Metric assigned to the [X] axis',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.metrics_combo : [],
}),
},
@ -636,7 +636,7 @@ export const controls = {
label: 'Y Axis',
default: null,
description: 'Metric assigned to the [Y] axis',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.metrics_combo : [],
}),
},
@ -645,7 +645,7 @@ export const controls = {
type: 'SelectControl',
label: 'Bubble Size',
default: null,
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.metrics_combo : [],
}),
},
@ -1009,7 +1009,7 @@ export const controls = {
'Numerical columns will be aggregated with the aggregator. ' +
'Non-numerical columns will be used to label points. ' +
'Leave empty to get a count of points in each cluster.',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.all_cols : [],
}),
},
@ -1057,7 +1057,7 @@ export const controls = {
description: 'The radius of individual points (ones that are not in a cluster). ' +
'Either a numerical column or `Auto`, which scales the point based ' +
'on the largest cluster',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: [].concat([['Auto', 'Auto']], state.datasource.all_cols),
}),
},
@ -1176,7 +1176,7 @@ export const controls = {
label: '',
default: [],
description: '',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
datasource: state.datasource,
}),
},
@ -1186,7 +1186,7 @@ export const controls = {
label: '',
default: [],
description: '',
mapStateToProps: (state) => ({
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.metrics_combo
.concat(state.datasource.filterable_cols) : [],
datasource: state.datasource,

View File

@ -4,7 +4,7 @@ import visTypes, { sectionsToRender } from './visTypes';
export function getFormDataFromControls(controlsState) {
const formData = {};
Object.keys(controlsState).forEach(controlName => {
Object.keys(controlsState).forEach((controlName) => {
formData[controlName] = controlsState[controlName].value;
});
return formData;
@ -78,7 +78,7 @@ export function applyDefaultFormData(form_data) {
const controlNames = getControlNames(vizType, datasourceType);
const controlOverrides = viz.controlOverrides || {};
const formData = {};
controlNames.forEach(k => {
controlNames.forEach((k) => {
const control = Object.assign({}, controls[k]);
if (controlOverrides[k]) {
Object.assign(control, controlOverrides[k]);

View File

@ -762,6 +762,6 @@ export function sectionsToRender(vizType, datasourceType) {
datasourceType === 'table' ? sections.sqlaTimeSeries : sections.druidTimeSeries,
viz.controlPanelSections,
datasourceType === 'table' ? sections.sqlClause : [],
datasourceType === 'table' ? sections.filters[0] : sections.filters
datasourceType === 'table' ? sections.filters[0] : sections.filters,
);
}

View File

@ -1,5 +1,5 @@
import $ from 'jquery';
const d3 = require('d3');
import d3 from 'd3';
// Color related utility functions go in this object
export const bnbColors = [

View File

@ -9,7 +9,7 @@ function UTC(dttm) {
dttm.getUTCDate(),
dttm.getUTCHours(),
dttm.getUTCMinutes(),
dttm.getUTCSeconds()
dttm.getUTCSeconds(),
);
}
export const tickMultiFormat = d3.time.format.multi([

View File

@ -1,12 +1,12 @@
import $ from 'jquery';
const utils = require('./utils');
// vis sources
/* eslint camel-case: 0 */
import $ from 'jquery';
import Mustache from 'mustache';
import vizMap from '../../visualizations/main.js';
import vizMap from '../../visualizations/main';
import { getExploreUrl } from '../explorev2/exploreUtils';
import { applyDefaultFormData } from '../explorev2/stores/store';
const utils = require('./utils');
/* eslint wrap-iife: 0*/
const px = function () {
let slice;
@ -217,14 +217,14 @@ const px = function () {
timer = setInterval(stopwatch, 10);
$('#timer').removeClass('label-danger label-success');
$('#timer').addClass('label-warning');
$.getJSON(this.jsonEndpoint(), queryResponse => {
$.getJSON(this.jsonEndpoint(), (queryResponse) => {
try {
vizMap[formData.viz_type](this, queryResponse);
this.done(queryResponse);
} catch (e) {
this.error('An error occurred while rendering the visualization: ' + e);
}
}).fail(err => {
}).fail((err) => {
this.error(err.responseText, err);
});
},

View File

@ -143,8 +143,8 @@ export function formatSelectOptionsForRange(start, end) {
}
export function formatSelectOptions(options) {
return options.map((opt) =>
[opt, opt.toString()]
return options.map(opt =>
[opt, opt.toString()],
);
}

View File

@ -17,7 +17,7 @@ class CreatedContent extends React.PureComponent {
};
}
renderSliceTable() {
const mutator = (data) => data.map(slice => ({
const mutator = data => data.map(slice => ({
slice: <a href={slice.url}>{slice.title}</a>,
favorited: moment.utc(slice.dttm).fromNow(),
_favorited: slice.dttm,
@ -34,7 +34,7 @@ class CreatedContent extends React.PureComponent {
);
}
renderDashboardTable() {
const mutator = (data) => data.map(dash => ({
const mutator = data => data.map(dash => ({
dashboard: <a href={dash.url}>{dash.title}</a>,
favorited: moment.utc(dash.dttm).fromNow(),
_favorited: dash.dttm,

View File

@ -17,7 +17,7 @@ export default class Favorites extends React.PureComponent {
};
}
renderSliceTable() {
const mutator = (data) => data.map(slice => ({
const mutator = data => data.map(slice => ({
slice: <a href={slice.url}>{slice.title}</a>,
creator: <a href={slice.creator_url}>{slice.creator}</a>,
favorited: moment.utc(slice.dttm).fromNow(),
@ -35,7 +35,7 @@ export default class Favorites extends React.PureComponent {
);
}
renderDashboardTable() {
const mutator = (data) => data.map(dash => ({
const mutator = data => data.map(dash => ({
dashboard: <a href={dash.url}>{dash.title}</a>,
creator: <a href={dash.creator_url}>{dash.creator}</a>,
favorited: moment.utc(dash.dttm).fromNow(),

View File

@ -1,8 +1,9 @@
import React from 'react';
import TableLoader from './TableLoader';
import moment from 'moment';
import $ from 'jquery';
import TableLoader from './TableLoader';
const propTypes = {
user: React.PropTypes.object,
};

View File

@ -44,7 +44,7 @@ export default class TableLoader extends React.PureComponent {
<Table {...tableProps}>
{this.state.data.map((row, i) => (
<Tr key={i}>
{columns.map(col => {
{columns.map((col) => {
if (row.hasOwnProperty('_' + col)) {
return (
<Td key={col} column={col} value={row['_' + col]}>

View File

@ -1,15 +1,13 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import ReactDOM from 'react-dom';
import { Badge, Col, Label, Row, Tabs, Tab, Panel } from 'react-bootstrap';
import App from './components/App';
import { appSetup } from '../common';
const $ = window.$ = require('jquery');
/* eslint no-unused-vars: 0 */
const jQuery = window.jQuery = $;
require('bootstrap');
require('./main.css');
import './main.css';
appSetup();
const profileViewContainer = document.getElementById('app');
const bootstrap = JSON.parse(profileViewContainer.getAttribute('data-bootstrap'));
@ -17,5 +15,5 @@ const bootstrap = JSON.parse(profileViewContainer.getAttribute('data-bootstrap')
const user = bootstrap.user;
ReactDOM.render(
<App user={user} />,
profileViewContainer
profileViewContainer,
);

View File

@ -99,7 +99,7 @@ export function areObjectsEqual(obj1, obj2) {
if (!obj1 || !obj2) {
return false;
}
if (! Object.keys(obj1).length !== Object.keys(obj2).length) {
if (!Object.keys(obj1).length !== Object.keys(obj2).length) {
return false;
}
for (const id in obj1) {

View File

@ -1,15 +1,16 @@
const $ = window.$ = require('jquery');
/* eslint no-unused-vars: 0 */
const jQuery = window.jQuery = $;
require('../stylesheets/welcome.css');
require('bootstrap');
require('datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
import 'datatables.net';
import dt from 'datatables.net-bs';
import d3 from 'd3';
import '../stylesheets/welcome.css';
import { appSetup } from './common';
appSetup();
require('datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
dt(window, $);
const d3 = require('d3');
function modelViewTable(selector, modelView, orderCol, order) {
// Builds a dataTable from a flask appbuilder api endpoint
let url = '/' + modelView.toLowerCase() + '/api/read';

View File

@ -39,9 +39,10 @@
"homepage": "https://github.com/airbnb/superset#readme",
"dependencies": {
"autobind-decorator": "^1.3.3",
"babel-register": "^6.24.1",
"bootstrap": "^3.3.6",
"bootstrap-datepicker": "^1.6.0",
"brace": "^0.9.1",
"brace": "^0.10.0",
"brfs": "^1.4.3",
"cal-heatmap": "3.6.2",
"classnames": "^2.2.5",
@ -52,29 +53,30 @@
"d3-tip": "^0.7.1",
"datamaps": "^0.5.8",
"datatables-bootstrap3-plugin": "^0.5.0",
"datatables.net": "^1.10.13",
"datatables.net-bs": "^1.10.12",
"font-awesome": "^4.6.3",
"gridster": "^0.5.6",
"immutability-helper": "^2.0.0",
"immutable": "^3.8.1",
"jquery": "^2.2.1",
"jquery": "^3.2.1",
"lodash.throttle": "^4.1.1",
"mapbox-gl": "^0.26.0",
"moment": "^2.14.1",
"moments": "0.0.2",
"mustache": "^2.2.1",
"nvd3": "1.8.5",
"react": "^15.3.2",
"react": "^15.5.1",
"react-ace": "^4.1.5",
"react-addons-css-transition-group": "^15.4.2",
"react-addons-shallow-compare": "^15.4.2",
"react-alert": "^1.0.14",
"react-bootstrap": "^0.30.3",
"react-bootstrap-table": "^2.3.8",
"react-dom": "^15.3.2",
"react-bootstrap-table": "^3.1.7",
"react-dom": "^15.5.1",
"react-draggable": "^2.1.2",
"react-gravatar": "^2.6.1",
"react-grid-layout": "^0.13.1",
"react-grid-layout": "^0.14.4",
"react-map-gl": "^1.7.0",
"react-redux": "^5.0.2",
"react-resizable": "^1.3.3",
@ -87,11 +89,10 @@
"redux-localstorage": "^0.4.1",
"redux-thunk": "^2.1.0",
"shortid": "^2.2.6",
"style-loader": "^0.13.0",
"supercluster": "https://github.com/georgeke/supercluster/tarball/ac3492737e7ce98e07af679623aad452373bbc40",
"topojson": "^1.6.22",
"urijs": "^1.18.10",
"victory": "^0.17.0",
"victory": "^0.18.4",
"viewport-mercator-project": "^2.1.0"
},
"devDependencies": {
@ -105,31 +106,32 @@
"babel-preset-es2015": "^6.14.0",
"babel-preset-react": "^6.11.1",
"chai": "^3.5.0",
"codeclimate-test-reporter": "^0.3.3",
"css-loader": "^0.23.1",
"codeclimate-test-reporter": "^0.4.1",
"css-loader": "^0.28.0",
"enzyme": "^2.0.0",
"eslint": "^2.13.1",
"eslint-config-airbnb": "^9.0.1",
"eslint-plugin-import": "^1.11.1",
"eslint-plugin-jsx-a11y": "^1.2.0",
"eslint-plugin-react": "^5.2.2",
"eslint": "^3.19.0",
"eslint-config-airbnb": "^14.1.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.10.3",
"exports-loader": "^0.6.3",
"file-loader": "^0.8.5",
"file-loader": "^0.11.1",
"github-changes": "^1.0.4",
"ignore-styles": "^5.0.1",
"imports-loader": "^0.6.5",
"imports-loader": "^0.7.1",
"istanbul": "^1.0.0-alpha",
"jsdom": "^8.0.1",
"jsdom": "^9.12.0",
"json-loader": "^0.5.4",
"less": "^2.6.1",
"less-loader": "^2.2.2",
"mocha": "^2.4.5",
"react-addons-test-utils": "^15.3.2",
"sinon": "^1.17.6",
"style-loader": "^0.13.0",
"less-loader": "^4.0.3",
"mocha": "^3.2.0",
"react-addons-test-utils": "^15.5.1",
"react-test-renderer": "^15.5.1",
"sinon": "^2.1.0",
"style-loader": "^0.16.1",
"transform-loader": "^0.2.3",
"url-loader": "^0.5.7",
"webpack": "^1.13.1",
"webworkify-webpack": "1.0.6"
"webpack": "^2.3.3",
"webworkify-webpack": "2.0.4"
}
}

View File

@ -1,11 +1,12 @@
import React from 'react';
import Select from 'react-select';
import AsyncSelect from '../../../javascripts/components/AsyncSelect';
import { shallow } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import sinon from 'sinon';
import AsyncSelect from '../../../javascripts/components/AsyncSelect';
describe('AsyncSelect', () => {
const mockedProps = {
dataEndpoint: '/slicemodelview/api/read',
@ -14,20 +15,20 @@ describe('AsyncSelect', () => {
};
it('is valid element', () => {
expect(
React.isValidElement(<AsyncSelect {...mockedProps} />)
React.isValidElement(<AsyncSelect {...mockedProps} />),
).to.equal(true);
});
it('has one select', () => {
const wrapper = shallow(
<AsyncSelect {...mockedProps} />
<AsyncSelect {...mockedProps} />,
);
expect(wrapper.find(Select)).to.have.length(1);
});
it('calls onChange on select change', () => {
const wrapper = shallow(
<AsyncSelect {...mockedProps} />
<AsyncSelect {...mockedProps} />,
);
wrapper.find(Select).simulate('change', { value: 1 });
expect(mockedProps.onChange).to.have.property('callCount', 1);

View File

@ -11,7 +11,7 @@ describe('CopyToClipboard', () => {
it('renders', () => {
expect(
React.isValidElement(<CopyToClipboard {...defaultProps} />)
React.isValidElement(<CopyToClipboard {...defaultProps} />),
).to.equal(true);
});
});

View File

@ -13,7 +13,7 @@ describe('ModalTrigger', () => {
it('is a valid element', () => {
expect(
React.isValidElement(<ModalTrigger {...defaultProps} />)
React.isValidElement(<ModalTrigger {...defaultProps} />),
).to.equal(true);
});
});

View File

@ -1,9 +1,9 @@
import React from 'react';
import CodeModal from '../../../javascripts/dashboard/components/CodeModal';
import { mount } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import CodeModal from '../../../javascripts/dashboard/components/CodeModal';
describe('CodeModal', () => {
const mockedProps = {
@ -11,7 +11,7 @@ describe('CodeModal', () => {
};
it('is valid', () => {
expect(
React.isValidElement(<CodeModal {...mockedProps} />)
React.isValidElement(<CodeModal {...mockedProps} />),
).to.equal(true);
});
it('renders the trigger node', () => {

View File

@ -1,9 +1,9 @@
import React from 'react';
import CssEditor from '../../../javascripts/dashboard/components/CssEditor';
import { mount } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import CssEditor from '../../../javascripts/dashboard/components/CssEditor';
describe('CssEditor', () => {
const mockedProps = {
@ -11,7 +11,7 @@ describe('CssEditor', () => {
};
it('is valid', () => {
expect(
React.isValidElement(<CssEditor {...mockedProps} />)
React.isValidElement(<CssEditor {...mockedProps} />),
).to.equal(true);
});
it('renders the trigger node', () => {

View File

@ -1,9 +1,9 @@
import React from 'react';
import RefreshIntervalModal from '../../../javascripts/dashboard/components/RefreshIntervalModal';
import { mount } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import RefreshIntervalModal from '../../../javascripts/dashboard/components/RefreshIntervalModal';
describe('RefreshIntervalModal', () => {
const mockedProps = {
@ -11,7 +11,7 @@ describe('RefreshIntervalModal', () => {
};
it('is valid', () => {
expect(
React.isValidElement(<RefreshIntervalModal {...mockedProps} />)
React.isValidElement(<RefreshIntervalModal {...mockedProps} />),
).to.equal(true);
});
it('renders the trigger node', () => {

View File

@ -1,10 +1,10 @@
import React from 'react';
import SliceCell from '../../../javascripts/dashboard/components/SliceCell';
import { mount } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { slice } from './fixtures';
import SliceCell from '../../../javascripts/dashboard/components/SliceCell';
describe('SliceCell', () => {
const mockedProps = {
@ -14,7 +14,7 @@ describe('SliceCell', () => {
};
it('is valid', () => {
expect(
React.isValidElement(<SliceCell {...mockedProps} />)
React.isValidElement(<SliceCell {...mockedProps} />),
).to.equal(true);
});
it('renders five links', () => {

View File

@ -5,6 +5,7 @@ import sinon from 'sinon';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import CheckboxControl from '../../../../javascripts/explorev2/components/controls/CheckboxControl';
const defaultProps = {

View File

@ -18,7 +18,7 @@ describe('ExploreActionButtons', () => {
it('renders', () => {
expect(
React.isValidElement(<ExploreActionButtons {...defaultProps} />)
React.isValidElement(<ExploreActionButtons {...defaultProps} />),
).to.equal(true);
});

View File

@ -30,7 +30,7 @@ describe('FilterControl', () => {
it('renders Filters', () => {
expect(
React.isValidElement(<FilterControl {...defaultProps} />)
React.isValidElement(<FilterControl {...defaultProps} />),
).to.equal(true);
});

View File

@ -34,7 +34,7 @@ describe('Filter', () => {
it('renders Filters', () => {
expect(
React.isValidElement(<Filter {...defaultProps} />)
React.isValidElement(<Filter {...defaultProps} />),
).to.equal(true);
});

View File

@ -24,7 +24,7 @@ describe('RunQueryActionButton', () => {
it('is a valid react element', () => {
expect(
React.isValidElement(<RunQueryActionButton {...defaultProps} />)
React.isValidElement(<RunQueryActionButton {...defaultProps} />),
).to.equal(true);
});

View File

@ -2,11 +2,12 @@ import React from 'react';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import { defaultFormData } from '../../../../javascripts/explorev2/stores/store';
import { SaveModal } from '../../../../javascripts/explorev2/components/SaveModal';
import { Modal, Button, Radio } from 'react-bootstrap';
import sinon from 'sinon';
import { defaultFormData } from '../../../../javascripts/explorev2/stores/store';
import { SaveModal } from '../../../../javascripts/explorev2/components/SaveModal';
const defaultProps = {
can_edit: true,
onHide: () => ({}),

View File

@ -1,7 +1,7 @@
import { it, describe } from 'mocha';
import { expect } from 'chai';
import URI from 'urijs';
import { getExploreUrl } from '../../../javascripts/explorev2/exploreUtils.js';
import { getExploreUrl } from '../../../javascripts/explorev2/exploreUtils';
describe('utils', () => {
const formData = {
@ -19,41 +19,41 @@ describe('utils', () => {
compareURI(
URI(getExploreUrl(formData, 'base', false, 'http://superset.com')),
URI('/superset/explore/table/1/').search({ form_data: sFormData })
URI('/superset/explore/table/1/').search({ form_data: sFormData }),
);
});
it('getExploreUrl generates proper json url', () => {
compareURI(
URI(getExploreUrl(formData, 'json', false, 'superset.com')),
URI('/superset/explore_json/table/1/').search({ form_data: sFormData })
URI('/superset/explore_json/table/1/').search({ form_data: sFormData }),
);
});
it('getExploreUrl generates proper json forced url', () => {
compareURI(
URI(getExploreUrl(formData, 'json', true, 'superset.com')),
URI('/superset/explore_json/table/1/')
.search({ form_data: sFormData, force: 'true' })
.search({ form_data: sFormData, force: 'true' }),
);
});
it('getExploreUrl generates proper csv URL', () => {
compareURI(
URI(getExploreUrl(formData, 'csv', false, 'superset.com')),
URI('/superset/explore_json/table/1/')
.search({ form_data: sFormData, csv: 'true' })
.search({ form_data: sFormData, csv: 'true' }),
);
});
it('getExploreUrl generates proper standalone URL', () => {
compareURI(
URI(getExploreUrl(formData, 'standalone', false, 'superset.com')),
URI('/superset/explore/table/1/')
.search({ form_data: sFormData, standalone: 'true' })
.search({ form_data: sFormData, standalone: 'true' }),
);
});
it('getExploreUrl preserves main URLs params', () => {
compareURI(
URI(getExploreUrl(formData, 'json', false, 'superset.com?foo=bar')),
URI('/superset/explore_json/table/1/')
.search({ foo: 'bar', form_data: sFormData })
.search({ foo: 'bar', form_data: sFormData }),
);
});
});

View File

@ -1,10 +1,11 @@
import React from 'react';
import App from '../../../javascripts/profile/components/App';
import { Col, Row, Tab } from 'react-bootstrap';
import { mount } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { user } from './fixtures';
import App from '../../../javascripts/profile/components/App';
describe('App', () => {
const mockedProps = {
@ -12,7 +13,7 @@ describe('App', () => {
};
it('is valid', () => {
expect(
React.isValidElement(<App {...mockedProps} />)
React.isValidElement(<App {...mockedProps} />),
).to.equal(true);
});
it('renders 2 Col', () => {

View File

@ -1,10 +1,10 @@
import React from 'react';
import CreatedContent from '../../../javascripts/profile/components/CreatedContent';
import TableLoader from '../../../javascripts/profile/components/TableLoader';
import { mount } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { user } from './fixtures';
import CreatedContent from '../../../javascripts/profile/components/CreatedContent';
import TableLoader from '../../../javascripts/profile/components/TableLoader';
describe('CreatedContent', () => {
@ -13,7 +13,7 @@ describe('CreatedContent', () => {
};
it('is valid', () => {
expect(
React.isValidElement(<CreatedContent {...mockedProps} />)
React.isValidElement(<CreatedContent {...mockedProps} />),
).to.equal(true);
});
it('renders 2 TableLoader', () => {

View File

@ -1,10 +1,11 @@
import React from 'react';
import Favorites from '../../../javascripts/profile/components/Favorites';
import TableLoader from '../../../javascripts/profile/components/TableLoader';
import { mount } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { user } from './fixtures';
import Favorites from '../../../javascripts/profile/components/Favorites';
import TableLoader from '../../../javascripts/profile/components/TableLoader';
describe('Favorites', () => {
const mockedProps = {
@ -12,7 +13,7 @@ describe('Favorites', () => {
};
it('is valid', () => {
expect(
React.isValidElement(<Favorites {...mockedProps} />)
React.isValidElement(<Favorites {...mockedProps} />),
).to.equal(true);
});
it('renders 2 TableLoader', () => {

View File

@ -1,10 +1,11 @@
import React from 'react';
import RecentActivity from '../../../javascripts/profile/components/RecentActivity';
import TableLoader from '../../../javascripts/profile/components/TableLoader';
import { mount } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { user } from './fixtures';
import RecentActivity from '../../../javascripts/profile/components/RecentActivity';
import TableLoader from '../../../javascripts/profile/components/TableLoader';
describe('RecentActivity', () => {
@ -13,7 +14,7 @@ describe('RecentActivity', () => {
};
it('is valid', () => {
expect(
React.isValidElement(<RecentActivity {...mockedProps} />)
React.isValidElement(<RecentActivity {...mockedProps} />),
).to.equal(true);
});
it('renders a TableLoader', () => {

View File

@ -1,9 +1,10 @@
import React from 'react';
import Security from '../../../javascripts/profile/components/Security';
import { mount } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { user, userNoPerms } from './fixtures';
import Security from '../../../javascripts/profile/components/Security';
describe('Security', () => {
@ -12,7 +13,7 @@ describe('Security', () => {
};
it('is valid', () => {
expect(
React.isValidElement(<Security {...mockedProps} />)
React.isValidElement(<Security {...mockedProps} />),
).to.equal(true);
});
it('renders 2 role labels', () => {

View File

@ -1,11 +1,12 @@
import React from 'react';
import UserInfo from '../../../javascripts/profile/components/UserInfo';
import Gravatar from 'react-gravatar';
import { Panel } from 'react-bootstrap';
import { mount } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { user } from './fixtures';
import UserInfo from '../../../javascripts/profile/components/UserInfo';
describe('UserInfo', () => {
@ -14,7 +15,7 @@ describe('UserInfo', () => {
};
it('is valid', () => {
expect(
React.isValidElement(<UserInfo {...mockedProps} />)
React.isValidElement(<UserInfo {...mockedProps} />),
).to.equal(true);
});
it('renders a Gravatar', () => {

View File

@ -1,8 +1,8 @@
import React from 'react';
import AlertsWrapper from '../../../javascripts/components/AlertsWrapper';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import AlertsWrapper from '../../../javascripts/components/AlertsWrapper';
describe('AlertsWrapper', () => {
it('is valid', () => {

View File

@ -1,8 +1,8 @@
import React from 'react';
import App from '../../../javascripts/SqlLab/components/App';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import App from '../../../javascripts/SqlLab/components/App';
describe('App', () => {
it('is valid', () => {

View File

@ -1,10 +1,11 @@
import React from 'react';
import ColumnElement from '../../../javascripts/SqlLab/components/ColumnElement';
import { mockedActions, table } from './fixtures';
import { mount } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { mockedActions, table } from './fixtures';
import ColumnElement from '../../../javascripts/SqlLab/components/ColumnElement';
describe('ColumnElement', () => {
const mockedProps = {
@ -13,7 +14,7 @@ describe('ColumnElement', () => {
};
it('is valid with props', () => {
expect(
React.isValidElement(<ColumnElement {...mockedProps} />)
React.isValidElement(<ColumnElement {...mockedProps} />),
).to.equal(true);
});
it('renders a proper primary key', () => {

View File

@ -1,16 +1,17 @@
import React from 'react';
import CopyQueryTabUrl from '../../../javascripts/SqlLab/components/CopyQueryTabUrl';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { initialState } from './fixtures';
import CopyQueryTabUrl from '../../../javascripts/SqlLab/components/CopyQueryTabUrl';
describe('CopyQueryTabUrl', () => {
const mockedProps = {
queryEditor: initialState.queryEditors[0],
};
it('is valid with props', () => {
expect(
React.isValidElement(<CopyQueryTabUrl {...mockedProps} />)
React.isValidElement(<CopyQueryTabUrl {...mockedProps} />),
).to.equal(true);
});
});

View File

@ -1,11 +1,12 @@
import React from 'react';
import HighlightedSql from '../../../javascripts/SqlLab/components/HighlightedSql';
import ModalTrigger from '../../../javascripts/components/ModalTrigger';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { mount, shallow } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import HighlightedSql from '../../../javascripts/SqlLab/components/HighlightedSql';
import ModalTrigger from '../../../javascripts/components/ModalTrigger';
describe('HighlightedSql', () => {
const sql = "SELECT * FROM test WHERE something='fkldasjfklajdslfkjadlskfjkldasjfkladsjfkdjsa'";

Some files were not shown because too many files have changed in this diff Show More