mirror of
https://github.com/apache/superset.git
synced 2024-09-12 00:29:39 -04:00
Put data preview in south pane (#1486)
* Put data preview in south pane
Before: data preview of a selected table appears as a modal, but for
some cases users may want to view data and edit sql at the same time
After:
- data preview of a selected table pops up a new tab in South Pane
- data are saved to local state and flushed in global store in
ResultSet component
* Moved dataPreviewId to table object
* Put back preview icon for fetching preview data
* Revert "Put back preview icon for fetching preview data"
This reverts commit b6f5dcfe64
.
* Added option to retrieve preview results after refresh
This commit is contained in:
parent
757e7de60c
commit
69f0a4e1cb
@ -20,6 +20,7 @@ export const QUERY_EDITOR_SET_SQL = 'QUERY_EDITOR_SET_SQL';
|
|||||||
export const QUERY_EDITOR_SET_SELECTED_TEXT = 'QUERY_EDITOR_SET_SELECTED_TEXT';
|
export const QUERY_EDITOR_SET_SELECTED_TEXT = 'QUERY_EDITOR_SET_SELECTED_TEXT';
|
||||||
export const SET_DATABASES = 'SET_DATABASES';
|
export const SET_DATABASES = 'SET_DATABASES';
|
||||||
export const SET_ACTIVE_QUERY_EDITOR = 'SET_ACTIVE_QUERY_EDITOR';
|
export const SET_ACTIVE_QUERY_EDITOR = 'SET_ACTIVE_QUERY_EDITOR';
|
||||||
|
export const SET_ACTIVE_SOUTHPANE_TAB = 'SET_ACTIVE_SOUTHPANE_TAB';
|
||||||
export const ADD_ALERT = 'ADD_ALERT';
|
export const ADD_ALERT = 'ADD_ALERT';
|
||||||
export const REMOVE_ALERT = 'REMOVE_ALERT';
|
export const REMOVE_ALERT = 'REMOVE_ALERT';
|
||||||
export const REFRESH_QUERIES = 'REFRESH_QUERIES';
|
export const REFRESH_QUERIES = 'REFRESH_QUERIES';
|
||||||
@ -31,7 +32,8 @@ export const REQUEST_QUERY_RESULTS = 'REQUEST_QUERY_RESULTS';
|
|||||||
export const QUERY_SUCCESS = 'QUERY_SUCCESS';
|
export const QUERY_SUCCESS = 'QUERY_SUCCESS';
|
||||||
export const QUERY_FAILED = 'QUERY_FAILED';
|
export const QUERY_FAILED = 'QUERY_FAILED';
|
||||||
export const CLEAR_QUERY_RESULTS = 'CLEAR_QUERY_RESULTS';
|
export const CLEAR_QUERY_RESULTS = 'CLEAR_QUERY_RESULTS';
|
||||||
export const HIDE_DATA_PREVIEW = 'HIDE_DATA_PREVIEW';
|
export const REMOVE_DATA_PREVIEW = 'REMOVE_DATA_PREVIEW';
|
||||||
|
export const CHANGE_DATA_PREVIEW_ID = 'CHANGE_DATA_PREVIEW_ID';
|
||||||
|
|
||||||
export function resetState() {
|
export function resetState() {
|
||||||
return { type: RESET_STATE };
|
return { type: RESET_STATE };
|
||||||
@ -39,10 +41,11 @@ export function resetState() {
|
|||||||
|
|
||||||
export function startQuery(query) {
|
export function startQuery(query) {
|
||||||
Object.assign(query, {
|
Object.assign(query, {
|
||||||
id: shortid.generate(),
|
id: query.id ? query.id : shortid.generate(),
|
||||||
progress: 0,
|
progress: 0,
|
||||||
startDttm: now(),
|
startDttm: now(),
|
||||||
state: (query.runAsync) ? 'pending' : 'running',
|
state: (query.runAsync) ? 'pending' : 'running',
|
||||||
|
cached: false,
|
||||||
});
|
});
|
||||||
return { type: START_QUERY, query };
|
return { type: START_QUERY, query };
|
||||||
}
|
}
|
||||||
@ -63,8 +66,8 @@ export function clearQueryResults(query) {
|
|||||||
return { type: CLEAR_QUERY_RESULTS, query };
|
return { type: CLEAR_QUERY_RESULTS, query };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hideDataPreview() {
|
export function removeDataPreview(table) {
|
||||||
return { type: HIDE_DATA_PREVIEW };
|
return { type: REMOVE_DATA_PREVIEW, table };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function requestQueryResults(query) {
|
export function requestQueryResults(query) {
|
||||||
@ -166,6 +169,10 @@ export function setActiveQueryEditor(queryEditor) {
|
|||||||
return { type: SET_ACTIVE_QUERY_EDITOR, queryEditor };
|
return { type: SET_ACTIVE_QUERY_EDITOR, queryEditor };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setActiveSouthPaneTab(tabId) {
|
||||||
|
return { type: SET_ACTIVE_SOUTHPANE_TAB, tabId };
|
||||||
|
}
|
||||||
|
|
||||||
export function removeQueryEditor(queryEditor) {
|
export function removeQueryEditor(queryEditor) {
|
||||||
return { type: REMOVE_QUERY_EDITOR, queryEditor };
|
return { type: REMOVE_QUERY_EDITOR, queryEditor };
|
||||||
}
|
}
|
||||||
@ -198,22 +205,35 @@ export function queryEditorSetSelectedText(queryEditor, sql) {
|
|||||||
return { type: QUERY_EDITOR_SET_SELECTED_TEXT, queryEditor, sql };
|
return { type: QUERY_EDITOR_SET_SELECTED_TEXT, queryEditor, sql };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mergeTable(table) {
|
export function mergeTable(table, query) {
|
||||||
return { type: MERGE_TABLE, table };
|
return { type: MERGE_TABLE, table, query };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addTable(query, tableName) {
|
export function addTable(query, tableName) {
|
||||||
return function (dispatch) {
|
return function (dispatch) {
|
||||||
let url = `/caravel/table/${query.dbId}/${tableName}/${query.schema}/`;
|
let url = `/caravel/table/${query.dbId}/${tableName}/${query.schema}/`;
|
||||||
$.get(url, (data) => {
|
$.get(url, (data) => {
|
||||||
dispatch(
|
const dataPreviewQuery = {
|
||||||
mergeTable(Object.assign(data, {
|
id: shortid.generate(),
|
||||||
|
dbId: query.dbId,
|
||||||
|
sql: data.selectStar,
|
||||||
|
tableName,
|
||||||
|
sqlEditorId: null,
|
||||||
|
tab: '',
|
||||||
|
runAsync: false,
|
||||||
|
ctas: false,
|
||||||
|
};
|
||||||
|
// Merge table to tables in state
|
||||||
|
dispatch(mergeTable(
|
||||||
|
Object.assign(data, {
|
||||||
dbId: query.dbId,
|
dbId: query.dbId,
|
||||||
queryEditorId: query.id,
|
queryEditorId: query.id,
|
||||||
schema: query.schema,
|
schema: query.schema,
|
||||||
expanded: true,
|
expanded: true,
|
||||||
}))
|
}), dataPreviewQuery)
|
||||||
);
|
);
|
||||||
|
// Run query to get preview data for table
|
||||||
|
dispatch(runQuery(dataPreviewQuery));
|
||||||
})
|
})
|
||||||
.fail(() => {
|
.fail(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -238,6 +258,27 @@ export function addTable(query, tableName) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function changeDataPreviewId(oldQueryId, newQuery) {
|
||||||
|
return { type: CHANGE_DATA_PREVIEW_ID, oldQueryId, newQuery };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reFetchQueryResults(query) {
|
||||||
|
return function (dispatch) {
|
||||||
|
const newQuery = {
|
||||||
|
id: shortid.generate(),
|
||||||
|
dbId: query.dbId,
|
||||||
|
sql: query.sql,
|
||||||
|
tableName: query.tableName,
|
||||||
|
sqlEditorId: null,
|
||||||
|
tab: '',
|
||||||
|
runAsync: false,
|
||||||
|
ctas: false,
|
||||||
|
};
|
||||||
|
dispatch(runQuery(newQuery));
|
||||||
|
dispatch(changeDataPreviewId(query.id, newQuery));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function expandTable(table) {
|
export function expandTable(table) {
|
||||||
return { type: EXPAND_TABLE, table };
|
return { type: EXPAND_TABLE, table };
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import TabbedSqlEditors from './TabbedSqlEditors';
|
|||||||
import QueryAutoRefresh from './QueryAutoRefresh';
|
import QueryAutoRefresh from './QueryAutoRefresh';
|
||||||
import QuerySearch from './QuerySearch';
|
import QuerySearch from './QuerySearch';
|
||||||
import Alerts from './Alerts';
|
import Alerts from './Alerts';
|
||||||
import DataPreviewModal from './DataPreviewModal';
|
|
||||||
|
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
@ -49,7 +48,6 @@ class App extends React.PureComponent {
|
|||||||
return (
|
return (
|
||||||
<div className="App SqlLab">
|
<div className="App SqlLab">
|
||||||
<Alerts alerts={this.props.alerts} actions={this.props.actions} />
|
<Alerts alerts={this.props.alerts} actions={this.props.actions} />
|
||||||
<DataPreviewModal />
|
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
{content}
|
{content}
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,8 +31,20 @@ class ResultSet extends React.PureComponent {
|
|||||||
this.state = {
|
this.state = {
|
||||||
searchText: '',
|
searchText: '',
|
||||||
showModal: false,
|
showModal: false,
|
||||||
|
results: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
// when new results comes in, save them locally and clear in store
|
||||||
|
if ((!nextProps.query.cached)
|
||||||
|
&& nextProps.query.results
|
||||||
|
&& nextProps.query.results.data.length > 0) {
|
||||||
|
this.setState(
|
||||||
|
{ results: nextProps.query.results },
|
||||||
|
this.clearQueryResults(nextProps.query)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
getControls() {
|
getControls() {
|
||||||
if (this.props.search || this.props.visualize || this.props.csv) {
|
if (this.props.search || this.props.visualize || this.props.csv) {
|
||||||
let csvButton;
|
let csvButton;
|
||||||
@ -83,6 +95,9 @@ class ResultSet extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
return <div className="noControls" />;
|
return <div className="noControls" />;
|
||||||
}
|
}
|
||||||
|
clearQueryResults(query) {
|
||||||
|
this.props.actions.clearQueryResults(query);
|
||||||
|
}
|
||||||
popSelectStar() {
|
popSelectStar() {
|
||||||
const qe = {
|
const qe = {
|
||||||
id: shortid.generate(),
|
id: shortid.generate(),
|
||||||
@ -105,10 +120,14 @@ class ResultSet extends React.PureComponent {
|
|||||||
fetchResults(query) {
|
fetchResults(query) {
|
||||||
this.props.actions.fetchQueryResults(query);
|
this.props.actions.fetchQueryResults(query);
|
||||||
}
|
}
|
||||||
|
reFetchQueryResults(query) {
|
||||||
|
this.props.actions.reFetchQueryResults(query);
|
||||||
|
}
|
||||||
render() {
|
render() {
|
||||||
const query = this.props.query;
|
const query = this.props.query;
|
||||||
const results = query.results;
|
const results = (this.props.query.cached) ? this.state.results : query.results;
|
||||||
let sql;
|
let sql;
|
||||||
|
|
||||||
if (this.props.showSql) {
|
if (this.props.showSql) {
|
||||||
sql = <HighlightedSql sql={query.sql} />;
|
sql = <HighlightedSql sql={query.sql} />;
|
||||||
}
|
}
|
||||||
@ -181,6 +200,16 @@ class ResultSet extends React.PureComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (query.cached) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={this.reFetchQueryResults.bind(this, query)}
|
||||||
|
>
|
||||||
|
click to retrieve results
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (<Alert bsStyle="warning">The query returned no data</Alert>);
|
return (<Alert bsStyle="warning">The query returned no data</Alert>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,43 @@
|
|||||||
import { Alert, Tab, Tabs } from 'react-bootstrap';
|
import { Alert, Tab, Tabs } from 'react-bootstrap';
|
||||||
import QueryHistory from './QueryHistory';
|
import QueryHistory from './QueryHistory';
|
||||||
import ResultSet from './ResultSet';
|
import ResultSet from './ResultSet';
|
||||||
import { areArraysShallowEqual } from '../../reduxUtils';
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
import * as Actions from '../actions';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { areArraysShallowEqual } from '../../reduxUtils';
|
||||||
|
|
||||||
import shortid from 'shortid';
|
import shortid from 'shortid';
|
||||||
|
|
||||||
|
/*
|
||||||
|
editorQueries are queries executed by users passed from SqlEditor component
|
||||||
|
dataPrebiewQueries are all queries executed for preview of table data (from SqlEditorLeft)
|
||||||
|
*/
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
queries: React.PropTypes.array.isRequired,
|
editorQueries: React.PropTypes.array.isRequired,
|
||||||
|
dataPreviewQueries: React.PropTypes.array.isRequired,
|
||||||
actions: React.PropTypes.object.isRequired,
|
actions: React.PropTypes.object.isRequired,
|
||||||
|
activeSouthPaneTab: React.PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
activeSouthPaneTab: 'Results',
|
||||||
};
|
};
|
||||||
|
|
||||||
class SouthPane extends React.PureComponent {
|
class SouthPane extends React.PureComponent {
|
||||||
|
switchTab(id) {
|
||||||
|
this.props.actions.setActiveSouthPaneTab(id);
|
||||||
|
}
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps) {
|
||||||
return !areArraysShallowEqual(this.props.queries, nextProps.queries);
|
return !areArraysShallowEqual(this.props.editorQueries, nextProps.editorQueries)
|
||||||
|
|| !areArraysShallowEqual(this.props.dataPreviewQueries, nextProps.dataPreviewQueries)
|
||||||
|
|| this.props.activeSouthPaneTab !== nextProps.activeSouthPaneTab;
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
let latestQuery;
|
let latestQuery;
|
||||||
const props = this.props;
|
const props = this.props;
|
||||||
if (props.queries.length > 0) {
|
if (props.editorQueries.length > 0) {
|
||||||
latestQuery = props.queries[props.queries.length - 1];
|
latestQuery = props.editorQueries[props.editorQueries.length - 1];
|
||||||
}
|
}
|
||||||
let results;
|
let results;
|
||||||
if (latestQuery) {
|
if (latestQuery) {
|
||||||
@ -29,22 +47,59 @@ class SouthPane extends React.PureComponent {
|
|||||||
} else {
|
} else {
|
||||||
results = <Alert bsStyle="info">Run a query to display results here</Alert>;
|
results = <Alert bsStyle="info">Run a query to display results here</Alert>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dataPreviewTabs = props.dataPreviewQueries.map((query) => (
|
||||||
|
<Tab
|
||||||
|
title={`Preview for ${query.tableName}`}
|
||||||
|
eventKey={query.id}
|
||||||
|
key={query.id}
|
||||||
|
>
|
||||||
|
<ResultSet query={query} visualize={false} csv={false} actions={props.actions} />
|
||||||
|
</Tab>
|
||||||
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="SouthPane">
|
<div className="SouthPane">
|
||||||
<Tabs bsStyle="tabs" id={shortid.generate()}>
|
<Tabs
|
||||||
<Tab title="Results" eventKey={1}>
|
bsStyle="tabs"
|
||||||
|
id={shortid.generate()}
|
||||||
|
activeKey={this.props.activeSouthPaneTab}
|
||||||
|
onSelect={this.switchTab.bind(this)}
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
title="Results"
|
||||||
|
eventKey="Results"
|
||||||
|
>
|
||||||
<div style={{ overflow: 'auto' }}>
|
<div style={{ overflow: 'auto' }}>
|
||||||
{results}
|
{results}
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Query History" eventKey={2}>
|
<Tab
|
||||||
<QueryHistory queries={props.queries} actions={props.actions} />
|
title="Query History"
|
||||||
|
eventKey="History"
|
||||||
|
>
|
||||||
|
<QueryHistory queries={props.editorQueries} actions={props.actions} />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
{dataPreviewTabs}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SouthPane.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default SouthPane;
|
function mapStateToProps(state) {
|
||||||
|
return {
|
||||||
|
activeSouthPaneTab: state.activeSouthPaneTab,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch) {
|
||||||
|
return {
|
||||||
|
actions: bindActionCreators(Actions, dispatch),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SouthPane.propTypes = propTypes;
|
||||||
|
SouthPane.defaultProps = defaultProps;
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(SouthPane);
|
||||||
|
@ -25,7 +25,8 @@ const propTypes = {
|
|||||||
latestQuery: React.PropTypes.object,
|
latestQuery: React.PropTypes.object,
|
||||||
networkOn: React.PropTypes.bool,
|
networkOn: React.PropTypes.bool,
|
||||||
tables: React.PropTypes.array.isRequired,
|
tables: React.PropTypes.array.isRequired,
|
||||||
queries: React.PropTypes.array.isRequired,
|
editorQueries: React.PropTypes.array.isRequired,
|
||||||
|
dataPreviewQueries: React.PropTypes.array.isRequired,
|
||||||
queryEditor: React.PropTypes.object.isRequired,
|
queryEditor: React.PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,6 +71,7 @@ class SqlEditor extends React.PureComponent {
|
|||||||
ctas,
|
ctas,
|
||||||
};
|
};
|
||||||
this.props.actions.runQuery(query);
|
this.props.actions.runQuery(query);
|
||||||
|
this.props.actions.setActiveSouthPaneTab('Results');
|
||||||
}
|
}
|
||||||
stopQuery() {
|
stopQuery() {
|
||||||
this.props.actions.stopQuery(this.props.latestQuery);
|
this.props.actions.stopQuery(this.props.latestQuery);
|
||||||
@ -224,7 +226,8 @@ class SqlEditor extends React.PureComponent {
|
|||||||
{editorBottomBar}
|
{editorBottomBar}
|
||||||
<br />
|
<br />
|
||||||
<SouthPane
|
<SouthPane
|
||||||
queries={this.props.queries}
|
editorQueries={this.props.editorQueries}
|
||||||
|
dataPreviewQueries={this.props.dataPreviewQueries}
|
||||||
actions={this.props.actions}
|
actions={this.props.actions}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -118,6 +118,15 @@ class TabbedSqlEditors extends React.PureComponent {
|
|||||||
database = this.props.databases[qe.dbId];
|
database = this.props.databases[qe.dbId];
|
||||||
}
|
}
|
||||||
const state = (latestQuery) ? latestQuery.state : '';
|
const state = (latestQuery) ? latestQuery.state : '';
|
||||||
|
|
||||||
|
const dataPreviewQueries = [];
|
||||||
|
this.props.tables.forEach((table) => {
|
||||||
|
const queryId = table.dataPreviewQueryId;
|
||||||
|
if (queryId && this.props.queries[queryId]) {
|
||||||
|
dataPreviewQueries.push(this.props.queries[queryId]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const tabTitle = (
|
const tabTitle = (
|
||||||
<div>
|
<div>
|
||||||
<div className={'circle ' + state} /> {qe.title} {' '}
|
<div className={'circle ' + state} /> {qe.title} {' '}
|
||||||
@ -152,7 +161,8 @@ class TabbedSqlEditors extends React.PureComponent {
|
|||||||
<SqlEditor
|
<SqlEditor
|
||||||
tables={this.props.tables.filter((t) => (t.queryEditorId === qe.id))}
|
tables={this.props.tables.filter((t) => (t.queryEditorId === qe.id))}
|
||||||
queryEditor={qe}
|
queryEditor={qe}
|
||||||
queries={this.state.queriesArray}
|
editorQueries={this.state.queriesArray}
|
||||||
|
dataPreviewQueries={dataPreviewQueries}
|
||||||
latestQuery={latestQuery}
|
latestQuery={latestQuery}
|
||||||
database={database}
|
database={database}
|
||||||
actions={this.props.actions}
|
actions={this.props.actions}
|
||||||
|
@ -51,18 +51,7 @@ class TableElement extends React.PureComponent {
|
|||||||
|
|
||||||
removeTable() {
|
removeTable() {
|
||||||
this.setState({ expanded: false });
|
this.setState({ expanded: false });
|
||||||
}
|
this.props.actions.removeDataPreview(this.props.table);
|
||||||
dataPreviewModal() {
|
|
||||||
const query = {
|
|
||||||
dbId: this.props.table.dbId,
|
|
||||||
sql: this.props.table.selectStar,
|
|
||||||
tableName: this.props.table.name,
|
|
||||||
sqlEditorId: null,
|
|
||||||
tab: '',
|
|
||||||
runAsync: false,
|
|
||||||
ctas: false,
|
|
||||||
};
|
|
||||||
this.props.actions.runQuery(query);
|
|
||||||
}
|
}
|
||||||
toggleSortColumns() {
|
toggleSortColumns() {
|
||||||
this.setState({ sortColumns: !this.state.sortColumns });
|
this.setState({ sortColumns: !this.state.sortColumns });
|
||||||
@ -192,12 +181,6 @@ class TableElement extends React.PureComponent {
|
|||||||
'Original table column order'}
|
'Original table column order'}
|
||||||
href="#"
|
href="#"
|
||||||
/>
|
/>
|
||||||
<Link
|
|
||||||
className="fa fa-search-plus pull-left m-l-2"
|
|
||||||
onClick={this.dataPreviewModal.bind(this)}
|
|
||||||
tooltip="Data preview"
|
|
||||||
href="#"
|
|
||||||
/>
|
|
||||||
{table.selectStar &&
|
{table.selectStar &&
|
||||||
<CopyToClipboard
|
<CopyToClipboard
|
||||||
copyNode={
|
copyNode={
|
||||||
@ -211,7 +194,7 @@ class TableElement extends React.PureComponent {
|
|||||||
<Link
|
<Link
|
||||||
className="fa fa-trash table-remove pull-left m-l-2"
|
className="fa fa-trash table-remove pull-left m-l-2"
|
||||||
onClick={this.removeTable.bind(this)}
|
onClick={this.removeTable.bind(this)}
|
||||||
tooltip="Remove from panel"
|
tooltip="Remove table preview"
|
||||||
href="#"
|
href="#"
|
||||||
/>
|
/>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
@ -16,8 +16,6 @@ export const defaultQueryEditor = {
|
|||||||
|
|
||||||
export const initialState = {
|
export const initialState = {
|
||||||
alerts: [],
|
alerts: [],
|
||||||
showDataPreviewModal: false,
|
|
||||||
dataPreviewQueryId: null,
|
|
||||||
networkOn: true,
|
networkOn: true,
|
||||||
queries: {},
|
queries: {},
|
||||||
databases: {},
|
databases: {},
|
||||||
@ -25,6 +23,7 @@ export const initialState = {
|
|||||||
tabHistory: [defaultQueryEditor.id],
|
tabHistory: [defaultQueryEditor.id],
|
||||||
tables: [],
|
tables: [],
|
||||||
queriesLastUpdate: 0,
|
queriesLastUpdate: 0,
|
||||||
|
activeSouthPaneTab: 'Results',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sqlLabReducer = function (state, action) {
|
export const sqlLabReducer = function (state, action) {
|
||||||
@ -86,19 +85,44 @@ export const sqlLabReducer = function (state, action) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (existingTable) {
|
if (existingTable) {
|
||||||
|
if (action.query) {
|
||||||
|
at.dataPreviewQueryId = action.query.id;
|
||||||
|
}
|
||||||
return alterInArr(state, 'tables', existingTable, at);
|
return alterInArr(state, 'tables', existingTable, at);
|
||||||
}
|
}
|
||||||
at.id = shortid.generate();
|
at.id = shortid.generate();
|
||||||
return addToArr(state, 'tables', at);
|
// for new table, associate Id of query for data preview
|
||||||
|
at.dataPreviewQueryId = null;
|
||||||
|
let newState = addToArr(state, 'tables', at);
|
||||||
|
if (action.query) {
|
||||||
|
newState = alterInArr(newState, 'tables', at, { dataPreviewQueryId: action.query.id });
|
||||||
|
}
|
||||||
|
return newState;
|
||||||
},
|
},
|
||||||
[actions.EXPAND_TABLE]() {
|
[actions.EXPAND_TABLE]() {
|
||||||
return alterInArr(state, 'tables', action.table, { expanded: true });
|
return alterInArr(state, 'tables', action.table, { expanded: true });
|
||||||
},
|
},
|
||||||
[actions.HIDE_DATA_PREVIEW]() {
|
[actions.REMOVE_DATA_PREVIEW]() {
|
||||||
const queries = Object.assign({}, state.queries);
|
const queries = Object.assign({}, state.queries);
|
||||||
delete queries[state.dataPreviewQueryId];
|
delete queries[action.table.dataPreviewQueryId];
|
||||||
|
const newState = alterInArr(state, 'tables', action.table, { dataPreviewQueryId: null });
|
||||||
return Object.assign(
|
return Object.assign(
|
||||||
{}, state, { showDataPreviewModal: false, queries, dataPreviewQueryId: null });
|
{}, newState, { queries });
|
||||||
|
},
|
||||||
|
[actions.CHANGE_DATA_PREVIEW_ID]() {
|
||||||
|
const queries = Object.assign({}, state.queries);
|
||||||
|
delete queries[action.oldQueryId];
|
||||||
|
|
||||||
|
const newTables = [];
|
||||||
|
state.tables.forEach((t) => {
|
||||||
|
if (t.dataPreviewQueryId === action.oldQueryId) {
|
||||||
|
newTables.push(Object.assign({}, t, { dataPreviewQueryId: action.newQuery.id }));
|
||||||
|
} else {
|
||||||
|
newTables.push(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Object.assign(
|
||||||
|
{}, state, { queries, tables: newTables, activeSouthPaneTab: action.newQuery.id });
|
||||||
},
|
},
|
||||||
[actions.COLLAPSE_TABLE]() {
|
[actions.COLLAPSE_TABLE]() {
|
||||||
return alterInArr(state, 'tables', action.table, { expanded: false });
|
return alterInArr(state, 'tables', action.table, { expanded: false });
|
||||||
@ -116,8 +140,7 @@ export const sqlLabReducer = function (state, action) {
|
|||||||
newState = Object.assign({}, state, { queries });
|
newState = Object.assign({}, state, { queries });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newState.dataPreviewQueryId = action.query.id;
|
newState.activeSouthPaneTab = action.query.id;
|
||||||
newState.showDataPreviewModal = true;
|
|
||||||
}
|
}
|
||||||
newState = addToObject(newState, 'queries', action.query);
|
newState = addToObject(newState, 'queries', action.query);
|
||||||
const sqlEditor = { id: action.query.sqlEditorId };
|
const sqlEditor = { id: action.query.sqlEditorId };
|
||||||
@ -127,7 +150,7 @@ export const sqlLabReducer = function (state, action) {
|
|||||||
return alterInObject(state, 'queries', action.query, { state: 'stopped' });
|
return alterInObject(state, 'queries', action.query, { state: 'stopped' });
|
||||||
},
|
},
|
||||||
[actions.CLEAR_QUERY_RESULTS]() {
|
[actions.CLEAR_QUERY_RESULTS]() {
|
||||||
return alterInObject(state, 'queries', action.query, { results: [] });
|
return alterInObject(state, 'queries', action.query, { results: [], cached: true });
|
||||||
},
|
},
|
||||||
[actions.REQUEST_QUERY_RESULTS]() {
|
[actions.REQUEST_QUERY_RESULTS]() {
|
||||||
return alterInObject(state, 'queries', action.query, { state: 'fetching' });
|
return alterInObject(state, 'queries', action.query, { state: 'fetching' });
|
||||||
@ -144,6 +167,7 @@ export const sqlLabReducer = function (state, action) {
|
|||||||
rows,
|
rows,
|
||||||
state: 'success',
|
state: 'success',
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
|
cached: false,
|
||||||
};
|
};
|
||||||
return alterInObject(state, 'queries', action.query, alts);
|
return alterInObject(state, 'queries', action.query, alts);
|
||||||
},
|
},
|
||||||
@ -160,6 +184,9 @@ export const sqlLabReducer = function (state, action) {
|
|||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
},
|
},
|
||||||
|
[actions.SET_ACTIVE_SOUTHPANE_TAB]() {
|
||||||
|
return Object.assign({}, state, { activeSouthPaneTab: action.tabId });
|
||||||
|
},
|
||||||
[actions.QUERY_EDITOR_SETDB]() {
|
[actions.QUERY_EDITOR_SETDB]() {
|
||||||
return alterInArr(state, 'queryEditors', action.queryEditor, { dbId: action.dbId });
|
return alterInArr(state, 'queryEditors', action.queryEditor, { dbId: action.dbId });
|
||||||
},
|
},
|
||||||
|
@ -24,9 +24,9 @@ describe('TableElement', () => {
|
|||||||
React.isValidElement(<TableElement {...mockedProps} />)
|
React.isValidElement(<TableElement {...mockedProps} />)
|
||||||
).to.equal(true);
|
).to.equal(true);
|
||||||
});
|
});
|
||||||
it('has 3 Link elements', () => {
|
it('has 2 Link elements', () => {
|
||||||
const wrapper = shallow(<TableElement {...mockedProps} />);
|
const wrapper = shallow(<TableElement {...mockedProps} />);
|
||||||
expect(wrapper.find(Link)).to.have.length(3);
|
expect(wrapper.find(Link)).to.have.length(2);
|
||||||
});
|
});
|
||||||
it('has 14 columns', () => {
|
it('has 14 columns', () => {
|
||||||
const wrapper = shallow(<TableElement {...mockedProps} />);
|
const wrapper = shallow(<TableElement {...mockedProps} />);
|
||||||
|
@ -11,6 +11,7 @@ export const table = {
|
|||||||
schema: 'caravel',
|
schema: 'caravel',
|
||||||
name: 'ab_user',
|
name: 'ab_user',
|
||||||
id: 'r11Vgt60',
|
id: 'r11Vgt60',
|
||||||
|
dataPreviewQueryId: null,
|
||||||
partitions: {
|
partitions: {
|
||||||
cols: ['username'],
|
cols: ['username'],
|
||||||
latest: 'bob',
|
latest: 'bob',
|
||||||
@ -183,6 +184,7 @@ export const queries = [
|
|||||||
tab: 'Demo',
|
tab: 'Demo',
|
||||||
runAsync: false,
|
runAsync: false,
|
||||||
ctas: false,
|
ctas: false,
|
||||||
|
cached: false,
|
||||||
id: 'BkA1CLrJg',
|
id: 'BkA1CLrJg',
|
||||||
progress: 100,
|
progress: 100,
|
||||||
startDttm: 1476910566092.96,
|
startDttm: 1476910566092.96,
|
||||||
@ -217,6 +219,7 @@ export const queries = [
|
|||||||
tab: 'Demo',
|
tab: 'Demo',
|
||||||
runAsync: true,
|
runAsync: true,
|
||||||
ctas: false,
|
ctas: false,
|
||||||
|
cached: false,
|
||||||
id: 'S1zeAISkx',
|
id: 'S1zeAISkx',
|
||||||
progress: 100,
|
progress: 100,
|
||||||
startDttm: 1476910570802.2,
|
startDttm: 1476910570802.2,
|
||||||
@ -247,8 +250,6 @@ export const queries = [
|
|||||||
|
|
||||||
export const initialState = {
|
export const initialState = {
|
||||||
alerts: [],
|
alerts: [],
|
||||||
showDataPreviewModal: false,
|
|
||||||
dataPreviewQueryId: null,
|
|
||||||
networkOn: true,
|
networkOn: true,
|
||||||
queries: {},
|
queries: {},
|
||||||
databases: {},
|
databases: {},
|
||||||
@ -257,6 +258,7 @@ export const initialState = {
|
|||||||
tables: [],
|
tables: [],
|
||||||
workspaceQueries: [],
|
workspaceQueries: [],
|
||||||
queriesLastUpdate: 0,
|
queriesLastUpdate: 0,
|
||||||
|
activeSouthPaneTab: 'Results',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const query = {
|
export const query = {
|
||||||
@ -267,4 +269,5 @@ export const query = {
|
|||||||
tempTableName: null,
|
tempTableName: null,
|
||||||
runAsync: false,
|
runAsync: false,
|
||||||
ctas: false,
|
ctas: false,
|
||||||
|
cached: false,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user