Moved dataPreviewId to table object

This commit is contained in:
Vera Liu 2016-10-31 15:31:37 -07:00
parent 902d26bba8
commit 56f5b736dd
9 changed files with 133 additions and 86 deletions

View File

@ -32,8 +32,7 @@ export const REQUEST_QUERY_RESULTS = 'REQUEST_QUERY_RESULTS';
export const QUERY_SUCCESS = 'QUERY_SUCCESS';
export const QUERY_FAILED = 'QUERY_FAILED';
export const CLEAR_QUERY_RESULTS = 'CLEAR_QUERY_RESULTS';
export const HIDE_DATA_PREVIEW = 'HIDE_DATA_PREVIEW';
export const CLOSE_DATA_PREVIEW = 'CLOSE_DATA_PREVIEW';
export const REMOVE_DATA_PREVIEW = 'REMOVE_DATA_PREVIEW';
export function resetState() {
return { type: RESET_STATE };
@ -66,12 +65,8 @@ export function clearQueryResults(query) {
return { type: CLEAR_QUERY_RESULTS, query };
}
export function hideDataPreview() {
return { type: HIDE_DATA_PREVIEW };
}
export function closeDataPreview(dataPreviewQueryId) {
return { type: CLOSE_DATA_PREVIEW, dataPreviewQueryId };
export function removeDataPreview(table) {
return { type: REMOVE_DATA_PREVIEW, table };
}
export function requestQueryResults(query) {
@ -209,21 +204,33 @@ export function queryEditorSetSelectedText(queryEditor, sql) {
return { type: QUERY_EDITOR_SET_SELECTED_TEXT, queryEditor, sql };
}
export function mergeTable(table) {
return { type: MERGE_TABLE, table };
export function mergeTable(table, query) {
return { type: MERGE_TABLE, table, query };
}
export function addTable(query, tableName) {
return function (dispatch) {
let url = `/caravel/table/${query.dbId}/${tableName}/${query.schema}/`;
$.get(url, (data) => {
dispatch(
mergeTable(Object.assign(data, {
const dataPreviewQuery = {
dbId: query.dbId,
sql: data.selectStar,
tableName,
sqlEditorId: null,
tab: '',
runAsync: false,
ctas: false,
};
// Run query to get preview data for table
dispatch(runQuery(dataPreviewQuery));
// Merge table to tables in state
dispatch(mergeTable(
Object.assign(data, {
dbId: query.dbId,
queryEditorId: query.id,
schema: query.schema,
expanded: true,
}))
}), dataPreviewQuery)
);
})
.fail(() => {

View File

@ -0,0 +1,58 @@
import * as Actions from '../actions';
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Modal } from 'react-bootstrap';
import ResultSet from './ResultSet';
const propTypes = {
queries: React.PropTypes.object,
actions: React.PropTypes.object,
showDataPreviewModal: React.PropTypes.bool,
dataPreviewQueryId: React.PropTypes.string,
};
class DataPreviewModal extends React.PureComponent {
hide() {
this.props.actions.hideDataPreview();
}
render() {
if (this.props.showDataPreviewModal && this.props.dataPreviewQueryId) {
const query = this.props.queries[this.props.dataPreviewQueryId];
return (
<Modal
show={this.props.showDataPreviewModal}
onHide={this.hide.bind(this)}
bsStyle="lg"
>
<Modal.Header closeButton>
<Modal.Title>
Data preview for <strong>{query.tableName}</strong>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<ResultSet query={query} visualize={false} csv={false} actions={this.props.actions} />
</Modal.Body>
</Modal>
);
}
return null;
}
}
DataPreviewModal.propTypes = propTypes;
function mapStateToProps(state) {
return {
queries: state.queries,
showDataPreviewModal: state.showDataPreviewModal,
dataPreviewQueryId: state.dataPreviewQueryId,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(DataPreviewModal);

View File

@ -5,33 +5,34 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import React from 'react';
import { areArraysShallowEqual } from '../../reduxUtils';
import shortid from 'shortid';
// editorQueries are queries executed by users
// passed from SqlEditor component
// queries are all queries executed
// passed from global state for the purpose of data preview tabs
/*
editorQueries are queries executed by users passed from SqlEditor component
dataPrebiewQueries are all queries executed for preview of table data (from SqlEditorLeft)
*/
const propTypes = {
editorQueries: React.PropTypes.array.isRequired,
queries: React.PropTypes.object.isRequired,
dataPreviewQueries: React.PropTypes.array.isRequired,
actions: React.PropTypes.object.isRequired,
dataPreviewQueryIds: React.PropTypes.array,
activeSouthPaneTab: React.PropTypes.string,
};
const defaultProps = {
dataPreviewQueryIds: [],
activeSouthPaneTab: 'Results',
};
class SouthPane extends React.PureComponent {
closeDataPreviewTab(id) {
this.props.actions.closeDataPreview(id);
}
switchTab(id) {
this.props.actions.setActiveSouthPaneTab(id);
}
shouldComponentUpdate(nextProps) {
return !areArraysShallowEqual(this.props.editorQueries, nextProps.editorQueries)
|| !areArraysShallowEqual(this.props.dataPreviewQueries, nextProps.dataPreviewQueries)
|| this.props.activeSouthPaneTab !== nextProps.activeSouthPaneTab;
}
render() {
let latestQuery;
const props = this.props;
@ -47,24 +48,15 @@ class SouthPane extends React.PureComponent {
results = <Alert bsStyle="info">Run a query to display results here</Alert>;
}
const dataPreviewTabs = props.dataPreviewQueryIds.map((id) => {
const query = props.queries[id];
const tabTitle = (
<div>
{query.tableName}
<i className="fa fa-close" onClick={this.closeDataPreviewTab.bind(this, id)} />
</div>
);
return (
<Tab
title={tabTitle}
eventKey={id}
key={id}
>
<ResultSet query={query} visualize={false} csv={false} actions={props.actions} />
</Tab>
);
});
const dataPreviewTabs = props.dataPreviewQueries.map((query) => (
<Tab
title={query.tableName}
eventKey={query.id}
key={query.id}
>
<ResultSet query={query} visualize={false} csv={false} actions={props.actions} />
</Tab>
));
return (
<div className="SouthPane">
@ -97,9 +89,7 @@ class SouthPane extends React.PureComponent {
function mapStateToProps(state) {
return {
dataPreviewQueryIds: state.dataPreviewQueryIds,
activeSouthPaneTab: state.activeSouthPaneTab,
queries: state.queries,
};
}

View File

@ -25,7 +25,8 @@ const propTypes = {
latestQuery: React.PropTypes.object,
networkOn: React.PropTypes.bool,
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,
};
@ -225,7 +226,8 @@ class SqlEditor extends React.PureComponent {
{editorBottomBar}
<br />
<SouthPane
editorQueries={this.props.queries}
editorQueries={this.props.editorQueries}
dataPreviewQueries={this.props.dataPreviewQueries}
actions={this.props.actions}
/>
</Col>

View File

@ -118,6 +118,15 @@ class TabbedSqlEditors extends React.PureComponent {
database = this.props.databases[qe.dbId];
}
const state = (latestQuery) ? latestQuery.state : '';
const dataPreviewQueries = [];
this.props.tables.forEach((table) => {
const queryId = table.dataPreviewQueryId;
if (queryId) {
dataPreviewQueries.push(this.props.queries[queryId]);
}
});
const tabTitle = (
<div>
<div className={'circle ' + state} /> {qe.title} {' '}
@ -152,7 +161,8 @@ class TabbedSqlEditors extends React.PureComponent {
<SqlEditor
tables={this.props.tables.filter((t) => (t.queryEditorId === qe.id))}
queryEditor={qe}
queries={this.state.queriesArray}
editorQueries={this.state.queriesArray}
dataPreviewQueries={dataPreviewQueries}
latestQuery={latestQuery}
database={database}
actions={this.props.actions}

View File

@ -51,18 +51,7 @@ class TableElement extends React.PureComponent {
removeTable() {
this.setState({ expanded: false });
}
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);
this.props.actions.removeDataPreview(this.props.table);
}
toggleSortColumns() {
this.setState({ sortColumns: !this.state.sortColumns });
@ -192,12 +181,6 @@ class TableElement extends React.PureComponent {
'Original table column order'}
href="#"
/>
<Link
className="fa fa-search-plus pull-left m-l-2"
onClick={this.dataPreviewModal.bind(this)}
tooltip="Data preview"
href="#"
/>
{table.selectStar &&
<CopyToClipboard
copyNode={
@ -211,7 +194,7 @@ class TableElement extends React.PureComponent {
<Link
className="fa fa-trash table-remove pull-left m-l-2"
onClick={this.removeTable.bind(this)}
tooltip="Remove from panel"
tooltip="Remove table preview"
href="#"
/>
</ButtonGroup>

View File

@ -23,7 +23,6 @@ export const initialState = {
tabHistory: [defaultQueryEditor.id],
tables: [],
queriesLastUpdate: 0,
dataPreviewQueryIds: [],
activeSouthPaneTab: 'Results',
};
@ -86,29 +85,29 @@ export const sqlLabReducer = function (state, action) {
}
});
if (existingTable) {
if (action.query) {
at.dataPreviewQueryId = action.query.id;
}
return alterInArr(state, 'tables', existingTable, at);
}
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]() {
return alterInArr(state, 'tables', action.table, { expanded: true });
},
[actions.CLOSE_DATA_PREVIEW]() {
[actions.REMOVE_DATA_PREVIEW]() {
const queries = Object.assign({}, state.queries);
const newDataPreviewQueryIds = [];
delete queries[action.dataPreviewQueryId];
state.dataPreviewQueryIds.forEach((id) => {
if (action.dataPreviewQueryId !== id) {
newDataPreviewQueryIds.push(id);
}
});
delete queries[action.table.dataPreviewQueryId];
const newState = alterInArr(state, 'tables', action.table, { dataPreviewQueryId: null });
return Object.assign(
{}, state, {
queries,
dataPreviewQueryIds: newDataPreviewQueryIds,
activeSouthPaneTab: 'Results',
});
{}, newState, { queries });
},
[actions.COLLAPSE_TABLE]() {
return alterInArr(state, 'tables', action.table, { expanded: false });
@ -126,9 +125,6 @@ export const sqlLabReducer = function (state, action) {
newState = Object.assign({}, state, { queries });
}
} else {
const newIds = state.dataPreviewQueryIds;
newIds.push(action.query.id);
newState.dataPreviewQueryIds = newIds;
newState.activeSouthPaneTab = action.query.id;
}
newState = addToObject(newState, 'queries', action.query);

View File

@ -24,9 +24,9 @@ describe('TableElement', () => {
React.isValidElement(<TableElement {...mockedProps} />)
).to.equal(true);
});
it('has 3 Link elements', () => {
it('has 2 Link elements', () => {
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', () => {
const wrapper = shallow(<TableElement {...mockedProps} />);

View File

@ -11,6 +11,7 @@ export const table = {
schema: 'caravel',
name: 'ab_user',
id: 'r11Vgt60',
dataPreviewQueryId: null,
partitions: {
cols: ['username'],
latest: 'bob',
@ -255,8 +256,8 @@ export const initialState = {
queryEditors: [defaultQueryEditor],
tabHistory: [defaultQueryEditor.id],
tables: [],
workspaceQueries: [],
queriesLastUpdate: 0,
dataPreviewQueryIds: [],
activeSouthPaneTab: 'Results',
};