mirror of https://github.com/apache/superset.git
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:
parent
a2b30f35fc
commit
366ecefbaa
|
@ -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]
|
||||
|
|
|
@ -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';"
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
})
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ export default class AlertsWrapper extends React.PureComponent {
|
|||
render() {
|
||||
return (
|
||||
<AlertContainer
|
||||
ref={ref => {
|
||||
ref={(ref) => {
|
||||
global.notify = ref;
|
||||
}}
|
||||
offset={14}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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`);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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>
|
||||
<i className="fa fa-code" />
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
|
|
@ -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" />}
|
||||
/>
|
||||
|
||||
<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>
|
||||
<i className="fa fa-link" />
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -77,7 +77,7 @@ export const exploreReducer = function (state, action) {
|
|||
{
|
||||
chartStatus: 'success',
|
||||
queryResponse: action.queryResponse,
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
[actions.CHART_UPDATE_STARTED]() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -9,7 +9,7 @@ function UTC(dttm) {
|
|||
dttm.getUTCDate(),
|
||||
dttm.getUTCHours(),
|
||||
dttm.getUTCMinutes(),
|
||||
dttm.getUTCSeconds()
|
||||
dttm.getUTCSeconds(),
|
||||
);
|
||||
}
|
||||
export const tickMultiFormat = d3.time.format.multi([
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -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()],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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]}>
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -11,7 +11,7 @@ describe('CopyToClipboard', () => {
|
|||
|
||||
it('renders', () => {
|
||||
expect(
|
||||
React.isValidElement(<CopyToClipboard {...defaultProps} />)
|
||||
React.isValidElement(<CopyToClipboard {...defaultProps} />),
|
||||
).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ describe('ModalTrigger', () => {
|
|||
|
||||
it('is a valid element', () => {
|
||||
expect(
|
||||
React.isValidElement(<ModalTrigger {...defaultProps} />)
|
||||
React.isValidElement(<ModalTrigger {...defaultProps} />),
|
||||
).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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 = {
|
|
@ -18,7 +18,7 @@ describe('ExploreActionButtons', () => {
|
|||
|
||||
it('renders', () => {
|
||||
expect(
|
||||
React.isValidElement(<ExploreActionButtons {...defaultProps} />)
|
||||
React.isValidElement(<ExploreActionButtons {...defaultProps} />),
|
||||
).to.equal(true);
|
||||
});
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ describe('FilterControl', () => {
|
|||
|
||||
it('renders Filters', () => {
|
||||
expect(
|
||||
React.isValidElement(<FilterControl {...defaultProps} />)
|
||||
React.isValidElement(<FilterControl {...defaultProps} />),
|
||||
).to.equal(true);
|
||||
});
|
||||
|
|
@ -34,7 +34,7 @@ describe('Filter', () => {
|
|||
|
||||
it('renders Filters', () => {
|
||||
expect(
|
||||
React.isValidElement(<Filter {...defaultProps} />)
|
||||
React.isValidElement(<Filter {...defaultProps} />),
|
||||
).to.equal(true);
|
||||
});
|
||||
|
|
@ -24,7 +24,7 @@ describe('RunQueryActionButton', () => {
|
|||
|
||||
it('is a valid react element', () => {
|
||||
expect(
|
||||
React.isValidElement(<RunQueryActionButton {...defaultProps} />)
|
||||
React.isValidElement(<RunQueryActionButton {...defaultProps} />),
|
||||
).to.equal(true);
|
||||
});
|
||||
|
|
@ -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: () => ({}),
|
|
@ -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 }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue